- Home /
Behind the scenes voodoo w/ Instantiate
I'm trying to wrap my head around the relationship between Unity objects and C# classes and types. To illustrate, I have a prefab called PuckPrefab. It has two C# scripts attached as components: FallScriptCS and OtherPropsCS. I also have a manager object called LogistixMgr, that has a single script component called LogistixScript. So the setup looks like this:
PuckPrefab
FallScriptCS
OtherPropsCS
LogistixMgr
LogistixScript
Here's the subtle and confusing part. LogistixScript has a member variable:
public class LogistixScript : MonoBehaviour {
public FallScriptCS _puckPrefab;
In the Unity editor pane, I drag the PuckPrefab object onto the __puckPrefab_ field. This seems explicable, in a way: a puckPrefab has a component that is a FallScriptCS; if we think of "having a component" as kinda-sorta like "being a superclass of" then it makes sense why Unity would allow me to map a PuckPrefab object to a variable of type FallScriptCS.
Later, however, I have this bit of code (also in LogistixScript):
FallScriptCS newPuck = Instantiate(_puckPrefab, pos, rot) as FallScriptCS;
What this seems to do is to instantiate an actual PuckPrefab object complete with its FallScriptCS component (as opposed to instantiating just a FallScriptCS object by itself). This is actually what I want it to do, but that's not the point. The point is: how is the compiler figuring out how to do all of this behind my back? The type that is being instantiated is declares as FallScriptCS; the compiler seems to be saying: "Well, you can't actually instantiate a script; but the thing to which this particular script is attached happens to be a GameObject, and I'll instantiate that instead."
The implication seems to be that if I make a generic container, like:
Dictionary<string, FallScriptCS> pDict = new Dictionary<string, FallScriptCS>();
... and then populate it with the instantiated item:
pDict.add(newPuck)
then what's getting added is actually a GameObject?
So I guess I'm asking is what is the relationship between a GameObject (like PuckPrefab) having components, and those components being treated (sort of) like descendants in a type hierarchy? What are the rules for type checking and object identity that makes this work?
Answer by whydoidoit · Feb 01, 2013 at 08:37 PM
Ok so don't think inheritance think composition. It's really something you could do yourself, but it kind of messes you up when you first get started.
GameObject is an class that contains a list of components, an array of them if you like.
You attach instances of these objects to the GameObject which it stores in the array and then it calls special functions on them when the framework dictates. Because they all have references to common things like Transform for positioning they appear to be one object, but they are not. They are just children in an array that are provided the privilege of changing things about where their container is etc. GetComponent is just a call to search through that array and return an object of the right type if it exists or null if it does not.
So what the editor does when you drag on that game object is it calls GetComponent on it and sees if it has the right type for the variable. If it does it stores that component.
Now Transform, your scripts etc etc all inherit from Component - which of course has a reference to its container - GameObject.
So when you call Instantiate it sees its a Component derivative, creates the associated GameObject and all of its Components in its array (deep copies it effectively) then returns the Component type that was used to find the GameObject reference.
The property transform just does this:
Go to the game object list of components
Search it for one that is Transform
return that
That's why you should cache it. It's not really a stored value, it's a search.
BTW a quick way to cache it if you haven't already
public class SomeScript : $$anonymous$$onoBehaviour
{
public new Transform transform;
void Awake()
{
transform = base.transform;
}
}
After that your transform is cached, but the rest of your code does not need to change.
What a great answer, just exactly what I was hoping for. Thanks!
No problem - BTW it's one of those other weird techniques - but you can change things in the prefab before Instantiate and it will instantiate with those values before Awake.
prefab.GetComponent< $$anonymous$$yScript>().someVariable = someValue;
var newThing = Instantiate(prefab) as Whatever;
btw, is it confirmed that GetComponent does its search w/ a loop? One might imagine that a GameObject indexes its components in a hash table, which would make returning the component O(1). I checked the dox, but there's no mention of it.
They're pretty smart about this stuff, so it's probably o(1) but bear in $$anonymous$$d that a Send$$anonymous$$essage is 150x slower than a delegate or event.
Answer by Jessy · Feb 01, 2013 at 08:38 PM
If a game object, component or script instance is passed, Instantiate will clone the entire game object hierarchy, with all children cloned as well. All game objects are activated.
If the object is a Component or a GameObject then entire game object including all components will be cloned.
That's what UnityEngine.Instantiate does. It's not just a constructor + deserializer or something. You don't use constructors on components; they don't exist independently of game objects.
http://docs.unity3d.com/Documentation/ScriptReference/Object.Instantiate.html
Thanks. Believe it or not, I started on the Instantiate page, but apparently was blinded by my own preconceptions of Instantiation == construction and so didn't absorb it. Appreciate the pointer.