Communication between Scripts – Examples in Unity

Introduction

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.

All the IMPORTANT information is summarized in the following TUTORIAL FROM MY YOUTUBE CHANNEL




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

Previous Steps

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.

Fig. 1: We create two scripts called ScriptA and ScriptB.

In the hierarchy we are going to create two empty GameObjects to be able to create instances of both Scripts.

Fig. 2: Create two Empty GameObjects in the hierarchy to contain 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.

Fig. 3: Select object A and in the inspector, using Add Component, add ScriptA to its components.
Fig. 4: Another way to add components is to drag them directly into the GameObject inspector.

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.

Fig. 5: We open both Scripts in some editor. We can see some code already written.

Functions and Variables from “ScriptA”

In the first Script we are going to write the following code:

Fig. 6: Fields and methods belonging to Script A.

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.

Fig. 7: Fields and methods belonging to Script B.

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).

Dot Operator

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.

Fig. 8: The dot operator allows us to read and write the “name” field, which is a string defined as public.

Fig. 9: The point operator allows us to execute the “Function1” method defined as public.

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.

Fig. 10: In the GameObjectA inspector, we put a name to know that it is that instance of the Script A class.

Fig. 11: In the GameObjectB inspector, we put a name to know that it is that instance of the Script B class.

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.

Fig. 12: When running the game, the ScriptA object reference is found due to the FindObjectOfType<> instruction.

Fig. 13: This message is printed because a method defined in ScriptA is executed from the ScriptB.



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.

Fig. 14: A second instance of the ScriptA class is added to the GameObjectA with a different name.

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.

Fig. 15: When running the game we now see that the Function1 method of the instance called Luke is being executed.

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.

Fig. 16: We modify the ScriptB in this way so that it executes the functions of the two instances of ScriptsA.

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.

Fig. 17: When running the game we see that the two references of the ScriptA objects are found and we have them stored in the array.

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.

Fig. 18: In the console we see that the Function1 methods of each ScriptA object are executed.



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.

Fig. 19: We modify the ScriptB in this way to try another way of having the reference of the objects.

In the inspector we can see the new fields for “ScriptA” objects (figure 20).

Fig. 20: In the inspector we have fields for two ScriptA objects.

We will modify a little the GameObjects of the hierarchy, we will have an Empty GameObject called A-Luke and another called A-George.

Fig. 21: Instead of the GameObjectA we will have two empty GameObjects for each instance of the ScriptA class.

In each GameObject we assign the ScriptA component and complete the corresponding name, see figures 22 and 23.

Fig. 22: In the GameObject A-Luke we add the ScriptA component and enter the corresponding name.

Fig. 23: In the GameObject A-George we add the ScriptA component and enter the corresponding name.

Now let’s take the GameObjects from the hierarchy and assign them in the corresponding fields of the “ScriptB”

Fig. 24: With these GameObjects we will drag and drop in the ScriptB fields.

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.

Fig. 25: We can assign the GameObject directly to the corresponding field whenever there is such an object among its components.

Fig. 26: The references of both ScriptA objects have been assigned to the corresponding fields.

In console we see that the messages are printed due to the execution of “Function1” method on both instances.

Fig. 27: When running the game we see that the Function1 methods of both instances are executed.

Before finishing I would like to change the order of execution of the Function1 methods in the ScriptB, as seen in figure 28.

Fig. 28: Reverse the order of the instructions and perform another test.

When we do this and enter in the Play Mode again we see that the messages on the console switch places, figure 29.

Fig. 29: We see that the messages change the order of appearance in the console.



Conclusion

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.

Exit mobile version
Secured By miniOrange