Unity Game Development Blueprints
上QQ阅读APP看书,第一时间看更新

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:

  1. 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.
  2. 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:

  1. Delete the Start function and its contents.

    The next function that we see included is the Update function. Also inherited from MonoBehaviour, 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.

  2. 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!

  3. 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:

  4. Below the Rotation function, we now need to add in our Movement function the following code. Uncomment the Movement 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.

  5. 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;
    }
  6. 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 our Scenes folder we created earlier.
  7. 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!