C# is an object oriented language, and since these Head First C# Unity Labs are all about getting practice writing C# code, it makes sense that these labs will focus on creating objects.
In Chapter 3 we learned about creating objects in C#, using a class as a template to create instances of it, where each instance gets its own fields. In this Unity Lab you’ll create instances of a Unity GameObject and use them in a complete, working game.
The goal of the next two Unity Labs is to create a simple game using the familiar billiard ball from the last Lab. In this Lab, you’ll build on what you learned about C# objects and instances to start building the game. You’ll use a prefab—Unity’s tool for creating instances of GameObjects—to create lots of instances of a GameObject. And you’ll use scripts to make your GameObjects fly around your game’s 3D space.
Unity is all about building games. So in the next two Unity Labs, you’ll use what you’ve learned about C# to build a simple game. Here’s the game that you’re going to build:
So let’s get started. The first thing you’ll do is get your Unity project set up. But this time we’ll keep the files a little more organized, so you’ll create separate folders for your materials and scripts—and one more folder for prefabs (which you’ll learn about later in the Lab).
Before you begin, close any Unity project that you have open. Also close Visual Studio—you’ll let Unity will open it for you.
Create a new Unity project using the 3D template, just like you did for the previous Unity Labs. Give it a name to help you remember which Lab it goes with (“Unity Labs 3 and 4”).
Choose the “Wide” layout so your screen matches the screenshots.
Create a folder for your materials underneath the Assets folder. Right-click on the Assets folder in the Project window and choose Create >> Folder. Name it Materials.
Create another folder under Assets named Scripts.
Create one more folder under Assets named Prefabs.
Double-click on your new Materials folder to open it. You’ll create a new material here.
Go to https://github.com/head-first-csharp/fourth-edition/tree/master/Unity%20Labs/Billiard_Balls and download the texture file 1 Ball Texture.png
into a folder on your computer, then drag it into your Materials folder—just like you did with the downloaded file in the first Unity Lab, except this time drag it into the Materials folder you just created instead of the parent Assets folder.
Now you can create the new material. Right-click on the Materials folder in the Project window and choose Create >> Material. Name your new material 1 Ball. You should see it appear in the Materials folder in the Project window.
In the first Unity Lab you had Unity create a material automatically for you by dragging a texture map file onto a GameObject. This time you’re creating the material manually. Just like last time, you may need to click the Download button on the GitHub page to download the PNG image file.
Make sure the 1 Ball material is selected in the Materials window, so it shows up in the Inspector. Click on the 1 Ball Texture
file and drag it into the box to the left of the Albedo label.
You should now see a tiny little picture of the 1 ball texture in the box to the left of Albedo in the Inspector.
Now your material looks like a billiard ball when wrapped around a sphere.
Create a new Sphere GameObject with a script called OneBallBehaviour:
Choose 3D >> Sphere from the GameObject menu to create a sphere.
Drag your new 1 Ball material onto it to make it look like a billiard ball.
Next, create a new folder under Assets in the Project window. Name your new folder Scripts
. Then right-click on the new folder and create a new C# script named OneBallBehaviour.
Drag the script onto the Sphere in the Hierarchy window. Select the Sphere and make sure a Script component called “One Ball Behaviour” shows up in the Inspector window.
Double-click on your new script to edit it in Visual Studio. Add exactly the same code that you used in BallBehavior in the first Unity Lab, then comment out the Debug.DrawRay line in the Update method.
Your OneBallBehaviour script should now look like this:
Now modify the Start method to move the sphere to a random position when it’s created. You’ll do it by setting transform.position
, which changes the position of the GameObject in the scene. Here’s the code to position your ball at a random point—add it to the Start method of your OneBallBahaviour script:
Use the Play button in Unity to run your game. A ball should now be circling the Y axis at a random point. Stop and start the game a few times. The ball should spawn at a different point in the scene each time.
You’ve used the Random class in the .NET System namespace a few times already. You used it to scatter the animals in the animal match game in Chapter 1 and to pick random cards in Chapter 3. But this Random class is different—try hovering over the Random keyword in Visual Studio.
You can see from the code that this new Random class is different than the one you used before. Earlier called Random.Next()
to get a random value, and that value was a whole number. This new code uses Random.value
, but that’s not a method—it doesn’t end with parentheses. (It’s actually a property, which you’ll learn about in Chapter 5.)
Let’s use the Visual Studio debugger to see the kinds of values that this new Random class gives you. Use the Attach to Unity button () to attach Visual Studio to Unity. Then add a breakpoint to the line you added to the Start method.
Now go back to Unity and start your game. It should break as soon as you press the play button in Unity. Hover your cursor over Random.value—make sure it’s over value
. Visual Studio will show you its value in a tooltip:
Move your mouse cursor off of value
, then go back and hove over it again. Do it a few more times. You’ll get a different random value
each time. That’s how UnityEngine.Random works: it gives you a new random value between 0 and 1 each time you access its value
property.
Press Continue () to resume your game. It should keep running—the breakpoint was only in the Start method, which is just called once for each GameObject instance, so it won’t break again. The go back to Unity and stop the game.
You can’t edit scripts in Visual Studio while it’s attached to Unity, so click the square Stop Debugging button to detach the Visual Studio debugger from Unity.
In Unity, a prefab is a GameObject that you can instantiate in your scene. In Chapter 2 you learned about instances, and how you can create objects by instantiating a class. Unity makes it really easy to take advantage of objects and instances, so you can build games that reuse the same GameObjects over and over again. Let’s turn your one ball GameObject into a prefab.
Go to the Hierarchy window, select your Sphere, and rename it by pressing F2 (or right-click and choose Rename). Change the name to OneBall.
If you try to edit your code but find that Visual Studio wouldn’t let you make any changes, that means Visual Studio is probably still attached to Unity! Press the square Stop Debugging button to detach it.
You might have noticed that the name changed in the inspector window.
Now we can turn your GameObject into a prefab. Use the Project window to create a new folder under Assets called Prefabs, then drag OneBall from the Hierarchy window into the Prefabs folder.
OneBall should now appear in your Prefabs folder. And notice that OneBall is now blue in the Hierarchy window. This indicates that it’s now a prefab – Unity turned it blue to tell you that an instance of a prefab is in your hierarchy. That’s fine for some games, but for this game, we want all of the instances of the balls to be created by scripts.
Right-click on OneBall in the Hierarchy window and delete the OneBall prefab. You should now only see it in the Project window, and not in the Hierarchy window or the scene.
Have you been saving your scene as you go? Save early, save often!
The game needs a way to add balls to the scene (and eventually keep track of the score, and whether or not the game is over). We’ll do this by adding a script called GameController.cs to the main camera.
Right-click on the Scripts folder in the Project window and create a new script called GameController. Your new script will use two methods available in any GameObject script:
The Instantiate method creates a new instance of a GameObject. When you’re instantiating GameObjects in Unity, you don’t typically use the new keyword like you saw in Chapter 2. Instead, you’ll use the Instantiate method, which you’ll call from the AddABall method.
The InvokeRepeating method calls another method in the script over and over again. In this case, it will wait one and a half seconds, then call the AddABall method once a second for the rest of the game.
Here’s the source code for it:
Your new GameController script needs to be attached to a GameObject to run. Luckily, the Main Camera is just another GameObject—it happens to be one with a Camera component and an AudioListener component—so let’s attach your new script to it. Drag your GameController script out of the Scripts folder in the Project window and onto the Main Camera in the Hierarchy window.
You’ll see a component for it, exactly like you would for any other GameObject.
The OneBallPrefab field still says None, so we need to set it. Drag OneBall out of the Prefabs folder and onto the box next to the One Ball Prefab label.
Now the GameController’s OneBallPrefab field contains a reference to the OneBall prefab:
Now back to the code and look closely the AddABall method. It passes the OneBallPrefab field to Instantiate. You just set that field so that it contains your prefab. So every time GameController calls its AddABall method, it will create a new instance of the OneBall prefab.
Your game is all ready to run. The GameController script attached to the Main Camera will wait 1.5 seconds, then instantiate a OneBall prefab every second. Each instantiated OneBall’s start method will move it to a random position in the scene, and its Update method will rotate it around the Y axis every 2 seconds using OneBallBehaviour fields (just like in the last Lab). Watch as the play area slowly fills up with rotating balls:
Unity calls every GameObject’s Update method before each frame. That’s called the update loop.
When you instantiate GameObjects in your code, they show up in the Hierarchy window when you run your game.
Each of the balls flying around the scene is an instance of the OneBall prefab. Each of the instances has its own instance of the GameController class. You can use the Hierarchy window to track all of the OneBall instances—as each one is created, a “OneBall(Clone)” entry is added to the Hierarchy.
Click on any of the OneBall(Clone) items to view it in the Inspector. You’ll see its Transform values change as it rotates, just like in the last Lab.
We’ve included some coding exercises in the Unity Labs. They’re just like the exercises in the rest of the book – and remember, it’s not cheating to peek at the solution.
Run your game. Once a few balls have been instantiated, click the pause button—the Unity editor will jump back to the Scene view. Click one of the OneBall instances in the Hierarchy window to select it. The Unity editor will outline it in the Scene window to show you which object you selected. Go to the Transform component in the Inspector window and set its Z scale value to 3 to make the ball stretch.
Start your simulation again—now you can track which ball you’re modifying. Try changing its DegressPerSecond, XRotation, YRotation, and ZRotation fields like you did in the last lab.
While the game is running, switch between the Game and Scene views. You can use the Gizmos in the scene view while the game is running, even for GameObject instances that were created using the Instantiate method (rather than added to the Hierarchy window).
Try clicking the Gizmos button at the top of the toolbar to toggle them on and off. You can turn on the Gizmos in the Game view, and you can turn it off in the Scene view.
Did you notice that occasionally some of the balls overlap each other?
Unity has a powerful physics engine that you can use to make your GameObjects behave like they’re real, solid bodies—and one thing that solid shapes don’t do is overlap each other. So to prevent that overlap, you just need to tell Unity that your OneBall prefab is a solid object.
Stop your game, then click on the OneBall prefab in the Project window to select it. Then go to the Inspector and scroll all the way down to the bottom to the Add Component button.
Click the button to pop up the Component window. Choose Physics to view the physics components, then select Rigidbody to add the component.
Run your game again—now you won’t see balls overlap. Occasionally one ball will get created on top of another one. When that happens, the new ball will knock the old one out of the way.
Let’s run a little physics experiment to prove that the balls really are rigid now. Start your game, then pause it as soon as there are two balls created. Go to the hierarchy window. If it looks like this:
Then you’re editing the prefab—click the button in the top right corner of the Hierarchy window to get back to the scene (you may need to expand SampleScene again).
Select the first OneBall instance and use its Transform component in the Inspector to set its position to (0, 0, 0).
Select the second OneBall instance and set its position to (0, 0, 0) as well.
Select all of the other instances, right-click, and choose Delete to delete them from the scene so only the two overlapping balls are left.
Unpause your game—the balls can’t overlap now, so instead they’ll be rotating next to each other.
You can use the Hierarchy window to delete GameObjects from your scene while the game is running.
Stop the game in Unity and Visual Studio and save your scene. Save early, save often!
You’re halfway done with the game! You’ll finish it in the next Unity Lab. In the meantime, this is a great opportunity to practice your paper prototyping skills. We gave you a description of the game at the beginning of this Unity Lab. Try creating a paper prototype of the game. Can you come up with ways to make it more interesting?