Introduction

In this post series I'll write about how to program a simple game in Unity, along with some explanations about game development and programming in Unity in general. If you want to learn the basics for making your first game or if you already work in other areas of game development (e.g. concept art, 3D modelling, animation) and want to know more about game programming, I hope this series will be useful for you.

Three main topics will be explored:

  • Understanding the concept of game loop and how it applies to Unity;
  • An overview of the most used script type in Unity, the MonoBehaviour;
  • Implementation of a simple but funcional version of the game Breakout.

For this series, you'll need:

  • A basic understanding of programming: variables, loops, functions and a bit about classes
  • A recent version of Unity, any 2019 version should suffice;
  • A code editor; I'll be using Visual Studio Community 2019, but if you want a lighter approach I'd recommend Visual Studio Code with the C# plugin.

This is part 2; let's talk about how to begin implementing a version of the game Breakout. Click here for part 1.


Initial setup

Create an 2D project and place it somewhere. Then, configure the camera as orthographic with size 10 and set the game view to 1024x768 for a nice 4:3 proportion for the game.

Orthographic camera size on the left, 4:3 proportion (1024x768) on the right

As said before, we'll implement a version of the game Breakout, which will help you understand about player input, collision handling and moving things every frame.

Now, let's begin with the player controllable entity: the paddle.

Implementing the Paddle

I'll be using a Breakout sprite set from here, but you're free to use whatever sprite you want or just to create a default sprite inside Unity itself. Select a suitable paddle sprite and drag it into the Scene view, automatically creating a GameObject with the SpriteRenderer component, and set its X position to zero and Y to -8. Let's call it Player, as this is the entity that the player will control in the game.

Now let's add a script to the Player GameObject called PlayerController: with the Player selected on the Hierarchy, click on Add Component and write PlayerController, click in New Script and confirm the name.

Double click on the newly created script to open in the editor and then get rid of the comments, the first two "using" statements and the Start() function; we won't need any of them here. This is what you should be seeing:

using UnityEngine;

public class PlayerController : MonoBehaviour
{
    void Update()
    {
        
    }
}

The player will be able to move the paddle left and right, and that's what we'll implement on the Update() function. Simple, right? (famous last words)

On the script we have to detect when the player pressed the left or right keys to move the paddle left or right, and to stop moving when the player releases them. Also, we still don't have a speed for the paddle movement, which will be specified via a public variable, let's set it to 5 in the inspector meaning in our context "move 5 units per second".

The first implementation would be something like this:

using UnityEngine;

public class PlayerController : MonoBehaviour
{
    public float moveSpeed;
    
    void Update()
    {
        if(Input.GetKeyDown(KeyCode.LeftArrow)) // 1
        {
            Vector3 newPosition = transform.position; // 2 
            newPosition.x += moveSpeed; // 3
            transform.position = newPosition; // 4
        }
        // Omitting right movement for now
    }
}
First attempt for paddle movement

Let's break down this code:

  1. The Input.GetKeyDown() function receives a key code and will return true in the frame that this key is pressed;
  2. Store the current position in the newPosition variable;
  3. Update the X component with the move speed;
  4. Apply this updated position to the object.

This is what the component will look like in the inspector, fill the Move Speed with the value 5 and run the project.

So, it kinda works, but why the paddle moves at the speed of sound? Two things are happening here:

  • The Update() function is running 60 times per second;
  • The speed, as said before, is set to 5 units per second.

The result is that we're adding the full speed multiple times per second, which makes the paddle move much faster than intended! In other words, we're disregarding the frame duration when moving the paddle, and we'll now account for that.

For solving this, we need a crucial information: how much time has passed since the last frame? Fortunately, Unity has the Time.deltaTime variable, which is updated at each frame and gives us exactly what we need. Let's fix our code:

void Update()
{
    if(Input.GetKeyDown(KeyCode.LeftArrow))
    {
        Vector3 newPosition = transform.position; 
        newPosition.x += moveSpeed * Time.deltaTime;
        transform.position = newPosition;
    }
}
Paddle movement taking frame duration into account

Multiplying moveSpeed with Time.deltaTime will give the correct displacement to be applied on this frame, so that the end result is that if you keep pressing the left arrow the paddle will effectively move 5 units in a given second.

Considering movement to the right, this is what the script should look like now:

using UnityEngine;

public class PlayerController : MonoBehaviour
{
    public float moveSpeed;
    
    void Update()
    {
        if(Input.GetKeyDown(KeyCode.LeftArrow))
        {
            Vector3 newPosition = transform.position;
            newPosition.x += moveSpeed * Time.deltaTime;
            transform.position = newPosition;
        }
        else if(Input.GetKeyDown(KeyCode.RightArrow))
        {
            Vector3 newPosition = transform.position;
            newPosition.x -= moveSpeed * Time.deltaTime;
            transform.position = newPosition;
        }
    }
}

Notice that for moving to the right we just have to subtract the displacement, instead of adding it.

At this point you can freely move the paddle, and also set a different move speed if you desire. However, if you go too far in each direction, the paddle will happily go off the screen! To fix this, you'll need something to limit its movement so it stops on certain thresholds before exiting either side of the screen. In other words, the paddle X coordinate must be contained inside a numeric range with both thresholds.

First, add two new public variables indicating the left and right screen limits for the paddle, in the same way that moveSpeed was added, and then implement the threshold: the X coordinate won't be able to go beyond those values. The code should look like this:

[omitting class declaration and braces]

public float moveSpeed;
public float leftScreenLimit;
public float rightScreenLimit;

void Update()
{
    if(Input.GetKeyDown(KeyCode.LeftArrow))
    {
        Vector3 newPosition = transform.position;
        newPosition.x += moveSpeed * Time.deltaTime;
        if(newPosition.x < leftScreenLimit) // 3
        {
            transform.position = newPosition; // 2
        }
    }
    else if(Input.GetKeyDown(KeyCode.RightArrow))
    {
        Vector3 newPosition = transform.position;
        newPosition.x -= moveSpeed * Time.deltaTime;
        if(newPosition.x > rightScreenLimit) // 1
        {
            transform.position = newPosition; // 2
        }
    }
}

Let's break it down:

  1. When moving right, is X coordinate greater than the limit?
  2. If the condition is satisfied, effectively change the position of the paddle
  3. This is also checking X against the limit, but notice that is checks if it's "less than" instead of "greater than" - we'll speak about this in a bit

The reason the left limit check is a "less than" is because if you move an object to the left its X coordinate gets smaller and smaller, until becoming negative; this check guarantees that it will not be lower than the lower value possible, enforcing the constraint.

With the code in place, choose two values for the left and right screen limits - you can just move the paddle and copy the X coordinate where you want it to stop on both sides - and insert into the component. Finally, we have a functional paddle!

You can certainly try, but you can never really leave

That's it for this post. In Part 3 we'll add a ball and bricks you can break!