This section is just two funny rules about classes jammed together. The enum/class section isn’t really new. It’s more funny stuff that using private can do. And remember, you never need to use private. But static is a new rule, used to make real globals.
In a class, public and private also apply to enum’s or any other classes you write. So far it didn’t matter, since we only used them from inside of that class. But outsiders can only use the ones you make public.
An example, this makes one public and private of each:
Inside of Player, we can use all 4 of these (obviously, since we’ve been doing it for a while). Outside, we can’t see hatT or Lion, since they’re private.
We’d make them private for roughly the usual reason: they’re “helper” classes
and enums. No one outside of the class should be using them, or should even see
them in the pop-up.
To use the public ones outside of player, we’re required to add player-dot in front. Here’s an example of a new class Kitten using them:
The reason should make sense. We could have one Dog in a file by itself, and another inside of Player, and another inside of testA. Three different Dog classes. Their names would be Dog, and Player.Dog, and testA.Dog. It’s the usual namespace situation.
And we like it that way. If testA needs its own personal Dog class, it should be
able to have one.
Obviously, you can skip the first part from inside the class. Inside of Player we
can use Dog as a shortcut for the real name, Player.Dog.
You can’t have a public variable of a private type. It’s easy to do by accident:
You get a really cool error: Inconsistent accessibility: field type ‘Player.Lion’ is less accessible than field ‘Player.L1’.
The problem is obvious, but takes some thought. Anyone outside the class trying to use L1 will need to look up what Lion’s are, which is private, so they won’t find it. They can’t possible use L1 if they aren’t allowed to read its type.
When you get this error, it almost always means that you wanted to make the
class public, and simply forgot to.
You might remember the special way to put a class in the Inspector from way back:
Now we know why we needed the extra public in front of the class. We can’t
make d1 public without having the class itself be public.
And finally, as usual, if you ever have any problems caused by private classes or private enums, make everything public. You never actually need to make something private.
static is a special word that allows us to make true global variables and functions. Write something inside of a class and add static in front. Now it’s a true global, using that class as a namespace.
The trick is most common with functions, First here’s an example making a global function, inside of Dog:
Because of the static, isEqual is a normal function. You can’t call it with a Dog in front, and wouldn’t want to. But anyone, anywhere could call it like if( Dog.isEqual(pet1, pet2) ).
The Dog in front is just a namespace, telling us how to find isEqual.
This is a pretty typical example. We wanted to write a regular function to
compare two Dogs. Then we thought it would be nice to group it with the dogs, so
we put it inside with static. An advantage is we can now write Dog-dot and find
it.
I think the biggest confusion about static is that it goes in front like
public/private, but it’s completely different. Public/private don’t change how
something works, just who can use it. But static is a change in how it
works.
Our old function Vector3.Distance(v1, v2) uses this trick. It’s a normal function telling you how far apart the two input points are. It’s put inside the Vector3 namespace to make it easier to find:
The static is telling us we don’t use v1.Distance(v2, v3). It’s not a member
function. Notice how the inside is all A.x and B.y. There’s no direct use of “our”
member variables, since we’re a normal function.
Here’s an example writing desc both ways:
Notice the first has no dog input, since a dog calls it: d1.desc1();. The second has an input since it’s a normal function: Dog.desc2(d1);.
We’ve seen the idea of a namespace as a folder for global functions. C# fakes this using the static rule and a do-nothing class. Here, Rnd is a folder for some dice-rolling functions:
We now have global functions pct and roll_xDy, who’s full names are Rnd.pct and Rnd.roll_xDy. Technically Rnd is a class, but not really. We’re abusing the rules to use it as a folder.
It’s an idiom. For people who haven’t seen the trick, a class with no member
variables is horribly confusing. But long-time users don’t even see the word class
anymore. This is the C# way of making a namespace.
Unity has several of these. Mathf holds functions like Mathf.Sqrt(8) (square root) and trig functions and so on. Mathf is clearly a folder – a namespace. But the actual way it’s written is a struct with no fields and all static functions.
static in front of a variable stops it from being a field, and turns it into a regular global. This makes two globals:
Anyone, anywhere can use gameStats.livesLeft--; or if(gameStats.currentLevel==5). As before, gameStats isn’t really a class.
To compare, if they didn’t have static, we’d start with no variables. And
declaring gs1, gs2, and gs3, would create 3 sets. Adding static means we
start with them, and can never make any more. They’re regular declared
variables.
Unity has global variables using this trick. The global for the time in seconds
since it started, is Time.time. It turns out time is a static float, and Time is a class
abused to be a namespace.
There’s rarely a reason to mix static’s into a real class, but you can do it. Suppose I want a variable holding the total number of Dog’s ever made. I may as well keep it in the Dog class, as a static:
This gives us one global, named Dog.count. Each Dog we declare has name and
age, as usual.
It’s common enough to put global functions and variables in the same fake class. For example:
gameStats.reset() is a global function, changing global variables.
I’ve been defining classes, and everything else, inside of scripts since that was the only place we had.
If multiple scripts in a program wanted to use a class, it’s fine to put it in a file by itself. We’d create this file the normal way, and anyone could declare things from it:
Notice how pretty this is. It’s our normal definitions, without any of the extra stuff from a script.
Also notice how these are “naked”. They’re not wrapped in a namespace or anything. Dog’s entire name, for anyone, is just Dog. Any script could declare Dog d; or use catBreed cb1=catBreed.tabby;.
We could throw a namespace around them. class Pet { ... } around the whole thing. Then we’d have Pet.Dog d1 and so on. The advantage is the usual scope help: with these hidden inside of Pet, another part of the program can re-use the names.