Implementing player movement
Now, at this point, we have a great-looking game, but nothing at all happens. Let's change that now using our player. Perform the following steps:
- Right-click on the Scripts folder you created earlier, click on Create, and select the C# Script label. Once you click on it, a script will appear in the Scripts folder, and it should already have focus and should be asking you to type a name for the script—call it
PlayerBehaviour
. - Double-click on the script in Unity, and it will open MonoDevelop, which is an open source integrated development environment (IDE) that is included with your Unity installation.
After MonoDevelop has loaded, you will be presented with the C# stub code that was created automatically for you by Unity when you created the C# script.
Let's break down what's currently there before we replace some of it with new code. At the top, you will see two lines:
using UnityEngine; using System.Collections;
Tip
Downloading the example code
You can download the example code files for all Packt Publishing 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.
The engine knows that if we refer to a class that isn't located inside this file, then it has to reference the files within these namespaces for the referenced class before giving an error. We are currently using two namespaces.
The UnityEngine
namespace contains interfaces and class definitions that let MonoDevelop know about all the addressable objects inside Unity.
The System.Collections
namespace contains interfaces and classes that define various collections of objects, such as lists, queues, bit arrays, hash tables, and dictionaries.
We will be using a list, so we will change the line to the following:
using System.Collections.Generic;
The next line you'll see is:
public class PlayerBehaviour : MonoBehaviour {
You can think of a class as a kind of blueprint for creating a new component type that can be attached to GameObjects
, the objects inside our scenes that start out with just a Transform
and then have components added to them. When Unity created our C# stub code, it took care of that; we can see the result, as our file is called PlayerBehaviour
and the class is also called PlayerBehaviour
. Make sure that your .cs
file and the name of the class match, as they must be the same to enable the script component to be attached to a game object. Next up is the: MonoBehaviour
section of the code. The :
symbol signifies that we inherit from a particular class; in this case, we'll use MonoBehaviour
. All behavior scripts must inherit from MonoBehaviour
directly or indirectly by being derived from it.
Inheritance is the idea of having an object to be based on another object or class using the same implementation. With this in mind, all the functions and variables that existed inside the MonoBehaviour
class will also exist in the PlayerBehaviour
class, because PlayerBehaviour
is MonoBehaviour
.
For more information on the MonoBehaviour
class and all the functions and properties it has, check out http://docs.unity3d.com/ScriptReference/MonoBehaviour.html. Directly after this line, we will want to add some variables to help us with the project. Variables are pieces of data that we wish to hold on to for one reason or another, typically because they will change over the course of a program, and we will do different things based on their values.
Add the following code under the class definition:
// Movement modifier applied to directional movement. public float playerSpeed = 2.0f; // What the current speed of our player is private float currentSpeed = 0.0f; /* * Allows us to have multiple inputs and supports keyboard, * joystick, etc. */ public List<KeyCode> upButton; public List<KeyCode> downButton; public List<KeyCode> leftButton; public List<KeyCode> rightButton; // The last movement that we've made private Vector3 lastMovement = new Vector3();
Between the variable definitions, you will notice comments to explain what each variable is and how we'll use it. To write a comment, you can simply add a //
to the beginning of a line and everything after that is commented upon so that the compiler/interpreter won't see it. If you want to write something that is longer than one line, you can use /*
to start a comment, and everything inside will be commented until you write */
to close it. It's always a good idea to do this in your own coding endeavors for anything that doesn't make sense at first glance.
Note
For those of you working on your own projects in teams, there is an additional form of commenting that Unity supports, which may make your life much nicer: XML comments. They take up more space than the comments we are using, but also document your code for you. For a nice tutorial about that, check out http://unitypatterns.com/xml-comments/.
In our game, the player may want to move up using either the arrow keys or the W key. You may even want to use something else. Rather than restricting the player to just having one button, we will store all the possible ways to go up, down, left, or right in their own container. To do this, we are going to use a list, which is a holder for multiple objects that we can add or remove while the game is being played.
Note
For more information on lists, check out http://msdn.microsoft.com/en-us/library/6sh2ey19(v=vs.110).aspx
One of the things you'll notice is the public
and private
keywords before the variable type. These are access modifiers that dictate who can and cannot use these variables. The public
keyword means that any other class can access that property, while private
means that only this class will be able to access this variable. Here, currentSpeed
is private because we want our current speed not to be modified or set anywhere else. But, you'll notice something interesting with the public
variables that we've created. Save your script by pressing Ctrl + S and then go back into the Unity project and drag-and-drop the PlayerBehaviour
script onto the playerShip
object. Before going back to the Unity project though, make sure that you save your PlayerBehaviour
script. Not saving is a very common mistake made by people working with MonoDevelop
. Have a look at the following screenshot:
You'll notice now that the public
variables that we created are located inside Inspector for the component. This means that we can actually set those variables inside Inspector without having to modify the code, allowing us to tweak values in our code very easily, which is a godsend for many game designers. You may also notice that the names have changed to be more readable. This is because of the naming convention that we are using with each word starting with a capital letter. This convention is called CamelCase (more specifically headlessCamelCase).
Now change the Size of each of the Button variables to 2
, and fill in the Element 0 value with the appropriate arrow and Element 1 with W
for up, A
for left, S
for down, and D
for right. When this is done, it should look something like the following screenshot:
Now that we have our variables set, go back to MonoDevelop
for us to work on the script some more.
The line after that is a function definition for a method called Start
; it isn't a user method but one that belongs to MonoBehaviour
. Where variables are data, functions are the things that modify and/or use that data. Functions are self-contained modules of code (enclosed within braces, {
and }
) that accomplish a certain task. The nice thing about using a function is that once a function is written, it can be used over and over again. Functions can be called from inside other functions:
void Start () { }
Start
is only called once in the lifetime of the behavior when the game starts and is typically used to initialize data.
Note
If you're used to other programming languages, you may be surprised that initialization of an object is not done using a constructor function. This is because the construction of objects is handled by the editor and does not take place at the start of gameplay as you might expect. If you attempt to define a constructor for a script component, it will interfere with the normal operation of Unity and can cause major problems with the project.
However, for this behavior, we will not need to use the Start
function. Perform the following steps:
- Delete the
Start
function and its contents.The next function that we see included is the
Update
function. Also inherited fromMonoBehaviour
, this function is called for every frame that the component exists in and for each object that it's attached to. We want to update our player ship's rotation and movement every turn. - Inside the
Update
function (between{
and}
), put the following lines of code:// Rotate player to face mouse Rotation(); // Move the player's body Movement();
Here, I called two functions, but these functions do not exist, because we haven't created them yet, which is why the text shows up as Red inside of MonoDevelop. Let's do that now!
- Below the
Update
function and before}
that closes the class at the end of the file, put the following function to close the class:// Will rotate the ship to face the mouse. void Rotation() { // We need to tell where the mouse is relative to the // player Vector3 worldPos = Input.mousePosition; worldPos = Camera.main.ScreenToWorldPoint(worldPos); /* * Get the differences from each axis (stands for * deltaX and deltaY) */ float dx = this.transform.position.x - worldPos.x; float dy = this.transform.position.y - worldPos.y; // Get the angle between the two objects float angle = Mathf.Atan2(dy, dx) * Mathf.Rad2Deg; /* * The transform's rotation property uses a Quaternion, * so we need to convert the angle in a Vector * (The Z axis is for rotation for 2D). */ Quaternion rot = Quaternion.Euler(new Vector3(0, 0, angle + 90)); // Assign the ship's rotation this.transform.rotation = rot; }
Now if you comment out the
Movement
line and run the game, you'll notice that the ship will rotate in the direction in which the mouse is. Have a look at the following screenshot: - Below the
Rotation
function, we now need to add in ourMovement
function the following code. Uncomment theMovement
function call if you commented it out earlier:// Will move the player based off of keys pressed void Movement() { // The movement that needs to occur this frame Vector3 movement = new Vector3(); // Check for input movement += MoveIfPressed(upButton, Vector3.up); movement += MoveIfPressed(downButton, Vector3.down); movement += MoveIfPressed(leftButton, Vector3.left); movement += MoveIfPressed(rightButton, Vector3.right); /* * If we pressed multiple buttons, make sure we're only * moving the same length. */ movement.Normalize (); // Check if we pressed anything if(movement.magnitude > 0) { // If we did, move in that direction currentSpeed = playerSpeed; this.transform.Translate(movement * Time.deltaTime * playerSpeed, Space.World); lastMovement = movement; } else { // Otherwise, move in the direction we were going this.transform.Translate(lastMovement * Time.deltaTime * currentSpeed, Space.World); // Slow down over time currentSpeed *= .9f; } }
Now inside this function I've created another function called
MoveIfPressed
, so we'll need to add that in as well. - Below this function, add in the following function as well:
/* * Will return the movement if any of the keys are pressed, * otherwise it will return (0,0,0) */ Vector3 MoveIfPressed( List<KeyCode> keyList, Vector3 Movement) { // Check each key in our list foreach (KeyCode element in keyList) { if(Input.GetKey(element)) { /* * It was pressed so we leave the function * with the movement applied. */ return Movement; } } // None of the keys were pressed, so don't need to move return Vector3.zero; }
- Now, save your file and move back into Unity. Save your current scene as
Chapter_1.unity
by going to File | Save Scene. Make sure to save the scene to ourScenes
folder we created earlier. - Run the game by pressing the play button. Have a look at the following screenshot:
Now you'll see that we can move using the arrow keys or the W A S D keys, and our ship will rotate to face the mouse. Great!