I often get issues where my scripts are not running in the order I want, I roughly understand how Unity works with the components execution order but I woud like not to rely on “Edit > Project Settings > Script Execution Order” because I think it’s gonna end up being messy.
What’s the proper way to do it through code that doesn’t use shenanigans like OnEnable(), Awake(), Start()?
Haha, yes I mean they’re useful but I don’t want to use them to decide which script should start before the other one. Basically they’re not supposed to be used for taking care of the script execution order. I do use them of course.
Ah okay… well, in that case…
Generally, you want to setup anything on the game object/script in Awake and stuff that relies on other scripts being “awake” in Start.
I guess you already know that, too… Beyond that, I’m not sure what issue(s) you’re having.
There are a few situations where I will yield a frame at the start to do some setup…
Can you give an example of a problem you face, maybe that would help for some answers/insight?
Okay, I guess keeping in mind that Awake should be always used to GetComponent and instantiating stuff is a good idea.
Well the example I last remember was with OnEnable() and OnDisable() I wanted to register/unregister to an event but the class I wanted to be registered to was not instantiated yet.
If you want to have more controll you might want your own ObjectHandler which can work like shown below.
It is just an example, maybe you use different controllers for different objects. You can also just skip IControlledObject and simply enable or call SetActive(true) on your objects in a routine like this. Then you instantiate the objects before and disable them right after.
For my project I use a few different managers which controll different objects and I controll the instantiation, setup and execution (Run()) in the GameManager in its Loading routine.
I also avoid Update() and my Manager calls the update, but this is just a personal preference and not suited for all applications
public interface IControlledObject
{
void Setup();
void Run();
void Stop();
}
public class MyObject : MonoBehaviour, IControlledObject
{
public bool IsRunning = false;
public void Run()
{
IsRunning = true;
}
public void Stop()
{
IsRunning = false;
}
public void Setup()
{
}
void Update()
{
if (!IsRunning) return;
//Execute Stuff
}
}
public class ObjectManager : MonoBehaviour
{
List<IControlledObject> Objects;
public void SetupObjects()
{
for (int i = 0; i < Objects.Count; i++)
{
Objects[i].Setup();
}
}
public void RunObjects()
{
for (int i = 0; i < Objects.Count; i++)
{
Objects[i].Setup();
}
}
}
public class GameManager : MonoBehaviour
{
ObjectManager ObjectManager;
void OnGameLoad()
{
//LoadObjects and initialize
ObjectManager.SetupObjects();
//Do stuff...
//Start Game:
ObjectManager.RunObjects();
}
}
Here is an alternative, maybe that makes more sense. This is how I controll all of my Applications usually. It is also great for references (dependency injection).
public class GameManager : MonoBehaviour
{
CameraController Camera;
EnemiesController Enemies;
FancyStuffController FancyStuff;
void OnGameLoad()
{
//LoadObjects and initialize
Camera.Setup(this);
Enemies.Setup(this);
FancyStuff = new FancyStuff(Enemies); //Handle non MonoBehaviours easily
//Do stuff...
//Start Game:
Camera.Run();
Enemies.Run();
FancyStuff.Run();
}
}
Self-initialization in Awake, initialization requiring any other scripts in Start- following just that one guideline will save you thousands of headaches, and there’s no need to mess with Script Execution Order except in very specific cases. OnEnable always happens after Awake, but before Start, so you have to be careful that you don’t specifically reference other MonoBehaviours’ data that require the Start functions to have run before you can get the information you need from them- but that’s the case in Start too. You can delay a frame if necessary- coroutines and invoke functions are both common for more precise load sequences.
I would personally examine the data structure and see if there’s anything you can offset to a static manager elsewhere instead.That’s me though- every single GameObject and MonoBehaviour I use is simply a shell- the absolute minimum to get the job done, and everything that holds real data is in a static manager somewhere. I’m not a fan of using GameObjects to the sole proprietors of any data that isn’t strictly used for physics or on-screen display, as it makes management of that data painful.
So a proper utilization of Awake/Start/OnEnable seems to be the answer for most situations, ok. Thanks for the ideas!
I had been trying to avoid static classes or managers because I used to not use componentization enough but it definitely has its uses, I’ll start using them more again + getting components everywhere gets quite a bit tedious.
This has made me think of more questions if that’s alright:
If GetComponent() has some overhead to it then isn’t generally speaking static classes more efficient/ a better approach?
Unity has this GameObject <==> Transform link where they have a reference to each other. Is this something I should try and recreate on most of my components to avoid unnecessary GetComponent calls?
It was mentioned to me that it was a bad thing to have classes refer to each other (in c++ classes), they called it circle … (some word I forgot). Say I created a MonsterManager and that in my Monster class I had a reference to the manager. Is that necessarily bad design? Is it because of idea that the class should be a “black box” or is it something related to performance?
Apologies for going slightly off-topic and asking a question. But what is the problem with using script execution order? I often use it, for example, with my items to ensure my ItemDatabase is created before my inventory initializes. Is this bad practice?
I barely use it because I think it’s eventually going to get messy. I can already imagine myself thinking “Why did I call that script before that one etc…”. Other thing I can think of is in a situation where you’d want to use your code in some other projects it’d mean you’d have to also integrate the execution order. Basically I want my code to work without the help of some other variable that might make it fail.
An alternative way to handle that would be to have the inventory add a listener to ItemDatabase for when it updates with new data. That’s pretty much how all UI should behave, actually.
Then it doesn’t really matter whether the inventory or ItemDatabase starts first.
I would use the execution order mainly for the Update calls. You basically describe an initialization process which happens only once and during startup of the game. This and because you maybe want a proper loading routine which is capable to load the game over many frames and can show a progress bar which tells you what is currently being done.
So my suggestion is to make a structured Loading Routine which does all the Initilization stuff such as setting up ItemDatabase and let the execution Order handle things like Update Routines. So that your Inputs are called before your camera controller, the game logic and then the view and rendering stuff. Some of that of course you can controll with LateUpdate)(), but there is no EarlyUpdate().
You need some kind of controllers otherwise you end up with issues just as you have described. What if you make changes to code? Who guarantees that the execution order of low level objects like DatabaseItem is still correct if you make changes to the inventory, like adding another type of object.
But I think the issue the OP has is exactly when the listener would be assigned so that database exists already.
It ties your code with your project file, for one. Try to port your inventory into a different project, and this execution order is lost.
It’s better to enforce it in code. One way to do this is to use Awake in one and Start in the other, and that works 99% of the time. For the other 1%, there are other techniques to employ. Here is an example of code that’s guaranteed to execute in the correct order, both during the “Awake” phase of execution.
// First.cs
public bool isInitialized = false;
public event System.Action OnInitialize;
void Awake() {
if (!isInitialized) Initialize();
}
public void Initialize() {
Debug.Log("First is initializing");
if (OnInitialize != null) OnInitialize();
isInitialized = true;
}
// Second.cs
public First first;
void Awake() {
first = GetComponent<First>();
if (first.isInitialized ) {
Initialize();
}
else {
first.OnInitialize += Initialize;
}
}
public void Initialize() {
Debug.Log("Second is initializing");
}
As long as you self-initialize in Awake, and find objects in Start(), that still shouldn’t matter.
The database itself should exist by the time you reach Start(). It may or may not be empty at the time the listener is registered. I’m saying make it so that it doesn’t matter whether it’s empty at that point or not.
Try to pull whatever data it has at that point, and add a listener to receive new data after that point.
Am I the only one thinking this way of initializing things is absolutely awful ? I really don’t understand why Unity is forcing players to do that, but maybe I am missing something.
While doing “self-initialization” (if such a thing exists…) in Awake() and anything else requiring other components in Start(), there is no Stop() method to be used in combination with the OnDestroy() method. Thus, what can be fixed by applying this “good practice” described above for initialization cannot be done simmetrically for uninitialization. So in the end, it just moves the problem without resolving it.
I’m in a situation where I have to attach/dettach event handlers from within a component to a member of another object, member which only exists once the other object is properly initialized. While I can attach that event in Start(), when I’m sure that Awake() has been called on the other object, I cannot detach it in Stop(), because there’s no such method. If I detach it in OnDestroy(), nothing guarantees me that the other objet hasn’t been destroyed first, in which case its member I need to detach the event from might just be null.
Unity is capable of so many advanced things, I can’t beleive something as simple as a Start() / Stop() symertry cannot be performed. And at this day, I still don’t know of any way to do that cleanly. The only way I know is creating all objects at run-time, using Instantiate() and DestroyImmediate(), which is a reliable way to be sure that objects are awaken, started and destroyed in the expect order, but that prevents me from using editor created content, which is dirty, harder to maintain, has worse performances, etc…
Also script exceution order is way too “global” to fix that kind of issue. For a given set of components, I do not necessarily always want the same initialization order.
For the rest, first Unity is not forcing users anything. It might not be easy all the time but you can define your own ways of how execution, initialization, Updates, and else are being called. You don’t have to use MonoBehavior.Update, Start etc.
That has nothing to do with Unity. You can either make sure that this objects exists while something is listening to it or use a simple null check.
But in the end you are right, especially when you have bigger projects simply relying on Awake(), Start() etc is not gonna work. Build your own handlers around that, just as I have described above . These are just two examples.
I would expect Stop() to be the symetrical of Start(), just like OnDestroy() is the symetrical of Awake().
Start() is only called once all Awake() have been called. Stop() would be called before all OnDestroyed() are called.
In Stop() you’d detach the component from whichever other object it is related to, and in OnDestroy you’d do the self-uninitialization. That would be the exact opposite of Start and would fix many issues. The existence of this thread, among others, is just evidence showing there’s something unclear and/or missing about this subject that causes most developpers to have headaches.
Yes Unity is “forcing players”. You HAVE to use the Unirt events (Awake(), Start(), etc) if you want to be able to use editor-initialized content. And the fact that objects are initialized in a random order is an issue. Why are they not making objects initialize in the order they are in, inside the object tree ? Objects could initialize in that order, recursively. Parents could initialize before children, or the other way, but at least it would be predictable.
OnDisable is NOT the symetrical of Start(), it’s the symetrical of Enable(). Sorry but this seems obvious to me. OnDisable is called whenever the object is disabled, but Start() is NOT call when the object is enabled (again). Thus if you disable and then re-enable an object, you’d expect it to be in the same state as it was before you disabled it, which is not the case if it got uninitialized in OnDisable() and never re-initialized due to not being started again.
Also you’re not going to perform any uninitialization in OnDisable because the object must just be temporarily disabled/suspended to be used again later.
I read your solution above, and while it provides a solution for the initialization, it does not for uninitialization. And even if you added some sort of Unsteup() method, that would be some kind of Stop() method, I would only work when that controller object would be destroyed, which would not be enough to cover all cases, unless you write one of these controllers for every case.