This chapter is sort of a break. Just some rules about different ways and places to declare variables. We’ll use them a little, and every programmer needs to know them, eventually. But they won’t hurt your head as much as those if examples.
So far, we’ve been declaring variables in two places: inside of Start and Update,
or outside of them (the Inspector variable trick.) Both ways are legal, but
they have different rules for how long the variables live and who can see
them.
Variables declared outside Start or Update are called global. Global variables live for the entire program, and everyone can see and use them.
Variable declared inside Start or inside Update are local. Local variables can only
be used in the part where they were declared. When that part finishes, local variables
are destroyed. A local variable in Update is created and destroyed each time Update
runs.
The technical term for where a variable exists is scope. The scope of global
variables is the entire program. The scope of local variables is inside the {}’s where
they were declared. Being destroyed by leaving the {}’s is called going out of
scope.
Here’s a small example of local variables not existing outside where they were made. n is declared in Start, so doesn’t exist in Update:
Local variables also don’t “use up” the name outside their scope. This is a good thing. It means we can declare one n in Start, and a different n in Update:
This is why local variables were invented. Imagine you have a very large program,
with Start, Update and piles of other things like it. When you declare local variables
in one section, you never have to worry about whether someone also declared them in
some other section.
You could declare everything as global. But programs are usually easier to read if you use global only for things you need to keep around between Updates.
Here’s an example using move-and-wrap-around. It snaps y lower as x crosses thresholds. x has to be global, since it’s remembering where I am. But y is better as a local, since I recompute it each time:
y is really just a temporary, used to help place the Cube at the correct height. Declaring it as a local, right before we use it, makes it easier to see that.
You’re allowed to declare globals anywhere in the big curly-braces. It doesn’t have to be at the top. They all get declared first – the compiler scans for global as an early step. The rule about needing to declare a variable before you use it is real, but it’s only for local variables.
Just to show it can be done, this silly program declares n1, n2 and n3 in funny but legal places:
Most people declare them at the top. But I sometimes declare a global just before
the part that uses it – like right before Update.
Friendly languages auto-initialize all variables: numbers to 0 and strings to "". Languages that want to run faster never auto-initialize. C# splits the difference – it auto-initializes global variables, but not locals. This can be confusing:
There’s a special rule allowing declare-and-init for global vars. It’s a special rule since global variables aren’t run like regular lines – they’re magically declared all at once. This means you can’t give them values based on other globals:
A traditional program set every global’s value as the first thing it does. In Unity, that would be in Start.
You’re allowed to declare variables inside of an if/else {}. They’re local to that little area and go away after it’s done. That’s called block scope since the thing the {}’s make is officially called a block.
The idea is the same as global vs. local – keep variables from cluttering things up by getting rid of them when you’re done. Here if I have more cats than dogs I’ll temporarily compute extraCats. It’s gone when the {} block ends, which is what we want:
It works in either place. extraDogs only lives inside of the else brackets.
A short, real use is for a variable swap. We need an extra variable to make the switch, but not afterwards:
It looks nice to declare tmp exactly when we need it, and it won’t interfere with the rest of our program, since it’s deleted right away.
Sometimes when you want to change a global, you accidentally redeclare it, like this:
This isn’t a red-dot error – it’s a much worse non-error error. The warning is local cats shadows global cats.
We now have a local cats which hides the global one we meant to use. That code sets local cats to 15. But when Start ends local cats is thrown away. Global cats stayed 0 the whole time.
Whenever you use a variable, just think “Am I changing an existing global, or
making a new local variable?”
A similar error can happen with block scope. This accidentally makes food only exist inside the if:
We have to declare it outside the if:
Most systems have lots and lots of built-in globals. A standard computer trick is to
group them into things like folders, called namespaces.
For example, Unity has global variables for the screen’s height and width, which way a mobile device is being held, and so on. These are all in a namespace which they named Screen.
The rule for looking inside a namespace is to use a dot. If you type Screen-dot
(screen and then a period) the pop-up will show the options. Screen.width is the
width, in pixels, of the window. It’s really the width variable in the Screen
namespace.
For fun, you can test by having Update copy Screen.width into an Inspector global. While running this, you can resize your window and watch ht change:
You might notice that the pop-up for Screen says public sealed class, and
not namespace. namespace is just the general term. Some languages use the actual
word namespace in the language. Others use the term package. C# uses a few
different ways. But no matter what, they all work like folder.variable.
Here are more namespaces and stuff in them, just for fun examples. You won’t need to know them:
Time.time is one of my favorite examples of how namespaces work. The Time namespace holds a lot of things about time and timing things, so Time is a good name for it. The most commonly used thing in it is how many seconds the game has been running. That variable should have an easy, short name, so we picked time.
Together, it’s the time variable in the Time namespace – Time.time.
You can use the same variable in different namespaces. That’s really a version of the
scope rules. That’s why System.Math.PI and Mathf.PI are both legal. You could
have Screen.height and Raccoon.height and also name one of your variables
height.
In the editor, typing the dot triggers the pop-up. Suppose you type Screen.wi,
click away, come back, and the pop-up is gone. To get it back, delete down to just
Screen and retype the dot.
Later on, C# re-uses the dot in a different way. When we come to it, I’ll write
this again and explain it. I want to sort of pre-warn you about it, since it’s one of
those things that can be really confusing.
The using’s at the top of the program are about namespaces. You don’t need to know this – only keep reading if those two lines at the top of every program are bugging you. UnityEngine is the master namespace for all the Unity built-ins. Screen is really inside of UnityEngine. The real width of the screen is UnityEngine.Screen.width.
using UnityEngine; at the top lets you skip it everywhere else. it says: if you can’t find something they typed, try looking for it in UnityEngine. But, again, not vital to know just now.