4. Code Comfort – Unity 3.x Game Development by Example

Chapter 4. Code Comfort

We've reached the precipice: we're staring at a game that doesn't do much and WON'T do much more unless we write some code. If you're brand new to this stuff, code is scary or, rather, the idea of code is scary. Will you have to type a bunch of ones and zeroes, or cryptic punctuation along thousands of monotonous line numbers? If you have ever tried developing a game or if you have ever been taught to program using some ancient language in a high school computer course, code might have been the thing to undo you. But, here you are giving it one more crack at the bat. The world NEEDS a three-dimensional keep-up game, by cracky! It's time to show the world what you're made of.

What is code?

Code is just a series of instructions that you give to Unity to make it do stuff. We write lines (sentences) of code describing what we want to accomplish; these lines are called statements. Unity reads these statements and carries out our instructions. In Unity, you usually take a set of instructions and "stick" it to a GameObject. From now on, we'll use Unity's terminology Script to describe a collection of code statements.

Time for action – Writing your first Unity script

Let's open our keep-up game project from the previous chapter, if it's not open already. We will write a really simple script and stick it to our Ball Game Object.

In the Project panel, right-click on an empty chunk of space and choose Create | JavaScript. Alternatively, you can click on Assets | Create | JavaScript in the menu at the top of the screen, or use the Create button at the top of the Project panel. A new script is added to the Project panel, inviting you to type a name for it. Name the script as "DisappearMe". It's also a good idea to use the same Create menu to make a folder and name it "Scripts", then drag your new DisappearMe script into it to keep your Project panel organized.

A brand new window opens up. This is Unity's default script editor, called Monodevelop. Its main panel looks a lot like a basic text editor because that's all that scripts really are plain old boring text.

Note

Rolling your own

If you have your own favorite script editor, you can configure Unity to launch it instead. But, for the remainder of the book, we'll use the default editor.

A leap of faith

The first piece of code (the first lines of our script) looks like this:

function Update () {
}

Click to place your cursor after the first curly bracket, and press the Enter key to make some space between the top and bottom curly braces. Press the Tab key to indent, to make your code look pretty, if the editor doesn't automatically do it for you. Then, type a single line of code so that your script looks like this:

function Update () {
   renderer.enabled = false;
}

Note

Downloading the example code

You can download the example code files for all Packt books you have purchased from your account at http://www.PacktPub.com. If you purchased this book elsewhere, you can visit http://www.PacktPub.com/support and register to have the files e-mailed directly to you.

You will notice that as you type each word in this line of code, entire drop-down lists pop open with a dizzying list of strange-looking options. This is called Code Hinting, and while it may seem annoying when you first encounter it, you'll be erecting a shrine to it by the time you have finished this book. Code hinting brings a programming language's entire dictionary to your fingertips, saving you the hassle of looking things up or misspelling special keywords.

A little asterisk ("star") appears next to your script's name whenever you have unsaved changes. Save your script by pressing Ctrl + S or Command + S on the keyboard, or by choosing File | Save from the menu. For the remainder of this book, we'll assume that you've saved any modifications that you've made to your scripts before trying them out.

Lick it and stick it

Return to Unity. You should still see your DisappearMe script in the Project panel. In order to attach it to the ball, drag-and-drop the file over the Ball Game Object in the Hierarchy panel. If drag-and-drop isn't your thing, you can also select the Ball Game Object, and choose Component | Scripts | Disappear Me from the menu. Once you do this, it may not look as if anything happened. To confirm that you did it correctly, click on the Ball. At the bottom of the Inspector panel where components of the Ball are listed, you should see your DisappearMe script.

Disappear Me!

Uncheck the Maximize on Play button in the Game view to experience the full effect of your script. (Cue circus music) And now, ladies and gentlemen, thrill to the astounding spectacle that you have created with a single line of code! Click on the Play button to test your game, and watch with amazement as your Ball disappears!

What just happened?

A good magician never reveals his tricks, but let's break down that piece of code we wrote to see what's going on behind the scenes.

It's all Greek to me

First, we created a JavaScript script. Unity scripts are written in three languages that are somewhat like English: JavaScript, C#, and Boo. You may have already dabbled in JavaScript if you've tried your hand at web development. Unity's version of JavaScript (called "UnityScript") is a bit different because it talks about Unity-related things and runs much faster than your father's JavaScript.

In this book, we'll use JavaScript because it's the simplest of the three languages to learn. For this reason, many of the online Unity scripting tutorials you'll find are also written in JavaScript.

Note

Stay sharp

JavaScript may be the best learning language for Unity beginners, but if you end up getting serious about developing games with this software, consider learning C#. It's much closer to a "real" programming environment, and it gives you certain organizational advantages that you won't get with JavaScript.

The first thing that we did was to write a line of code between two curly braces. I like to think of curly braces as delicious sandwich buns that group code together. The single lines of code are like the thin layers of salami or tomato in the sandwich. Above the curly braces is the description, or declaration, of the sandwich. It's like saying: We are now going to make a hoagie top sandwich bun, yummy ingredients, bottom sandwich bun.

In more technical, less sandwich terms, the area grouped by the buns is called a statement block. The layers between the buns are called statements. And the type of sandwich we're making, the Update sandwich, is known as a function.

You'll never go hungry again

A function is a piece of the script that can be executed, or called, over and over again. It's like having a sandwich that you can eat as many times as you want. We use functions to organize our code, and to house lines of code that we may need more than once.

The function we used is called Update. Just as there's an ongoing physics process in the background of our game that we can tap into to make our ball move and bounce, there's an ongoing Update loop as well. Update is eaten (or called) again and again and again while our game runs. Any script lines, or statements, that we put inside the Update function tap into that loop.

Notice the way the Update function is declared. On a restaurant menu, we might declare that our Street-Fightin' Hoagie is a scrumptious offering of mile-high pastrami with lettuce, tomatoes, bacon, and cucumbers, topped with a fried egg and slathered with mustard. We can declare a function much more simply. It starts with the word function, and adds the function name and a pair of round brackets. If our hoagie was a JavaScript function, it might look like this:

function Hoagie() {
}

