A basic use of pointers is to aim at someone else’s stuff and play with it. Unity has a bunch of Cubes. We should be able to aim pointers at them.
A main reason for using a fancy program like Unity is how easily it allows us to do that. There will be an empty slot, and we’ll drag in the Cube we want. Inside our program, we’ll have a properly aimed pointer. In Unity, that slot will be in the Inspector.
Once we can get pointers to several Cubes, we can use our old tricks to move them around.
The things in a Unity scene are GameObject’s, configured to be Lights, Cameras, and Cubes. GameObject is a regular class, which means we can declare GameObject g1;. It will be a pointer.
The class has a field named transform. It’s the transform we’ve been using for
ourself all along. If g1 is a GameObject, we can move it with the familiar-looking
g1.transform.position = new Vector3(-7,4,0);.
Put together, this script, on any object, will let us reach out and move some other Cube:
As usual, g appears in the Inspector. It’s currently null but it says GameObject(none) to be a little nicer. We’re suppose to find a Cube, in the panel with names, and drag it in. When our program runs, g will point to that Cube.
That’s almost too easy. It’s the reason programs like Unity exist. They let us
cheat by setting up pointers without needing to write code.
Before using them more, let’s check that GameObject is mostly a normal class. We’re allowed to use new with it:
When we run, an empty object named fire engine appears in the panel. That’s a
slight cheat – Unity tracks GameObjects. It won’t track our own classes, Dogs, for
example.
Back to our regular examples, this one uses two pointers. You’d drag a different thing onto each:
Nothing special, but it’s new. We weren’t able to change 2 different things before,
only ourself.
We can test this a little by dragging the same Cube into both slots. It’s legal for 2 pointers to go to the same place. It will turn blue. g1 turns it green, then g2 immediately turns it blue.
We could leave g1 null. Click the little circle on the far right of g1 and select None
from the dropdown. We’ll get a standard null reference exception. g2’s Cube won’t
change – the error cut off that line.
A fun example which moves two Cubes at different speeds:
This is nothing new, except it’s neat to have one script run a Cube race.
We can use the trick where a 3rd pointer chooses one of them, like the activePet example from the last chapter:
An alternate way of getting a pointer is the Find command. It searches through all gameObjects in the panel, by name, and returns a pointer. This mini-program finds "cube1" and "cube2" and turns them different colors:
Notice how Find uses the namespace trick. It’s name is Find and it’s in the
GameObject namespace.
The moral here is that once you have a pointer, it doesn’t matter how you got it.
We can use c1 and c2 in the usual way.
Finally, let’s use null some more. This next code hopes c1 was preset. If not, it guesses some names. It uses the standard null-check:
As you might guess, Find returns null is there isn’t anything with that name. null is really the perfect not-found value.
Unity has a type of clone function named Instantiate. It takes a pointer to one gameObject, makes an identical new one, and returns a pointer.
Here’s some simple code making 10 random copies of a Cube. Warning, warning, warning: do not use this to copy yourself. The copy will run its copy of the script, copying itself, and so on forever. c1 should point to something without this script on it:
In a normal program we’d need to save those 10 pointers somewhere, or else we’re
simply making time-wasting garbage. Here we’re taking advantage of how Unity
tracks all gameObjects.
This next one is mostly the same, except it makes them 1-at-a-time, when we press space. Then, to use one extra pointer, the previous one also turns red:
Notice how ball1 never changes. Its job is to remember the “master ball” for the
Instantiate copy command. Meanwhile previousBall is a typical moving pointer,
temporarily remembering one ball.
Instantiate is also overloaded. We’re allowed to give it a position where we want the new item:
That’s a typical time-saving overload. All it does extra is run the position= line which we ran before. But 1 line saved for something we do often is a useful shortcut.
This entire section has nothing to do with programming. But if you use Instantiate
with Unity for real it’s probably good to know.
Suppose we want lots of explosions using Instantiate. We need a master explosion, but it should be hidden – off the edge of the world or something – and also frozen somehow. That’s a pain, so Unity provides a nice way.
Take any Cube and drag it down into the Project window. Unity copies it there, with a corner-cube icon. It counts as a Cube, but it’s not visible anywhere and its scripts won’t run. It’s the perfect master object for copying. Unity calls that a prefab.
Any of the scripts above work with these. Find a prefab (cube icon which you
made in the Project panel) and drag it into the gameObject slot. Your script is now
making new objects out of thin air.
The whole process looks like this: suppose you know how to make explosions.
Make one (using a particleSystem), then drag it into Project to make a prefab from
it. Delete the original. Anyone with public GameObject blast; can now get a link
to the master explosion, for copying.
As long as we’re here, we may as well see the other use of Unity prefabs. Once you have a prefab, you can quickly make real copies of it by dragging into the scene. More than that, the copies are linked. Changing the prefab (the original, in the Project panel) changes all copies.
But don’t let that confuse you about how programs work. In a program, copies are copies – there’s no link to the original.
You may have noticed GetComponent<Renderer>().material.color is a 3-stage lookup. It calls a function to get our renderer, then the material, then finally the color. Material is a class, which means we can get a pointer to one. We can create a shortcut pointer to our own material:
The old stuff is still there. Start has GetComponent<Renderer>().material, which it saves, and Update has dot-color. Material myMat is a pointer, since Material is a class.
Not super useful, but it’s pretty neat that we can do it. We can’t get a
shortcut to color, since Color is a struct. You can never have a pointer to a
struct.
We can do the same trick with not-us. Suppose we have a link to the floor, and often want to change the floor’s color. We can save the floor’s material as a shortcut:
I think floor.GetComponent<Renderer>() is pretty neat.
You may have noticed that our classes in the Inspector don’t need new’s. Unity does it automatically. But we’re allowed to use new if we want:
Pressing the A key does the usual thing: it creates a fresh Dog, abandoning the old one. The Inspector looks like it’s only erasing the age, but it’s really tracking that fresh Dog.
Put another way, do anything you want with classes visible in the Inspector. It won’t restrict anything – it merely tracks and displays the current one.