I have a script attached to an UI gameobject that find and get reference to the canvas root gameobject. Right now i keep refreshing the reference every update() in case the gameobject is moved to other place in hierarchy and the canvas root changed .
But i found it performance heavy for my script to keep running GetComponentInParent<Canvas>().rootCanvas every single update() especially when the object is at the bottom of a hierarchy with 1000+ gameobject. So i want my script to only find root canvas at start() and when the object hierarchy changed.
I've found https://docs.unity3d.com/ScriptReference/EditorApplication-hierarchyChanged.html but it is editor-only and won't follow after build. Is there any way to do something similar to OnHierarchyChanged() ? Also using loop to check the current state of hierarchy is out of option .
Some ideas:
First one, GetComponentInParent<Canvas> is expensive, but what
about simply call Transform.parent? You only need to instantiate
parent reference at the begining, and even if you check it on
Update, is less expensive.
Second one, if you know (and you should know) which are the events
that changes your hierarchy, you can create your own delegate,
event, action whatever to track it.
And finally, I'm not sure about this one, but have you checked
Transform.hasChanged? I think this last one won't work, cause
only affect rotation, position etc...but I can't assure it right
now.
To exemplify Idea 2 (I think the other 2 are pretty clear):
using UnityEngine;
using System.Collections;
public class ClassThatCanChangeHierarchy : MonoBehaviour
{
private List<GameObject> objectsThatWantToKnow = new List<GameObject>();
private void MethodThatChangeHierarchy()
{
//your code that affects hierarchy...
foreach(GameObject go in objectsThatWantToKnow)
{
go.GetComponent<ClassThatWantsToKnowWhenHierarchyChanges>().OnHierarchyChange?.Invoke();
}
}
}
using UnityEngine;
using System;
public class ClassThatWantsToKnowWhenHierarchyChanges : MonoBehaviour
{
public Action OnHierarchyChange = null;
private void Awake()
{
OnHierarchyChange = () => Debug.log("hierarchy has changed");
}
}
Related
When I put this script into the object, a cube, it does not register the platform it hits beneath when I make the gameObject the platform. I'm trying to get the two objects to detect collision between each other but it does not seem to work. Neither objects are trigger checked in the Box Colliders. The cube has a rigid body with gravity checked but the platform does not. This is a 3D game.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Collision : MonoBehaviour
{
public Transform gameObject;
void onCollisionEnter(Collision col)
{
if (col.gameObject);
{
Debug.Log("Yes");
}
}
}
As Sven Viking already said, you have to change the syntax from onCollisionEnter to OnCollision enter. Also, let's take a look at your logic:
You want to check that, if your cube object collides with another object, and if that object is the same as the one you're referencing, you want to see a log of that in your console. Currently, however, you're asking for something else.
First of all, your platform is not stored as a gameObject, it's stored as a Transform. Those two are different things, the Transform stores the position/rotation/scale of an object, while 'GameObject' is the entire object, includng its Transform. Also, you named your variable gameObject, which you should never do - Never name variables with the same name as existing elements.
'col' represents the collision. col.gameObject is the gameObject of that collision. There's no logic demanded there. You're just making a reference. What you want, instead, is compare the gameObject of the collision with your referenced gameObject(the platform) and, if both of them are the same, then you can proceed. Therefore, what you want is:
public class CollisionScript : MonoBehaviour
{
public GameObject platform;
void OnCollisionEnter(Collision col)
{
if (col.gameObject == platform);
{
Debug.Log("Yes");
}
}
}
Let me know if this solves your problem.
EDIT: I see now you also named your class 'Collision', which I'm pretty sure would break things, as Collision is already a class within Unity(you can check that looking at "Collision col", that indicates there is a class named Collision). You should change both the class name on the top of the script and the file name to reflect that change.
What I'm trying to do is to make the GameManager object persistent between scenes, reading a bit I realized that the DontDestroyOnLoad() method allows this behavior, however I don't understand why it doesn't allow me to instantiate objects in new scenes.
The following code perfectly replicates the main problem:
using UnityEngine;
using UnityEngine.SceneManagement;
public class GameManager : MonoBehaviour
{
public GameObject objectPrefab;
private void Awake()
{
DontDestroyOnLoad(gameObject);
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
SceneManager.LoadScene("Scene2");
GameObject instance = Instantiate(objectPrefab);
}
}
}
From Unity docs:
"When using SceneManager.LoadScene, the scene loads in the next frame, that is it does not load immediately. This semi-asynchronous behavior can cause frame stuttering and can be confusing because load does not complete immediately."
The instantiation happens right away and then one frame latter scene change is triggered. So your game object is instantiated in the old scene and lives there for one frame.
As shingo proposed in the comments you can use SceneManager.sceneLoaded to execute code right after a scene is loaded.
I have a script with a public UnityEvent and I am trying to drag a scene object to the object slot so I can access its methods but its not accepting the object. I'm not sure what i'm doing wrong.
The scene object is a prefab instance and I did try unpacking the prefab but it didnt make a difference.
In a comment you mentioned the script you are talking about is a StateMachineBehaviour.
StateMachineBehaviour does not inherit from MonoBehaviour but rather from ScriptableObject
ScriptableObject instances "live" in the Assets not a certain Scene
You (usually) can not have any Scene references in any assets like Prefabs or ScriptableObjects.
There are some workarounds however that still allow you to do that. You can e.g. create ScriptableObject containers for every value you want to pass. Something like e.g.
[CreateAssetMenu]
public class GameObjectReference : ScriptableObject
{
public GameObject gameObject;
public void SetActive(bool value)
{
if(gameObject) gameObject.SetActive(value);
}
}
such an instance of GameObjectReference now is a ScriptableObject and thus also "lives" in the Assets => you can reference it in any other asset as Prefabs and other ScriptableObjects.
So all you need to do is make sure that you set this value from within your scene e.g. using
[ExecuteInEditMode]
public class GameObjectReferenceSetter : MonoBehaviour
{
public GameObjectReference gameObjectReferenceAsset;
private void Awake()
{
gameObjectReferenceAsset.gameObject = gameObject;
}
}
You can create such a reference-asset and setter pair for each type you need and transpass the required public methods so you can call them from the UnityEvent.
Having the [ExecuteInEditMode] this should also be set already in EditMode. However since "officially" it is still not possible to have a scene reference on a ScriptableObject field the value usually says Type missmatch but references the correct object as you should see when you click on the field.
Another alternative is using some kind of dependency-injection (for Unity an often mentioned and free solution is e.g. Zenject). It is a bit complex to set it up the first time but once you got it working it is more flexible and better scaleable since you wouldn't need to implement a wrapper for each type you want to pass in to a StateMachineBehaviour.
More information and how-tos about Zenject can be found on their github page
I asked How can I adjust shape/dimensions of one clone to affect all other clones in the scene view and the accepted answer was spot on. It could only clone one game object. I tried making some adjustments but the only solution I came up with was adding duplicate methods for additional objects. This doesn't work well when dealing with several game objects to be cloned.
How can I clone several unique game objects so that adjusting the components/properties of one clone would affect all other clones of that object in the scene view?
Please note that I don't want to achieve this at runtime and I don’t want to use prefabs. I am using this to help with creation of complex levels so the live update of clones being adjusted is very important.
Additionally, I also need a way to turn off the this repeated property/component replication on each clone, preferably with a button.
I don’t want to use prefabs
The new prefab system in Unity is exactly what you need. It fits all of your requirements:
Clone several unique game objects
The prefab system is made for cloning unique gameobjects. It even supports prefab nesting.
I don't want to achieve this at runtime
Great, prefabs only update globally when you click the override button in the editor.
I need a way to turn off the this repeated property/component replication on each clone
That's equivalent to unpacking the object (breaking the connection).
If you have a good reason to avoid using prefabs, you can always write a custom script that tracks changes in the properties you want to share, and updates all other objects immediately. You can make that script run in edit mode by adding the [ExecuteInEditMode] attribute to the class it's in, just don't forget to disable it when running the project. Again, I highly recommend using prefabs instead.
You should use a ScriptableObject as data container and attach that to the gameobject, all clones will use the same synchronized ScriptableObject.
You should use events. Unity3d tutorials has a good, simple explanation: https://unity3d.com/learn/tutorials/topics/scripting/events
Is this only for editing the objects in the editor? If so, then it sounds like prefabs are the way to go; you can directly edit the prefab and all of its 'clones' in the scene will have all changes, including all monobehaviours, transforms, and whatnot replicated to that of the prefab.
If you need this to work at runtime, then you will likely need some code to do this for you. You haven't quite provided enough clarification as to what exactly you want to do, so for the below example I'll assume that you have a gameobject with a mesh or sprite component, and want its size/scale modified alongside all of its "clones";
using UnityEngine;
using System.Collections.Generic;
public class ShapeClone : MonoBehaviour
{
//This will hold references to the other "clone" gameobjects.
public List<GameObject> otherClones = new List<GameObject>();
//All the "clones" in the list otherClones will have their scale matched to this gameobject's scale
public bool leader;
private void Update()
{
if (leader) //Only change other clones' scales if marked as leader, to avoid every single clone
//overriding each other's scale every single frame, which could be rather chaotic
{
for (int i = 0; i < otherClones.Count; i++)
{
//setting each of the other clones' scale to that of this object.
otherClones[i].transform.localScale = this.transform.localScale;
}
}
}
}
The above is a brief example to give you an idea and is by no means extensive, but you should be able to apply it to what you're trying to do; for example, if you wanted to replicate the colour of sprites across gameobjects instead, you can modify otherClones to be a list of Sprite references instead, and instead of setting the scale in update, you can set the colour of each of the Sprite components to that of this object.
If you're only needing this functionality in the editor and not during runtime, though - I highly recommend going with the first option using prefabs, as it will give you far more functionality at a fraction of the cost, performance wise.
It sounds like you have an object that has several clones. You want changing the shape or dimensions of any of those objects to affect the other ones?
For this to happen, each object needs to know about the other ones. You can do this decentralized (each object contains a reference to each other) or centralized (one object governs the rest).
The centralized approach is more simple so I'll give a simple example.
public class Shape
{
public int length;
}
public class ShapeCentral
{
public List<Shape> shapes = new List<Shape>();
public void CloneShape()
{
//instantiate new shape
shapes.Add(new Shape());
}
public void SetCloneLength(int l)
{
shapes.ForEach(x => x.length = l);
}
}
As you can see, one object can control all the clones at once. The trick is to not create clones using other methods or you will run into trouble.
If you want to tighten up your variable access (which I recommend, its a good exercise) you could use a publisher/subscriber pattern. In this, when a new clone is instantiated, it subscribes to the SetCloneLength method. When you want to change the length, the central class publishes that message and it is sent to all the subscribers.
The difference here is that in my example, the central class needs to keep track of all the clones, in publisher/subscriber, you don't.
Create script CopycatManager that will hold a leader and then use dedicated setters for copying the other object properties that have the same type. If a property is a default one may need to set up either a proxy of such property within' the script or play with triggers. I would recommend proxy. Like this:
class CopycatManager {
public GameObject leader;
SomeAttributeType attributeToCopyFromLeader {get; private set}
void Start () {
// The first CopycatManager to start is the leader
List<CopycatManager> allCMs = parent.GetComponentsInChildren();
CopycatManager foundLeader = allCMs.Find(o => o.leader == o);
if (foundLeader == null) {
// There's no leader yet, set yourself a leader
leader = this;
} else {
// Found a leader, accept
leader = foundLeader;
}
}
public void SetAttribute (SomeAttributeType newVal) {
// If we're setting the attribute of the leader - we should set this attribute for all children
if (leader == gameObject) {
// Find all copycat manager scripts attached to children of current parent
// Meaning siblings
// WARNING: It will include children of siblings and the leader itself
// WARNING: It will not include parents of the Copycat Manager type, add if required
List<CopycatManager> allCMs = parent.GetComponentsInChildren();
foreach (CopycatManager manager in allCMs) {
SetAttributeFromLeader (newVal);
}
} else {
// Non-leader is attempting to change attribute - call leader
leader.SetAttribute(newVal);
}
}
// Called by leader to each child
public void SetAttributeFromLeader (SomeAttributeType newVal) {
attributeToCopyFromLeader = newVal;
}
}
Make sure to assign a new leader if the old one destroyed. Only destroy objects with CopycatManager through dedicated function.
make all items that need scaling children of an empty called WorldObjects then scale the world object, it will scale all its children accordingly. you can then either manually or through script remove the parent to make objects independent. best way without prefabs...
use a singleton class. add that script to all the objects, then you can make a call to one and it will adjust all of them.
you can also do this with a static class, but the singleton approach is cleaner, and gives you more options.
public class MySingleton
{
private static MySingleton fetch; // keep the static reference private
public bool myBool = false;
// and expose static members through properties
// this way, you have a lot more control over what is actually being sent out.
public static bool MyBool { get { return fetch ? fetch.myBool : false; } }
void Awake()
{
fetch = this;
}
}
read here for some great information on both options!
I have a model with an animator; and a controller script to make it move, then I created a simple class called "TheEntity", which hold a name, and an int for the energy. When the time goes by, the energy goes down, so the mesh walk around or perform animations at random.
public class TheEntity()
{
public string name;
public int energy;
public TheEntity()
{
// make a random name
name = "joe"+ rnd.next(1,1000).toString();
energy = rnd.next(20, 100);
}
}
When the energy value goes to 0, the mesh "goes to sleep", and regenerate again in a certain amount of time.
If I have a list of TheEntity instances, as it would be if I have a list of NPC, what would be the correct way to assign a mesh to each entity class in the list?
Should I have a script on the mesh, that has a reference to a TheEntity class. and assign it at runtime when I load the mesh prefab?
Or should I put the whole "TheEntity" class script on the mesh, and save it as prefab, so every time that I load the prefab, I will have a mesh with a related TheEntity instance directly?
"Things" in the Unity scene are GameObject , that's all there is to it.
Your class must be a MonoBehaviour to be on a game object.
In Unity, everything is a MonoBehaviour (a Component). There is, quite literally, nothing else whatsoever in Unity.
(Of course, you may have some "raw" non-Unity classes for things like say math calculations, but that's irrelevant.)
It's just that simple.
public Class Entity:Monobehaviour
{
}
attach that to an empty game object. Add the models (meshes) .. or whatever you want. Add sound effects, add anything.
Regarding "changing the mesh", no problem.
Do that in a routine in Entity, if you like.
public Class Entity:Monobehaviour
{
public void ChooseRandomMesh()
{
}
public void ChooseRandomColorPattern()
{
}
public void RunForTheHills()
{
}
public void AttackHero()
{
}
}
If you prefer, write a Component which does nothing other than randomly change the mesh.
public Class Entity:Monobehaviour
{
public void RandomizeEntity()
{
}
public void ChooseRandomColorPattern()
{
}
}
.. and attach that script to the game object, also.
In Unity, everything is a MonoBehaviour (a Component), it's that simple.
Regarding making it a prefab, if you want to, sure do that. Read any of thousands of tutorials on prefabs.
There's a critical comment you made in your comments below:
"Also the entity class is not MonoBehaviour..."
Here's an incredibly critical point in understanding Unity:
1) You're quite right that your "model" or "AI" or "database connection" sort of has "nothing to do" with unity game objects. (They of course don't have a "position" or "mass!" or anything, right?!)
HOWEVER HOWEVER HOWEVER HOWEVER HOWEVER HOWEVER HOWEVER HOWEVER HOWEVER
2) In Unity unless a class is a MonoBehaviour: you can't do anything with it/ You can't even access the run loop, it's a total nonstarter.
THUS THUS THUS THUS THUS THUS THUS THUS THUS THUS THUS THUS THUS THUS
3) In Unity all the stuff like that, IS IN FACT a MonoBehaviour AND YOU SIMPLY sit it on an empty game object. (Usually the name in the Heirarchy starts with underscore, say, so you know it's "not really" a conventional game object.)
The simple bottom line is in your preload scene (you must have one in 100% of projects) you have all your "AI" and "model" and "database" stuff, just sitting on one or more "pretend" markers (marker == otherwise empty game object).
Bottom line, when you say below "your model is not a MonoBehaviour" that is wrong. It will have to be a MonoBehaviour (if you think about it, it's absolutely inevitable you'll need to access the runloop, if nothing else) ... just make it a MonoBehaviour and put it on a marker, almost certainly in your preload.
I hope it makes sense. Essay on the topic.