With great sandwich comes great responsibility

There are a few rules and "best practices" to follow when declaring functions.

  • Your function name should start with a capital letter.

  • You must never start a function with a number or some weirdo character like the Rod of Asclepius or you will get an error. An error is a written notice that Unity sends you when something you typed doesn't work or doesn't make sense.

  • You can press the Enter key to drop the top sandwich bun down a line. Some programmers (like me) prefer writing code this way so that they can see the open or closed sandwich buns lined up, but other programmers prefer code that doesn't spread out too much.

  • In this book, we'll use both approaches just to keep you on your toes.

Examining the code

Let's look closely at the line of code that we wrote:

renderer.enabled = false;

The semicolon at the end of the line is like the period at the end of a sentence. Nearly all single-line statements must end in a semicolon or the code might break. When code breaks, Unity uses a special popped-up window called the console to tell us about it. When you have code that throws an error, we say there is a bug in your code.

Note

Semi-confusing

So why doesn't the function declaration have a semicolon? Why don't the curly braces each have one? It's because they're a different beast. They're not a single line of code they're more like a house where code lives. If you start seeing the declaration and statement block as one complete thing instead of three different things, you'll be well on your way to getting past the confusing bracket hurdle that many new programmers face.

In order to understand the rest of that line, you need to realize that there is a LOT of code going on behind the scenes that you can't see. It looks like a bunch of pretty pictures to you and me, but the Unity team had to write code to make it look that way. Behind each instance of your GameObject, and behind the Unity program itself, there are thousands of lines of code telling your computer what to show you.

Renderer is one such piece of code. When you set the enabled property of your ball's renderer to false, you're saying that you don't want Unity to draw the triangles in the mesh that makes up your ball.

Time for action – Find the Mesh Renderer component

Does renderer sound familiar to you? We already saw a component called Mesh Renderer when we created our Paddle and Ball Game Objects. If you don't remember, have a look at the following steps:

  1. Select the Ball, if you haven't already.

  2. Look in the list of the components of the Ball in the Inspector panel. There should be one component there called Mesh Renderer.

  3. If you see only the name of the component, click on the gray arrow next to the component name to expand the component.

Aha! What do we have here? Something called Mesh Renderer it has a checkmark next to it. What happens if you click to uncheck that checkbox?

Go on try it!

The ball disappeared. No surprises there. We saw something similar happen when we clicked on the checkmark beside an entire Game Object in the last chapter.

But, I wonder, does this Mesh Renderer component have anything to do with the "renderer" we talked about in our DisappearMe script? Checking that checkbox certainly seemed to have the same effect as running a script that said renderer.enabled = false;.

Let's be bold here. We need to figure this out. We'll leave the checkbox unchecked and modify our script to get a solid answer.

Time for action – Make the ball re-appear

  1. Double-click on the DisappearMe script. MonoDevelop will open up and display the script.

  2. Change the word false to true.

  3. Save the script.

  4. Click on the Play button to test your game.

    Your script should look like this:

    function Update () {
       renderer.enabled = true;
    }
    

Bingo! The Ball started out invisible and then magically appeared. That means that the Mesh Renderer component and the renderer that we referred to in our script are the same thing. And the checkbox in the Inspector panel is actually the enabled property in checkbox form instead of true and false, it shows us checked and unchecked states! In fact, you can even keep an eye on the checkbox when you click on Play and see it change states. That tingling sensation means it's working.

Ding!

Hopefully, a little lightbulb has appeared above your head at this point. You may be wondering what else you see in that Inspector panel that you can get your grubby mitts on through code. Let's take a quick glance at what else is in that Mesh Renderer component:

  • A checkbox labeled Cast Shadows (this is a Unity Pro feature)

  • Another checkbox labeled Receive Shadows

  • Something a little more complicated involving Materials

Unless you're some kind of whiz kid, it's unlikely that you'll figure out how to fiddle with this stuff on your own. Let's take a trip to the Unity Language Reference to see what it tells us about the Renderer class.

Time for action – Journey to the Unity Script Reference

The Unity Script Reference is like a dictionary that contains every single word of Unity JavaScript we'll ever need. It's organized extremely well, is searchable, and has hyperlinks to related sections. Look that up in your Funk and Wagnalls.

  1. Make sure that the Ball is still selected.

  2. Find the Mesh Renderer component of the Ball in the Inspector panel.

  3. Click on the blue book icon with the question mark on it to open the manual.

Your default browser should open, and the Mesh Renderer page of the Unity manual will load. Note that you're not viewing this information online. These are HTML files stored locally on your computer that Unity displays in your browser. You can find this same Unity manual online at this address: http://unity3d.com/support/documentation/Manual/index.html.

The manual tells you how to work with the things that you see in the Unity interface. The page you're looking at now tells you everything you always wanted to know about the Mesh Renderer component. Click on the Scripting button at the top-right of the page; the Unity Script Reference should appear.

As we want to explore the Renderer class, type the word Renderer into the search field at the top-left of the page. (My browser asked me to enable ActiveX controls to view the page; yours might do the same). Click on the link to the Renderer class at the top of the resulting list.

The Renderer class

The Renderer class lists a bunch of stuff that might look like so much gibberish to you. It has these lists:

  • Variables

  • Messages sent

  • Inherited variables

  • Inherited functions

  • Inherited class functions

From that list, the only familiar word might be "functions", which we just learned are reusable bundles of code (or endlessly eatable sandwiches, if you prefer). As we write more code in this chapter, we'll come to understand what variables are. For now, focus on the things listed under the Variables section.

One of the variables is called enabled. Do you remember when you wrote renderer.enabled = false;? You've already used a variable, perhaps without knowing it. And, check it out some of the other things that we noticed in the Mesh Renderer component are listed here. There are variables called castShadows and receiveShadows, which we saw as checkboxes in the Inspector panel. There are also some material-related variables. At the bottom of the list, there's a variable called isVisible, which appears to do something different than the enabled variable.

Have a go hero - Pulling the wings off of flies

