In this tutorial
- Accessing another script from inside the object
- How does GetComponent work?
- Getting many components
- Interaction between objects.
- Interaction between objects within the hierarchy
One recurrent issue when starting with Unity is how to access the members in one script from another script. Many would think that dereferencing from the script name would be enough, they quickly realize it is not.
When developing a program, variables are stored in the memory at different locations. If an object could see any other object members, there would a risk to modify them even though it was not intended. If for instances two objects hold the same script with the same variable names, the compiler would not be able to figure out which one we are referring to.
To prevent this issue, each object in memory cannot see other objects. It is then necessary to tell one object where the variable it needs is in memory. The whole pointer principle has been dropped long ago and now we are using reference instead (which actually uses pointer internally).
There are actually various way to access variables, some are better than others and some are simply to be used in particular situations.
To summarize, there are three ways:
-GetComponent, the most common, also the one that confuses most at first
-SendMessage, it might look easier to grasp but it is also less efficient.
-static variable, the easiest to use but also the most complicated to fully understand at first.
In this tutorial we will cover the GetComponent section using Boo.
Create a new scene and add an empty game objects and name it “ObjectA”. Create two scripts named “ScriptA” and “ScriptB”. Add both scripts to ObjectA.
Open up both scripts in the editor and add these lines:
import UnityEngine import System.Collections class ScriptA (MonoBehaviour): public varBinA as int public otherBinA as int def Update (): if Input.GetKeyDown (KeyCode.Space): varBinA = ScriptB.GetVarB() otherBinA = ScriptB.varB if varBinA > 0: ScriptB.AddVarB(10)
import UnityEngine class ScriptB (MonoBehaviour): public varB as int def Start (): varB = 20 def Update (): if Input.GetKeyDown(KeyCode.P): Debug.Log ("varB in ScriptB $varB") def GetVarB () as int: return varB def AddVarB (num as int): varB += num
Trying this code will simply return error “An object reference is required to access non-static members”.
We need to tell ScriptA where is ScriptB so that it can access its members. Since our scripts are both on the same object we simply need to declare a variable of type of the script we wish to access and use GetComponent to find the script.
Accessing another script inside the object
Modify ScriptA with:
import UnityEngine class ScriptA (MonoBehaviour): public scriptB as ScriptB public varBinA as int public otherBinA as int def Start (): scriptB = GetComponent[of ScriptB]() def Update (): if Input.GetKeyDown (KeyCode.Space): varBinA = scriptB.GetVarB() otherBinA = scriptB.varB if varBinA > 0: scriptB.AddVarB(10)
With the modifications, run the game, press P, it should show 20 in the console, press Space and then P again, the value is now 30. It was also possible to access varB and assign it directly to varBInA since varB is public.Note that accessing methods is done the same as for variables.
If the members were private, it would not be possible to access them.
How does GetComponent works?
First we declare an object of the type of the script we want to reach.
The GetComponent function will look inside the object to find a component corresponding to the type we passed. If none is found a null reference is returned and our script variable will not do anything. If a component of type ScriptB is found then the address of that component is passed to the variable script, the variable now points to the ScriptB component stored somewhere in memory. In order to access the public members of ScriptB, we just need to dereference the script variable using the dot operator.
Private members will remain inaccessible.
So I can cache component with GetComponent?
Yes, and it is recommended to do so for some regularly used component like the Transform.
When accessing built-in component such as Rigidbody, Transform, Renderer,...etc, Unity allows "direct" access like this:
def Update(): transform.position.x += 10
But what Unity does behind is actually:
def Update(): GetComponent[of Transform]().position.x += 10
GetComponent should be avoided inside Update so cache with a few simple lines of code like:
trans as Transform def Start(): trans = GetComponent[of Transform]() def Update(): trans.position.x +=10
This will save some computation.
Getting many components
What if our ObjectA has many ScriptB within itself. Duplicate the ScriptB twice so ObjectA has three of them.
import UnityEngine class ScriptA (MonoBehaviour): public scriptB as (ScriptB) def Start (): scriptB = GetComponents[of ScriptB]() def Update (): if Input.GetKeyDown (KeyCode.Space): for i in range(scriptB.Length): scriptB[i].AddVarB(i)
Run the game, press P, the console prints out three 20. Press Space and P again, the console should look like this:
Because I used the value of i in the loop as parameter of the function, each script has received a different value. Note that since the loop starts from 0, one script does not change.
Interaction between objects.
We can also get two objects to interact in a similar way. Create a new object named “ObjectB”. Remove the ScriptB component from ObjectA and add it to ObjectB.
Run the game, you can print but pressing Space will throw a Null reference exception. This is due to the fact that the ScriptB is no more on ObjectA so GetComponent simply does not find it. We need to find the ObjectB first using GameObject.Find(“ObjectB”).
You only need to add the line below in the ScriptA:
def Start (): scriptB =GameObject.Find ("ObjectB").GetComponent[of ScriptB]()
GameObject.Find() first finds the object passed as parameter and then looks for the required component inside of it. It is recommended to perform those actions in the Start or at a time of the game where lags will not affect the gameplay. GameObject.Find and GetComponent are expensive functions since they need to iterate through the whole Hierarchy. If your scene only includes 10 objects, it will be fine but if it holds thousands of them you will surely see a bad effect. Consider calling these in the Start function.
Interaction between objects within the hierarchy
Drag ObjectB into ObjectA.
Run the game and you will notice it works fine. Still, we can make it happen better by using commands that tell the compiler that ObjectB is a child of ObjectA. Hence, no need search all the objects of the scene.
def Start (): scriptB =transform.Find ("ObjectB").GetComponent[of ScriptB]()
GameObject is swapped for transform. Indeed, a child object belongs to the transform of the parent so we need to look into the transform to find our object.
Even better, we can use:
def Start (): scriptB =GetComponentInChildren[of ScriptB]()
Note that it also works if you have children of children holding the script. Create an “ObjectC”, add ScriptB to it and add the object to ObjectB. Now A has B which has C.
Change ScriptB for:
def Update(): if Input.GetKeyDown (KeyCode.P): Debug.Log ("$gameObject.name $varB")
Run the game, you will see that both scripts are found and altered.
GetComponentsInChildren will work the same if all children have many instances of one script.
Interaction in action
In order to give a visual and practical representation, the video below summarizes some of the uses for GetComponent.