Unity has a lot of great built-in tools to solve the most common problems in game development, such as the ones we have seen so far. Even two games of the same genre have their own little differences that make the game unique, and Unity cannot foresee that, so that’s why we have scripting. Through coding, we can extend Unity’s capabilities in several ways to achieve the exact behavior we need, all through a well-known language—C#. But aside from C#, Unity also has Visual Scripting, a way to generate code through a node graph tool. This means you can create scripts without writing code but by dragging nodes, boxes that represent actions that can be chained:
Figure 5.1: Example of a Visual Scripting graph
While essentially both ways can achieve the same result, we can use them for different things. Usually, the core logic of the game is written in C# due to it being usually huge and very performance sensitive. But sometimes using visual scripts instead allows non-programmer team members, like artists or game designers, to have more freedom to edit minor changes in the game, especially regarding balancing or visual effects.
Another example would be game designers prototyping ideas through visual scripts that later programmers will convert to C# scripts when the idea is approved. Also, C# programmers can create nodes for Visual Script programmers to use.
The way to mix these tools varies widely between teams, so while in the next chapters we are going to focus mainly on C#, we are going to also see the Visual Scripting equivalent version of the scripts we are going to create. This way you will have the opportunity to experiment when convenient to use one or the other according to your team structure.
In this chapter, we will examine the following scripting concepts:
We are going to create our own Unity components, learning the basic structure of a script and the way that we can execute actions and expose properties to be configured, both with C# and Visual Scripting. We are not going to create any of our actual game codes here, just some example scripts to set the ground to start doing that in the next chapter. Let’s start by discussing the basics of script creation.
The first step to creating behavior is to create script assets; these are files that will contain the logic behind the behavior of our components. Both C# and Visual Scripting have their own type of asset to achieve that, so let’s explore how to do that in both tools.
Having some programming knowledge is required in this book. However, in this first section, we are going to discuss a basic script structure to make sure you have a strong foundation to follow when we code the behaviors of our game in the following chapters. Even if you are familiar with C#, try not to skip this section because we will cover Unity-specific structures of the code.
In this section, we will examine the following script creation concepts:
We are going to create our first script, which will serve to create our component, discussing the tools needed to do so and exploring how to expose our class fields to the editor. Let’s start with the basics of script creation.
Support for Visual Scripting is added by installing the Visual Scripting package in the Package Manager as we did with other packages in previous chapters, but as Unity does that automatically for us when we create the project, we don’t need any further setup. That means the rest of this section will take care of setting up the tools needed to work with C#.
One thing to consider before creating our first C# script is how Unity compiles the code. While coding, we are used to having an Integrated Development Environment (IDE), which is a program to create our code and compile or execute it. In Unity, we will just use an IDE as a tool to create the scripts easily with coloring and auto-completion because Unity doesn’t have a custom code editor (if you have never coded before, these are valuable tools for beginners). The scripts will be created inside the Unity project and Unity will detect and compile them if any changes are made, so you won’t compile them in the IDE. Don’t worry, even if not compiling and running the code in the IDE, it is possible to debug, add breakpoints, and check the data on the variables and structures using the IDE and Unity together.
We can use Visual Studio, Visual Studio Code, Rider, or whatever C# IDE you’d like to use, but when you install Unity, you will probably see an option to install Visual Studio automatically, which allows you to have a default IDE. This installs the free version of Visual Studio, so don’t worry about the licenses here. If you don’t have an IDE on your computer and didn’t check the Visual Studio option while installing Unity, you can do the following:
Figure 5.2: Adding a module to the Unity installation
Figure 5.3: Installing Visual Studio
Figure 5.4: Accepting the terms and conditions
If you have a preferred IDE, you can install it yourself and configure Unity to use it. If you can afford it or you are a teacher or a student (as it is free in these cases), I recommend Rider. It is a great IDE with lots of C# and Unity features that you will love; however, it is not vital for this book. In order to set up Unity to use a custom IDE, do the following:
Figure 5.5: Selecting a custom IDE
Finally, some IDEs, such as Visual Studio, Visual Studio Code, and Rider, have Unity integration tools that you need to install in your project, which is optional but can be useful. Usually, Unity installs these automatically, but if you want to be sure that they are installed, follow these steps:
Figure 5.6: Enabling Unity Registry mode
Figure 5.7: Custom IDE editor extension installation—in this case, the Rider one
Now that we have an IDE configured, let’s create our first script.
C# is an object-oriented language, and this is no different in Unity. Any time we want to extend Unity, we need to create our own class—a script with the instructions we want to add to Unity. If we want to create custom components, we need to create a class that inherits from MonoBehaviour
, the base class of every custom component.
We can create C# script files directly within the Unity project using the editor, and you can arrange them in folders right next to other assets
folders. The easiest way to create a script is by following these steps:
Figure 5.8: The New script option
MyFirstScript
, but for the scripts that you will use for your game, try to enter descriptive names, regardless of the length: Figure 5.9: Naming the script
It is recommended that you use Pascal case for script naming. In Pascal case, a script for the player’s shooting functionality would be called PlayerShoot
. The first letter of each word of the name is in uppercase and you can’t use spaces.
Scripts
folder:Figure 5.10: Script asset
component
class:Figure 5.11: Our script added to a GameObject
Now that we have created a component
class, remember that a class is not the component itself. It is a description of what the component should be—a blueprint of how a component should work. To actually use the component, we need to instantiate it by creating a component based on the class. Each time we add a component to an object using the editor, Unity is instantiating it for us. Generally, we don’t instantiate components using the new C# keyword, but by using the editor or specialized functions.
Now, you can add your new empty component to other objects as you would any other component by using the Add Component button in the Inspector window. Then you can look for the component in the Scripts category or search it by name:
Figure 5.12: Adding a custom component in the Scripts category
Something that you need to consider here is that we can add the same component to several GameObjects. We don’t need to create a class for each GameObject that uses the component. I know this is basic programmers’ knowledge but remember that we are trying to recap the basics here.
Now that we have our component, let’s explore how it looks and carry out a class structure recap by following these steps:
Scripts
folder you created previously.MonoBehaviour
type and the script name are colored the same:Figure 5.13: A new script opened in the Rider IDE
using
keyword—include common namespaces. Namespaces are like code containers, which is, in this case, code created by others (such as Unity, C# creators, and so on). We will be using namespaces quite often to simplify our tasks; they already contain solved algorithms that we will use. We will be adding and removing the using
component as we need; in my case, Rider is suggesting that the first two using
components are not necessary because I am not using any code inside them, and so they are grayed out. But for now, keep them as you will use them in later chapters of this book. Remember, they should always be at the beginning of the file:Figure 5.14: The using sections
public class
, is where we declare that we are creating a new class that inherits from MonoBehaviour
, the base class of every custom component. We know this because it ends with : MonoBehaviour
. You can see how the rest of the code is located inside brackets right below that line, meaning that the code inside them belongs to the component:Figure 5.15: The MyFirstScript class definition inherits from MonoBehaviour
Now that we have our C# script, let’s add fields to configure it.
In previous chapters, when we added components as Rigidbody
or as different kinds of colliders, adding the components wasn’t enough. We needed to properly configure them to achieve the exact behavior that we need. For example, Rigidbody
has the Mass
property to control the object’s weight, and the colliders have the Size
property to control their shape. This way, we can reuse the same component for different scenarios, preventing the duplication of similar components. With a Box
collider, we can represent a cube or rectangular box just by changing the size properties. Our components are no exception; if we have a component that moves an object and if we want two objects to move at different speeds, we can use the same component with different configurations.
Each configuration is a field or variable where we can hold the parameter’s value. We can create class fields that can be edited in the editor in two ways:
public
, but breaking the encapsulation principleNow, we are going to cover both methods, but if you are not familiar with Object-Oriented Programming (OOP) concepts, such as encapsulation, I recommend you use the first method.
Suppose we are creating a movement script. We will add an editable number field representing the velocity using the first method—that is, by adding the public
field. We will do this by following these steps:
Figure 5.16: Creating a speed field in our component
The public
keyword specifies that the variable can be seen and edited beyond the scope of the class. The float
part of the code says that the variable is using the decimal number type, and speed
is the name we chose for our field—this can be whatever you want. You can use other value types to represent other kinds of data, such as bool
to represent checkboxes or Booleans
and string
to represent text.
Figure 5.17: The loading wheel
Remember that Unity will compile the code; don’t compile it in the IDE.
Figure 5.18: A public field to edit data that the component will use later
In case you don’t see the speed variable, please check the section at the end of this chapter called Common beginner C# script errors, which will give you tips about how to troubleshoot compilation errors.
public
field, we create a private
field, encouraging encapsulation and exposing it using the SerializeField
attribute, as shown in the following screenshot. Figure 5.19: Exposing private attributes in the Inspector window
If you are not familiar with the OOP concept of encapsulation, just use the first method, which is more flexible for beginners. If you create a private
field, it won’t be accessible to other scripts because the SerializeField
attribute only exposes the variable to the editor. Remember that Unity won’t allow you to use constructors, so the only way to set initial data and inject dependencies is via serialized private
fields or public
fields and setting them in the editor (or using a dependency injection framework, but that is beyond the scope of this book). For simplicity, we will use the first method in most of the exercises in this book.
If you want, try to create other types of variables and check how they look in the Inspector. Try replacing float
for bool
or string
, as previously suggested. Consider that not every possible C# type is recognized by Unity; through this book, we will learn the most commonly supported ones. Now that we know how to configure our components through data, let’s use that data to create some behavior.
Now that we have our C# script, let’s see how to do the same in Visual Scripting.
As we need to create a Script Asset for C# scripts, we need to create the Visual Scripting equivalent called Script Graph and also attach it to our GameObject, although using a different approach this time. Before continuing, it is worth noticing that our objects must only have C# or the Visual Scripting version, but not both, or the behavior will be applied twice, once per version.
Essentially, only do the steps for the version you want to try or do both steps in different objects if you want to experiment.
Let’s create a Visual Script doing the following:
Figure 5.20: Adding a Script Machine component
Figure 5.21: Using the New button to create a Visual Scripting Graph asset
Figure 5.22: Visual Scripting asset editor
What we did is create the Visual Graph asset that will contain the code of our script, and attached it to a GameObject through the Script Machine component. Unlike C# scripts, we can’t attach the Graph Asset directly; that’s why we need the Script Machine to run the component for us.
Regarding fields, the ones we created in the C# scripts are contained in the script itself, but for Visual Graph they work a little bit differently. When we added the Script Machine component, another one was added: the Variables component. This will hold all the variables for all the Visual Script Graph that a GameObject can contain. That means that all graphs we add to our object will share those variables. You can create graph-specific variables if you want, but they won’t be exposed in the Inspector, and this way also simplifies the access of variables from other objects’ scripts. Also remember you will want to add several graphs to the object, given that each graph will take care of different behaviors, in a way in which we can mix and match them according to our needs.
In order to add a variable to our GameObject that can be used by our graph, let’s do the following:
speed
. If you don’t see that option, click the triangle at the left of the Variables component name.Figure 5.23: Creating variables for the Visual Graph
We created a speed
variable that we can configure in the GameObject to alter the way all Visual Scripts Graphs attached to our GameObject will work, or at least the ones that use that Variable
value. Consider that maybe you will have different kinds of speed, like movement and rotational speed, so in real cases you might want to be a little bit more specific with the variable name.
The Variables
component used in Visual Scripting is also called Blackboard, a common programming technique. This Blackboard is a container of several values of our object, like a memory or database, that several other components of our object will then query and use. C# scripts usually contain their own variables inside instead. With our scripts created and ready to be configured, let’s see how to make both of them do something.
Now that we have a script, we are ready to do something with it. We won’t implement anything useful in this chapter, but we will settle the base concepts to add interesting behavior to the scripts we are going to create in the next chapters.
In this section, we are going to cover the following concepts:
We are going to explore the Unity event system, which will allow us to respond to different situations by executing instructions. These instructions will also be affected by the value of the editor. Finally, we are going to discuss common scripting errors and how to solve them. Let’s start by introducing the concept of Unity events in C#.
Unity allows us to create behavior in a cause-effect fashion, which is usually called an event system. An event is a situation that Unity is monitoring—for example, when two objects collide or are destroyed, Unity tells us about this situation, allowing us to react according to our needs. As an example, we can reduce the life of a player when it collides with a bullet. Here, we will explore how to listen to these events and test them by using some simple actions.
If you are used to event systems, you will know that they usually require us to subscribe to some kind of listener or delegate, but in Unity, there is a simpler method available. For C# scripts we just need to write a function with the exact same name as the event we want to use—and I mean exactly. If a letter of the name doesn’t have the correct casing, it won’t execute, and no warning will be raised. This is the most common beginner’s error that is made, so pay attention. For Visual Scripting we will be adding a special kind of node, but will discuss that after the C# version.
There are lots of events or messages to listen to in Unity, so let’s start with the most common one—Update
. This event will tell you when Unity wants you to update your object, depending on the purpose of your behavior; some don’t need them. The Update
logic is usually something that needs to be executed constantly—to be more precise, in every frame. Remember that every game is like a movie—a sequence of images that your screen switches through fast enough to look like we have continuous motion. A common action to do in the Update
event is to move objects a little bit, and by doing this, every frame will make your object constantly move.
We will learn about the sorts of things we can do with Update
and other events or messages later. Now, let’s focus on how to make our component at least listen to this event. Actually, the base script already comes with two event functions that are ready to use, one being Update
and the other one Start
. If you are not familiar with the concept of methods in C#, we are referring to the snippet of code in the following screenshot, which is already included in our script. Try to find it in yours:
Figure 5.24: A function called Update, which will be executed with every frame
You will notice a (usually) green line of text (depending on the IDE) above the void Update()
line—this is called a comment. These are basically ignored by the compiler. They are just notes that you can leave to yourself and must always begin with //
to prevent Unity from trying to execute them and failing. We will use this to temporarily disable lines of code later.
Now, to test whether this actually works, let’s add an instruction to be executed all the time. There’s no better test function than print
. This is a simple instruction that tells Unity to print a message to the console, where all kinds of messages can be seen by the developers to check whether everything is properly working. The user will never see these messages. They are similar to the classic log files that developers sometimes ask you for when something goes wrong in the game and you are reporting an issue.
In order to test events in C# using functions, follow these steps:
print("test");
within the event function. In the following screenshot, you can see an example of how to do that in the Update
event. Remember to write the instruction exactly, including the correct casing, spaces, and quote symbols:Figure 5.25: Printing a message in all the frames
Remember to save the file before switching back to Unity from the IDE. This is the only way that Unity knows your file has changed. Some IDEs, such as Rider, save the file automatically for you, but I don’t recommend you use auto-save, at least in big projects (you don’t want accidental recompilations of unfinished work—that takes too long in projects with lots of scripts).
"test"
every frame on the Console tab. If you don’t see this, remember to save the script file before playing the game.Start
function. Add print("test Start");
to it, save the file, and play the game. The full script should look as follows:Figure 5.26: The script that tests the Start and Update functions
If you check the console now and scroll all the way up, you will see a single "test Start"
message and lots of "test"
messages following it. As you can guess, the Start
event tells you that the GameObject is created and allows you to execute the code that needs to happen just once at the beginning of its lifetime.
For the void Update()
syntax, we will say to Unity that whatever is contained within the brackets below this line is a function that will be executed in all the frames. It is important to put the print
instruction inside the Update
brackets (the ones inside the brackets of the class). Also, the print
function expects to receive a value to print inside its parenthesis, called an argument or parameter. In our example we want to print simple text, and in C# it must be enclosed with quotation marks. Finally, all instructions inside functions such as Update or Start must end with a semicolon.
Here, I challenge you to try to add another event called OnDestroy
using a print
to discover when it executes. A small suggestion is to play and stop the game and look at the bottom of the console to test this one.
For advanced users, you can also use breakpoints if your IDE allows you to do that. Breakpoints allow you to freeze Unity completely before executing a specific code line to see how our field’s data changes over time and to detect errors. Here, I will show you the steps to use breakpoints in Rider, but the Visual Studio version should be similar:
Figure 5.27: A breakpoint in the print instruction
Figure 5.28: Attacking our IDE with a Unity process
Figure 5.29: Changing from release mode to debug mode
Stopping the debugging process won’t close Unity. It will just detach the IDE from the editor.
Now let’s explore the Visual Scripting equivalent of using events and instructions.
The same concept of events and instructions remains in Visual Scripting, but of course this will be done with nodes in the graph. Remember a node represents an instruction of the graph, and we can connect them to chain the effects of each instruction. In order to add events and the print instruction on our graph, do the following:
Figure 5.30: Deleting nodes
start
inside the Search box. It can take a while the first time.Figure 5.31: Searching the On Start event node
print
node, select the one that says Mono Behaviour:Print. This means that when the On Start event happens, the connected node will be executed, in this case print. This is how we start to chain instructions to events:Figure 5.32: Creating a print node connected to the event
Figure 5.33: Creating a string literal node
Figure 5.34: Specifying the message to print
You can chain more actions to the On Start by dragging the pin at the right (Flow Output Pin) of the Print node, and chaining new nodes, but we will do that later. Now that we have our scripts doing something, let’s make the instructions use the fields we created so the scripts use their configurations.
We have created fields to configure our components’ behavior, but we have not used them so far. We will create meaningful components in the next chapter, but one thing we will often need is to use the fields we have created to change the behavior of the object. So far, we have no real use of the speed
field that we created. However, following the idea of testing whether our code is working (also known as debugging), we can learn how to use the data inside a field with a function to test whether the value is the expected one, changing the output of print
in the console according to the field’s value.
In our current C# script, our speed
value doesn’t change during runtime. However, as an example, if you are creating a life system with shield damage absorption and you want to test whether the reduced damage calculation is working properly, you might want to print the calculation values to the console and check whether they are correct.
The idea here is to replace the fixed message inside the print
functions with a field. When you do that, print
will show the field’s value in the console. So, if you set a value of 5
in speed
and you print it, you will see lots of messages saying 5
in the console, and the output of the print
function is governed by the field. To test this, your print
message within the Update
function should look as follows:
Figure 5.35: Using a field as a print function parameter
As you can see, we just put the name of the field without quotation marks. If you use quotation marks, you will print a "
speed"
message. In other scenarios, you can use this speed
value within some moving functions to control how fast the movement will be, or you can perhaps create a field called "fireRate"
(fields use camel case instead of Pascal case, with the first letter being in lowercase) to control the cooldown time between one bullet and the next:
Figure 5.36: Printing the current speed
Now, to make the Visual Script graph print the value of the speed variable we created in the Variables component, let’s do the following.
Figure 5.37: Dragging variables to the graph to be used in the nodes
Print
needs to execute, it will execute Get Variable
to get the text to read:Figure 5.38: Connecting the speed variable to the print node
With all this, we now have the necessary tools to start creating actual components. Before moving on, let’s recap some of the common errors that you will likely encounter if this is your first time creating scripts in C#.
The Visual Scripting scripts are prepared in a way in which you make fewer errors, not allowing you to write incorrect syntax like C# script does. If you are an experienced programmer, I bet you are quite familiar with them, but let’s recap the common errors that will make you lose lots of time when you are starting with C# scripting. Most of them are caused by not copying the shown code exactly. If you have an error in the code, Unity will show a red message in the console and won’t allow you to run the game, even if you are not using the script. So, never leave anything unfinished.
Let’s start with a classic error, a missing semicolon, which has resulted in many programmer memes and jokes. All fields and most instructions inside functions (such as print
), when called, need to have a semicolon at the end. If you don’t add a semicolon, Unity will show an error, such as the one in the screenshot on the left in Figure 5.39, in the console. You will also notice that this also has an example of bad code, where the IDE is showing a red icon suggesting something is wrong in that place:
Figure 5.39: An error in the print line hinted by the IDE and the Unity console
You will notice that the error shows the exact script (MyFirstScript.cs
), the exact line of code (14
, in this case), and usually, a descriptive message—in this case, ;
expected—
as a way to specify the instruction ends there, so the compiler can process the next instruction as a separate one. You can simply double-click the error and Unity will open the IDE highlighting the problematic line. You can even click on the links in the stack to jump to the line of the stack that you want.
I already mentioned why it is important to use the exact case for every letter of the instruction. However, based on my experience of teaching beginners, I need to stress this particular aspect more.
The first scenario where this can happen is in instructions. In the following screenshots, you can see how a badly written print
function looks—that is, the error that the console will display and how the IDE will suggest that there is something wrong. First, in the case of Rider, the instruction is colored red, saying that the instruction is not recognized (in Visual Studio, it will show a red line instead). Then, the error message says that Print
does not exist in the current context, meaning that Unity (or C#, actually) does not recognize any instruction named Print
. In another type of script, Print
in uppercase may be valid, but not in regular components, which is why the “in the current context” clarification exists:
Figure 5.40: Error hints when writing an instruction wrong
Now, if you write an event with the wrong casing, the situation is worse. You can create functions such as Start
and Update
with whatever name you want for other purposes. Writing update
or start
is perfectly valid as C# will think that you are going to use those functions not as events but as regular functions. So, no error will be shown, and your code will just not work. Try to write update
instead of Update
and see what happens:
Figure 5.41: The wrong casing in the Update function will compile the function but won’t execute it
Another error is to put instructions outside the function brackets, such as inside the brackets of the class or outside them. Doing this will give no hint to the function as to when it needs to execute. So, a print
function outside an Event
function makes no sense, and it will show an error such as the ones in the following Figures 5.42 and 5.43.
This time, the error is not super descriptive. C# is expecting you to create a function or a field—the kind of structures that can be put directly inside a class:
Figure 5.42: Misplaced instruction or function call
Finally, another classic mistake is to forget to close open brackets. If you don’t close a bracket, C# won’t know where a function finishes and another starts or where the class function ends. This may sound redundant, but C# needs that to be perfectly defined. In the following screenshots, you can see how this would look:
Figure 5.43: Missing closed brackets
This one is a little bit difficult to catch because the error in the code is shown way after the actual error. This is caused by the fact that C# allows you to put functions inside functions (not used often) and so C# will detect the error later, asking you to add a closing bracket. However, as we don’t want to put Update
inside Start
, we need to fix the error before, at the end of Start
. The error message will be descriptive in the console, but again, don’t put the close bracket where the message suggests you do so unless you are 100% sure that position is correct.
You will likely face lots of errors aside from these ones, but they all work the same. The IDE will show you a hint and the console will display a message; you will learn them with time. Just have patience as every programmer experiences this. There are other kinds of errors, such as runtime errors, code that compiles but will fail when being executed due to some misconfiguration, or the worst: logic errors, where your code compiles and executes with no error but doesn’t do what you intended.
In this chapter, we explored the basic concepts that you will use while creating scripts. We discussed the concept of a script’s assets and how the C# ones must inherit from MonoBehaviour
to be accepted by Unity to create our own scripts. We also saw how to mix events and instructions to add behavior to an object and how to use fields in instructions to customize what they do. All of this was done using both C# and Visual Scripting.
We just explored the basics of scripting to ensure that everyone is on the same page. However, from now on, we will assume that you have basic coding experience in some programming language, and you know how to use structures such as if
, for
, array
, and so on. If not, you can still read through this book and try to complement the areas you don’t understand with a C# introduction book as you need.
In the next chapter, we are going to start seeing how we can use what we have learned to create movement and spawning scripts.
Read this book alongside other users, Unity game development experts, and the author himself.
Ask questions, provide solutions to other readers, chat with the author via Ask Me Anything sessions, and much more.
Scan the QR code or visit the link to join the community.