Communication between Scripts – Examples in Unity
In this article we are going to see how to use functions that are defined in a Script from any other Script.
Communication between Scripts is an informal name for this problem, it means that being in one Script we will be able to access the context of another Script.
This is important, especially for creating object-oriented solutions, as it allows us to have specialized scripts that take care of specific tasks and allow other scripts to make use of that functionality.
English subtitles available for the video about communication between scripts.
What should we take into account?
Before looking at a particular application, I leave a list of points to keep in mind that I think are important to understand the whole foundation behind the communication between Scripts.
About the Scripts
In this case we are going to see an example in Unity, so the two Scripts we are going to use are going to be an extension of the MonoBehaviour class. This in practical terms means that when the game starts, a Start() method will be executed and in each frame of the game the Update() method will be executed.
About the execution of Scripts
For these scripts to run, they must be assigned to one or more GameObjects in the hierarchy. In other words, there must be an Instance of the class defined in the Script.
Instances of a class
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 belong to the same class, but their state will not necessarily be the same.
Having the reference of an object
This is one of the fundamental points to pay attention to and try to understand in depth.
Objects are the instances of a class and in order to access its functionality we must have the reference of this object, that is to say to find it among all the objects present in the program and to have it stored in a field to be able to use it.
This is similar to when we want to use a variable, we need to have that variable defined to use it in the Script.
The Dot Operator
The dot operator in several programming languages allows us to access the fields and methods defined as public within a class.
If we have the object reference, we can use the dot operator to execute any of its public methods or read and write any of its public fields.
Practical example of Communication 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 other Script.
In the hierarchy we are going to create two empty GameObjects to be able to create instances of both Scripts.
In the GameObjectA we add as component the ScriptA and to the GameObjectB the ScriptB. We can do this from the inspector with the Add Component button or doing drag and drop.
Code inside the Scripts
Let’s write the instructions we’ll use to resolve communication between Scripts. In this case I use Visual Studio as a code editor.
We opened both scripts and found some code already written.
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 called Function1 and Function2, the first public and the second private.
The public method will be the function that we will be able to execute from any other Script using the dot operator.
As we can see in figure 6 what it does is to print in console what the Function2 method returns. This method returns a concatenated text that allows us to see who is executing the Function1 method and which instance of the ScriptA class it is. This will be better understood later.
Now we are going to work on Script B, which will be the one that calls the function defined in the other Script A. In figure 7 we see the ScriptB instructions.
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 object that is instance of that class. So we have to define a ScriptA type object to which we give the name “scriptA” (first letter with lowercase), as we see in line 13 of figure 7.
This is only half of the work, we have declared that the ScriptB is going to have a reference of a ScriptA type object, but we have not yet found such a reference.
To find the reference of an object in a Script there are several ways, in this case I will use the method FindObjectOfType<> of MonoBehaviour, to this method we enter the type of object you have to look for and will return the reference of the first object of that type that you find in the hierarchy. See line 18 of figure 7.
Finally, we execute the function defined in the other Script A from this Script B (line 20 of 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 object of Script A followed by a point, we can see the list of fields and methods that we can use.
In figure 8 we see the name field that was the string defined as public in the ScriptA and in figure 9 we see the Function1 method, also defined as public. What we don’t see is the Function2 method since it was declared as private.
First exectution test
Before hitting the Play button, we select GameObjects A and B and enter names in the “name” fields that let us know what the object is.
The Script A instance will be called “George (A)” and the Script B instance will be called “Mike (B)”, as we see in figures 10 and 11.
When running the game we see in the GameObjectB inspector that the ScriptA field now 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 exectution test
Let’s go a little deeper into the concept of programming object as an instance of a class.
Choose GameObject A 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<> instruction in line 18 of figure 7, has found the reference of a single ScriptA type object, which we have called Luke (A).
We are going to 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.
The problem solved is shown below:
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 many references of ScriptsA objects.
In line 18 of figure 16 the change is rather subtle, instead of running FindObjectOfType<> FindObjectsOfType<> is executed, this makes that all the references of that object are found.
The last thing we do is go through the array of ScriptA objects using a foreach loop and execute the Function1 method to all of them, as can be seen in lines 20 to 23 of figure 16.
When running the game again, we see that now in the GameObjectB inspector we have the ScriptA object references (figure 17), we have an array of dimension 2.
As can be seen in figure 18, two messages are now printed on the console, the first corresponds to the execution of Luke’s Function1 method and the second to George’s Function2 execution.
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 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 their respective Function1 methods can be executed.
In console we see that the messages are printed product of the execution of Function1.
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 run the game again we see that the messages on the console change places, figure 29.
We have seen how to call functions that are defined in one Script from another Script, to which we gave the informal name “Communication between Scripts”.
In order for this to be done, the method or function to be performed must be declared as public, which allows 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, bearing in mind that there can be more than one object that contains the method to execute.
Understand in depth the concept of programming object, which is the instance of a class and that to access its fields and public methods we must have the reference of that object, we can build more complex solutions and we are entering the subject of object-oriented programming.