If you were the type of kid who disassembled your parents' clock radio, or got up close and personal with the insects in your backyard to see what made them tick, this is your time to shine. The Language Reference is your gateway to every special reserved word ("keyword") in the Unity language. Try clicking on the enabled variable in that list. The resulting page not only repeats the explanation of the variable, but it also provides an example of how you might use that variable in code. You can even use the drop-down lists on the right to see the C# and Boo translations. (Go ahead check them out! Fortune favors the bold.)

If you're wired a certain way, you've already thrown this book down and are eagerly scouring the Language Reference looking for code you can mess around with. That's okay. We'll be here when you get back. If you are still a little wary of this foreign language and would like a little more guidance using it, read on.

What's another word for "huh"?

Perhaps the most challenging thing about using a language reference as a beginner is that you don't know what you don't know. The language is searchable to the tiniest detail, but if you don't know Unity's particular word for something, you'll still be lost. It's like not knowing how to spell a certain word. You can look it up in a dictionary, but if you don't know how to spell it, you might have trouble looking it up!

If you can't find what you're looking for, your best plan of attack is to bust out the synonyms. Try typing in any word you can think of that's related to what you want to do. If you want to hide or show something, try searching words such as visible, visibility, visual, see, show, appearance, draw, render, hide, disappear, and vanish! Even if it's a long shot, try "POOF!" You never know.

If you've exhausted your vocabulary and you still come up short, you can randomly click on words in the documentation and read about what they do. Another approach is to start scouring the Internet for Unity tutorials. Many developers like to share their code to help beginners like you learn new things. You might not understand what all this code does, but in grazing through it, you could find a line of code that looks like it might do what you're trying to do. Copy it, paste it into your own script, and start playing around. If it breaks your game, then good! You might think that you're not learning anything, but you are: you're learning how to not break your game. One final resource is chat channels, which you can find by using an Internet Relay Chat client, but as a Unity n00b (new user), you have to be careful and respectful of the way you speak to real people who know far more than you do. In most chat channels, there's very little love for new users who don't exhaust existing online resources before asking a question.

Be sure to check the appendix at the back of this book for a great list of Unity resources.

It's been fun

Our first attempt at scripting has been a laugh and a half, but we're no closer to making that paddle work in our keep-up game than when we started. Let's undo some of the work we did and get started on a script that's crucial to our game.

Time for action – Unstick the script

Let's remove the script from the Ball and recheck the Mesh Renderer component to make sure everything's back to normal.

  1. Make sure that the Ball is still selected.

  2. Find the Mesh Renderer component of the Ball in the Inspector panel, and make sure that the component is checked. The Ball should reappear in your Scene view.

  3. Find the DisappearMe script at the bottom of the Inspector panel.

  4. Right-click on the name of the component, and click on Remove Component. On a Mac, you can Control + click, or click the little black gear icon and choose Remove Component from the drop-down menu. Now, the script is no longer associated with your Game Object.

Gone, but not forgotten

It's just as important to learn how to remove a script from a GameObject as it is to learn about adding one. Note that we could also have unchecked the checkbox in the DisappearMe script component to temporarily disable it.

The DisappearMe script is no longer acting on the Ball, but we haven't deleted it. You should still be able to see it in the Project panel. To delete it, click on the script and press the Delete key on your keyboard, or Command + Backspace if you're on a Mac. If you want to keep the script around to remember what you did in this chapter, leave it.

Note

A Script for all seasons

You may already have guessed that the DisappearMe script is not exclusive to the Ball Game Object. You can drag-and-drop the script on top of the Ball, the Paddle, or any other Game Object in your Scene. As long as that Game Object has a renderer component, the script will work.

Why code?

We'll write our next script armed with some juicy knowledge. We know that Game objects are backed by code. Some of that code is invisible to us, and we can tap into it only through scripting. Other code is exposed to us in the Inspector panel in the form of components with a GUI (like checkboxes and drop-down menus) on top.

You may already wonder why, when Unity gives us such a friendly and easy-to-use checkbox to click, we would ever want to bother writing code to do something. It's because the controls you fiddle with while you build your game are no good to you while your game is actually running.

Imagine you want a Game Object to suddenly appear in response to something your player does while playing your game. What if your player can grab a power-up that displays a second paddle on the screen? In that case, that checkbox is useless to you. Your player isn't going to enjoy your game inside the Unity 3D program, and it's silly to suggest that you'll be there sitting on his lap to click that checkbox whenever he collects the double paddle power-up. You need to write code to tell Unity what to do when you're not there anymore. It's like equipping a baby bird with all the skills it needs to survive in the world, and then booting it out of the nest. No one's going to be around to click that checkbox for you, baby bird.

Equip your baby bird

Let's teach Unity what to do when we're not around, and the player wants to move the paddle to bounce the ball. Make sure that the Mesh Renderer component of your Ball is enabled (checked). We're going to create a brand new script and attach it to the paddle.

Time for action – Creating a new MouseFollow script

  1. In the Project panel, right-click/alternate click on an empty chunk of space and choose Create | JavaScript. Alternatively, you can click on Assets | Create | JavaScript in the menu at the top of the screen, or use the Create button at the top of the Project panel.

  2. A new script is added to the Project panel. Name it MouseFollow.

  3. Drag-and-drop your new MouseFollow script onto your Paddle Game Object.

  4. Double-click to open the script in MonoDevelop. Just as before, we're going to add a single, simple line of code inside the curly braces (sandwich buns) of the Update function (sandwich):

    function Update () {
       transform.position.x = 2;
    }
    
  5. Save the script and click on Play to test your game. Like pulling the chair out from beneath someone when he tries to sit down, your Paddle should act like a total jerk, and pop out of the way to let your Ball plummet to its doom. Not cool, Paddle.

    Not cool.

What just happened?

Just as we saw with the Mesh Renderer component, the Transform is also a component of your GameObject. It's the first attached component on the list in the Inspector when you select your Paddle. As we learned in Chapter 3, Game #1: Ticker Taker, the Transform component decides how the GameObject is positioned, rotated, and scaled (made larger or smaller).

In the Unity environment, the Transform component of our Paddle was set to position 0 in the X-axis. But, we changed this with our line of code, setting the x property of the paddle's transform to 2. The result is that the first time the Update function is called, the paddle appears to jump out of the way to two units along the X-axis.

