This is the first tutorial in a series about managing objects. It covers creating, tracking, saving, and loading simple prefab instances. It builds on the foundation laid by the tutorials in the Basics section.

This tutorial is made with Unity 2017.3.1p4.

Creating Objects On Demand

You can create scenes in the Unity editor and populate them with object instances. This allows you to design fixed levels for your game. The objects can have behavior attached to them, which can alter the state of the scene while in play mode. Often, new object instances are created during play. Bullets are fired, enemies spawn, random loot appears, and so on. It might even be possible for players to create custom levels inside the game.

Creating new stuff during play is one thing. Remembering it all, so the player can quit and later return to the game is something else. Unity doesn't automatically keep track of the potential changes for us. We have to do that ourselves.

In this tutorial we'll create a very simple game. All it does is spawn random cubes in response to pressing a key. Once we're able to keep track of the cubes between play sessions, we can increase the game's complexity in a later tutorial.

Game Logic Because our game is so simple, we'll control it with a single Game component script. It will spawn cubes, for which we'll use a prefab. So it should contain a public field to hook up a prefab instance. using UnityEngine; public class Game : MonoBehaviour { public Transform prefab; } Add a game object to the scene and attach this component to it. Then also create a default cube, turn it into a prefab, and give the game object a reference to it. Game setup.

Player Input We're going to spawn cubes in response to player input, so our game must be able to detect this. We'll use Unity's input system to detect key presses. Which key should be used to spawn a cube? The C key seems appropriate, but we can make this configurable via the inspector, by adding a public KeyCode enumeration field to Game . Use C as the default option when defining the field, via an assignment. public KeyCode createKey = KeyCode.C; Create key set to C. We can detect whether the key is pressed by querying the static Input class in an Update method. The Input.GetKeyDown method returns a boolean that tells us whether a specific key was pressed in the current frame. If so, we have to instantiate our prefab. void Update () { if (Input.GetKeyDown(createKey)) { Instantiate(prefab); } } When exactly does Input.GetKeyDown return true ? It does so only during the frame that the key's state has changed from not-pressed to pressed, because the player pressed on it. Typically, the key remains in the pressed state for a few frames until the player lets go of the button, but Input.GetKeyDown returns true only during the first frame. In contrast, Input.GetKey keeps returning true each frame that the key is held down. There is also Input.GetKeyUp , which returns true during the frame that the player let go of the key.

Randomized Cubes While in play mode, our game now spawns a cube each time we press the C key, or whichever key you configured it to respond to. But it looks like we only get a single cube, because they all end up at the same position. So let's randomize the position of each cube that we create. Keep track of the instantiated Transform component, so we can change its local position. Use the static Random.insideUnitSphere property to get a random point, scale it up to a radius of five units, and use that as the final position. Because that's more work than just a trivial instantiation, put the code for that in a separate CreateObject method and invoke it when the key is pressed. void Update () { if (Input.GetKeyDown(createKey)) { // Instantiate(prefab); CreateObject(); } } void CreateObject () { Transform t = Instantiate(prefab); t.localPosition = Random.insideUnitSphere * 5f; } Randomly placed cubes. The cubes now spawn inside a sphere instead of all at the exact same position. They can still overlap, but that's fine. However, they're all aligned and that doesn't look interesting. So let's give each cube a random rotation, for which we can use the static Random.rotation property. void CreateObject () { Transform t = Instantiate(prefab); t.localPosition = Random.insideUnitSphere * 5f; t.localRotation = Random.rotation; } Randomized rotations. Finally, we can also vary the size of the cubes. We'll use uniformly-scaled cubes, so they're always perfect cubes, just with different sizes. The static Random.Range method can be used to get a random float inside a certain range. Let's go from small size 0.1 cubes up to regular size 1 cubes. To use this value for all three dimensions of the scale, simple multiply Vector3.one with it, then assign the result to the local scale. void CreateObject () { Transform t = Instantiate(prefab); t.localPosition = Random.insideUnitSphere * 5f; t.localRotation = Random.rotation; t.localScale = Vector3.one * Random.Range(0.1f, 1f); } Randomized uniform scale.

Starting a New Game If we want to begin a new game, we have to exit play mode and then enter it again. But that's only possible in the Unity Editor. The player would need to quit our app and start it again to be able to play a new game. It's much better if we could begin a new game while remaining in play mode. We could start a new game by reloading the scene, but this isn't necessary. We can suffice with destroying all the cubes that were spawned. Let's use another configurable key for that, using N as the default. public KeyCode createKey = KeyCode.C; public KeyCode newGameKey = KeyCode.N; New-game key set to N. Check whether this key is pressed in Update , and if so invoke a new BeginNewGame method. We should only handle one key at a time, so only check for the N key if the C key isn't pressed. void Update () { if (Input.GetKeyDown(createKey)) { CreateObject(); } else if (Input.GetKey(newGameKey)) { BeginNewGame(); } } void BeginNewGame () {}

Keeping Track of Objects Our game can spawn an arbitrary number of randomized cubes, which all get added to the scene. But Game has no memory of what it spawned. In order to destroy the cubes, we first need to find them. To make this possible, we'll have Game keep track of a list of references to the objects it instantiated. Why not just use GameObject.Find ? This is possible for simple cases, where it's easy to distinguish between objects and there aren't many in the scene. For larger scenes, relying on GameObject.Find is a bad idea. GameObject.FindWithTag is better, but it's best to keep track of things yourself if you know you'll need them later. We could add an array field to Game and fill it with references, but we don't know ahead of time how many cubes will be created. Fortunately, the System.Collections.Generic namespace contains a List class that we can use. It works like an array, except that its size isn't fixed. How can the list's size be dynamic? Internally, List uses an array to store its contents, which it initializes at some size. Items added to the list get put in this array, until it is full. If more items are added, the list will copy the contents of the full array to a new larger array and uses that one from now on. We could do this array management manually, but List takes care of it for us. Also, Unity supports List fields just like it supports array fields. They're editable via the inspector, their contents are saved by the editor, and they survive recompilation while in play mode. using System.Collections.Generic; using UnityEngine; public class Game : MonoBehaviour { … List objects; … } But we don't want a generic list. We specifically want a list of Transform references. In fact, List insists that we specify the type of its contents. List is a generic type, which means that it acts like a template for specific list classes, each for a concrete content type. The syntax is List<T> , where the template type T is appended to the generic type, between angle brackets. In our case the correct type is List<Transform> . List <Transform> objects; Like an array, we have to ensure that we have a list object instance before we use it. We'll do that by creating the new instance in the Awake method. In the case of an array, we'd have to use new Transform[] . But because we're using a list, we have to use new List<Transform>() instead. This invokes the special constructor method of the list class, which can have parameters, which is why we have to append round brackets after the type name. void Awake () { objects = new List<Transform>(); } Next, add a Transform reference to our list each time we instantiate a new one, via the Add method of List . void CreateObject () { Transform t = Instantiate(prefab); t.localPosition = Random.insideUnitSphere * 5f; t.localRotation = Random.rotation; t.localScale = Vector3.one * Random.Range(0.1f, 1f); objects.Add(t); } Do we have to wait until the end of CreateObject before adding the reference? We could have added the reference to the list as soon as we got hold of it, so directly after the assignment of the result of Instantiate to the local variable. I just put it at the end to point out that we should only add fully-initialized things to the list.