Variables

If I were data modeled by a computer, there would be variables which describe me including one to reference my name (a value which hasn’t changed) and my age (a value which does change every year). Some variables could only be determined by me, such as the name of my favorite movie, and others might be controlled in whole or in part by external sources, such as my marital status. All of these kinds of relationships can be modeled in code when you know the right vocabulary. In this lesson, we will create a class which implements the example I just provided, and then we will use our understanding of variables to animate an example solar system, all with a single easy script.

Modeling a Human

To begin, create a new scene. Add an empty GameObject (from the menu bar choose “GameObject -> Create Empty”) and then in the inspector click the “Add Component” button. Type “Human” in the prompt which appears. There are no components by this name, so it should show “New Script”. Press the Enter key and you will see the script options. Make sure the Language option is set to C# and press the Enter key again. This will both create a new script and attach it to the object simultaneously. Finally, “duplicate” the GameObject so that you will have two “Humans” (from the menu bar choose “Edit -> Duplicate”).

Open the “Human” script in MonoDevelop (or your favorite code editor). For our first task, let’s figure out how to name our Humans. All GameObjects already have a variable to store its name. This is what you see listed in the Hierarchy panel and in the top most part of the Inspector. Both of our objects currently have the name “GameObject” which I personally believe to be a terrible name for a human. As a side note, whenever you view the name of your GameObject, you are “reading” or “getting” the variable. Now, let’s “write” or “set” the variable. Provide a different human-sounding name for each one by typing it into the field in the inspector.

Now, that wasn’t very programmer-ish of us was it? Let’s see how we would do the same thing via code. Modify the Start method as follows:

void Start () {
	Debug.Log("Hello, my name is " + name);
}

Save your script and Run the scene. You should see two messages print to the console window (one message sent from each human you created). I saw the messages, “Hello, my name is Jon” and “Hello, my name is Jen” which by some amazing coincidence happens to be my name, and my wife’s name.

What we’ve done is to “read” or “get” the value stored by a variable called “name”, append it to the string of text, and pass the whole message as a parameter to the method which printed the message to the console.

To “write” or “set” a variable, we use something called an assignment operator. In order to confuse you, programmers from long ago decided that this operator would look like the equal sign (and in order to actually check for equality they decided to use TWO equal signs – sigh). So, to “write” or “set” a variable you could use a line of code like the following:

name = "Fred";

If you were to put that line of code in the start method above the line which prints the “Hello” message, then when you run the scene you will have two humans greeting you with the message, “Hello, my name is Fred”. For as long as the scene continues to play, Fred will be their name, as you can verify in the inspector. However, when you stop the scene, the names will revert to the values they were before we pressed play (in my case, Jon and Jen). What has happened is that Unity is doing something called “Serialization” – basically this just means that any variables on a GameObject which Unity “understands” will be saved at Edit time with the scene as a default starting value. This allows you to set up your levels and make sure a scene will begin the same way every time.

Fields

GameObjects don’t have a variable for age, so next I will show how to “declare” your own variable called a “field”. At a minimum all variables must be declared with two things, a “DataType” and a “Name” by which you “identify” the variable. Add the following line to your script, inside the class (in between the brackets), but outside of the methods. By convention, most class level variables appear at the top of the class.

