In order for your game to work, you need to define what actually happens in your game. Unity provides you with the foundations of what you need, such as rendering graphics, getting input from the player, and playing audio; it’s up to you to add the features that are unique to your game.
To make this happen, you write scripts that get added to your game’s objects. In this chapter, we’ll introduce you to Unity’s scripting system, which uses the C# programming language.
You have a choice of languages when programming in Unity. Unity officially supports two different languages: C# and “JavaScript.”
We put JavaScript in quotes because it’s not actually the JavaScript language that you might be familiar with from the wider world. Instead, it’s a language that looks like JavaScript, but has multiple differences from its namesake. It’s different enough that it’s often called “UnityScript,” by both users of Unity and sometimes the Unity team themselves.
We don’t use Unity’s JavaScript in this book for a couple of reasons. The first is that Unity’s reference material tends to show C# examples more than JavaScript, and we get the feeling that the use of C# is preferred by Unity’s developers.
Secondly, when you use C# in Unity, it’s the same language you’ll find anywhere else, whereas Unity’s version of JavaScript is very specific to Unity. This means that it’s easier to find help about the language.
When writing scripts for Unity games, you write in a language called C#. We’re not going to explain the fundamentals of programming in this book (we don’t have the space!), but we’ll highlight some main points to keep in mind.
A great general reference on the C# language is C# in a Nutshell, by Joseph and Ben Albahari (O’Reilly, 2015).
To give you a quick introduction, we’ll take a chunk of C# code, and highlight some important elements:
using
UnityEngine
;
namespace
MyGame
{
[RequireComponent(typeof(SpriteRenderer))]
class
Alien
:
MonoBehaviour
{
public
bool
appearsPeaceful
;
private
int
cowsAbducted
;
public
void
GreetHumans
(
)
{
Debug
.
Log
(
"Hello, humans!"
)
;
if
(
appearsPeaceful
=
=
false
)
{
cowsAbducted
+
=
1
;
}
}
}
}
The using
keyword indicates to the user which packages you’d like to use. The UnityEngine
package contains the core Unity types.
C# lets you put your types in namespaces, which means that you can avoid naming collisions.
Attributes are placed between square brackets, and let you add additional information about a type or method.
Classes are defined using the class
keyword, and you specify the superclass after a colon. When you make a class a subclass of MonoBehaviour
, it can be used as a script component.
Variables attached to classes are called fields.
Unity’s scripting system is powered by the Mono framework. Mono is an open source implementation of Microsoft’s .NET Framework, which means that in addition to the libraries that come with Unity, you also have the complete set of libraries that come with .NET.
A common misconception is that Unity is built on top of Mono. Unity is not built on Mono; it merely uses Mono as its scripting engine. Unity supports scripting, through Mono, using both the C# language and the UnityScript language (what Unity calls “JavaScript;” see Languages in Unity).
The versions of C# and the .NET Framework available in Unity are older than the most current versions. At the time of writing in early 2017, the version of the C# language available is 4, while the version of the .NET Framework available is 3.5. The reason for this is that Unity uses its own fork of the Mono project, which diverged from the mainline branch several years ago. This has meant that Unity can add features that are specific to their uses, which are primarily mobile-oriented compiler features.
Unity is in the middle of updating its compiler tools to make the latest versions of the C# language and the .NET Framework available to users. Until that happens, your code will be a few versions behind.
For this reason, if you’re looking for C# code or advice around the web, you should search for Unity-specific code most of the time. Similarly, when you’re coding C# for Unity, you’re going to be using a combination of Mono’s API (for generic things that most platforms provide) and Unity’s API (for game engine-specific things).
MonoDevelop is the development environment that’s included with Unity. MonoDevelop’s main role is to be the text editor that you write your scripts with; however, it contains some useful features that can make your life easier when programming.
When you double-click on any script file in your project, Unity will open the editor that’s currently configured. By default, this will be MonoDevelop, though you can configure it to be any other text editor you like.
Unity will automatically update the project in MonoDevelop with the scripts in your project, and will compile your code when you return to Unity. This means that all you need to do to edit your scripts is to save your changes, and return to the editor.
There are several features in MonoDevelop that can save you a lot of time.
In MonoDevelop, press Ctrl-Space (on both PC and Mac). MonoDevelop will display a pop-up window that offers a list of suggestions for what to type next; for example, if you’re halfway through typing a class name, MonoDevelop will offer to complete it. Press the up and down arrows to select from the list, and press Enter to accept the suggestion.
When you press Alt-Enter (Option-Enter on a Mac), MonoDevelop will offer to perform certain tasks that edit your source code. These tasks include things like adding or removing braces around if
statements, automatically filling in the case labels for switch
statements, or splitting a variable’s declaration and assignment into two lines.
Unity will automatically rebuild your code when you return to the editor. However, if you press Command-B (F7 on a PC), all of your code will be built in MonoDevelop. The files that result from this won’t be used in this game, but doing this means that you’re able to verify that there are no compilation errors in your code before you return to Unity.
Unity scenes are composed of game objects. On their own, they’re invisible objects, and have nothing but a name. Their behavior is defined by their components.
Components are the building blocks of your game, and anything you see in the Inspector is a component. Each component has a different responsibility; for example, Mesh Renderers display 3D meshes, while Audio Sources play sound to the user. Scripts that you write are components as well.
Create the script asset. Open the Assets menu, and choose Create → Script → C# Script.
Name the script asset. A new script file will appear in the folder you had selected in the Project panel, ready for you to name.
Double-click the script asset. The script will open in the script editor, which defaults to MonoDevelop. Most of your scripts will start off looking like this:
using
UnityEngine
;
using
System.Collections
;
using
System.Collections.Generic
;
public
class
AlienSpaceship
:
MonoBehaviour
{
// Use this for initialization
void
Start
(
)
{
}
// Update is called once per frame
void
Update
(
)
{
}
}
The name of the class, in this case AlienSpaceship
, must be the same as the asset filename.
The Start
function is called before the Update
function is called for the first time, and is where you might put code that initializes variables, loads stored preferences, or sets up other scripts and GameObjects.
The Update
function is called every frame, and is an opportunity to include code that responds to input, triggers another script, or moves things around—anything that needs to happen.
You may be familiar with constructors from other programming environments. In Unity, you don’t construct your MonoBehaviour
subclasses yourself, because the construction of objects is performed by Unity itself, and does not necessarily take place when you think it might.
Script assets in Unity do not actually do anything—none of their code is executed—until they are attached to a GameObject (seen in Figure 3-1). There are two main ways to attach a script to a GameObject:
Dragging the script asset onto the GameObject. This can be done with either the Inspector or the Hierarchy panel.
Using the Component menu. You will find all the scripts that are in the project under Component → Scripts.
Since scripts are primarily exposed in the Unity editor through being attached, as components, to GameObjects, Unity allows you to expose properties in your script as editable values in the Inspector. To do this, you create a public variable in your script. Everything specified as public will be visible in the editor; you can also set variables to private, though.
public
class
AlienSpaceship
:
MonoBehaviour
{
public
string
shipName
;
// "Ship Name" will appear in the Inspector
// as an editable field
}
When your script is added as a component to a game object, it appears in the Inspector when that object is selected. Unity will automatically display all variables that are public
, in the order they appear in your code.
private
variables that have the [SerializeField]
attribute will also appear. This is useful for when you want a field to be visible in the Inspector, but not accessible to other scripts.
The Unity editor will display the variable name by capitalizing the first letter of each word, and placing a space before existing capital letters. For example, the variable shipName
is displayed as “Ship Name” in the editor.
Scripts are able to access the different components that are present on a GameObject. To do this, you use the GetComponent
method.
// gets the Animator component on this object, if it exists
var
animator
=
GetComponent
<
Animator
>();
You can also call GetComponent
on other objects, to get components attached to them.
You can also get the components that are attached to parent or child objects, using the GetComponentInChildren
or GetComponentInParent
methods.
Your MonoBehaviour
s have several methods that are particularly important to Unity. These methods are called at different times during the component’s life cycle, and are opportunities to run the right behavior at the right moment. This section lists the methods in the order that they’re run.
The Start
method is called immediately before the first call to an object’s Update
method.
You might wonder why there are two opportunities for setting up an object: Awake
and Start
. After all, doesn’t that just mean that you’ll pick one of them at random?
There’s actually a very good reason for it. When you start a scene, all objects in it run their Awake
and Start
methods. Critically, however, Unity makes sure that all objects have finished running their Awake
methods before any Start
methods are run.
This means that any work that’s done in an object’s Awake
method is guaranteed to have been done by the time another object runs its Start
method. This can be useful, such as in this example, where object A uses a field set up by object B:
// In a file called ObjectA.cs
class
ObjectA
:
MonoBehaviour
{
// A variable for other scripts to access
public
Animator
animator
;
void
Awake
()
{
animator
=
GetComponent
<
Animator
>();
}
}
// In a file called ObjectB.cs
class
ObjectB
:
MonoBehaviour
{
// Connected to the ObjectA script
public
ObjectA
someObject
;
void
Awake
()
{
// Check to see if someObject has set its 'animator'
// variable
bool
hasAnimator
=
someObject
.
animator
==
null
;
// May print 'true' OR 'false', depending on which
// one happens to run first
Debug
.
Log
(
"Awake: "
+
hasAnimator
.
ToString
());
}
void
Start
()
{
// Check to see if someObject has set its 'animator'
// variable
bool
hasAnimator
=
someObject
.
animator
==
null
;
// Will *always* print 'true'
Debug
.
Log
(
"Start: "
+
hasAnimator
.
ToString
());
}
}
In this example, the ObjectA
script is on an object that also has an Animator component attached. (The Animator itself does nothing in this example, and could just as easily be any other kind of component.) The ObjectB
script has been set up so that its someObject
variable is connected to the object containing the ObjectA
script.
When the scene begins, the ObjectB
script will log twice—once in its Awake
method, and once in its Start
method. In both cases, it will try to figure out if its someObject
variable’s animator
field is not null, and print either “true” or “false.”
If you were to run this example, the first log message, which runs in ObjectB
’s Awake
method, would be either “true” or “false,” depending on which script’s Awake
method ran first. (Without manually setting up an execution order in Unity, it’s impossible to know which runs first.)
However, the second log message, which runs in ObjectB
’s Start
method, is guaranteed to return “true.” This is because, when a scene starts up, all existing objects will run their Awake
methods before any Start
methods are run.
The Update
method is run every single frame, as long as the component is enabled, and the object that the script is attached to is active.
Update
methods should do as little work as possible, because they’re run every single frame. If you do some long-running work in an Update
method, you’ll slow down the rest of the game. If you need to do something that will take some time, you should use a coroutine (described in the following section).
Unity will call the Update
method on all scripts that have one. Once that’s done, it will call LateUpdate
method on all scripts that have one. Update
and LateUpdate
have a similar relationship to that of Awake
and Start
: no LateUpdate
methods will be called until all Update
methods have been run.
This is useful for when you want to do work that relies on some other object to have done work in Update
. You can’t control which objects run their Update
method first; however, when you write code that runs in LateUpdate
, you’re guaranteed that any work in any object’s Update
method will have completed.
In addition to Update
, the FixedUpdate
method can be used. While Update
is called once per frame, FixedUpdate
is called a fixed number of times each second. This can be useful when working with physics, where you need to apply forces at regular intervals.
Most functions do their work and return immediately. However, sometimes you need something to take place over time. For example, if you want an object to slide from one point to another, you need that movement to happen over multiple frames.
A coroutine is a function that runs over multiple frames. In order to create a coroutine, first create a method that has a return type of IEnumerator
:
IEnumerator
MoveObject
()
{
}
Next, use the yield return
statement to make the coroutine temporarily stop, allowing the rest of the game to carry on. For example, to make an object move forward by a certain amount every frame,1 you’d do this:
IEnumerator
MoveObject
()
{
// Loop forever
while
(
true
)
{
transform
.
Translate
(
0
,
1
,
0
);
// move 1 unit on the Y
// axis every frame
yield
return
null
;
// wait until the next frame
}
}
If you include an infinite loop (such as the while (true)
in the previous example), then you must yield during it. If you don’t, it will loop forever without giving the rest of your code a chance to do any other work. Because your game’s code runs inside Unity, you run the risk of causing Unity to freeze up if you enter an infinite loop. If that happens, you’ll need to force Unity to quit, and may lose unsaved work.
When you yield return
from a coroutine, you temporarily pause the execution of the function. Unity will resume execution later; the specifics of when it resumes depends on what value you yield return
with.
For example:
yield
return
null
waits until the next frame
yield
return
new
WaitForSeconds
(
3
)
waits three seconds
yield
return
new
WaitUntil
(()
=>
this
.
someVariable
==
true
)
waits until someVariable
equals true
; you can also use any expression that evaluates to a true
or false
variable
To stop a coroutine, you use the yield break
statement:
// stop this coroutine immediately
yield
break
;
Coroutines will also automatically stop when execution reaches the end of the method.
Once you have a coroutine function, you can start it. To start a coroutine, you don’t call it on its own; instead, you use it in conjunction with the StartCoroutine
function:
StartCoroutine
(
MoveObject
());
When you do this, the coroutine will start executing until it either reaches a yield break
statement, or it reaches the end.
In addition to the yield return
examples we just looked at, you can also yield return
on another coroutine. This means that the coroutine you’re yield
ing from will wait until the other coroutine ends.
It’s also possible to stop a coroutine from outside of it. To do this, keep a reference to the return value of the StartCoroutine
method, and pass it to the StopCoroutine
method:
Coroutine
myCoroutine
=
StartCoroutine
(
MyCoroutine
());
// ... later ...
StopCoroutine
(
myCoroutine
);
There are two ways to create an object during gameplay. The first is by creating an empty GameObject, and attaching components to it by using code; the second involves duplicating another object (called instantiation). The second method is more popular because you can do everything in a single line of code, so we’ll discuss it first.
When you create new objects in Play Mode, those objects will disappear when you stop the game. If you want them to stick around, follow these steps:
Select the objects you want to save.
Copy them, either by pressing Command-C (Ctrl-C on a PC), or opening the Edit menu and choosing Copy.
Leave Play Mode. The objects will disappear from the scene.
Paste, either by pressing Command-V (Ctrl-V on a PC), or opening the Edit menu and choosing Paste. The objects will reappear; you can now work with them in Edit Mode.
In Unity, instantiating an object means that it, along with all of its components, child objects, and their components, are copied. This is particularly powerful when the object you’re instantiating is a prefab. Prefabs are prebuilt objects that you save as assets. This means that you can create a single template of an object, and instantiate many copies of it across many different scenes.
To instantiate an object, you use the Instantiate
method:
public
GameObject
myPrefab
;
void
Start
()
{
// Create a new copy of myPrefab,
// and position it at the same point as this object
var
newObject
=
(
GameObject
)
Instantiate
(
myPrefab
);
newObject
.
transform
.
position
=
this
.
transform
.
position
;
}
The Instantiate
method’s return type is Object
, not GameObject
. You’ll need to do a cast in order to treat it as a GameObject
.
The other way you can create objects is by building them up yourself through code. To do this, you use the new
keyword to construct a new GameObject
, and then call AddComponent
on it to add new components.
// Create a new game object; it will appear as
// "My New GameObject" in the hierarchy
var
newObject
=
new
GameObject
(
"My New GameObject"
);
// Add a new SpriteRenderer to it
var
renderer
=
newObject
.
AddComponent
<
SpriteRenderer
>();
// Tell the new SpriteRenderer to display a sprite
renderer
.
sprite
=
myAwesomeSprite
;
The Destroy
method removes an object from the scene. Notice that we didn’t say game object, but object! Destroy
is used for removing both game objects and components.
To remove a game object from the scene, call Destroy
on it:
// Destroy the game object that this script is attached to
Destroy
(
this
.
gameObject
);
Destroy
works on both components and game objects.
If you call Destroy
and pass in this
, which means the current script component, you won’t remove the game object, but instead the script will end up removing itself from the game object it’s attached to. The game object will stick around, but will no longer have your script attached.
An attribute is a piece of information that you can attach to a class, variable, or method. Unity defines several useful attributes that you can use, which change the behavior of the class or how it’s presented in the Editor.
The RequireComponent
attribute, when attached to a class, allows you to specify to Unity that the script requires that another type of component be present. This is useful when your script only makes sense when that kind of component is attached. For example, if your script only does one thing, such as changing the settings of an Animator, it makes sense that that class should require an Animator to be present.
To specify the type of component that your component requires, you provide the type of component as a parameter, like so:
[RequireComponent(typeof(Animator))]
class
ClassThatRequiresAnAnimator
:
MonoBehaviour
{
// this class requires that an Animator also
// be attached to the GameObject
}
If you add a script that requires a certain component to a GameObject, and that GameObject doesn’t already have that component, Unity will automatically add one for you.
The Header
attribute, when added to a field, causes Unity to draw a label above the field in the Inspector. Space
works similarly, but adds empty space. Both are useful for visually organizing the contents of the Inspector.
For example, Figure 3-2 shows the Inspector’s rendering of the following code:
public
class
Spaceship
:
MonoBehaviour
{
[Header("Spaceship Info")]
public
string
name
;
public
Color
color
;
[Space]
public
int
missileCount
;
}
Normally, only public
fields are displayed in the Inspector. However, making variables public
means that other objects can directly access them, which means that it can be difficult for an object to have full control over its own data. However, if you make the variable private
, Unity won’t display it in the Inspector.
To get around this, add the SerializeField
attribute to private
variables you want to appear in the Inspector.
If you want the opposite behavior (that is, the variable is public
, but doesn’t appear in the Inspector), then you can use the HideInInspector
attribute:
class
Monster
:
MonoBehaviour
{
// Appears in Inspector, because it's public
// Accessible from other scripts
public
int
hitPoints
;
// Doesn't appear in Inspector, because it's private
// Not accessible from other scripts
private
bool
isAlive
;
// Appears in Inspector, because of SerializeField
// Not accessible from other scripts
[SerializeField]
private
int
magicPoints
;
// Doesn't appear in Inspector, because of HideInInspector
// Accessible from other scripts
[HideInInspector]
public
bool
isHostileToPlayer
;
}
By default, your scripts will only run their code in Play Mode; that is, the contents of your Update
method will only run when the game is actually running.
However, it can sometimes be convenient to have code that runs all the time. For these cases, you can add the ExecuteInEditMode
attribute to your class.
The component life cycle performs differently in Edit Mode as compared to Play Mode. When in Edit Mode, Unity will only redraw itself when it has to, which generally means in response to user input events like mouse clicks. This means that the Update
method will run only sporadically instead of continuously. Additionally, coroutines won’t behave the way you expect.
Moreover, you can’t call Destroy
in Edit Mode, because Unity defers the actual removal of an object until the next frame. In Edit Mode, you should call DestroyImmediate
instead, which removes an object right away.
For example, here’s a script that makes an object always face its target, even when not in Play Mode:
[ExecuteInEditMode]
class
LookAtTarget
:
MonoBehaviour
{
public
Transform
target
;
void
Update
()
{
// Don't continue if we don't have a target
if
(
target
!=
null
)
{
return
;
}
// Rotate to look at target
transform
.
LookAt
(
target
);
}
}
If you were to attach this script to an object, and set its target
variable to another object, the first object would rotate to face its target in both Play Mode and Edit Mode.
The Time
class is used to get information about the current time in your game. There are several variables available in the Time
class (and we strongly recommend you look at the documentation for it!), but the most important and commonly used variable is deltaTime
.
Time.deltaTime
measures the amount of time since the last frame was rendered. It’s important to realize that this time can vary a lot. Doing this allows you to perform an action that’s updated every frame, but needs to take a certain amount of time.
In “Coroutines”, the example we used was one that moves the object one unit every frame. This is a bad idea, because the number of frames in a second can vary quite a lot. For example, if the camera is looking at a very simple part of your scene, the frames per second could be very high, whereas looking at more visually complex scenes could result in very low framerates.
Because you can’t be sure of the number of frames per second you’re running at, the best thing to do is to take into account Time.deltaTime
. The easiest way to explain this is with an example:
IEnumerator
MoveSmoothly
()
{
while
(
true
)
{
// move 1 unit per second
var
movement
=
1.0f
*
Time
.
deltaTime
;
transform
.
Translate
(
0
,
movement
,
0
);
yield
return
null
;
}
}
As we saw in “Awake and OnEnable”, it’s sometimes convenient to log some information to the Console, either for diagnostic purposes, or to warn you about some problem.
The Debug.Log
function can be used to do this. There are three different levels of logging: info, warning, and error. There’s no functional difference between the three types, except warnings and errors are each more visible and prominent than the last.
In addition to Debug.Log
, you can also use Debug.LogFormat
, which allows you to embed values in the string that’s sent to the Console:
Debug
.
Log
(
"This is an info message!"
);
Debug
.
LogWarning
(
"This is a warning message!"
);
Debug
.
LogError
(
"This is a warning message!"
);
Debug
.
LogFormat
(
"This is an info message! 1 + 1 = {0}"
,
1
+
1
);
1 This is actually not a great idea, for reasons that are explained in “Time in Scripts”, but it’s the simplest example.