The thought may have already struck you: if you can control the x position of the Paddle, you can probably also control its z and y positions just as easily. And if position is available to you, rotation and scale can't be far behind! But, which keywords should you use to change these properties? rotation and scale are two good guesses, but we'd rather be sure.

To satisfy your curiosity, let's hit the Unity Script Reference again; this time, we'll take a shortcut. Highlight the word transform in your script and press Ctrl + ' on your keyboard, or Command +' on a Mac (that's the apostrophe character). You'll zip directly to the Script Reference with a list of hits as if you'd searched for "transform" in the search field. Note that you're taken to the online documentation, rather than the local copy that the blue book icon showed you. Click on the Transform class at the top of the list to see the wonders that await you.

A capital idea

The transform component is listed as Transform with an uppercase T. When we refer to it in code, we use a lowercase "t". In the Language Reference, it has an uppercase "T" again. But, if you've already made the mistake of using an uppercase "T" in your code, Unity threw you an error in the console window. What gives? Unity's language is case sensitive, which means that a word with a capital letter is treated as a completely different thing than the same word with a small letter. So, "transform" and "Transform" are as different from each other as the words "night" and "day".

Capital "T" Transform is a class. A class is like a blueprint that you use to make other things. You might implement power-ups in your keep-up game. Your capital "P" Powerup class describes what a power-up should look like and how it should behave. You might create a new power-up using your Powerup class, and label it "powerup" with a small "p". The "P" Powerup contains the instructions for building something, and "p" power-up is the name you gave to the thing you built with that blueprint.

So, in this case, capital "T" Transform is the class, or blueprint, that describes how a transform works. Small "t" transform is the name our GameObject gives to its transform, which was built using the Transform blueprint. This upper-case class/lower-case instance is a coding convention, but so is not shouting at the dinner table; we'll follow the convention to keep things civil.

Here are a few more examples to help you understand:

  • Car is the class (blueprint). We use it to make a new car instance, which we call "car" (small "c").

  • House is the class (blueprint). We use it to build a new house instance, which we call "house" (small "h").

We can use these classes to create multiple copies, or instances, of the thing. The Car class could stamp out many things, which we could call "car1", "car2", and "car3". We could also call those things "sedan", "convertible", and "suv". In Unity-land, the developers decided to call the thing that was created with the Transform class "transform". It could just as easily have been called "pinkElephant", but "transform" makes more sense.

Animating with code

We talked about how the Update function is called again and again by Unity. We're about to see what that really means by making a slight adjustment to our code.

Time for action – Animating the paddle

  1. Jump back into your MouseFollow script if you're not there already by double-clicking on it in the Project panel.

  2. Change the line of code in the Update function ever so slightly so that it reads as follows:

    function Update () {
       transform.position.x += 0.2;
    }
    

The changes are very subtle. We added a plus sign (+) before the equals sign (=), and we made the number smaller by adding a zero and a decimal place in front of it, changing it from 2 to 0.2.

Save the script and test your game. The Paddle should scoot out of the way, and fly straight off the right-edge of the screen!

What just happened - what witchcraft is this?

We made the 2 smaller, because the Paddle would have just rocketed off the screen in a twinkling and we may not have seen it. However, there's a tiny bit of code magic going on in that += bit.

By changing the transform.position.x property with += instead of =, we're saying that we want to add 0.2 to the x property. As the Update function is called again and again, the x position constantly increases. Let's follow the logic:

  • The first time Update is called, x is 0. We add 0.2, and the Paddle moves to 0.2.

  • The second time Update is called, x is 0.2. We add 0.2, and the Paddle moves to 0.4.

  • Every time Update is called, the Paddle's x position increases. We get a real sense of how often the Update function is called by how quickly the Paddle moves across the screen in tiny 0.2 increments.

Note

Any excuse to work less

+= is a bit of programmer shorthand. It's the same as typing:

transform.position.x = transform.position.x + 0.2;

But that's way more typing, and the less you have to type the better. Excessive typing kills 80% of computer programmers before their 40th birthday. I just made that stat up, so it's probably off by a few percentage points.

What just happened why didn't the paddle animate before?

When we wrote the line transform.x = 2, the Paddle just jumped into position; it didn't go anywhere, like it does now. Why is that?

The Update function is still getting called multiple times. But, each time, it's putting the Paddle at two units on the X-axis. The value of x changes on every Update, but it changes to the same thing. So, once the Paddle is in position, it doesn't appear to move.

With our new modified line of code, the Paddle's x position is changing by 0.2 every time the Update function is called, so the Paddle moves across the screen.

An important part of being a beginner programmer is keeping a positive attitude. You should start with the assumption that what you want to do can be done anything is possible. We now know how to set the position of the paddle. With your positive, can-do attitude, you might imagine that to get the paddle moving around with the mouse, you could find the position of the mouse, and set the transform.position.x property to match.

But, what words do you need to use to get the position of the mouse? For the answer, let's dive back into the Unity Language Reference.

Pick a word (almost) any word

We're going to put the Language Reference through some serious stress testing here. We're going to arm ourselves with every synonym for "mouse" we can think of. Here's a list that I painstakingly brainstormed: mouse, screen position, cursor, pointer, input device, small rodent, two-button, aim, point. One of those has to do the trick. And, if we come up empty-handed, we're going to hit those online tutorials hard until we find our answer.

Go ahead, fire up the Unity Language Reference and type mouse into the Search field. Scour the resulting list. We will not back down. We will not take "no" for an… oh, hello, what's this?

Midway down the list, there's this entry:

Input.mousePosition

The current mouse position in pixel coordinates.

Well, uh… ahem. That was easy. I guess we won't be needing this synonyms list then.

Screen coordinates versus world coordinates

Click on the Input.mousePosition entry and check out the resulting page. The Language Reference tells us that we have a new origin to deal with. Unity treats our screen like a flat, 2D plane, with (0, 0) the origin in the bottom-left corner of the screen like a bar graph from fourth grade.

We have a code example here, but it looks a little hairy. What's a Physics.Raycast? I have no idea. And how do we get the x, y, and z values for Input.mousePosition?

The answer is a tiny bit sneaky. Look at the top of the screen where it tells us that Input.mousePosition is a Vector3. What's a Vector3? I dunno. Click on it. Ah, the resulting page tells us that a Vector3 has x, y, and z properties along with a slew of other useful stuff. That shall do nicely.

Move the paddle

We are ready to rock. If we just set the paddle's x position to the mouse's screen position by using Input.mousePosition, we should be able to use the mouse to move the paddle. Change your line of code so that it looks like this:

transform.position.x = Input.mousePosition.x;

Save your script and try it out.

Worst. Game. Ever.

It may look like nothing actually happened. Your paddle is gone. Super. Obviously, controlling the paddle with the mouse isn't so simple.

But, don't despair. Try placing your mouse at the left edge of the screen, and then move it very, very slowly. You should see your paddle pass across the screen. You'll notice that even the tiniest horizontal movement of your mouse sends the paddle rocketing off into Never Neverland. This will not do.

See the matrix

We need to figure out what kind of numbers our mouse is throwing out. We can do this by using the Debug.Log() function. Whatever information we ask Debug.Log() to display will show up at the bottom of the screen while we test our game.

Time for action – Listening to the paddle

  1. Add this line of code beneath the existing line of code:

    Debug.Log(Input.mousePosition.x);
    
  2. Your entire script should look like this:

    function Update () {
    transform.position.x = Input.mousePosition.x;
    Debug.Log(Input.mousePosition.x);
    }
    
  3. Save and test your game.

Look at the very bottom of the screen for the results of your Debug.Log() statement. As you move the mouse cursor left and right, you'll see this number changing. It goes from 0 when your mouse is at the left edge, and rapidly increases. The upper limit depends on your monitor resolution; on my screen, Input.mousePosition.x maxes out at 1279! Earlier, a value of 2 put the paddle nearly all the way off the screen. With Debug.Log() reporting these crazy big numbers, we can see why our code behaves the way it does.

A tiny bit o' math

This code's not going to work. Our paddle moves only to the right, along the positive X-axis, because we're working only with positive numbers. We need some negative numbers in there so that the paddle will move to the left at some point. However, at what point?

Hmm… what if we take half of the screen's width and subtract it from Input.mousePosition.x? What does that do for us?

A quick trip to the Unity Language Reference tells us how to find the width of the screen in pixels. Let's divide that number by two and subtract it from the mouse position.

Change the Debug.Log() function call to look like this:

Debug.Log(Input.mousePosition.x - Screen.width/2);

Save and test. Watch the bottom of the screen for the result.

Tracking the numbers

When the mouse cursor is near the left edge of the screen, you get negative numbers. When it's closer to the right edge, you get positive numbers. And, if you try to position your cursor at exactly the halfway point, you get zero. This makes sense. We know that when the mouse is at the left edge, Input.mousePosition.x is zero. If we subtract half of the screen width from zero (on my monitor, that's 640 pixels), we get -640 at the left edge instead.

