Before we can move forward with our lectures, there is one important concept that we need to understand when it comes to objects.
Variables And Computer Memory
When we create a new object by typing:
Player warrior = new Player();
What happens behind the scenes is that a new spot is created in the memory for that object, and the warrior variable is pointing to that spot in memory:
But what does that even mean?
Well let’s take a look at this example:
int a = 5;
Debug.Log("The value of a is: " + a);
int b = a;
b = 1;
Debug.Log("The value of a is: " + a);
Debug.Log("The value of b is: " + b);
If we run the game this is what we will see in the Console:
First we create variable a and give it a value of 5, when we print the value of that variable we see 5 is being printed in the console.
Then we create variable b and assign a to it, which means variable b will have the same value as variable a and we see that after we print the value in the console.
After that we changed the value of b to 1 and we printed values for both a and b in the console and saw that the value for variable a is still 5 and value for variable b is 1.
This is expected behaviour, so what is the point of me explaining this?
Well let’s take a look at this example now:
Player warrior = new Player();
warrior.SetHealth(100);
Debug.Log("Warriors Health is: " + warrior.GetHealth());
Player newWarrior = warrior;
newWarrior.SetHealth(23);
Debug.Log("Warriors Health is: " + warrior.GetHealth());
Debug.Log("NEW Warriors Health is: " + newWarrior.GetHealth());
When we run the game this is what we will see in the Console:
First we set the health value of the warrior variable to 100 and we print that to the console. The expected value is printed.
After that we create a new Player variable and name it newWarrior but we set its value to be the previous warrior variable that we created.
After we change the health value of new Warrior to 23 and print the health value for both newWarrior and warrior variable we see that both of these values are now 23 even though we only changed the health value of newWarrior.
So what is the case here?
How Does A Reference Variable Work?
Going back to our visual example of what happens when we create an object variable where a spot is created in memory for that object and the variable is only a reference to that spot, when we create a new object variable of the same type(newWarrior) and we set it to be equal to the previously created variable(warrior) then the new variable (newWarrior) is pointing to the same spot in memory and a new spot in memory for that reference will not be created:
This means that any change we make to the newly created variable(newWarrior) will also apply to the previously created variable(warrior) because both variables are pointing to the same spot in memory and they are referencing the same object.
This is the reason why the health value of warrior changed even though we changed the newWarrior’s health value. And this is the difference between object variables and primitive type variables like an int or a float.
This is very important to know especially when working with Unity because every game object that is placed in the scene is counted as an object on it’s own.
And if we get a reference to that object from multiple places, any change we make to it will be applied to that single game object, and if we don’t understand this concept we can mess things up very quickly.
Unitys Get Component Function
So far we’ve been creating new objects of the Player class inside of our LearningHowToProgram class and then we were running the game to test the outcome.
The reason for this is because only class that inherit(more on this in the next lecture) from MonoBehaviour can be attached on game objects in the scene.
When we first created the Player class we removed that part and now we are going to put it back. The new Player class should look like this:
public class Player : MonoBehaviour
{
private int health = 100;
private string playerName;
public void Attack()
{
Debug.Log("The " + playerName + " is attacking");
}
public void SetHealth(int healthValue)
{
health = healthValue;
}
public int GetHealth()
{
return health;
}
public void SetName(string newName)
{
playerName = newName;
}
public string GetName()
{
return playerName;
}
}
Now that the Player class is inheriting from MonoBehaviour, in the editor in the hierarchy tab Right Click -> Create Empty, this will create a new empty game object.
Double click on the new object and rename it to Player and then attach the Player script on that game object.
Inside the LearningHowToProgram class add the following lines of code:
private Player warrior;
void Start()
{
warrior = GameObject.Find("Player").GetComponent();
warrior.SetName("Warrior");
Debug.Log("The name of our character is: " + warrior.GetName()); warrior.Attack();
}
If we run the game this is what we will see in the Console:
So what are all these lines of code doing?
First we declared a private variable type of Player and we named it warrior.
In the Start function we got a reference to the Player object in the Scene by using GameObject.Find(“Player”).GetComponent<Player>() – the first part of this code e.g. GameObject.Find(name of object) will find a game object inside the Scene or inside the Hierarchy tab that has the name that we specify as the parameter.
In our case we named the game object Player, and we provided Player as the name for the parameter.
One important thing to note here is that the name of the object inside the Hierarchy needs to be exactly the same as the name you provide in code.
If the name of the object in the Hierarchy is player with lower case p, and in the code you provide Player with capital P, then this will not work and you will get a null reference exception.
We will see more examples of this in our development.
Then we used GetComponent<Player>() this will get the Player script component that is attached on that game object and it will store a reference to it inside of our warrior variable.
We already talked about how can we attach scripts on game objects, and that is nothing new:
And by using GetComponent() we get a reference to the specified component, in this case we got a reference to the Player script.
And this counts as an object and our warrior variable is now pointing to the Player script and we can use the warrior variable to manipulate it.
If we modify the code inside the LearningHowToProgram class like this:
private Player warrior;
private Player archer;
void Start()
{
warrior = GameObject.Find("Player").GetComponent();
warrior.SetName("Warrior");
Debug.Log("The name of our character is: " + warrior.GetName());
archer = GameObject.Find("Player").GetComponent();
archer.SetName("Archer");
Debug.Log("The name of our character is: " + archer.GetName());
Debug.Log("The name of the warrior character is: " + warrior.GetName());
}
And when we run the game this is what we will see in the Console:
We will see that the warrior and the archer variable are pointing to the same object e.g. same spot in memory and they are referencing one and the same object.
If we go inside the editor and in the Hierarchy tab Right Click -> Create Empty, then double click this new game object and name it Player2.
Let’s modify the previous code to the new one:
private Player warrior;
private Player archer;
void Start()
{
warrior = GameObject.Find("Player").GetComponent();
warrior.SetName("Warrior");
Debug.Log("The name of our character is: " + warrior.GetName());
archer = GameObject.Find("Player2").GetComponent();
archer.SetName("Archer");
Debug.Log("The name of our character is: " + archer.GetName());
Debug.Log("The name of the warrior character is: " + warrior.GetName());
}
When we run the game this is what we see in the Console:
As you can see now the warrior variable is pointing to one object, and the archer variable is pointing to the second object and both variables are now references for different spots in memory.
This is the concept on which Unity works and that’s why it is really important that we understand that.
From now on, whenever we want to manipulate a component that is attached on a game object we will use GetComponent to get the specified component.
That can be done in another class or inside the same class and we will see examples of getting a reference to the Rigidbody(a component that applies physics to a game object) so that we can move a game object, or getting the Animator component(a component that animates a game object) to animate the player and so on.
So far we laid out a very strong foundation when it comes to programming with C# in Unity and we learned quite a lot of things.
All of these concepts will be repeated over and over when we start creating our games and we will see with real world examples how everything works together to form a playable game.
Where To Go From Here
In this lecture you learned about object references and how to get components in Unity Engine.
To continue your learning journey you can take a look at our Introduction To Inheritance Lecture which is the next lecture in this tutorial series.
2 thoughts on “C# Programming With Unity – Object References And GetComponent”
as a beginner, these tutorials have been absolutely helpful — thank you for them. I do have a question though, and it relates to the rest of these beginner blogposts – are there intentional mistakes? e.g. creating an empty object under the hierarchy in Unity 2021.3.14f1 Personal* but not attaching a component/script to the object (ie “Player2”) and generating the same code will return a nullpointerexception
Yep I got the same error. I think one step was missed, which was to attach the player script to the new player 2 object.
After doing that, it should work.