int age;
TIP:
There are many data types you can choose from when declaring your variables (see the full list here: http://msdn.microsoft.com/en-us/library/ms228360(v=vs.90).aspx). The most common ones you will want include:

  • bool – this is a value type which defaults to false, and can only hold “false” or “true”
  • int – this is a value type which defaults to 0, and can hold whole numbers, example: 42
  • float – this is a value type which defaults to 0, and can hold numbers with decimals, example: 3.14159f
  • string – a special type which defaults to null (nothing), and can hold arrays of characters, example: “Hello”

Because you are a Unity user you will also frequently use several other data types as well such as:

  • GameObject – this is a reference type which defaults to null and can hold a reference to an active GameObject
  • Transform – this is a reference type which defaults to null and can hold a reference to an active Transform
  • Vector3 – this is a value type (a struct) which defaults to zero on each of its sub-fields (x, y, and z)
  • RigidBody – this is a reference type which defaults to null and can hold a reference to an active RigidBody

TIP:
There are several other words which might appear in a variable declaration before the data type. “readonly” indicates a variable which can only be assigned at the time an object is created (by a class “Constructor” or while the variable is initiated). “const” is similar, but must be initialized in the declaration only. “static” indicates a variable which belongs to the class itself rather than instances of the class (for example many of the variables in the “Time” class are static).

By declaring the variable at the root level of our class, we are giving it class-level “scope”. This means that the variable will be visible everywhere within the class, even inside of methods inside of the class. Note that you can also declare variables inside of a method, but their scope will be more limited, and other methods or classes won’t have access to those variables.

Save your script and return to Unity. Look at one of your Humans and attempt to set the age variable we just declared… hey wait why don’t we see it? Variables have something called “publicity”. When you don’t specify the publicity of a variable it defaults to “private” which means that only your class knows about it. Unity only shows variables which are marked “public” or by “[SerializeField]” which is a special way to expose a property to the Unity inspector without allowing other scripts to know about the variable. Either of the following lines will allow your variable to appear in the inspector, although for now, we will use the “public” declaration:

public int age;
[SerializeField] int age;
TIP:
There is a way to see “private” and “protected” variables in the inspector. You can set the inspector mode to “Debug” by clicking the button in the upper right (three stacked horizontal lines) which appears just to the right of the lock. In Debug mode, you will be able to see the values of private variables and “backing fields”, however you still won’t be able to write to them.

With the age variable set to public, you can now see it appear in the inspector. Why does it have a value already? Our age variable was initialized to zero, because that is the default value for that data type, and because we did not initialize it to any other value in our declaration. We could have defined our variable like the following:

public int age = 23;

This declaration looks like it did before, with the exception that we also have the assignment operator, and a value which is assigned as default. If you modify your script to use this example and then return to Unity, the Human scripts there will still show zero. That is because 23 is only assigned when the object is first “created”, but our scripts had already been created and serialized with the value of 0. If you created a new GameObject and added the Human script, the new Human would have the default applied. You can also click the gear icon for our script component and choose “Reset” which will reload the default values into the script.

Properties

In the event that you want to set constraints on the way a variable can change, such as the favorite movie which only the Human itself will decide, you can use something called a “property”. Properties look very similar to “fields” which is what we created with our “age” variable, with one big difference – the property “encapsulates” the field which means that outside scripts don’t actually have direct access to the field, and you can control what they are allowed to read or write. Following is an example of a property.

public string favoriteMovie { get; set; }

With the exception of the brackets, and “get” and “set” words, this looks very similar to the declaration of a field. You must include either a “getter” or “setter” (the get and set keywords), but you don’t have to specify both. Furthermore, you can have different publicity levels for each. In this example, other scripts will be able to read and write the value of favoriteMovie. We can change it like so:

public string favoriteMovie { get; private set; }

Now, other classes can find out what this Human’s favorite movie is, but they do not have the ability to change it. Only methods within the Human class can modify the stored value.

Even though this property is public, you won’t be able to see it in the Inspector. Properties don’t actually store any data like a field does. What’s happening is that the C# compiler automatically creates another field, which is “private” and reads that value when using the getter, and writes to the field when using the setter. The following form is functionally identical but helps illustrate the former “short hand” version.

public string favoriteMovie { 
	get {
		return _favoriteMovie;
	}
	private set {
		_favoriteMovie = value;
	}
}
private string _favoriteMovie;

If you specify your own “backing field” for a property in this way, you can also add the “[SerializeField]” tag and then you would have the best of both worlds – your data is encapsulated and you can still configure it easily with the inspector. On the other hand, you also add a lot of code to your scripts this way, and it can slow down development and make a script harder to read and maintain. I usually only turn my fields into properties if I need to limit access in some way or respond whenever a value changes.

TIP:
The getter and setter of a property are very similar to methods. The getter is like a method with a return type matching the property type and which doesn’t take any parameters. The setter is like a method with a return type of void but which accepts one parameter. Whatever statements appear between the brackets of the getter and setter will execute when the property is read or written, respectively.

Properties do not have to utilize a backing field. Some properties are calculated dynamically like in the following code:

public GameObject spouse;
public bool IsMarried
{
	get
	{
		return spouse != null;
	}
}

Here we declared a public field for a reference to another GameObject called “spouse”. As long as the field actually points to an active GameObject, the IsMarried property will return “true”, otherwise it will return false. The “!=” means “is not equal to”.

Modeling a Solar System

The human example served to illustrate various vocabulary for variables, but it wasn’t very inspiring because it didn’t “do” anything. Let’s make a more interesting example and allow the variables we define to have an affect on something else.

Create a new scene to work in. If you happen to have some nice textures and know how to apply them to spheres, then create a sphere and apply your texture. Otherwise, just create a cube, so that it will be more obvious when we use code to make it rotate (from the menu bar choose “GameObject -> 3D Object -> Cube”).

Create a new script called “Motor” and implement it as follows:

using UnityEngine;
using System.Collections;

public class Motor : MonoBehaviour 
{
	public float turnRate;

	void Update () {
		transform.Rotate( new Vector3(0, turnRate * Time.deltaTime, 0) );
	}
}

On line 6 of this script we defined the variable “turnRate”, which will determine the speed in “degrees per second”, at which an object is rotating. So as an example, to make an object turn in a full circle every second you would want to assign the value “360”. This unit of measurement would make a great code “comment”, even though I excluded it since I am explaining it here and I want to keep the code snippets small. Note that we don’t assign the value of the variable anywhere in this script because it will be assumed that the person using the script will assign it in in the editor, or via another script.

TIP:
When you assign values to a float in the inspector, you can fill in normal whole or decimal values as you would normally see them. If we were to assign a numerical value to a float variable in code, it should be followed by the letter ‘f’ such as “turnRate = 3.14f;”. Whole numbers, such as “10” are assumed to be of type “int”, and numbers with decimals are assumed to be of type “double” by the compiler. A float data type can contain any value that an int can hold, but not any value that a double can hold, and is why the ‘f’ is required.

On line 8 we see a method which is included in the default template code called “Update”. Unity calls this method once per frame to allow you to modify game objects incrementally. Fast systems might call this method 100’s of times per second, whereas slow machines or mobile phones might only call it 30 times per second. If you were to make changes to objects using fixed values, then it would appear to play “faster” on the better machine. In order to create a sample which appears to run at the same speed across all platforms we multiply our desired speed against “Time.deltaTime” so that the actual rate we use is not constant, but varies based on the amount of time which has actually passed. For example, on the mobile phone example Time.deltaTime might be 0.0333… and on the faster machine something like 0.01 instead.

The statement we put inside the method is compound (it is doing multiple things). I could have been more verbose as in the following example which is functionally equivalent:

float frameTurnRate = turnRate * Time.deltaTime;
Vector3 rotation = new Vector3(0, frameTurnRate, 0);
transform.Rotate( rotation );

This example shows that we create a new float variable which holds the turn rate for this frame. We also create a Vector3, which is a special variable called a struct. You create the Vector3 using something called a “Constructor” which takes three parameters which represent values for three axis: X, Y, and Z. Finally, we pass the Vector3 to a method which actually rotates the object itself.

Sometimes, expanding the code like this helps it to be more readable, but in this case, I felt that defining it in a single line was fine. Some reasons I would have gone with the later version include:

  • needing to use the variables in more than one place
  • the single line being excessively long so that I would have to scroll horizontally to read it all
  • the nested parenthesis might be confusing

Attach the Motor script to whatever 3D Object you ended up creating, and assign a value to the turnRate. Run the scene. Note that you can even change the turnRate while the game is running and the speed of the motor will instantly update.

Stop the scene (because changes made in Play mode don’t save). Duplicate your 3D Object and scale it down. We will treat our first object as if it is the “Earth” and the new smaller object as if it is the “Moon”. You can make each object turn on its axis at a different rate. But how can you use the same script to make the moon orbit around the earth? The answer is to use object hierarchy. Create an Empty GameObject. Parent the moon to the Empty GameObject and then offset it far enough that it is outside of the “Earth”. Now add our Motor script to the Empty GameObject and you will see the moon orbit around the earth while turning on its axis! Note that the effect will be more pronounced if the rate at which the earth spins is different than the rate at which the orbit object spins. If you parented both the earth and empty game object to another empty game object, you could have them orbit around the sun. In this way you can slowly build up the complexity of your scene until you have created a working model of the Solar System.

Summary

In this lesson, we covered how to declare, read and write values to variables. We showed how the use of properties could encapsulate a field, and control how its values are accessed. We also learned that properties don’t require a backing field and can return values dynamically instead. We completed two examples, one to implement some variables describing a human, and the second to model and animate a 3D solar system.

2 thoughts on “Variables

  1. Thanks a lot for this tutorial. I have a small question. You wrote:
    —————-
    In order to create a sample which appears to run at the same speed across all platforms we multiply our desired speed against “Time.deltaTime” so that the actual rate we use is not constant, but varies based on the amount of time which has actually passed. For example, on the mobile phone example Time.deltaTime might be 0.0333… and on the faster machine something like 0.01 instead.
    —————-

    But I though that the whole point of Time.deltaTime was that something ran evenly across all devices? Didn’t the variation came from the difference in framerates?

    1. Yes the point of Time.deltaTime is so that your game runs evenly across all devices, but the way it works is by returning different values to accommodate the differences in framerates. The value it holds is the amount of actual time there was between frames.

      It might be a little confusing still, so I would recommend running some tests where you log your frame rates and the delta time to the console. See how the numbers change when the frame rate goes up or down and do some math to see that the multiplier would allow the same kinds of distances to be covered over the different number of frames per second.

Leave a Reply to Zen Cancel reply

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