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
- SendMessage and BroadcastMessage
- Interactive functions
Introduction
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 variables, the easiest to use but also the most complicated to fully understand at first.
In this tutorial we will cover the GetComponent section using C#.
Create a new scene and add an empty game objects and name it “StormTrooper”. Create two scripts named “Health” and “Force”. Add both scripts to StormTrooper.
Open up both scripts in the editor and add these lines:
Health.cs
using UnityEngine;
using System.Collections;
public class Health : MonoBehaviour
{
public int health = 5;
bool hasForce;
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
hasForce = Force.ReportForce();
if(hasForce)health = 10;
}
}
}
Force.cs
using UnityEngine;
using System.Collections;
public class Force : MonoBehaviour
{
private bool force;
void Start()
{
force = true;
}
void Update()
{
if (Input.GetKeyDown(KeyCode.P))
print("I have" + (force?" the":" no") + " force");
}
public bool ReportForce()
{
return force;
}
}
Trying this code will simply return error “An object reference is required to access non-static members”.
We need to tell Health where is Force 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 Health with:
using UnityEngine;
using System.Collections;
public class Health : MonoBehaviour
{
public Force script;
public int health = 5;
bool hasForce;
void Start(){
script = GetComponent<Force>();
}
void Update() {
if (Input.GetKeyDown(KeyCode.Space)) {
hasForce = script.ReportForce();
if(hasForce)health = 10;
}
}
}
With the modifications, run the game, press P, your StormTrooper now has 10 of health (Note that a StormTrooper does not have the force, this is only for the example). You access variables the same you accessed methods. Just make sure the members you are trying to reach are declared public or you will get an error.
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.
Force script;
Force is our type and script is just a name we provide for using the reference in the script. We could give it any name.
The GetComponent function will look inside the object (the StormTrooper instance) 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 Force is found then the address of that component is passed to the variable script, the variable now points to the Force component stored somewhere in memory. In order to access the public members of Force, we just need to dereference the script variable using the dot operator.
script.PublicMembers;
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:
void Update(){
transform.position += transform.forward;
}
But what Unity does behind is actually:
void Update(){
GetComponent<Transform>().position += GetComponent<Transform>().forward;
}
GetComponent should be avoided inside Update so cache with a few simple lines of code like:
Transform _transform;
void Start(){
_transform = GetComponent<Transform>();
}
void Update(){
_transform.position += _transform.forward;
}
This will save some computation.
Getting many components
What if our StormTrooper has many Force within itself (not likely though). Duplicate the Force twice so StormTrooper has three of them.
Health becomes:
using UnityEngine;
using System.Collections;
public class Health : MonoBehaviour
{
private Force [] scripts;
public int health = 5;
void Start()
{
scripts = GetComponents<Force>();
}
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
for (int i = 0; i<scripts.Length; i++)
if(scripts[i].ReportForce())health+=5;
}
}
}
Run the game, press Space, our StormTrooper is becoming a Jedi.
Interaction between objects.
We can also get two objects to interact in a similar way. Create a new object named “Sith”. Remove the duplicated component from the StormTrooper.
using UnityEngine;
using System.Collections;
public class DarkSide : MonoBehaviour {
const bool force = true;
Force script;
void Start () {
script = GameObject.Find ("StormTrooper").GetComponent<Force>();
}
void Update () {
if(Input.GetKeyDown (KeyCode.Space))
UseForceToControl ();
}
void UseForceToControl(){
if(!script.ReportForce())
print ("Do as I say.");
}
}
Modify the force script of the StormTrooper so that he does not have the force (The Sith would find this suspicious...)
Below is the line that finds the StormTrooper and get its Force component.
void Start () {
script =GameObject.Find ("StormTrooper").GetComponent<Force>();
}
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 (an army of clone) you will surely see a bad effect. Consider calling these in the Start function.
Interaction between objects within the hierarchy
Consider a vehicle, the character is attached to it when driving it. Let's think the vehicle has special feature if the driver has the force (better handling, faster and so on). As the driver is attached, he is below in the hierarchy.
The vehicle can find the Force component of the driver with the same principle we used previoulsy but we can make it happen faster by indicating to the vehicle that the driver is in its hierarchy.
Force script;
void Start () {
script =transform.Find ("Sith").GetComponent<DarkSideForce>();
}
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:
void Start () {
script =GetComponentInChildren<DarkSideForce>();
}
Now our vehicle can know that our Sith has the force and apply special feature.
In this example, we need to check for each kind of driver if it is a StormTrooper or a Sith. A better approach would have been to use inheritance so that the top base class would hold the force variable and one single function would be used for all type of driver. But inheritance and efficiency were not our main issue here.
SendMessage and BroadcastMessage
We have seen different ways toget two objects or scripts to interact. SendMessage and BroadcastMeassage are two other possibilties that may be simpler but also more expensive. The principle remains the same, we have a reference to an object and we want to modify a script it contains.
With SendMessage, you can call a function on an object without having to find a reference to the script. Now you tell me: "What is all this confusion ride if you can make it simple?"
And that you should seem logical, with GetComponent we indiacte exactly where the script is located in memory. With SendMessage, the compiler knows the object, but it will go through all the scripts and functions until it finds the lucky winner. This may affect your game if you use it often on multiple objects adn if the object has a lot of components and scripts (which are components by the way).
SendMessage seeks only the object to which the call is made, BroadcastMeassage launches a search on the target object but also on all of its children. Research will be even longer if the object contains a large hierarchy. A newcomer appeared with Unity4 SendMessageUpwards which you should have guessed does the same as BroadcastMessage but upwards so all of the parent of the object until root.
Ok let's consider our Stormtrooper has a function for receiving order
//Order.cs
using UnityEngine;
using System.Collections;
public class Order : MonoBehaviour
{
void ReceiveOrder(string order){
switch(order){
case "Fetch":
//Action
break;
// other cases
}
}
}
Now let's consider our StormTrooper meets a commander from the Empire:
// Commander.cs
void Update(){
if(Input.GetKeyDown(KeyCode.Space))
GameObject.Find("StormTrooper").SendMessage("ReceiveOrder","Fetch");
}
As you can see, the principle remains the same, I need a reference to the target object and then I send a message to it. Just like GetComponent it is better if you have cached the object reference. Also, I simplified the implementation but an enum would go way better than a string for the function (faster and less prone to error).
It is also possible to add a parameter to make sure the message finds a receiver or not.
Note that it is not possible to use a function returning a value. You would then have to go back up and use GetComponent.
Interactive functions
Unity uses many features that make two or more objects interact. The advantage of these functions is that they store a lot of information on the other object.
- Collision functions OnTriggerXXXX/OnCollisionXXXX
- Physics functions XXXXcast
Here it is for collision declaration:
void OnCollisionEnter(Collision colReference){}
void OnTriggerEnter(Collider colReference){}
The call of the function creates a variable of type Collision or Collider. The function will fill this instance with information on the collision and will hold a reference to the other object we are interacting with.
void OnCollisionEnter(Collision colReference){
if(colReference.gameObject.tag == "StormTrooper"){
Health script = colReference.GetComponent<Health>();
script.health -= 5;
}
}
Notice that I do not need to look for the other object as colReference as a reference to that object as member. Now I can perform my GetComponent as usual and access my public health variable.
Functions from the physics class have a similar principle. The data are not stored in a Collision/Collider instance but in a RaycastHit instance:
using UnityEngine;
using System.Collections;
public class CheckFlat: MonoBehaviour {
void Update() {
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit)){
float result = Vector3.Dot(hit.normal,Vector3.up);
if(Mathf.Abs(result) > 0.9)print ("Flat Enough");
else print ("Not Flat Enough");
}
}
}
I declare a variable of type RaycastHit, I pass it to the function. RaycastHit is a structure and then a value type, I need the out keyword to create a reference to that structure so that the function can modify the structure. What I am doing here is check how flat is the surface I am hitting. This is useful for strategic games where you want to build but not on a cliff.
I use the dot product of the terrain normal with the Vector3.Up and if the result is close enough to 1 meaning they are aligned, the ground is flat enough for construction.
Obviously, some more calculation are needed as this is only for one point and you would need to check the normal around but you get the idea. I created interaction between my mouse and the terrain using a raycast.
Interaction in action
In order to give a visual and practical representation, the video below summarizes some of the uses for GetComponent.