We'll use what are called hardcoded numbers through this next bit. Later on, we'll start asking Unity to dynamically determine the screen width for us. My screen is 1280 pixels wide, so I'm using 640 to represent half of its width. Your mileage may vary! Common screen widths are 800, 1024, 1152, 1280, 1600, or 1920. If you're not sure how wide your display is, click on the drop-down beneath the Game tab and choose Standalone instead of Free Aspect. In brackets next to the word "Standalone", you'll see your monitor screen width.

When the mouse is in the middle of the screen, Input.mousePosition.x is 640. It's at the halfway point. If we subtract half the screen width (640 in my case), we get zero.

When the mouse position is at the right edge of the screen, Input.mousePosition.x is almost at 1280 on my 1280-pixel-wide display (again, your mileage may vary). Subtract half the Screen.width and we get 640. -640 at the left edge, 0 in the middle, and 640 at the right edge.

Futzing with the numbers

This is promising, but we already know that these numbers are still too big. If we move the paddle to 640 units along the X-axis, it's going to wind up in Timbuktu. We've got a good positive or negative number scale going we just need to shrink that number down somehow. Let's try dividing our number by half of the screen's width.

Time for action – Logging the new number

  1. Change your Debug.Log() call so that it looks like this:

    Debug.Log( (Input.mousePosition.x -Screen.width/2) /
       (Screen.width/2) );
    

Ack! So many brackets! We use those brackets because we want the division and subtraction stuff to happen in the right sequence. You may remember order of operations from algebra class. BEDMAS: evaluate the Brackets first, then the Exponents, then Division and Multiplication, and finally, Addition and Subtraction.

To wrap your brain around it, here's what we're doing, in pseudocode:

(first thing)/(second thing)

We're dividing something by something else. The first thing is the -640 to 640 number range that we cooked up. The second thing is Screen.width/2 (the screen's midpoint). We wrap all of that in a tasty Debug.Log() shell:

Debug.Log((first thing)/(second thing));

Note

Pseudocode is fake code that will not work if you type it into a script. We're just using it to better understand the real code that we're writing. Some programmers use pseudocode to type their thoughts out with English words to help them puzzle through a problem. Then, they go back over the pseudocode and translate it into a language that the computer will understand JavaScript, C#, and so on.

Now, we really have something. If you save and test and move the mouse cursor around, you'll see that as you get closer to the left edge of the screen, you get closer to -1. And, as you get closer to the right edge, you approach 1. In the middle, it's zero.

Copy or rewrite the chunk of code from the Debug.Log() statement to the line above so that it now reads:

transform.position.x = (Input.mousePosition.x -Screen.width/2) /
   (Screen.width/2);

Save and test your file.

She's A-Work!

THAT'S what we're after. The paddle moves at a reasonable rate along with the mouse, and now we can actually bounce the ball around a little. Success! To get more than just a straight vertical bounce, try clipping the ball with the edge of the paddle. The ball should bounce off the screen at an erratic angle. Game over!

Click on the Play button again to stop testing your game.

Somebody get me a bucket

