- Home /
AddComponent passing variable before Start/Awake
How can we AddComponent() to a gameObject and passing variable to it, but the component can read the variable in Start() / Awake()? It seems Start / Awake already ran in AddComponent line... I just want it check once so it seems not optimised having it checked in Update()
MyClass mc = AddComponent("MyClass") as MyClass;
mc.myVar = 1;
In MyClass.cs:
public class MyClass : MonoBehaviour
{
public int myVar = 0;
void Start ()
{
if (myVar == 1)
{
// Do something
}
}
}
p.s. I tried Add component in one line with parameters, but it seems do nothing to Start() / Awake();
Can I ask what you're trying to accomplish? Generally if you need the script to start with the component on it, you need to assign it in the editor.
If you are trying to make sure that some gameobject actually has a component, then look at RequireComponent
I think you'll also end up in some kind of weird infinite loop when you try to add a component [to a GameObject] that already has that script on it.
thx for ur reply.
basically im trying to dynamically read a list of inventory items and add it's corresponding skill (eg. HPRegen, CallTank extending Skill class) class to a button, and pass some variable into it (eg. damage, cooldown etc)....
that's why I want the skill is set up in Starts(), of course I can put a bool in update to make the initialise run only once... but just curious about a better way
damn... you are right... just call Start() after passing the variable, or simply make a manual function like Initialze().... how easy....
$$anonymous$$essing things with lifecycle may have ugly circumstances in future. So, I wouldn't even try to do such thing. Why don't create another component with data (call it $$anonymous$$yData or something), add it and then add component $$anonymous$$yClass, which will rely on it (via GetComponent())? Code organization becomes simple when you first think of what you are trying to accomplish and what Unity means you have
Answer by Dani Phye · Nov 21, 2014 at 11:22 PM
This answer is a little late, but I was using NonGamingCoder's solution and I ran into a problem when I needed a component to create another component in Awake, but that third object initialization required the values I initialized in the second object during awake. So basically I needed something "before awake." I found a sorta hacky solution that seems to work pretty well though - and it is a general enough pattern it can be adapted to lots of different things once you get the hang of it.
Imagine we start with a GameManager object, and on GameManager's Awake we want to make a Player, which then makes a FishingPole - yet we want the player and the fishing pole to both have a reference to the GameManager at the beginning of their awakes. Then the code would be:
public class GameManager : MonoBehaviour
{
public Player player;
public Awake()
{
GameObject playerObject = new GameObject();
player = Player.AddPlayerComponent(playerObject, this);
player.gameObject.name = "Player";
}
}
public class Player : MonoBehaviour
{
public GameManager gameManager;
public FishingPole fishingPole;
public static Player AddPlayerComponent(GameObject objectAddingPlayerTo, GameManager gameManager)
{
objectAddingPlayerTo.SetActive(false);
Player player = objectAddingPlayerTo.AddComponent<Player>() as Player;
// Variable initialization goes here -
// All of this will be called before Player's awake because
// the object holding the Player component is currently not active.
player.gameManager = gameManager;
objectAddingPlayerTo.SetActive(true);
return player;
}
public Awake()
{
GameObject fishingPoleObject = new GameObject();
// We can use the gameManager variable here because it will already be initialized
// since akwake isn't called until sometime after we called
// objectPlayerAddingTo.SetActive(true);
fishingPole = FishingPole.AddFishingPoleComponenet(fishingPoleObject, this, gameManager);
fishingPole.gameObject.name = "Fishing Pole";
}
}
public class FishingPole: MonoBehaviour
{
Player player;
GameManager gameManager;
public static FishingPole AddFishingPoleComponent(GameObject objectAddingFishingPoleTo, Player player, GameManager gameManager)
{
objectAddingFishingPoleTo.SetActive(false);
FishingPole fishingPole = objectAddingFishingPoleTo.AddComponent<FishingPole>() as FishingPole;
fishingPole.player = player;
fishingPole.gameManager = gameManager;
objectAddingFishingPoleTo.SetActive(true);
return fishingPole;
}
public Awake()
{
// This will begin with player and gameManager already pointing to the other objects
}
}
This is excellent! Considering that many developers have a practice of extending $$anonymous$$onoBehaviour into a class of their own, this can be added as an extended feature and made to look less hack-ish ;)
Amazing answer. Tackles perfectly the problem in a way that can be used in other contexts. Solved one of my problems with some adapting and changing :)
I would suggest an extension method like this:
public static class GameObjectExtension
{
public static T AddComponentWithInit<T>(this GameObject obj, System.Action<T> onInit) where T : Component
{
bool oldState = obj.activeSelf;
obj.SetActive(false);
T comp = obj.AddComponent<T>();
if (onInit != null)
onInit(comp);
obj.SetActive(oldState);
}
}
With this extension method somewhere in your project you can do this:
GameObject someObject;
someObject.AddComponentWithInit<FishingPole >(f=>{f.player = player; f.game$$anonymous$$anager = game$$anonymous$$anager});
This allows you to specify a custom callback which is executed before Awake. Inside that callback you get the reference to the newly added component. It also doesn't force the gameobject to be active at the end but restores the state it had when the method was called. So if the GameObject was already deactivated it stays deactivated.
Another approach would be to use a temp class with IDisposable to allow a neat disable block in case you want to add multiple components:
public class GameObjectDeactivateSection : IDisposable
{
GameObject go;
bool oldState;
public GameObjectDeactivateSection(GameObject aGo)
{
go = aGo;
oldState = go.activeSelf;
go.SetActive(false);
}
public void Dispose()
{
go.SetActive(oldState);
}
}
public static class GameObjectExtension
{
public static IDisposable Deactivate(this GameObject obj)
{
return new GameObjectDeactivateSection(obj);
}
}
With those two classes you can simply do:
GameObject someObject;
using(someObject.Deactivate())
{
FishingPole pole = someObject.AddComponent<FishingPole>();
pole.player = player;
pole.game$$anonymous$$anager = game$$anonymous$$anager;
}
The gameobject will be deactivated inside the using section. As soon as the section is left the old state of the gameobject will be restored.
Great classes, easy to use. I just want to update it little bit. Add this to GameObjectExtension:
public static T AddComponent<T>(this GameObject gameObject, Action<T> action) where T : Component
{
using (gameObject.Deactivate())
{
T component = gameObject.AddComponent<T>();
if (action != null) action(component);
return component;
}
}
And now, you can do just this:
gameObject.AddComponent<AudioSource>(x => x.pitch = 0.1f);
A problem with disabling and enabling a GameObject is that it kills any Coroutines that were running on the object.
It also invokes OnDisable
and OnEnable
on all components in the game object's hierarchy, which might have side effects.
Answer by petrucio · Oct 26, 2017 at 06:39 AM
Credit to Dani Phye for the starting solution, but it's a giant wall of code that can be stripped down to the part the concerns people looking for the answer:
this.gameObject.SetActive(false);
var mc = this.AddComponent<MyClass>;
mc.myVar = 1;
// Awake of MyClass will only get called when the object is re-enabled below. Voila!
this.gameObject.SetActive(true);
Yes! Anyone still looking here, give this solution a try!
The caveat with this approach is that this will call OnDisable
and OnEnable
on every component in gameObject
's hierarchy, and it will also kill all coroutines running on those components.
hacky but ok! Hopefully your Behaviours don't call anything you don't want upon being enabled/disabled.
Answer by NonGamingCoder · Aug 29, 2013 at 08:25 AM
Dude,
I've been hitting this kind of thing recently. It's not the nicest solution, as in it's quite ugly, but here it goes:
Create objects during Awake()
Initialize during Start()
Eg.
public class MyClass : MonoBehaviour
{
public int myVar = 0;
public void Awake()
{
// We want to see what order things are getting called...
Debug.Log(string.Format("{1}@{0} Awake", name, GetType().Name));
}
public void Start()
{
Debug.Log(string.Format("{1}@{0} Start", name, GetType().Name));
if (myVar == 1)
{
// Do something
}
}
}
public class MyClassHost : MonoBehaviour
{
private MyClass _mc;
public void Awake()
{
Debug.Log(string.Format("{1}@{0} Awake", name, GetType().Name));
_mc = AddComponent<MyClass>();
}
public void Start()
{
Debug.Log(string.Format("{1}@{0} Start", name, GetType().Name));
// By the time we get to here, MyClass.Awake will have been called.
_mc.myVar = 1;
}
}
Like I said, it's not the nicest solution, you would normally want to do this all in one go, and if it wasn't unity, you'd do this in the constructor.
Also, Unity is going to call Start(), so don't do it yourself. Utilise Unity's lifecycle and let Unity do it's magic. Run this code as-is, and watch the console. You'll see Start() gets called, but maybe not in the order you were expecting.
Disclaimer: No warranty is provided.
Answer by DawidNorasDev · Jun 19, 2018 at 11:35 AM
Why do you only let script initialization happen in Start / Awake ? Make public method Init(), and call it after you have set everything you wanted. Or even call it with those parameters
Answer by K0m1x · Nov 15, 2021 at 09:51 AM
Turns out the way you do it is simple... Set the object to inactive before you addcomponent, and the awake function will only play when you set it to active again.
Your answer
Follow this Question
Related Questions
How to reference GameObject in Awake or Start 2 Answers
If a script is attached to more than one gameObject, will Start() & Awake() run more than once? 2 Answers
Strange Race condition for one prefab 1 Answer
How to change variables on a new gameobject's script 1 Answer
Can I use AddComponent to add (this.script) to an object? 1 Answer