In this article we see how to CALL FUNCTIONS and READ VARIABLES that are defined in a different script in Unity. This is especially important to create object-oriented solutions, because being able to access other scripts allows us to create scripts that solve specific problems and work with other scripts to achieve a bigger result, separating responsibility, increasing abstraction. In addition you will also find two videos about calling functions and reading variables from another script in Unity.
Videos on this topic:
How to CALL A FUNCTION from another script in Unity
How to READ A VARIABLE from another script in Unity
Interaction between scripts — A couple things to keep in mind
Let's look at a list of items to keep in mind to understand the basis behind the interaction between scripts.
About the Scripts
In this case we are going to see an example in Unity, therefore the two Scripts we are going to use will be an extension of the MonoBehaviour class. This, in practical terms, means that when the game starts, a "Start" function will be executed and also in each frame of the game an "Update" method will be automatically executed.
About the execution of Scripts
In order to execute the code that we will include in our scripts, those scripts must be assigned to at least one "GameObject" in the hierarchy of the scene we will run. In programming, there must exist an Instance of the class defined in the Script.
Instances of a classes
When we add the same Script to two different GameObjects we are creating two separate instances of the class, i.e. two objects that are going to be similar because they come from the same class, but their internal state will not necessarily be the same all the time.
Having the reference of an object
This is one of the key points to pay attention to and try to understand in depth.
Objects in programming are instances of a certain class and in order to access its functionality, that is to say read its public parameters or execute its public functions, we must have the reference of the object we need to use, in other words find the object among all the other objects that exist in the program.
The Dot Operator
The dot operator in several programming languages allows us to access the public fields and methods from an object. In other words, if we have the object reference, we can apply the dot operator to it in order to execute any of its public methods or read and write any of its public fields.
Practical example of Interaction between Scripts in C# — Unity
In any Unity project we create two Scripts, ScriptA and ScriptB, in the first one we define the function that will be executed from the second Script.
In the hierarchy we are going to create two empty GameObjects to be able to create instances of both Scripts.
In GameObjectA we add the ScriptA as component and in GameObjectB the ScriptB. We can do this from the inspector with the "Add Component" button or by dragging the script to the inspector of the GameObject.
Code inside the Scripts
Let's write the instructions we'll use to perform the interaction between Scripts.
When you create a new Script in Unity, it will come with some functions predefined, as you can see in figure 5. The "Start" and "Update" method, which will be automatically executed if the Script is assigned to at least one GameObject from the hierarchy.
Functions and Variables from "ScriptA"
In the first Script we are going to write the following code:
A public string called "name" that will help us identify which instance it is.
Two methods, one called "Function1" and the other "Function2", the first function we will define it as public and the second private, lines 10 and 16 respectively from figure 6.
We will be able to access the "Function1" method through the dot operator because it was defined with public visibility. , as it's defined as public.
As we can see in figure 6, "Function1" prints in console the result of the execution of "Function2". This method returns a text that will allow us to see who is the object that is executing the "Function1" method and which instance of the ScriptA class it is.
Functions and Variables from "ScriptB"
The "ScriptB" script will be the one that calls the function defined in the other Script A. In figure 7 we see the instructions defined in the "ScriptB" script.
We define a string to assign a name and know which instance it is.
To be able to execute functions defined in another Script we must have the reference of the instance of that class. So let's define a ScriptA type variable and name it "scriptA" (first letter with lowercase), as we see in line 13 of figure 7.
Here we only have half of the work done, we declared the
"A" variable inside "B", but this variable is empty (a null variable) and will remain empty unless we do something about it.
To find the reference of an object in a Script there are several ways, in this case I will use a function calle "FindObjectOfType<T>" where "T" is the type of the variable we want to find. This function will search in the hierarchy an object of the indicated type and return the reference of that object if it founds it. See line 18 of figure 7.
More methods to FIND REFERENCES in Unity
Now that we have the reference of the "A" object, inside "B" we can execute the "Function1" function that is defined inside the A Script. Line 20 from figure 7).
As mentioned before, the dot operator will allow us to access all the fields and public methods defined within the class.
In figures 8 and 9 we see that using the reference of the ScriptA-type object followed by a dot, we can see the list of fields and methods that are available to use.
In figure 8 we see the "name" variable that was the public string defined inside "ScriptA" and in figure 9 we see the "Function1" method, also defined as public. We can't see the "Function2" method because it was declared as private.
First exectution test
Before enter in the Play mode, we select GameObjects "A" and "B" from the hierarchy and write names in the "name" fields to be able to analyze the execution of our code.
The "ScriptA" instance will be called "George (A)" and the
"ScriptB" instance will be called "Mike (B)", as we see in figures 10 and 11.
When entering in the Play mode we see in the inspector of GameObjectB that the "ScriptA" field shows an object (unlike figure 11 where it said "none"). This means that the "ScriptB" was able to find the "ScriptA" reference.
Figure 13 shows the on-screen message, printed by the ScriptA Function1 method, which was called from the ScriptB.
The message says: "Mike(B) has executed my Function1. I'm George(A), by the way.
Second execution test
Let's go a little deeper into the concept of programming object as an instance of a class.
Choose GameObjectA from the hierarchy and add a second instance of the ScriptA class using the "Add Component" or drag and drop button.
This second instance will be called "Luke (A)", in figure 14 we see both instances of the class.
When running the game as it is, we see in the console that Mike (B) has executed the Function1 method of Luke (A), see figure 15.
What has happened here is that the "FindObjectOfType<>" function in line 18 of figure 7, has found a different reference of a ScriptA type object than before, the "name" variable from this references is "Luke (A)". A different instance of the same class as the one that has the text "George (A)" in its "name" variable.
Now lets modify the code in "ScriptB" so that it is able to find all the "ScriptA" references in the scene and execute the "Function1" method of each one of them.
In the object declaration we have now defined an array (or vector) of "ScriptA" type objects (line 13 figure 16), this means that we will be able to save several references of ScriptsA objects.
In line 18 of figure 16 the change is rather subtle, instead of running the "FindObjectOfType<T>" function we use "FindObjectsOfType<T>", this other function returns all the references of the indicated "T" type that is able to find in the hierarchy.
The last thing we do is go through all the elements of the array using a foreach loop and we execute the "Function1" method to all of them, as can be seen in lines 20 to 23 of figure 16.
When entering again in the Play Mode, we see that now in the GameObjectB inspector we have the "ScriptA" object references (figure 17) inside an array of size 2.
And now in the console appear two messages, the first corresponds to the execution of Luke's "Function1" method and the second to the execution of George's "Function2" method.
Third execution test
We are going to modify the ScriptB again to see another way to obtain the references of the objects and thus to be able to call their functions from another Script.
In this case, in the "ScriptB", we are going to declare two serialized "ScriptA" objects (they can also be public, the idea is that they appear in the inspector). These objects will be called "scriptALuke" and "ScriptAGeorge".
Then in the Start method we are going to execute the "Function1" methods directly using these objects. In this case it is not necessary to find the references through code since we will assign them directly in the inspector.
The modified "ScriptB" is shown below.
In the inspector we can see the new fields for "ScriptA" objects (figure 20).
We will modify a little the GameObjects of the hierarchy, we will have an Empty GameObject called A‑Luke and another called A‑George.
In each GameObject we assign the ScriptA component and complete the corresponding name, see figures 22 and 23.
Now let's take the GameObjects from the hierarchy and assign them in the corresponding fields of the "ScriptB"
In figures 25 and 26 we see that the references have been assigned manually, this means that we can execute the "Function1" methods from each instance.
In console we see that the messages are printed due to the execution of "Function1" method on both instances.
Before finishing I would like to change the order of execution of the Function1 methods in the ScriptB, as seen in figure 28.
When we do this and enter in the Play Mode again we see that the messages on the console switch places, figure 29.
We have seen how to call functions that are defined in other Scripts. This is only possible if the functions are declared with public visibility, this allows the access from external contexts.
In addition we must have the reference of the object that contains that method to execute, this can be achieved in several ways, in this article we saw three ways to do it. It's important to understand the concept of instances in programming, it can be two or more instances of the same class and therefore multiple objects with the same function defined but the result of the execution of that function on each instance could be different.
Understand in depth the concept of object in programming, which is the instance of a class and the fact that in order to access another object we must have the reference of that object, allow us to build more complex solutions, separating responsibility, increasing the abstraction of our solutions.