A common programmer mantra is "duplication is evil". The idea is that any time you type the same thing twice you could be wasting time and effort. Remember that the less typing we do, the less likely we are to drop dead from the programmer illness I totally fabricated. The less duplication we do, the less "maintenance" we have to do if something goes wrong later correcting one chunk of code is easier than correcting multiple duplicated chunks. But the rule for beginners is this: make it work first then make it elegant.

Notice that we have some duplication in this line:

transform.position.x = Input.mousePosition.x – Screen.width/2)/
   (Screen.width/2);

We've typed Screen.width/2 twice. That won't do! For starters, typing makes my hands tired. What's more, we're forcing the computer to do that complicated math calculation twice. Why not do the calculation once and ask the computer to remember the result? Then, any time we want to talk about the screen's midpoint, we can ask the computer to retrieve the result.

We do this by creating a variable. A variable is a reserved storage locker in memory. I like to think of it as a bucket that can hold one thing. Just as we saw with functions, variables are created by declaring them.

(To be totally honest, an extra division operation isn't going to bring your game to its knees. But, there are more "costly" operations we could ask Unity to perform, and learning to put things in variable buckets now is good practice to prepare us for the heavy lifting that we'll do later.)

Time for action – Declaring a variable to store the screen midpoint

  1. Modify your script so that it looks like this (you can get rid of the Debug.Log() function call):

    function Update () {
       var halfW : float = Screen.width/2;
       transform.position.x = (Input.mousePosition.x -halfW)/halfW;
    }
    

This code looks a lot cleaner. Not only is it easier to read, but we've knocked out some of those confusing brackets in the second line.

What just happened we've gone too Var

We've used the special var keyword to declare our variable (bucket). I chose the name halfW, which is short for "half width" half the width of the screen. You can choose any name you like for a variable as long as it isn't a reserved Unity keyword, and it doesn't break any of the naming rules that we discussed when we looked at naming functions. For example, 1myvar funfun will not work, because it begins with a number and has a space in the middle. Also, it sounds ridiculous and doesn't make any sense. Try your best to keep your variable names logical.

Note

F is for function

Note

The biggest difference between naming a function and naming a variable in Unity is that a function name should start with a capital letter, while a variable name should not. This is not a hard and fast rule, but it's called a "best practice", which means that everyone else does it, so you should too. Jump off a bridge with us!

We stuck a colon and the word float on the end of our variable name. Why? By using a colon, we're telling Unity what type of variable we're using. This lets the program know how big of a bucket to create. Giving a variable a type speeds up our game because Unity doesn't have to keep guessing what type of bucket halfW is.

float is short for a single-precision floating point. To you and me, that's a number with a decimal point in it. Here are a few more data types that Unity uses:

  • String: An alphanumeric bunch of characters like "Hello, my name is Herb" or "123 Fake St".

  • Boolean: Like a light switch, a Boolean can be only one of two states true or false.

  • int: An integer like 28 or -7.

Our halfW variable is typed as a float because we need that decimal place. But, we're not splitting the atom or anything, so we don't need to make it a double, which is a more accurate numerical data type.

Save the script and test your game to make sure everything is working correctly.

Using all three dees

Now that we know how to track the mouse left and right with the paddle, it's not a huge leap of logic to make our paddle track the y position of the mouse, and translate it into z coordinates. Remember that we're working with two different planes here:

  • The flat, two-dimensional plane of the computer screen

    • Horizontal X-axis

    • Vertical Y-axis

    • Origin point (0, 0) at the bottom-left of the screen

  • The deep, three-dimensional intersecting planes of our game world

    • Horizontal X-axis

    • Vertical Y-axis

    • Deep Z-axis

    • Origin point (0, 0, 0) in the middle of the world where the three planes meet

We're going to track the y movement of the mouse, and map it onto the z movement of the paddle to make it move toward and away from the player. If instead we map the mouse y position to the paddle's y position, the Paddle will move up and down from the ground to the sky, which is not quite what we're after.

Time for action – Following the Y position of the mouse

  1. Modify your code to add a few familiar-looking lines:

    function Update ()
    {
      var halfW:float = Screen.width/2;
      transform.position.x = (Input.mousePosition.x - halfW)/halfW;
      var halfH:float = Screen.height/2;
      transform.position.z = (Input.mousePosition.y - halfH)/halfH;
    }
    

The two new lines of code are almost identical to the first two lines. We've created a new variable and called it halfH (half height) instead of halfW. We're changing the z property of Input.mousePosition instead of x. When you save the script and test your game, you'll have a fully movable paddle to bounce your ball on.

Note

Math effect

Note

I actually put a little cheat into my code. I wanted the paddle to travel deeper and farther along the Z-axis with less mouse movement, so I changed my halfH variable declaration to this:

var halfH:float = Screen.height/3;

That's a third of the screen, not a half. Technically, I should change the name of my variable to something like "thirdH". But, do you want to get hung up on details all day or do you want to build a game? Fudge the number by changing that 2 to a 3, and let's move on. It'll be our little secret.

A keep-up game for robots

After all this effort for getting our paddle to move, the game still doesn't have much oomph to it. It's very easy to keep the ball bouncing because there's nothing to send it spinning off in any crazy directions. At least in Breakout or Arkanoid, you had interesting angles to work with. Our game doesn't have any walls to angle off, but we do have that paddle.

What if we angled the paddle as the player moved it around? An angled paddle would make the ball bounce in different directions and keep the player on his toes.

Once more into the breach

How do we make the paddle angle? Let's consult the language reference, armed with words that describe what we want to do: angle, rotation, rotate, spin, turn, bank, tilt, yaw, roll. Our second idea, rotation, holds the secret.

Time for action – Re-visiting the Unity Language Reference

  1. Type rotation into your script. It lights up. It's one of the magic keywords!

  2. Double-click to select the rotation keyword.

  3. Press Ctrl + ' or Command + ' to warp to the Unity Language Reference. You could also type rotation into the Search field of the reference if you already have it open.

A quick look at the resulting list turns up Transform.rotation. We've already been using Transform.position to move our paddle around it's not such a stretch to figure out that this is what we need. Click on the link!

Our work here is done

