- Home /
Is there a way to extend the two-step GameObject init process with Awake() and Start() to three steps?
Hi. I have a problem that, at the surface, seems to require a three-step init process. I know about the two-step process that Unity does provide with Awake() and Start(), but I have come to a point where I would like three steps. The following code is a simplification of the situation:
public class Manager
{
protected Action action;
public void RegisterAction (Action action)
{
this.action += action;
}
public void TriggerAction()
{
if (action != null)
{
action();
}
}
}
public class GameController : MonoBehaviour
{
public static GameController instance;
public Manager manager;
void Awake()
{
instance = this;
manager = new Manager();
}
void Start()
{
manager.TriggerEvent();
}
}
public class OtherController : MonoBehaviour
{
void OnEnable() // I want this to be called before any Start() is
// called and after ALL Awake() are called, but it
// is called immediately after the Awake() on the
// same object. (If the object is enabled, that is)
{
GameController.instance.manager.RegisterAction (OnAction);
}
protected void OnAction()
{
// Do stuff!
}
}
So, in words, I have a manager class in the model. This manager triggers actions that can be registered with it. The GameController class is a singleton-ish thing that creates the model in the Awake() method and kicks off the game in Start(), i.e. the triggers can be expected to fire in and after Start(). I have many controller objects in the scene that needs to perform actions on these triggers, they must register with the manager after it has been created but before any Start() method has been called.
Is this something that can be done? Sure, I can make every Start() in the game set a flag in the GameController and move the code in GameController.Start() to GameController.Update() surrounded by a test of that flag. However, this feels wrong.
Is there something wrong with my design pattern? Are there completely different ways of setting something like this up?
Best regards, Fredrik
Answer by JedBeryll · May 25, 2016 at 07:30 PM
Here's how i do this: my manager does the first things in it's Awake, at the end of that it starts a coroutine that waits for the end of frame. That starts a second method which iterates through some other controller objects and calls their Init method. (all the controllers derive from a BaseController class and the Init is a virtual method which the controllers override). I only need these 2 methods but you can continue this for as long as you like, after you iterate through the objects you can already reference them and do stuff in an order or wait another frame and then do stuff.
Answer by fre_ber · May 26, 2016 at 10:08 AM
[Edit: Hmm, did you remove your answer?]
Thanks. I think that your solution would work for me too and it is a bit more generic than what I alluded to in the question. However, both methods feel a bit clumsy. As a temporary fix, I implemented my thing and it turned out similar to this:
public class Manager
{
protected Action action;
public void RegisterAction (Action action)
{
this.action += action;
}
public void TriggerAction()
{
if (action != null)
{
action();
}
}
}
public class GameController : MonoBehaviour
{
public static GameController instance;
// FIXME: This is nasty code to simulate a three-step init:
public static bool isReadyForInit = true;
public static List<MonoBehaviour> waitingList = new List<MonoBehaviour>();
public static void WaitFor(MonoBehaviour b) { waitingList.Add(b); isReadyForInit = false; }
public static void IsReady(MonoBehaviour b) { waitingList.Remove(b); isReadyForInit = waitingList.Count == 0; }
public Manager manager;
void Awake()
{
instance = this;
manager = new Manager();
}
void Update()
{
if (isReadyForInit) // Wait until all relevant objects are ready
{
TriggerAction();
isReadyForInit = false; // We have now initialised, we don't need to init again.
}
}
}
public class OtherController : MonoBehaviour
{
void Awake()
{
GameController.WaitFor(this);
}
void Start()
{
GameController.instance.manager.RegisterAction (OnAction);
GameController.IsReady(this);
}
protected void OnAction()
{
// Do stuff!
}
}
As it is only one object, the GameController, that has to wait, I found this just a little bit simpler than the generic Init() functions called from a co-routine. It is ugly but it works. :-)
/Fredrik
Your answer
![](https://koobas.hobune.stream/wayback/20220612081548im_/https://answers.unity.com/themes/thub/images/avi.jpg)