I have a system that does initialization logic in Awake() and has an appropriate [DefaultExecutionOrder(-100)] attribute to guarantee its Awake() be called before each other "playable" game objects Awake(). Now I need the reverse logic, i need to call a function after each game object being destroyed.
I tried to traverse over root objects in main component's OnDestroy(), check if there's an object that is still alive (i.e. cast to bool gives true), and "pass the baton" of deinitialization to it (adding a CleanUp component on it). Then when this object's OnDestroy() is called it does the same logic, checks if there is at least one object alive, etc... The problem is that it doesn't work as i expected.
Also tried to make OnDestroy() async and check if there's no alive objects in the scene and wait for 100ms in a loop. The result is that playmode exits but the loop still goes on valid objects outside the playmode
Another approach was to instantiate an object with cleanup component in main component's OnDestroy() but Unity does not allow to instantiate in OnDestroy() event
Also tried to implement this logic in component's destructor but it was weirdly been called even before the Awake()
The other possible approach would be to use ISubsystem interface Unity provides but there's no clear documentation on how to use it.
I don't want to use any kind of "global factories" to instantiate and control objects
A possible way to workaround is to use other components' dependent logic in OnApplicationQuit() instead of OnDestroy() but it is the most fallback solution for me
What is the best way for component to be initialized before each other and outlive everyone?
Related
I have a "Main" scene consisting of a softozor game object which is controlled by the player. It's a flappy dinosaur. As a child of that softozor game object, I have setup another game object, Installer, consisting of a Transform component and a PlayerInstaller (Script) component:
The PlayerInstaller installs everything necessary for my player's logic. Finally, in the softozor game object, I have added a Game Object Context (Script) where I register the PlayerInstaller:
In addition to the softozor game object, I have also defined a SceneContext:
You'll notice that all installers lists are empty in that SceneContext. However, without that SceneContext registering nothing, the PlayerInstaller is not triggered. Playing the game with that setup works perfectly well, i.e. the PlayerInstaller is called and I can control my dinosaur to do whatever I want in my game.
So far, so good. Now, consider the following Scene Test:
public class PlayerTests : SceneTestFixture
{
[Inject]
private IPlayer _player;
[UnityTest]
public IEnumerator TestScene()
{
yield return LoadScene("Main");
_player.Flap();
yield return new WaitForSeconds(0.01f);
[...]
}
}
In that test, the _player member variable is not injected with an object satisfying the IPlayer contract. In fact, the PlayerInstaller.InstallBindings() is not called.
If I, instead, get rid of the Game Object Context (Script) component in my softozor game object, and register the PlayerInstaller in the SceneContext:
then I can play the game too, as before, and my test is running, i.e. the PlayerInstaller.InstallBindings() method is called during my Scene Test.
What is wrong with my first attempt where I register the PlayerInstaller in the softozor game object context?
I am working with
Zenject ver. 7.3.1
Unity 2019.1.8f1 PC, Mac & Linux Standalone
So you have two containers, the SceneContext and a GameObjectContext.
I think what happens here is that they both get installed, but your GameObjectContext is not added to the SceneContext -- it worked until the SceneContext actually needed to know about the GameObjectContext (which was the case in your scene test).
It's difficult to give more precise directions not knowing what is IPlayer and what you expect to be injected there, but it makes sense that it's not injected in your SceneContext, but only in your GameObjectContext.
By putting the PlayerInstaller in the SceneContext's MonoInstallers list, you technically solved that problem, but that's clearly not what you want since it makes the sub-container useless and breaks any kind of separation you were going for.
Instead, you need to interface both contexts using a façade: Sub-containers and Facades: Using GameObjectContexts (the explanation consists of an example, so it doesn't make sense for me to quote parts of it, but it's detailed and helpful)
Thanks to hugo, I'm starting to understand how to make it run. My current game components setup is as follows:
My PlayerInstaller is defined like this:
public class PlayerInstaller : MonoInstaller
{
[SerializeField]
Settings _settings;
public override void InstallBindings()
{
Container.BindInterfacesTo<Softozor>()
.AsSingle()
.WithArguments(_settings.Rigidbody);
Container.BindInterfacesTo<InputController>().AsSingle();
Container.Bind<InputState>().AsSingle();
Container.BindInterfacesTo<PlayerInputHandler>().AsSingle();
Container.BindInterfacesTo<PlayerMoveHandler>().AsSingle();
}
[Serializable]
public class Settings
{
public Rigidbody2D Rigidbody;
}
}
After reading the documentation advised by hugo, I checked which contexts are created during my scene tests. I was able to see that the method PlayerInstaller.InstallBindings was called in my scene tests. In addition, my Softozor object implementing the IPlayer interface was also injected into the PlayerMoveHandler class. That means that the Game Object Context was successfully built during the scene tests. The objects registered in that context are, however, not available from the Scene Context, which is the one I can access to in my Scene Tests. I naively assumed that whatever I would register in my Game Object Context could be retrieved in my Scene Test. It is apparent, however, that I can only access objects registered in the Scene Context.
At that point, if I really want to access the instance implementing IPlayer, I have one alternative: either I create a facade on the softozor game object and make the IPlayer publicly available on it, or I find a way to resolve instances from the Game Object Context. Is the latter possible at all?
In the end, I think I will redesign my scene test in such a way that I don't need access to the instance of type IPlayer, which is totally possible in my case. But I would be curious to know whether or not it's possible to resolve an object from the Game Object Context in a Scene Test. How do I do that?
I would like to populate the UI when I load a scene, with the correct data, instead of placeholders.
When I call "LoadSceneAsync", what would be the first object that is called, so I can fill the UI label with the correct data? I know that there is a scene GameObject, but I am not sure if that would fit my needs.
I am looking for some sort of constructor, called when a new scene object is loaded; to plug in my setup function.
You say
Indeed I did use "onlevelwasloaded" but the UI element may not be there, ready to go, when I invoke it, which lead to errors
That would be an incredibly sever bug in Unity! :)
Could it be that you are mixing-up Awake and Start somewhere?
One way to think of it is once you call Start, you know all the Awake have already run.
When I call "LoadSceneAsync", what would be the first object that is called, so I can fill the UI label with the correct data
You are still within the same frame.
Once you see LoadSceneAsync you can be absolutely assured everything is Awake 'd.
Or indeed once you use Start you can be absolutely assured everything is Awake 'd.
1) could it be that in some of your UI elements (or whatever) you are doing something in Start which you should do in Awake?
2) if (for some reason) you want to "wait until the next frame", perhaps just during development - then do that, wait a frame. You'll see a flicker, but if that's what you want to do (for some reason) do that.
3) note that if you mean you want to go to the net to get something, well of course you have to wait frames (use Update/coroutine) until the information comes back from the net, obviously. (How else could it be?)
Note that in practice, one should be using UnityEngine.Events.UnityEvent everywhere.
Maybe this is what you are looking for http://docs.unity3d.com/ScriptReference/MonoBehaviour.OnLevelWasLoaded.html
Relying on Unity internal functioning is not always the way to go. Particularly when dealing with RESTApi (which is somehow what you are dealing with here).
You cannot assume one object will be ready before another except if you control it.
Add a Controller script that uses Awake. In the Awake, call all the methods you are needing and use some callback to generate secondary code when primary is ready.
public class MyController: MonoBehaviour{
private ServerRequestController serverCtrl = null;
private UIController uiCtrl = null;
private void Awake(){
serverCtrl = this.gameObject.AddComponent<ServerRequestController>();
uiCtrl =this.gameObject.AddComponent<UIController>();
serverCtrl.GetData(uiCtrl.SetUI);
}
}
public class UIController:MonoBehaviour{
public void SetUI(Data data)
{
SetTopImage(data.topImage);
SetBottomImage(data.bottomImage);
// And so on
}
}
public class ServerRequestController:MonoBehaviour{
public void GetData(Action onCompletion){
// This may be a coroutine if you fetch from server
Data data = GetDataFromSomewhere();
// At this point, your data is ready
onCompletion(data);
}
}
Thanks to this, you are now able to know exactly when a piece of code is ready.
I have been trying to use SetActive () to turn on and off GameObjects.
I couldn't figure it out and ended up using:
GameObject.Find ("ObjectName").GetComponent<Image> ().enabled = false;
to turn off an image.
I am not trying to use the same script to turn off a GameObject that has multiple animations nested inside it.
GameObject.Find ("ObjectName").GetComponent<???> ().enabled = false;
GameObject.Find ("ObjectName").SetActive (false);
I am not sure what goes in the <>, but I have read I can do it with SetActive (), but that doesn't seem to work and gives me an "Object Reference not set to object" error.
So what is the difference between these two and how would I use them properly?
Using GetComponent allows you to enable/disable and interact with specific components on a GameObject.
For example, you may need to disable a GameObject's rigidbody at some point, but you still want that object, and everything else on it to be active. So you could simply say:
GameObject.Find("MyObject").GetComponent<Rigidbody>().enabled = false;
Note that what goes inside the "<>" is the class name of the component you want to interact with.
For example, if you had a script you have written yourself on a gameobject called MyScript, you could grab hold of it like so:
MyScript script = GamesObject.Find("MyObject").GetComponent<MyScript>().enabled = true;
Additionally, another good use of GetComponent is reading information from a script on an object.
For example, if you had a script called Health with a public variable HitPoints on an object, another script could gain access to that information using GetComponent.
if( enemyGameObject.GetComponent<Health>().HitPoints < 0 )
{
enemyGameObject.SetActive(false);
}
Using SetActive will enable and disable a GameObject entirely. This is less expensive than deleting / making a new object, and is thus often used in techniques like Object Pooling.
For example, if you had a GameObject that you needed disabled, but you knew you were going to need it again soon, it is far less expensive to just call
MyGameObject.SetActive(false);
...
MyGameObject.SetActive(true);
Than it is to delete that object entirely, and then make a new one when you are ready for it again.
To add up to the other answers, when a component is disabled it means the MonoBehaviour callbacks are not called anymore. So no Update, FixedUpdate, OnCollisionXXX and so on.
Awake is called when the object is created so it is obviously enabled by default at that stage. Start is called on the first run after Awake. So if you set the component off in Awake, Start will wait until you set it back on from elsewhere.
OnEnable/OnDisable are called when you modify the enabled property.
SetActive works on the GO and is pretty much a shortcut to disable all components at once.
gameObject.SetActive(false);
foreach(var comp in GetComponentsInChildren<MonoBehaviour>()){
comp.enabled = false;
}
those are fairly similar in result (maybe not in efficiency). Actually, when you set a game object on/off, the OnEnable/OnDisable of each component is called. All MonoBehaviour callbacks are not called anymore if you set the object off.
So, the choice is dependent on what you are after, if you wish to disable movement but still see the object and other actions:
GetComponent<Movement>().enabled = false;
if you wish to kill an enemy:
enemy.gameObject.SetActive(false);
Note that even though a component is disable, does not mean you cannot interact with it. You can still manually call any method on it. Consider this:
AnyComponent ac = gameObject.GetComponent<AnyComponent>();
ac.enabled = false;
ac.AnyMethod();
Valid and will do what it is meant to do considering it does not require an Update or a FixedUpdate (Physics action).
A deactivated component cannot be found with GetComponent, you can get it with GetComponentInChildren(true) since it also searches the game object and its children. I am not sure whether it returns the first found or the first active found.
myGameObject.SetActive(false);
AnyComponent ac = myGameObject.GetComponent();
AnyComponent acic = myGameObject.GetComponentInChildren(true);
even though the GO has a AnyComponent attached, ac is null, acic is not (seems to be a 5.3 feature though).
http://docs.unity3d.com/ScriptReference/Component.GetComponentInChildren.html
Finally, for a component to expose the tick, it needs to have a Start method in the script (don't ask why...).
Setting .enabled turns one component on a GameObject on or off, e.g. an Image. Using SetActive() turns the whole GameObject on or off.
Choosing which to use should correspond with what you want to disable.
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
1.I have a class in which I have a method called ProjectileMotion() with return type IEnumerator and this method is being called from Start().
2. The above script is attached to a prefab that is instantiated from another class.
Problem:
In IENumerator ProjectileMotion() method, I need to have updated position of an object which is moving continuously,so I declared
//assigned the o=gameobject to it and it is moving continuously
Public Transform Target;
In ProjectileMotion(), if I do Target.transform,position, it gives the starting position only and not where it was at the time of instantiation of the prefab to which this script is attached, However, I am able to get the updated position of Target in Update method(checked using Debug.Log).
But I can not call the ProjectileMotion() in Update method of course, what should I do to get updated position of Target every time the prefab is instantiated and so the script is called.
Simply put ...
If you want the code to execute when the component is "started" then call it on start.
OR
If you want the code to execute each frame after the component has started then call it on update.
Judging by the name of this method I would think this is something that updates the position of a projectile after a gun has instantiated it.
The position being updated is likely a per frame operation so.
I would put this in the update method.
EDIT:
UNLESS ...
Does this function do a multiframe animation?
Unity has the concept of coroutines that allow to do certain actions of several frames. in this case you might be better off doing something like this in your start method ...
StartCoroutine(ProjectileMotion)