October 21, 2012 - 11:22 am
[...] can find the exact same tutorial using C# here [...]
October 21, 2012 - 11:49 am
Yes, it is coming within a few minutes. Fact is I am working on it as you are trying to access it and I already modified the rest of the pages (which I shouldn’t).
Please forgive my lack of professionalism on this issue…
October 27, 2012 - 6:57 am
[...] using GameObject.Find() for instance will return a reference to the object in any scripts. See our GetComponent tutorial for more details on finding instances of classes during game [...]
January 5, 2013 - 6:14 pm
[...] fetch the CC so that you can manipulate it. For a tutorial on how and why fetching component, see here. We also make sure our object contains a CC using an attribute placed before the [...]
February 2, 2013 - 12:16 pm
Great work. The basic things that looked complicated for me like Shaders, Delegates etc are now much more clearer for me.
p.s. ScriptA.cs might be named Health.cs
February 3, 2013 - 8:20 am
Thanks I will got through it again. I changed the script names to make it easier than ScriptA ScriptB but it seems I forgot some…
Cheers
Fafase
February 14, 2013 - 11:55 pm
Thanks a lot, You solved all my queries.
March 14, 2013 - 8:56 pm
Lets say, i have a lot of statements in OnCollisionEnter function, we’ll be better to cache collider data and say:
void OnCollisionEnter(Collision colReference){
TAG = colReference.gameObject.tag
if(TAG == “StormTrooper”){
else if TAG ==
else if TAG ==
}
or not?