Well, would you look at that while we were sleeping, some kindly programmer elves cobbled together some shoes for us! The Transform.rotation page of the Language Reference has a chunk of code that "smoothly tilts a transform towards a target rotation". That sounds a lot like what we're trying to do.

Hey, I have an idea: I'll keep an eye on the door. You copy and paste this code into your game. If anyone asks, tell them "the book made me do it".

Time for action – Adding the sample code to your script

Add the new code to your existing game code. You'll need to shuffle a few lines around. Here it is with the new stuff highlighted:

var smooth = 2.0;
var tiltAngle = 30.0;
function Update ()
{
  var halfW:float = Screen.width/2;
  transform.position.x = (Input.mousePosition.x - halfW)/halfW;

  var halfH:float = Screen.height/3;
  transform.position.z = (Input.mousePosition.y - halfH)/halfH; 
   
  // Smoothly tilts a transform towards a target rotation.
  var tiltAroundZ = Input.GetAxis("Horizontal") * tiltAngle;
  var tiltAroundX = Input.GetAxis("Vertical") * tiltAngle;
  var target = Quaternion.Euler (tiltAroundX, 0, tiltAroundZ);
  // Dampen towards the target rotation
  transform.rotation = Quaternion.Slerp(transform.rotation,     target,Time.deltaTime * smooth);
}

Note

Nobody's perfect

Note

In my version of the Script Reference, there's a type-o. The last line ends with two semicolons instead of one. If your version has the same type-o, just delete the extra semicolon.

Note that two variables, smooth and tiltAngle, are outside the Update function at the top of the script. We'll discover why in a moment.

If you save the script and run the game now, the new rotation code won't work. We have to make a few adjustments. I've highlighted what you need to change in the following code:

var smooth = 5.0;
var tiltAngle = 30.0;

function Update ()
{
  var halfW:float = Screen.width/2;
  transform.position.x = (Input.mousePosition.x - halfW)/halfW;

  var halfH:float = Screen.height/3;
  transform.position.z = (Input.mousePosition.y - halfH)/halfH;   
   
  // Smoothly tilts a transform towards a target rotation.
  var tiltAroundZ = Input.GetAxis("Mouse X") * tiltAngle * 2;
  var tiltAroundX = Input.GetAxis("Mouse Y") * tiltAngle * 2;
  var target = Quaternion.Euler (tiltAroundX, 0, tiltAroundZ);
  // Dampen towards the target rotation
  transform.rotation = Quaternion.Slerp(transform.rotation, 
  target, Time.deltaTime * smooth);
}

Here's what's new:

  • We bumped up the smooth variable from 2.0 to 5.0 to make the motion more… well, smooth.

  • We asked Unity for Mouse X and Mouse Y instead of Horizontal and Vertical. Horizontal and Vertical are, by default, mapped to the arrow keys and the WASD keys on your keyboard. Mouse X and Mouse Y will report mouse movement.

  • Finally, we doubled the tilt effect by multiplying the tiltAroundZ and tiltAroundX values by 2.

Note

Available for comment

Note

Notice the line that says Smoothly tilts a transform towards a target rotation, and the one a bit farther down that says Dampen towards the target rotation. That sounds like plain English not code at all. The double-slashes before these two lines make them comments. Comments are ignored by Unity when we run our game. Comments enable you to type whatever you want in your code as long as you've got those two slashes in front. Many programmers use comments to explain to other programmers (or to themselves, in the future) what a piece of code is supposed to do. While you learn Unity, you can use comments to take notes on the new code concepts that you're learning.

Save the script and test your game. The paddle should tilt as you move your mouse around. It sort of works, but the way it tilts around the Z-axis makes the paddle fire the ball into crazy town. Stop testing your game. We're close very close.

One final tweak

One small adjustment stands between you and a fun keep-up game mechanic. We want the paddle to angle in the opposite direction to keep the ball bouncing inside the play area. Instead of multiplying by 2, we can multiply by -2 to flip the effect:

var tiltAroundX = Input.GetAxis("Mouse Y") * tiltAngle * -2;

Save and test. Hooray! The paddle tilts around the play area inclusively, keeping the ball more or less within our reach unless we get twitchy, and then we drop the ball. But that's the fun of keep-up moving the paddle just so to keep the ball in play.

What's a quaternion?

Well, that was lots of fun! We've got a working keep-up game mechanic. Now let's go and do something else.

Wait, what's a quaternion?

Oh, that? Don't worry about it. We have bigger fish to fry! In the next chapter, we'll…

WHAT THE HECK IS A QUATERNION??

Gosh, you're persistent. Can't you just leave it well enough alone?

I'm not going to sugarcoat it: 3D math is complex. A quaternion (like the one we used in our rotation code just now) is a beast of a mathematical concept. According to my mathematics dictionary (which I often pull out for light reads in the bathroom), "a quaternion forms a four-dimensional normed division algebra over the real numbers". There. Now do you understand?

Quaternions are clearly outside the scope of a beginner book. But, we can't just go swiping code from the Language Reference without even partially understanding it, so let's give it the old college try.

Educated guesses

These are the two lines from our code we'd like to better understand:

var target = Quaternion.Euler (tiltAroundX, 0, tiltAroundZ);
  // Dampen towards the target rotation
  transform.rotation = Quaternion.Slerp(transform.rotation, 
  target,Time.deltaTime * smooth);

Let's actually start from the bottom up.

In the final line of code, we're setting transform.rotation to something, which will turn the paddle somehow. That much, we get. We can probably also guess that Quaternion is one of those built-in classes that we looked at, like Input both Quaternion and Input start with a capital letter, and light up when we type them.

Slerp sounds weird, but it starts with a capital letter and has round brackets next to it. We've seen that same structure when we called functions earlier in our code, like Input.GetAxis() and Debug.Log(). And, just like those two functions, Slerp() needs some extra information to do what it does. These are called arguments, and we've used them a few times already. We stuck something inside the brackets of Debug.Log() to make it appear at the bottom of the Unity window. Giving data to a function to make it do what it does is called passing arguments.

