Scripting in Unity

Unity - Scripting

Unity – Scripting

In this article, I will introduce you to scripting in Unity 3.5. Unity is a powerful game editor that only limits you to what you can imagine. Scripting is where the magic happens which will bring your games to life. I assume the reader is familiar the Unity interface, if not you can refer to my previous article titled Introduction to Unity (http://3dgep.com/?p=3246).

This article is intended for designers and artists who have no previous programming experience. For this reason, I will go into details about JavaScript syntax that the experienced programmer may find boring.

Introduction

The Unity game engine supports the ability to program game behaviours using a variaty of scripting languages. The primary scripting languages supported by Unity are JavaScript, C#, and a dialect of Python called Boo.

Unity has full support for the .NET 2.0 framework when deploying your game as a desktop application and a subset of the .NET 2.0 framework when deploying as a web player or mobile device.

JavaScript

JavaScript Icon

JavaScript Icon

JavaScript is an easy to use, easy to learn scripting language and the primary scripting language used in this article.

JavaScript has a similar syntax to the Java programming language which is also similar to C++ style programming. It’s my recommended language for people who don’t have much programming experience.

C#

Visual C# Icon

Visual C# Icon

C# can be considered a “Programming Language” more than a scripting language. It can be used to create stand-alone applications using Microsoft’s .NET Framework.

Boo

Boo Icon

Boo Icon

Boo is a scripting language that is inspired by the popular Python scripting language. Although Boo is not widely used in Unity, it is one of the standard scripting languages supported by the Mono development API so naturally, it is supported by Unity as well.

 

In this article, I will focus on creating scripts using the JavaScript scripting language.

JavaScript Basics

Instead of simply writing JavaScript reference documentation, I will try to teach JavaScript by showing a series of examples that slowly build-up your knowledge of the JavaScript scripting language and how it is used in Unity.

If you haven’t done so already, launch your Unity editor and create a new empty project. If you are unsure how to get started with Unity, you can refer to my previous article titled Introduction to Unity. If you just want to skip to the part about creating your first project, click here.

Creating Scripts

If you created a new empty project, you should have a screen that looks something like the image below.

Unity - Empty Project

Unity – Empty Project

In the Project View create a new Folder called “Scripts”. Inside the Scripts folder, create a new folder called “JavaScript”. Your project view should look something like the image shown below.

Unity - Project View

Unity – Project View

Right-click on the “JavaScript” folder and select Create -> Javascript from the pop-up menu that appears.

Give the new script asset an interesting name like “HelloWorld” (Unity will add the “.js” extension for you automatically so you don’t have to add it in the file name here).

If you select the new JavaScript asset in the Project view, you will see the contents of the JavaScript file in the Inspector view.

Unity - JavaScript Import Settings

Unity – JavaScript Import Settings

Press the “Open…” button on the Inspector view.

Unity comes with a built-in source code editor called MonoDevelop-Unity which is a customized version of the MonoDevelop source code editor. By default, the MonoDevelop-Unity editor will open and your “HelloWorld.js” script should open and you should see something similar to what is shown below.

MonoDevelop - HelloWorld

MonoDevelop – HelloWorld

When you create a new JavaScript source file, Unity will provide some default template for you. Let’s take a look at what Unity provides by default.

#pragma strict

function Start () {

}

function Update () {

}

So this is not very interesting, but let’s take a look at what this script is doing.

On the first line you see “#pragma strict”. This is a special command (called a compiler directive) that is used to tell the JavaScript compiler to not allow dynamic typing in this script file. Basically this is an optimization that will allow your script to run faster. You should always use this command at the top of every JavaScript file.

The observant programmer will realize that this compiler directive is only available in JavaScript source files and not in C# or Boo scripts. This is because JavaScript has support for dynamic typing but this directive disables dynamic typing in this script file.

C# and Boo are statically typed languages and therefor this directive is always implied.

On line 3 a function called Start is defined.

A function is a group of commands that can be executed many times without having to rewrite any code. That function can be executed by accessing the name of the function.

For example, we could create a function called Jump in our script and anytime the user presses the “Jump” key on the keyboard or game controller, we would invoke the function that executes the group of commands that makes our character jump. Without functions, we would have to rewrite all of the commands needed to execute that action everywhere it is used. Imagine how difficult it becomes to keep this action the same everywhere it is used!

In this case, the Start function in this script is invoked when the game is started.

On line 7 another function called Update is declared. This function is called by the Unity engine on the script every time the game loop is ticked.

Before we go further, let’s take a look at what the game loop is.

The Game Loop

The game loop is a sequence of events that occur during the lifetime of your game. It usually starts off with some kind of initialization sequence, followed by an infinite (until the game is quit) loop which when terminated, is followed by some termination sequence. Which might look something like this:

Unity Game Flow Diagram

Unity Game Flow Diagram

When the player starts the game, the script components that are attached to the GameObject’s in the scene are loaded. When the script is loaded, it’s Awake() method is invoked (if it has been defined on the script).

If there are any statements defined in the global scope, then these statements are executed just before the Start() function is called for each GameObject.

The Start() function is called after all of the GameObjects in the scene have been loaded but before the first call to Update() or FixedUpdate() method. The Start() function is the best place to query for references to other GameObjects in the scene because when Start() is called, you know that all other GameObjects have been loaded.

After the Start() function returns, the game enters the main update loop. The update loop is executed indefinitely until the game is quit. There are actually 2 update loops in Unity. The main update loop and the fixed-time update loop.

Update Loop

The main update loop is executed as fast as possible regardless of processing power or complexity of the game logic. This means that this Update loop may execute more times per second on computers with faster CPU’s and less times per second on computers with slower CPU’s than your own.

This Update Loop may look something like the diagram shown below.

Unity Update Loop

Unity Update Loop

From the diagram, we can see that unless the user has quit the game, the Update() function will be called on all enabled script components in the scene that define the function.

After all script components have been updated, the LateUpdate() function is called. The LateUpdate() function can be used when you want to make sure that all GameObjects in the scene have been updated first before you do something else.

Fixed Update Loop

The FixedUpdate() function is executed at a fixed time step. You can configure the time between calls to FixedUpdate() in the project settings but generally you can leave the setting at it’s default value.

It is recommended to use the FixedUpdate() method rather than the Update() method to control things that effect physics objects (like the Rigidbody component) because this function is guaranteed to execute at a fixed time-step. When you can guarantee the time-step between frames, your simulations will be more stable and more predictable (and more reproducible) than if the time-step between frames is variable.

The flow diagram for the fixed update loop might look something like the image shown below.

Unity Fixed Update Loop

Unity Fixed Update Loop

As you can see from the diagram, as long as the user hasn’t quit the game and the fixed-timestep time has elapsed, execute the FixedUpdate() function.

We can test the execution order of these functions with the following script:

#pragma strict

Debug.Log("ExecutionOrder::global");

function Awake() {
	Debug.Log("ExecutionOrder::Awake");	
}

function Start () {
	Debug.Log("ExecutionOrder::Start");
}

function Update () {
	Debug.Log("ExecutionOrder::Update");
}

function FixedUpdate() {
	Debug.Log("ExecutionOrder::FixedUpdate");
}

function LateUpdate() {
	Debug.Log("ExecutionOrder::LateUpdate");
}

function OnRenderObject() {
	Debug.Log("ExecutionOrder::OnRenderObject");
}

Copy this code to a new Javascript asset (called “ExecutionOrder” for example) then drag-and-drop this script asset onto one GameObject in the scene.

Open the Console window (Ctrl-Shift-C) and play the game for about 1-2 seconds. You should see the following statements in the Console window.

ExecutionOrder::Awake
ExecutionOrder::global
ExecutionOrder::Start
ExecutionOrder::FixedUpdate
ExecutionOrder::Update
ExecutionOrder::LateUpdate
ExecutionOrder::FixedUpdate
ExecutionOrder::Update
ExecutionOrder::LateUpdate
ExecutionOrder::OnRenderObject
...

From this output you can confirm that the Awake() function is called first, then the statements in the global section are executed followed by the Start() function. Thereafter the Update and FixedUpdate loops are entered. Whether the Update() method or the FixedUpdate() method are invoked first or how many times Update() is called compared to FixedUpdate() is not strictly guaranteed.

The OnRenderObject Method

The OnRenderObject() function is invoked after the camera has rendered the scene but before the rendered scene is displayed on the screen. So this function could be used to render a mesh or other geometry after everything else in the scene has been rendered. For example, you could use this function to draw a silhouette or highlight around any geometry even if it’s occluded by other geometry in the scene (x-ray vision?)

If you look at the output from the previous example, you may notice that the OnRenderObject() function is not necessarily invoked after every Update() function. This implies that the game is Updated as fast as possible, but the scene is only Rendered when the screen needs to be redrawn (based on the refresh rate of your screen but usually 60 frames/second).

Variables

Of course, it would be difficult to implement any interesting behaviours if we only had functions. We need something else to store the current State of our GameOjects. For this, we use variables.

A variable is something that can hold data.

A variable declaration has the following form in JavaScript

var variableName [: type] [= value];

The var keyword is required when declaring variables (similar to the function keyword when you declare a function). The var keyword is always follwed by the variable name.

A variable name must always begin with a letter (‘a’-‘Z’) or an underscore (‘_’) followed by any combinations of letters (‘a’-‘Z’), numbers (‘0′-‘9′), or underscore (‘_’) characters. Variable names may not contain spaces.

For example, the following variable declarations are valid:

var a;         // OK: Single letter variables are okay,
               //  but not recommended.
var b;         // OK: variable names should be more meaningful.
var a0;        // OK: Starts with a letter follwed by a number.

var _rotation; // OK: Starts with an underscore
var Rotation;  // OK: Starts with a capital letter
var rotation;  // OK: Variable names are case-sensitive so 
               //   Rotation is different from rotation.

The following declarations are not valid:

var 123;           // ERROR: Starts with a number.
var 123a;          // ERROR: Starts with a number.
var @foo;          // ERROR: @ is not a valid character.
var rotation rate; // ERROR: Variable names cannot contain spaces.
var var;           // ERROR: Variable names cannot be reserved keywords.

Optionally, you can assign a value to the variable in the variable declaration.

var foo = 5;                  // foo is an integer.
var bar = 5.0f;               // bar is a floating-point value.
var token = 'a';              // token is a character.
var message = "Hello World!"; // message is a string.
var isFun = false;            // isFun is a boolean.

JavaScript uses type inference which means that the JavaScript compiler can infer the type of the variable based on its value. In this case, you don’t have to explicitly declare the type. In the example above, the value foo becomes an integer (int), bar becomes a floating-point value (float), token becomes a character (char),message becomes a string of characters (string), and isFun becomes a boolean.

Types

Optionally, you can also explicitly specify the variable type when you declare a variable.

var foo : int;         // foo is explicitly an integer.
var bar : float;       // bar is explicitly a floating-point value.
var message : String;  // message is explicitly a character string.
var isFun : boolean;   // isFun is explicitly a boolean.

Let’s examine the different primitive types that are supported in JavaScript.

Integer

An integer is declared using the int type in JavaScript. An integer is implemented as a System.Int32 in the .NET Framework.

var foo : int;

An integer () can be used to store any positive or negative whole number. It cannot be used to store numbers with a decimal points.

Integers can be in the range -2,147,483,648 to 2,147,483,647.

Float

A floating-point value is declared with the float type in JavaScript. The float type is implemented as a System.Single in the .NET Framework.

var bar : float;

A floating-point value is a Real () number. Real numbers can be used to represent values that occur in-between the whole numbers but it can also be used to represent whole numbers.

For example, the following values are all valid single-precision floating-point numbers:

var foo = 3.14159265359f; //  PI
var bar = 0.5f;           //  1/2
var fooBar = -0.5f;       // -1/2
var fooFoo = 5.0f;        //  5

The “f” post-fix on the value tells the compiler that the number represents a single-precision 32-bit floating-point value. This post-fix is optional in JavaScript.

A single-precision 32-bit floating-point value can be in the range -3.402823e38 to 3.402823e38.

Double

If the single-precision 32-bit floating-point value is not accurate enough for you (99.99% of the time, a float has enough precision), then you can use a double-precision 64-bit floating-point value (double). A double is implemented as a System.Double in the .NET Framework.

var bar : double;

For example, the following variable declarations are valid double-precision floating-point values:

var foo : double = 3.1415926535897932384626433832795; //  PI
var bar : double = 0.5;           //  1/2
var fooBar : double = -0.5;       // -1/2
var fooFoo : double = 5.0;        //  5

If you really want a double, you should explicitly declare the type for the variable otherwise it will be declared as a single-precision 32-bit floating point value (float).

A double-precision 64-bit floating-point value can be in the range -1.79769313486232e308 to 1.79769313486232e308

It is recommended that you only use single-precision 32-bit floating-point values (float) in your game. In most cases these values provide enough precision for your needs.

Double-precision 64-bit floating-point values will run slower than single-precision 32-bit floating point values when deploying to 32-bit builds and they also consume twice as much space in memory.

It is recommended to only use doubles when absolutely necessary.

Strings

A string is used to store a character string. A character string is any sequence of characters enclosed in double-quotes or single-quote characters.

var bar : String;

A String is implemented as a System.String in the .NET Framework.

For example, the following string declarations are all valid.

var playerName = "Jeremiah";
var message = "Hello, my name is \"Jeremiah\"";
var quote = '"To be or not to be..."';

These three strings demonstrate some interesting syntax rules. The first string (“Jeremiah”) is a simple double-quoted string. This is probably the most common type you will use.

The second string on line 2 is a double-quoted string that contains the double-quote character inside the string. In this case, we must escape the double-quote character inside the string with the back-slash character (‘\‘).

The third string on line 3 is a single-quoted string that contains the double-quote character inside the string. In this case we do not need to use the escape character (‘\‘) to insert the double-quote character.

The table below shows the different escape sequences that can be used in strings.

Escape Sequence Effect
\b Backspace (rarly used)
\f Form feed (rarly used)
\n Newline. A newline character will be inserted and the text will wrap to the next line.
\r Carriage return. Traditionally used to move the position of the cursor to the beginning of the current line. This escape sequence is generally not used.
\’ Single quote. Used in single-quoted strings to insert the single-quote character. It is not necessary to use this escape sequence in double-quoted strings.
\" Double quote. Used in double-quoted strings to insert the double-quote character. It is not necessary to use this escape sequence in single-quoted strings.
\\ Backslash character. Used to insert the backslash character.

Strings can be concatenated together using the ‘+‘ operator.

var string1 = "Hello";
var string2 = "World!";
var concatString = string1 + " " + string2;

The concatString variable will contain the string “Hello World!”.

Boolean

A boolean is logic data type that can have 1 of 2 possible values: true or false.

var bar : boolean;

The boolean type is implemented as the System.Boolean type in the .NET Framework.

Some valid values for the boolean data types are:

var isTrue = true;    // Value is true.
var isFalse = false;  // Value is false. (This is a bad name for this variable!)

The variable name “isFalse” is actually a really bad name for a boolean value. Can you explain why?

Arrays

What do you do if you need to declare a lot of things of the same type but you don’t want to declare a separate variable for each one? You use arrays.

There are multiple ways to declare an array in JavaScript.

Built-In Array

The built-in array is the preferred way to declare an array. It’s the fastest way to access elements sequentially in script.

A built-in array is declared in the following way:

var floatArray : float[]; // Un-sized static array.

The statement above will declare an un-sized array of floats. Using this method of declaring an array requires the user to populate the array in the Inspector. This array cannot be re-sized in script.

Literal Array

You can also initialize an array with literal values during declaration.

var floatArray = [ 1.0f, 2.0f, 3.0f ]; // Static array with 3 values.

In this case, a 3-element array is created with its first element initialized to 1.0f, the second element initialized to 2.0f, and the third to 3.0f. Using this method, the array can only be resized in the Inspector. This array cannot be re-sized in script but you should also not assume the array contains any values at all because the user can set the number of elements in this array to 0 in the Inspector if they want.

JavaScript Array

Unity provides the JavaScript array type to be consistent with standard JavaScript syntax.

var javascriptArray = new Array(); // JavaScript Array

A JavaScript array is not actually an array at all but rather an Object that behaves like an array.

JavaScript arrays are not exposed in the Inspector but they can be dynamically sized in script.

JavaScript arrays can be converted to built-in arrays by using the ToBuiltin function on the JavaScript array object.

var builtinArray : String[] = javascriptArray.ToBuiltin(String);

Accessing Array Elements

Regardless of type of array the individual array elements are accessed in the same way. To refer to a particular element of the array you use the index of the element in the array enclosed in square brackets ([ ]). The first array element is at index 0 and the second array element is at index 1 etc…

var stringArray = new String[5];

stringArray[0] = "Hello, ";
stringArray[1] = "my ";
stringArray[2] = "name ";
stringArray[3] = "is ";
stringArray[4] = "Jeremiah ";

We can query the number of elements in an array using the Length property of the array.

Debug.Log( "The stringArray contains " + stringArray.Length + " elements." );

The previous line will print “The stringArray contains 5 elements.”.

Class

A Class is a complex type that can contain both variables and functions. Classes are declared using the class keyword.

A class is not the same as a variable. You cannot assign values to the member variables of a class unless you first create an instance of that class. Let’s take a look at an example.

Suppose we declare a class called “Person“:

class Person
{
	var firstName : String;
	var lastName : String;
	var age : int;
	var isMale : boolean;
}

The Person class contains four member variables (firstName, lastName, age, and isMale).

We can create an instance of the class by declaring a variable of the class type.

var person : Person;  // Declare a variable of type Person.
                      // Identifier names are case-sensitive so
                      // person and Person are two different things!
var jeremiah = new Person(); // Create a new instance of a Person.

jeremiah.firstName = "Jeremiah"; // OK: jeremiah is an instance of Person.
person.firstName = "First";      // OK: person is a variable of type Person.
Person.firstName = "First";      // ERROR: Person is NOT an instance!

We can then access the different properties of the instance.

jeremiah.firstName = "Jeremiah";
jeremiah.lastName = "van Oosten";
jeremiah.age = 35;
jeremiah.isMale = true;

We can delete a class by setting its reference to null.

jeremiah = null;

If you try to access the properties of a deleted reference, you will get a NullReferenceException at run-time.

We can extend our class to contain functions. A special function called a constructor can be defined using the name of the class:

class Person
{
    var firstName : String;
    var lastName : String;
    var age : int;
    var isMale : boolean;

    // Default constructor.
    function Person() {
    }        
}

We can also initialize the properties of our class if we supply a constructor that accepts arguments.

class Person
{
    var firstName : String;
    var lastName : String;
    var age : int;
    var isMale : boolean;

    // Parameterized constructor.
    function Person( firstName : String, lastName : String, age : int, isMale : boolean )
    {
        this.firstName = firstName;
        this.lastName = lastName;
        this.age = age;
        this.isMale = isMale;
    }
}

// Create an instance of a person using the parameterized constructor.
var jeremiah = new Person( "Jeremiah", "van Oosten", 35, true );

Within the scope of the class, we can use the this keyword to refer to the instance of the class. In the previous example, we use the this keyword to differentiate between the class member variables and the constructor arguments.

We can also define member functions in the class.

class Person
{
	var firstName : String = "John";
	var lastName : String = "Doe";
	var age : int = 19;
	var isMale : boolean = true;
	
	function Person()
	{}
	
	function Person( firstName : String, lastName : String, age : int, isMale : boolean )
	{
		this.firstName = firstName;
		this.lastName = lastName;
		this.age = age;
		this.isMale = isMale;
	}

	function ToString() : String
	{
		var asText : String;
		
		asText =  "Name: " + firstName + " " + lastName + "\n";
		asText += "Age: " + age + "\n";
		asText += "Gender: " + (isMale ? "Male" : "Female");
		
		return asText;
	}
}

In the previous example, we declare a function called ToString() within the scope of the class. This function also demonstrates a feature of functions that hasn’t been introduced yet: return values (return values are not unique to class functions).

A function can return a value to the caller using the return keyword. In this case, the ToString() function returns a value of type String.

Rotation Script

Now that we have a good understanding of functions, variables, types and classes, let’s put it to good use. We will create a script that will rotate an object around the global Y-axis. We will do this in 2 different ways which we will see changes the way the object behaves.

First, let’s create a new script. In your Unity project, create a new JavaScript asset called “RotateObject“.

Open the new script in MonoDevelop and copy-paste the following code into the script.

#pragma strict

var variableRotationRate = 0.0f;
var fixedRotationRate = 0.0f;

function Update () {
	transform.Rotate( Vector3.up * variableRotationRate );
}

function FixedUpdate() {
	transform.Rotate( Vector3.up * fixedRotationRate );
}

Now create a scene with 2 cubes. Place the cubes close together (without overlapping) so you can see both of them in the scene view. Name one cube “Cube01” and the other “Cube02“.

Make sure you aim the main camera at the cubes (hint: use the scene view to frame the cubes in the view then use the Align with View command (Ctrl+Shift+F) to align the main camera with the scene view).

Add a directional light as a child of the main camera GameObject to light the cubes. Zero the X, Y, Z position and rotation values of the directional light so it is always pointing in the direction of the camera.

Attach the RotateObject script component to each cube in the scene.

You should see something similar to what is shown below.

Unity - Rotate Object Scene

Unity – Rotate Object Scene

Select Cube01 and set it’s Variable Rotation Rate value in the inspector to 1.

Select Cube02 and set it’s Fixed Rotation Rate value in the inspector to 1.

Run the game. You should see something similar to what is shown below.

You will notice that the two cubes are not synchronized despite the fact that we set both rotation rates to the same value of 1. This is because one cube is performing its rotation in the Update() function and the other is performing its rotation in the FixedUpdate() function. These two functions are being updated at different frequencies.

The frequency that the Update() function is invoked is also CPU bound. It will run faster on computers with faster CPUs and slower on computers with slower CPUs. How can we ensure that the cubes rotate at the same rate regardless of the CPU speed?

To solve this, we must take time into consideration.

Time

Unity provides a class object called Time that we can use to solve our frame-rate issues. The Time class provides a static member variable called deltaTime that we can use to adjust our rotation rate.

You can declare a variable as static by using the static keyword. You do not need an instance of a class to access static variables.

Let’s adjust the script to take time into consideration.

#pragma strict

var variableRotationRate = 0.0f;
var fixedRotationRate = 0.0f;

function Update () {
	transform.Rotate( Vector3.up * Time.deltaTime * ( variableRotationRate * 360.0f ) );
}

function FixedUpdate() {
	transform.Rotate( Vector3.up * Time.deltaTime * ( fixedRotationRate * 360.0f ) );
}

If you copy paste this code to the RotateObject.js script asset and run the game again, you may notice that the cubes are rotating really fast. I multiplied the rotation rate by 360 to make the cubes rotate at RotationRate rotations per second. Previously, we set the rotation rate to 1 for both variable and fixed rotation rates which will cause the cubes to rotate one full rotation every second.

Select Cube01 and set its Variable Rotation Rate to 0.25 and its Fixed Rotation Rate to 0.

Select Cube02 and set its Variable Rotation Rate to 0 and its Fixed Rotation Rate to 0.25.

Now the cubes should rotate at a rate of 90 degrees per second. It should take exactly 4 seconds to make a full rotation.

Now run the game again and you will notice that the two cubes rotate at the same speed!

There will be many situations where using the Time.deltaTime value will be useful.

Conditional Logic

We now have a better understand of types and variables in JavaScript but there is one more extremely important concept we must understand before we can make interesting gameplay. Without conditional logic, we couldn’t make any choices in our gameplay. We couldn’t tell our player to jump, or shoot, or taunt the other players.

In order to use conditional logic correctly, we must first understand some basics about logic. We will now put the boolean type introduced earlier to good use.

Boolean Logic

The most basic concept of boolean logic is the concept of “true” and “false“. We have a boolean data type that can be used to store these values.

We can compare values to test if a certain condition is true or false.

The table below shows the different comparison operators that are available.

Operator Operation Desctiption
== Equality The equality operator is used to test if two values are the same.
!= Inequality The inequality operator is used to test if two values are not the same.
> Greater than The greater than operator is used to test if the value on the left of the operator is greater than the value on the right.
< Less than The less than opertator is used to test if the value on the left of the operator is less than the value on the right.
>= Greater than or equal to The greater than or equal operator is used to test if the value on the left of the operator is greater than or the same as the value on the right.
<= Less than or equal to The less than or equal operator is used to test if the value on the left of the operator is less than or the same as the value on the right.

We can use these comparison operators together with the following logical operators to form complex logical conditions.

Operator Operation Description
&& And operator. Returns true if both left and right operands are true.
|| Or operator. Returns true if either the left or the right operands are true.
! Not operator. Reverses the value of the boolean expression (makes true false and false true).

The first type of conditional we will look at is the if statement.

If Statement

The If statement has the following syntax.

if ( condition )
{
    // Statements to execute if condition is true.
}

The following statements will all evaluate to true:

var a = 3;
var b = 5;
var c = 10;

var strHello = "Hello";
var strWorld = "World!";

var strHelloAgain = strHello;

if ( a < b ) // TRUE: 3 is LESS THAN 5
{}

if ( b < c ) // TRUE: 5 is LESS THAN 10
{}

if ( a < b && b < c ) // TRUE: 3 is LESS THAN 5 AND 5 is LESS THAN 10
{}

if ( c > a || a > c ) // TRUE: 10 is GREATER THAN 3 OR 3 is GREATER THAN 10
{}

if ( strHello != strWorld ) // TRUE: "Hello" is NOT EQUAL "World!"
{}

if ( strHello == strHelloAgain ) // TRUE: "Hello" is EQUAL to "Hello"
{}

The following statements all evaluate to false:

if ( a > b ) // FALSE: 3 is NOT GREATER THAN 5
{}

if ( b > c ) // FALSE: 5 is NOT GREATER THAN 10
{}

if ( a < b && b > c ) // FALSE: 3 is LESS THAN 5 but 5 is NOT GREATER THAN 10
{}

if ( a > b || b > c ) // FALSE: 3 is NOT GREATER THAN 5 and 5 is NOT GREATER THAN 10

if ( strHello == strWorld ) // FALSE: "Hello" is NOT EQUAL to "World!"
{}

Optionally, we can execute some other statements only if the if condition fails. This is called an if-then-else statement.

If-Then-Else

The if-then-else statement has the following syntax.

if ( condition )
{
    // Execute these statements if the condition is true.
}
else
{
    // Execute these statements if the condition is false.
}

We can also nest if-then-else statements.

if ( condition1 )
{
    // Execute these statements if condition1 is true.
}
else if ( condition2 )
{
    // Execute these statements if condition2 is true.
}
else
{
    // Execute these statements if neither condition1 or condition2 are true.
}

If you you find you have to write a lot of nested if-then-else statements, you may want to consider using a switch statement.

Switch Statement

The switch statement has the following syntax.

switch ( <expression> )
{
case <test1>:
    // Statements to execute if expression evaluates to test1.
    break; // Break-out of switch statement.
case <test2>:
    // Statements to execute if expression evaluates to test2.
    break; // Break-out of switch statement.
case <test3>:
    // Statements to execute if expression evaluates to test3.
    break;
default:
    // Statements to execute if no test passes.
}

Switch statements are very common way of implementing state-machines. Let’s look at a real-world example.

Suppose we have a character controller that can have the following states:

  • Idle: The character is not moving. Perform some “Idle” animation.
  • Walk: The character is walking. Perform the “Walk” animation.
  • Run: The character is running. Perform the “Run” animation.
  • Jump: The character is jumping. Perform the “Jump” animation.
  • Fire: The character is firing a weapon. Perform the “Fire” animation.

We could implement this state machine using the following switch statement.

#pragma strict

enum State
{
	Idle,
	Walk,
	Run,
	Jump,
	Fire
}

private var currentState : State = State.Idle;

function Update () 
{
	switch ( currentState )
	{
	case State.Idle:
		DoIdle();
		break;
	case State.Walk:
		DoWalk();
		break;
	case State.Run:
		DoRun();
		break;
	case State.Jump:
		DoJump();
		break;
	case State.Fire:
		DoFire();
		break;
	default:
		currentState = State.Idle;
	}
}

In this code example, we use an enumeration to define some valid states the character can have. We must also define a variable which will store the character’s current state. This variable is marked “private” which means that this variable will neither be available in the Inspector nor will it be accessible by other scripts attached to the GameObject.

If the character is in the Idle state, we execute the DoIdle() function.

If the character is in the Walk state, we execute the DoWalk() function.

If the character is in the Run state, we execute the DoRun() function.

If the character is in the Jump state, we execute the DoJump() function.

If the character is in the Fire state, we execute the DoFire() function.

If the character is in some invalid state, then we set the current state to a valid state and do nothing until the next time Update() is invoked on this script.

While Loops

There might be a situation where you need to execute a block of code as long as some condition is true. In this case, we can use a while loop.

The syntax for the while loop is:

while ( condition )
{
    // Do something as long as condition is true.
}

For example, the following example shows how we can spawn 15 enemies.

var numEnemies = 15;

var i = 0;
while ( i < numEnemies )
{
    SpawnEnemy();
    ++i;
}

Do-While Loops

In some cases, you may want to make that a block of code gets executed at least once before the while condition is tested. For this, we can use the do-while loop.

The do-while loop has the following syntax.

do
{
    // Do something before we test the while condition.
} while ( condition ); // Keep doing it as long as condition is true.

For Loops

The for loop is useful when you want to iterate arrays or other types of enumerable containers or you want to execute a statement block a certain number of times.

The syntax of the for loop is:

for ( <initializer>; <condition>; <iterator> )
{
    // Operate on element.
}

We can convert the enemy spawn example to use a for loop instead of a while loop.

var numEnemies = 15;

for ( var i = 0; i < numEnemies; ++i )
{
    SpawnEnemy();
}

For-In Loop

Iterating (looping over the elements of) arrays is so common in programming that there is a special construct in JavaScript that allows you to loop over the elements of a container. For this purpose, we can use the for-in loop.

The syntax of the for-in loop is:

for ( element in container )
{
    // Operate on element.
}

For example, we can iterate arrays using the following code:

var stringArray = new String[5];

stringArray[0] = "Hello, ";
stringArray[1] = "my ";
stringArray[2] = "name ";
stringArray[3] = "is ";
stringArray[4] = "Jeremiah ";

for ( val in stringArray )
{
	Debug.Log( val );
}

This will output the following code in the debug console:

Hello, 
my 
name
is 
Jeremiah
Only containers that implement the System.Collections.IEnumerable interface can be enumerated using the for-in loop. Both JavaScript arrays and built-in arrays implement the IEnumerable interface.

So I think that's enough blah-blah-blah. Let's put all this knowledge to good use and make a simple prototype of a wall breaking game.

Wall Breaker

In this tutorial you will put all of the knowledge you just learned to good use. We are going to make a simple demo called "Wall Breaker".

First, we going to create a wall of bricks (cubes). The width and height of our brick-wall should be tweakable.

Then we want to be able to shoot a ball at our wall and see how it collapses. We should be able to tweak the initial velocity of the ball.

Create A New Scene

Open your test project in Unity (or create a new project) and create a new scene.

Unity - Wall Breaker - New Scene

Unity - Wall Breaker - New Scene

Save your scene. I suggest you call your scene file "WallBreaker" and you save it in a folder called "Scenes".

Create A Plane

Create a new Plane GameObject in your scene (select GameObject -> Create Other -> Plane from the main menu).

Unity - Wall Breaker (2)

Unity - Wall Breaker (2)

Set the Position and Rotation of the Plane GameObject to 0 in all axes.

Scale the Plane to 10 units in the X axis and 10 units in the Z axis. Make sure the Scale in the Y axis is 1.

Unity - Wall Breaker (3)

Unity - Wall Breaker (3)

Save your scene!

Create a Brick

Create a new Cube GameObject in your scene (select GameObject -> Create Other -> Cube from the main menu).

Scale the Cube so that it is more "brick shaped". Set the Scale in the Y and Z axes to 0.5.

Unity - Wall Breaker (4)

Unity - Wall Breaker (4)

If the Cube was placed at the origin (position is at 0, 0, 0) then the Cube GameObject will be sitting inside the plane. Use the transform gizmo to move the cube just above the plane. We've scaled the Cube to 0.5 units in the Y axis so if we translate the Cube to 0.25 units in the Y axes then the Cube will be sitting directly on the Plane.

Unity - Wall Breaker (5)

Unity - Wall Breaker (5)

We want our Cube to be Physics controlled so we must add a Rigidbody component to the cube. Select the Cube GameObject in the Hierarchy view and select Component -> Physics -> Rigidbody from the main menu.

Unity - Wall Breaker (6)

Unity - Wall Breaker (6)

Save the scene!

Create a Canon Ball Prefab

We will also create a canon ball that will be used shoot at our brick wall.

Create a new Sphere GameObject (select GameObject -> Create Other -> Sphere). The position of the sphere is irrelevant because it will only be used to create a Prefab.

We also want the Sphere to be physics controlled so let's add a Rigidbody component to the Sphere GameObject. Select the Sphere GameObject in the Hierarchy view and select Component -> Physics -> Rigidbody from the main menu.

Unity - Wall Breaker (7)

Unity - Wall Breaker (7)

Create a folder in your Project view called Prefabs.

Now drag-and-drop the Sphere GameObject from the Hierarchy view into the Prefabs folder in the Project view. We now have a GameObject template (Prefab) that we can instantiate at run-time.

Delete the original Sphere GameObject from the scene.

We should have something similar to what is shown below.

Unity - Wall Breaker (8)

Unity - Wall Breaker (8)

Save your scene!

Adding the Logic

Create a new JavaScript asset in your Scripts folder called "WallBreaker". Open the new WallBreaker JavaScript file in MonoDevelop.

Initially, your script will contain the default JavaScript template.

#pragma strict

function Start () {

}

function Update () {

}

First, we need to define a few parameters that will be exposed in the Inspector view.

var brickPrefab : GameObject;
var ballPrefab : GameObject;

At the top of the source file, we declare two variables that will be used to store references to Prefabs that we will instantiate while the game is running. Initially, these variables are empty (null) and we cannot use empty variables. These variables will be set in the Inspector view.

Next, we also want to specify how many bricks will be used to build our wall.

var numBricksX : int = 5;
var numBricksY : int = 5;

For this, we will use numBricksX to specify how many bricks to place in the X axis (to the right of our original brick) and numBricksY is used to specify how many bricks to place in the Y axis (above the original brick). The default value of 5 for each of these variables will produce a wall that is 5x5 = 25 bricks. 25 bricks is not a very big wall but we will tweak these values in the Inspector later.

We also want to specify how much force will be applied to our canon ball when we shoot it at the brick wall.

// How much force to put on the ball.
var forceMultiplier : float = 100;

This may not be enough force to shoot our canon ball, but again we will be able to tweak this value in the Inspector later.

We will be able to shoot canon balls into the scene by clicking on the game window with the mouse. Each time we click, a new canon ball will be instantiated. If the user keeps clicking with the mouse, we will have more and more balls in our scene. At some point the Physics engine will start to get very very slow trying to update 2000 spheres in our scene. To prevent the player from adding an infinite number of canon balls in the scene, we will keep track of each new canon ball in an array and if there are more than 10 canon balls in the scene, we will remove the oldest one.

private var balls = new Array();

The balls array will be used to store references to all of the cannon balls that have been shot into the scene. Once our array gets to a certain size, we will remove the oldest ball in the list.

Since we want to make sure that the mouse cursor is always visible on screen, we will set the showCursor property of the Screen class to true.

function Awake()
{
	Screen.showCursor = true;
}

We will use the Start() function to build up our wall.

The Start Function

The Start function is executed after all of the GameObjects in the scene have been created. This is where we will use the brick prefab to build our brick wall.

function Start () 
{
	if ( brickPrefab != null )
	{
		var brickSize = brickPrefab.renderer.bounds.size;
				
		var X = brickPrefab.transform.position.x + brickSize.x;
		var Y = brickPrefab.transform.position.y;
		var Z = brickPrefab.transform.position.z;
		
		var brickOrientation = brickPrefab.transform.rotation;
		
		for( var i = 0; i < numBricksY; ++i )
		{
			for ( var j = 0; j < numBricksX; ++j )
			{
				if ( i == 0 && j == ( numBricksX - 1 ) ) break;
				Instantiate( brickPrefab, Vector3( X, Y, Z ), brickOrientation );
				X += brickSize.x;
			}
			X = brickPrefab.transform.position.x;
			Y += brickSize.y;
		}
	}
}

The first thing we do in this function is check to make sure our brick prefab is not null. There must be a valid reference to a GameObject before we can do anything with it. We will set a reference to the brick GameObject that exists in our scene using the Inspector later.

If we have a valid reference to a GameObject, we will query the size (dimensions) of the GameObject using the GameObject's Renderer component. The GameObject's Renderer component can be accessed using the renderer property of the GameObject.

Every type that derives from the Component type (including the GameObject type and MonoBehaviour type) have several predefined properties to access the more common component types. I suggest you familiarize yourself with the Component class, the GameObject class and the MonoBehaviour class.

On lines 29-31, we use the brick prefab's position in the scene to determine the initial position of our brick wall. On line 29 We add the size of the brick in the X axis so that we don't create the first brick inside the brick that is already in our scene.

On line 33, we store the orientation of the original brick. We will use this orientation to rotate the new bricks in the scene to match the orientation of the original brick.

To build our brick wall we will create several rows of bricks. The width of each row of bricks is determined by the numBricksX variable. The number of rows, or the height of the brick wall is determined by the numBricksY variable. For example, if numBricksX is 5 and numBricksY is 5, we will create 5 rows of 5 bricks for a total of 25 bricks.

The outer for-loop (on line 35) will loop through the rows of bricks. The inner for-loop (on line 37) will create a brick for each column of the brick wall.

On line 39 we check to see if we are at the end of the first row of bricks. If we are, we want to place 1 less brick to compensate for the original brick that is in the scene. If we didn't account for this extra brick, we'd have one extra brick on the first row of bricks (which would just look silly).

The break keyword will break-out of the currently executing for-loop. In this case, we are in the inner for-loop so the break keyword will break-out of the inner for-loop and the script will continue executing the outer for-loop.

On line 40, we use the Instantiate function to create a copy of our brick prefab. The first argument must be a valid reference to the GameObject we want to duplicate. The second argument to the Instantiate function is the position in world-space where we want the cloned object to be placed. And the third and final argument to the Instantiate function is the rotation of the cloned object. The Instantiate function will return a reference to the cloned object. If we don't plan on doing anything with the cloned object, we can simply ignore the returned reference.

On line 41, we update the X variable to the position where the next brick will be cloned.

Once we've created a row of bricks, we move up the Y axis to create another row of bricks.

And that's all there is to creating a grid of bricks.

TRY THIS: Insert the following line of code inside the inner for-loop (just after line 41) and run your game:

yield WaitForSeconds( 0.1 );

If done correctly, you will be able to see the bricks "popping" into existence as they are being instantiated. This will allow you to visualize how the for-loops are working to create the brick wall.

Now let's handle the canon ball firing.

The Update Function

In the Update() function, we will handle the firing of the canon ball.

function Update () 
{
    if ( Input.anyKeyDown && ballPrefab != null )
    {
        var ray : Ray = Camera.main.ScreenPointToRay( Input.mousePosition );
        var ball = Instantiate( ballPrefab, ray.origin, Quaternion.identity );
        // Put a force on the ball in the direction of the ray generated by the position of the mouse.
        ball.rigidbody.AddForce( ray.direction * forceMultiplier );
        balls.Push( ball );
    }
     
    if ( balls.length > 10 )
    {
       	var removeMe = balls[0] as UnityEngine.Object;
        balls.RemoveAt(0);
        Destroy(removeMe);
    }
}

Anytime the player presses a key on the keyboard or presses a button on the mouse, we will fire a ball into the scene. Of course, we also want to make sure that the ballPrefab has a valid reference to a prefab object.

If we have a valid reference to a prefab, we want to determine where the mouse cursor is in world-space. In order to determine the world-space position of the mouse cursor, we need to have a camera to project through. The Camera class has a reference to the main camera in the scene. Our simple scene only defines one camera and by default, this camera is the main camera.

The Camera class defines several functions to transform from screen-space (where our mouse cursor is) into world-space (where our bricks are) as well as transforming world-space positions back to screen-space (useful for adding 2D GUI elements to objects in 3D space). In this case, we use the Camera's ScreenPointToRay function to convert our mouse cursor position in screen-space into a 3D ray in world-space. The ray's origin will be the position of the camera (the eye position) and the direction of the ray will point from the camera's position through the screen at the position of the mouse cursor.

We can use the ray variable returned from the ScreenPointToRay() function for 2 things:

  • Use the Ray.origin property to determine the position to instantiate our canon ball.
  • Use the Ray.direction property to determine the direction to fire our canon ball.

On line 54 we use the Instantiate function again but this time to create a canon ball sphere object. In this case, we ray.origin to position the new sphere instance and the Quaternion.identity property to determine the rotation of the new sphere.

A Quaternion class is used to represent an orientation (rotation) in 3D space and the Quaternion.identity property represents a zero rotation (no rotation in any axis).

The next thing we need to do is to propel our canon ball forward in the direction we clicked on the screen. We do this by applying a force on the Rigidbody component of the new ball instance. Without a valid Rigidbody component on the ball prefab, we could not apply physical forces to the ball instance.

We use the ray.direction vector to determine the direction to fire the ball. The ray.direction property is a normalized (length of 1) vector that we can scale by multiplying by the forceMultiplier variable. The value of the forceMultiplier will determine how fast our ball is projected into the scene.

We also want to make sure the player cannot add an infinite number of balls in the scene. So we add the ball to the balls array using the Push() function on the array. This will add a reference of the ball instance to the last element of the array. If the ball array gets too long (more than 10 elements in this case), then we will remove instances starting from the first element. The RemoveAt() function will remove an element from the array at a particular index. All elements in the array that have an index greater than the index specified in this function will be shifted to fill the gap. By removing the element at index 0, we know we are always removing the oldest ball first.

GameObjects can be removed from the scene using the Destroy() method.

Adding the Logic to the Scene

If you run the game now nothing will happen. We haven't added the script we just created to the scene yet.

Create an empty GameObject in the scene. Rename the empty GameObject to "Logic Controller" or something similar.

Drag-and-drop the WallBreaker script asset from the Project view onto the Logic Controller GameObject in the Hierarchy view.

Unity - Wall Breaker (9)

Unity - Wall Breaker (9)

Before we can play the game, we need to assign the Brick Prefab and the Ball Prefab references to our Wall Breaker script component.

Drag the Cube GameObject (the original brick) from the Hierarchy view onto the Brick Prefab variable in the Wall Breaker script component attached to the Logic Controller GameObject.

Drag the Sphere prefab from the Project view onto the Ball Prefab variable in the Wall Breaker script component attached to the Logic Controller GameObject.

Unity - Wall Breaker (10)

Unity - Wall Breaker (10)

Save your scene!

If you play the game now, you probably won't see very much because we haven't added any lights to the scene yet.

Add a directional light to the Main Camera and zero the position and rotation so that the directional light is always pointing in the direction of the camera.

Tweaking and Polish

Now that we have the main functionality of our game working, we can tweak a few of the variables and add some polish.

  • Make the wall 15 bricks wide and 15 bricks high.
  • Adjust the force multiplier to fire the balls faster. I found that 1300 was a good force to shoot the ball.
  • Add spotlight that is looking down on your wall. Make the spotlight cone large enough to brighten the wall.
  • Add a texture to the original brick in the scene.
  • Add a texture to the ball prefab in the project view.
  • Position and orient the Main Camera so that your entire wall is in view when you play the game.
  • Apply any other tweak values or polish to your game to make it more fun.

Playing your Game

When you are finished, your final game should look something similar to what is shown below.

Click with your mouse in the window to fire a ball at the wall.

Conclusion

This article covers a lot of details. I introduced some basic JavaScript principles such as functions, variables, class objects, and logical operations. I also introduced instantiating prefabs from both a scene object and a prefab asset in the Project view using the Instantiate() function. We also learned how to use Arrays to store lists of objects. And you also learned how to keep your game running smoothly by destroying the excessive GameObjects using the Destroy() function.

I hope that you have learned enough to get you started scripting in Unity and you also learned how easy it is to add functionality to your game.

From here, I recommend you try to create your own interesting prototypes that may someday become the next block-buster game title!

References

The Unity Scripting Reference: http://docs.unity3d.com/Documentation/ScriptReference/index.html

4 thoughts on “Scripting in Unity

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>