I Have a MonoBehavior doing something like below:
public class MyMonoBehavior : MonoBehaviour
{
private Rigidbody m_Rigidbody;
private void Start()
{
m_RigidBody = GetComponent<Rigidbody>();
}
private void FixedUpdate()
{
m_Rigidbody.AddForce(Vector3.one); // May throw NullRefrenceException
}
}
The MyMonoBehavior is attached to a GameObject which is created at runtime.
Occasionally, m_Rigidbody.AddForce(Vector3.one); throws NullRefrenceException.
It seems that FixedUpdate run before Start. Is it a bug?
According to the Unity3d documentation:
Before the first frame update Start:
Start is called before the first
frame update only if the script instance is enabled.
For objects added to the scene, the Start function will be called on
all scripts before Update, etc are called for any of them. Naturally,
this cannot be enforced when an object is instantiated during
gameplay.
So it seems above all scripts include "MyMonoBehavior" its self? So its FixedUpdate can be executed before its Start.
Is my understanding correct? Or is this a bug of Unity3d?
My Unity3d version is 2017.3.1f1
That's why you have Awake. Basically, you should do initialization that's specific to your object in Awake, and leave interaction with other objects for Start. Awake is called as part of the object instantiation process, so I highly doubt you'd run into similar problems.
Related
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.
Suppose I have an audio manager singleton (present in all scenes) and all scenes have a button to enable and disable audio.
Suppose I'm in the scene A and I go to scene B.
In scene B, the buttons that referenced the functions of the singleton lose their reference because the audio manager object is destroyed, because a previous audio manager comes from scene A and the one from scene B is destroyed (since there cannot be 2 of it).
Is there a smart way to solve this problem?
Of course I can create a class whose methods call singleton functions and let them in all scenes, but that doesn't seem smart.
On mobile so sorry for the formatting. One way I can think of doing this is having an Init() function in your singleton. In the way you are describing it, this object is DontDestroyOnLoad() as well. In the method of Start() or Awake() you're find all other instances of this object and if another exists deleting the new one so the old one persists.
Inside of this function if another object is found, simply call a new Init() function that takes all the references you need as parameters so the old Singleton gets all the information of the new one, then the new one is deleted.
public class myClass : MonoBehaviour {
public static myClass i;
[SerializeField] private Gameobject myReference = null;
void Awake () {
if(!i) {
i = this;
DontDestroyOnLoad(gameObject);
}else
i.Init(myReference);
Destroy(gameObject);
}
public void Init(GameObject myNewRef)
{
myReference = myNewRef;
}
...
Hope the snippet adds a bit more clarify. Again formatting might be iffy.
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");
}
}
I see that we can initialize Variable in Awake() or Start() and Awake() will be called before Start().
When should we initialize in Awake and Start to have the best performance?
Usually Awake() is used to initialize if certain values or script are dependent on each other and would cause errors if one of them is initialized too late (awake runs before the game starts).
Awake is also called only once for every script instance.
Let me quote the Documentation:
[...] Awake is called after all objects are initialized so you can safely speak to other objects or query them using eg. GameObject.FindWithTag. Each GameObject's Awake is called in a random order between objects. Because of this, you should use Awake to set up references between scripts, and use Start() to pass any information back and forth. Awake is always called before any Start functions. This allows you to order initialization of scripts. Awake can not act as a coroutine.
and about Start():
Start is called on the frame when a script is enabled just before any
of the Update methods is called the first time.
Like the Awake function, Start is called exactly once in the lifetime
of the script. However, Awake is called when the script object is
initialised, regardless of whether or not the script is enabled. Start
may not be called on the same frame as Awake if the script is not
enabled at initialisation time.
Where the last part makes one big difference
To get to your question:
If the script is NOT enabled at the beginning of your game, and you don't need the variables to be initialized, start would be saving performance as awake() would be called regardless...
every variable would be initialized at the very beginning. At least that's the logical assumption I make.
This topic is well described in the official docmentation (Awake and Start).
This section describes why you might need two functions:
The Awake function is called on all objects in the scene before any
object's Start function is called. This fact is useful in cases where
object A's initialisation code needs to rely on object B's already
being initialised; B's initialisation should be done in Awake while
A's should be done in Start.
The difference between Awake and Start is that Start is called only when a script is enabled.
These two functions are called before the first Update method and there is no performance difference between them. I would say that Awake is used to initialize all objects (like a constructor), and Start is used to link the objects or do something before a game starts.
Awake is the equivalent of the ctor. It is called when a MonoBehaviour is created, before any other method if the object is active.
Start is run the first time the MonoBehaviour is activated. This can be right after Awake or long after. This allows to perform actions that are related to the current state of the app or objects.
For instance, you create an enemy, in Awake, you place everything that is basic initialisation. Then, the enemy is deactivated at the end of Awake. Later on, the enemy is about to be activated but you want to make it red if player is having some specific weapon, then you do it in Start.
IMPORTANT: If a prefab is created and the game object is off by default in the prefab, the Awake is not called until set on. In the case of a pool creation where prefab may be off, the awake is happening first time the pooled object is activated.
OnEnable is similar to Start but happens on every SetActive(true) and on start if enabled. This can be a candidate for your enemy willing to change color over the level based on the player magna for instance.
There's not much difference in the performance. But I can tell you a difference between them.
Take a simple example. Say if you want to print "HELLO" in console even if you have not activated the script in inspector, using Awake() function, you can print it in the console. But if you had written the same thing in Start() function and the script wasn't activated, you don't get any output in the console. That's the difference.
Codes in Start() function get executed only if the script is activated while, codes in Awake() function get executed even if the script is not activated. Try it !
I'd claim there is no real performance difference at all.
What Minzkraut stated
If the script is NOT enabled at the beginning of your game, and you don't need the variables to be initialized, start would be saving performance as awake() would be called regardless...
every variable would be initialized at the very beginning. At least that's the logical assumption I make.
is only semi true. If you do everything only in Start the UX might be even worse since instead of one bigger lag when starting the app - which in my eyes is totally acceptable - it might lead to smaller but more lags during game play which I personally would avoid as much as possible.
In general it was already often explained in the other answers how and when Awake and Start are called and that basically it is "just" a timing difference.
For details refer to Execution Order of Events
Leaving disabled components and inactive GameObjects aside my personal thumb rule is
Use Awake for everything where you don't rely on any other component and references.
E.g. set default field values, populate things using GetComponent etc
Use Start for everything where you do rely on other components such as accessing the results of GetComponent
This way these other components supposedly did already receive their Awake call so they themselves are already linked up and ready to be used by others.
This solves timing and dependency issues in the most cases.
Where this is not enough you would start to tweak the Script execution order or use events.
And then there is another quite important difference in the use case of using Instantiate or AddComponent
Awake (and OnEnable except using Instantiate and the component is disabled) will be called right away during the instantiation and any code line after Instantiate will be executed after it is finished.
Start however will be delayed until the end of that frame (or until enabling if the component was disabled in Awake).
This allows you to have enough time to use Instantiate and modify some field values before Start is called and can now base its behavior on the modified field values.
For example this
public class Example : MonoBehaviour
{
private class X : MonoBehaviour
{
public int x = -1;
private void Awake()
{
Debug.Log($"Awake {x}");
x = 12;
}
private void OnEnable()
{
Debug.Log($"OnEnable {x}");
}
private void Start()
{
Debug.Log($"Start {x}");
}
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
Debug.Log("Instantiate");
var x = gameObject.AddComponent<X>();
Debug.Log("After instantiate");
x.x = 42;
}
}
}
will produce the output
Instantiate
Awake -1
OnEnable 12
After Instantiate
Start 42
I would init event in OnDestroy() on Unity3D Statement because after reloading scene some destroyed script get event and throw MissingReferenceException on gameObject.
here how I declare event :
public delegate void SelectDelegate();
public static event SelectDelegate OnSelected;
and how I init it
void Destroy()
{
OnSelected = new SelectDelegate(OnSelected); //Dont work :'(
}
How can I init cleanly event for reloading scene
Ok, I am going to try a few things here:
In my dealings with Unity3D and C# I never instantiate a delegate as you do in the Destroy() method. Once declared, it can be accessed. When you register a delegate an instance should be created for you. See #4.
OnDestroy() is probably not the best place to init anything as it is called before that GameObject is destroyed. Therefore whatever you initialize will be gone soon.
I normally register delegates with something like myClassInstance.Onselected += MyHandlerFunction;. This is normally done in the OnEnable() function. To clean up I remove my delegates in the OnDisable() function with myClassInstance.Onselected -= MyHandlerFunction;.
Before dispatching an event always check it is not null with something like if( OnSelected != null ) OnSelected();.
If you want to avoid having a specific GameObject being destroyed when you load another scene use the DontDestroyOnLoad class.
Shouldn't Destroy() be OnDestroy()?