So, what do we have? A class (or blueprint) called Quaternion, with a function called Slerp() that asks for three pieces of information three arguments. For a better idea of which arguments Slerp() needs, type Quaternion.Slerp() into the script, and read the code hinting that pops up. Slerp() needs these three arguments to do what it does:

  • From, which needs to be of type UnityEngine.Quaternion

  • to, which needs to be of type UnityEngine.Quaternion

  • t, which needs to be of type float

You can read "as" as "needs to be of type …" if that makes things more clear. This just means that we can't pull any funny business by feeding the function a float type or a Boolean type when it's asking us for a Quaternion type.

We can already see that we're passing in the paddle's transform.rotation value as the from argument, which means that transform.rotation must be of type Quaternion.

For the to argument, we're passing target, which is a variable of type Quaternion. We defined it one line earlier.

Finally, for the t argument, we're passing Time.deltaTime and multiplying it by the value of our smooth variable, which we defined way up at the top of our script as 5.0.

Note

Time.deltaTime

Note

You'll see Time.deltaTime very often in your Unity travels. deltaTime is a property of the Time class; it represents the amount of time that elapsed between this frame and the last. You usually use it to make your game move according to time rather than according to the frame rate. Frame rates can change depending on how powerful a player's computer is, but time remains consistent.

More on Slerp

We've used our brains to try to figure out what this code is doing, so now it's time to fill in our knowledge gaps a little more.

Slerp is a frankenword meaning Spherical linear interpretation. You might already have guessed that it lets us move from one Quaternion rotation to another. The interpolation (or spread-outedness of the motion) happens across t, or time.

If we were to pseudocode this statement:

  transform.rotation = Quaternion.Slerp(transform.rotation,
    target,Time.deltaTime * smooth);

It might go something like this:

On every frame, rotate the paddle, starting at the paddle's current rotation. Rotate towards our target rotation. Use the amount of time that's elapsed between frames to stretch out (interpolate) the motion. Reduce the jerkiness of the motion by applying a smooth modifier.

Right on target

Last but not least, let's look at how we get that target Quaternion. We know why we need it: because we have to feed a to Quaternion to the Slerp() function. To better understand the penultimate line, let's break it down like we did before.

var target = Quaternion.Euler (tiltAroundX, 0, tiltAroundZ);

We're creating a variable called target, which is a bucket. Then, we're putting something in that bucket that we know is going to be of type Quaternion. We're calling the Euler function of the Quaternion class and passing it one argument.

Wait, did he just say "one" argument? I counted three. Yes, but try typing Quaternion.Euler() into your script, and reading the tooltip that pops up (sorry, Mac users, no tooltip for you. Try the Script Reference). The Euler function needs one argument of type Vector3. We've seen the Vector3 class before (earlier in this chapter). A quick trip to the Language Reference reminds us that a Vector3 is made up of three different parts: x, y, and z. That's why we're passing three arguments. If we already had a Vector3 assembled, we could pass our single Vector3 to the Euler function.

The rest is history. We're using our TiltAroundX and TiltAroundZ variables in the x and z slots, and because there's no change in the y rotation, we're passing zero. The Euler function gives us a return value, which is like putting money into a vending machine and getting change. We feed it values for x, y, and z (or a single Vector3), and it spits out a crazy-complex Quaternion for us that we probably couldn't have constructed on our own. With any luck, we'll get a candy bar too!

We take the resulting Quaternion, store it in a variable (bucket) called target, and use that as the to argument in the Slerp() function of the last line.

Have a go hero - Time to break stuff

But don't take my word for it. If you're still confused about what does what or you want to put this sample code through its paces, go for it. Here are a few things to try:

  • Change the values for the smooth and/or tiltAngle variables, and test your game (make sure that whatever value you try still has a decimal point). What effect do the new numbers have on the movement of the paddle?

  • Reverse any number in the code by putting a minus sign (-) in front of it.

  • Divide instead of multiplying.

  • Subtract instead of adding.

  • Try creating separate variables called tiltAngleX and tiltAngleZ to control the x and z tilt amounts independently.

  • Try creating a new variable of type Vector3 using the tiltAroundX, 0, and tiltAroundZ values. Then, pass the resulting Vector3 to the Quaternion.Euler function. Does your code still work?

Keep it up

That was some heavy-duty book-learnin'! Feel free to leave the room for a moment if you need to empty your brain. In this chapter, we:

  • Wrote our first Unity JavaScript

  • Applied the script to a Game Object

  • Learned how to modify components through code

  • Removed a script from a Game Object

  • Moved a Game Object with code

  • Hooked the position and rotation of a Game Object up to the mouse's position and movement

  • Dove into the Unity Script Reference and Component Reference to understand and to "borrow" some code

  • Took a crash course in programming to learn about:

    • functions and statement blocks

    • classes

    • data types

    • arguments

    • comments

    • logging

If you're still not grasping every little detail about programming, don't fret. Certain people are wired to just immediately get it, and some of us have to keep trying, failing, and trying again until that little light turns on. I tried and failed at programming my whole life, from the time I was about ten years old, until I gradually understood it. For me, it was less of a sudden light turning on, and more of a slow, steady burn, as the lightbulb filament steadily heated up as if on a dimmer switch. If you want to learn programming in Unity, you're not bound by your intelligence only by your determination and drive.

Beyond the game mechanic

We've added code to our keep-up game to control the paddle, and the game mechanic is amusing, but it's not a game! There's so much more to game development than just the core mechanic. Our game needs a proper starting point; it needs an ending; it needs a cue telling the player that he's failed when the ball falls on the ground; it needs a ground. Currently, you have to shut down and restart the game every time you drop the ball we need some kind of Play Again button. And, wouldn't it be nice to have a score counter on the screen telling the player how many bounces he got before dropping the ball? What about sound effects and music? And the box art! We have to have nice box art!

We may not get as far as shipping our game out to the shopping mall, but there's still a lot more we can do to make our game more gamey. We're going to come back to this game mechanic in a bit because there's definitely something promising here. But, first, we should learn how to put some of those crucial buttons and score counters on the screen. While we figure that out, we're going to use our new-found programming skillz to build a whole other game. Are you ready? Then journey with me to the laboratory of Professor Wrecker…