Beginner’s Guide: Create a Pong Clone in Unity: Part 7

What’s the Score?

Our game is missing something vital…a way to know who is the best player…a way for one player to beat the other…a game has to have a winner! In this part we’ll add scoring to our game. Here’s how we’ll do it: 

  • Add goals
  • Add a game manager script to keep track of the scores
  • Display the current score on the screen.

Goals

Adding goals uses some of the techniques we’ve already covered, so we’re going to get a bit of practice using GameObjects, collisions, and a little scripting.

Create a Goal Prefab

  • Create an empty GameObject and name it ‘GoalPrefab’.
  • Add a BoxCollider2D component to GoalPrefab (remember, Add Component via the Inspector with the GameObject selected).
  • Create a new C# script, and call it ‘GoalScript’. Leave this script empty for now.
  • Add GoalSCript to GoalPrefab (remember, drag the script into the Inspector).
  • Add a SpriteRenderer to GoalPrefab via the Inspector.
  • Populate the SpriteRenderer with the same sprite we used for the paddles, but change the colour to differentiate the goal from the wall.
The Color property is on the SpriteRenderer component in the Inspector.

If you look closely in the Scene window you’ll see that the sprite is a different shape to the collider (the collider is the green square):


This is because we added the collider first (if you add the sprite first, the collider will automatically shape itself to the sprite).

To fix this:

  • Click the cog in the upper-right corner of the collider component in Inspector.
  • From the drop-down menu select Reset:


Resetting the collider sets it to match the sprite shape.

Finally, move the goal so it overlaps one of the walls and stretch its height (just change the transform Y scale to something around 2.2). Place it so it slightly over the edge of the wall (so the ball can hit the goal) like in the image below (my goal is green and on the left). Change the transform’s Y position to zero to ensure the goal is centred.

You will notice the goal is on top of the wall, covering it where they overlap. This is because of Unity’s drawing order layers. To make the goal draw under the wall, change the Order in Layer property on the Sprite Renderer to -1.


Despite its name, GoalPrefab is currently just a GameObject, so turn it into a prefab by dragging it into the Prefabs folder, then delete the original GameObject from the Hierarchy.

Now make two copies of GoalPrefab in the scene Hierarchy and place them both appropriately (they will default to the position of the original, so move one to the opposite side of the screen by reversing its transform’s X position like we did with the walls earlier, e.g. from -7.02 to 7.02). Rename the two goals to Player1Goal and Player2Goal. Create another empty GameObject called Goals and place both goals inside.

Game Manager

  • Create an empty GameObject in the Hierarchy and name it GameManager.
  • Create a new C# script in the Scripts folder and name it GameManagerScript.
  • Add GameManagerScript to GameManager.

 Edit GameManagerScript

Open GameManagerScript for editing. Start by adding these variables.

We can declare two variables of the same type in a single line by separating them with a comma.
int playerOneScore, playerTwoScore;

Set them to start at zero by adding these lines to the Start() method:

playerOneScore = 0;
playerTwoScore = 0;

Also add the following method:

public void GoalScored(int playerNumber)
{
    // increase the score for whichever player scored
    if(playerNumber == 1)
        playerOneScore++;
    else if (playerNumber ==2)
        playerTwoScore++;
    
    // now check if the player has won
    if(playerOneScore == 3)
        GameOver(1);
    else if (playerTwoScore ==3)
        GameOver(2);
    
} 

For the non-programmers I’ll explain that code. We made this method public so it can be called from other scripts, and we gave it a parameter playerNumber, which we will use to tell this method who scored (i.e. player 1 or player 2).

playerOneScore++ is a shorthand way of writing playerOneScore = playerOneScore + 1
(i.e. we are increasing the value by 1).

We will make our game a ‘best of 5’ (or ‘first to 3’) competition, so this is why we call a method called GameOver() when a player reaches a score of 3 (e.g. if (playerTwoScore ==3)).

We call GameOver() in the above method, so we need to create it (your script editor will probably mark those lines as errors at the moment). For now we’ll just restart the game, and we’ll add some more code to this later.

Firstly, add this variable so our script can communicate with the ball:

[SerializeField]
BallScript gameBall;

Add the following method to GameManagerScript:

void GameOver(int winner)
{
    // this is called when a player reaches 3 points 

    // reset the scores
    playerOneScore = 0;
    playerTwoScore = 0; 
    gameBall.Reset ();
}
That final line (gameBall.Reset();) will be flagged as an error until we create the Reset() method in BallScript.

Open BallScript and add this Reset() method:

public void Reset()
{
    // reset the ball position and restart the ball movement
    myBody.velocity = Vector2.zero;
    transform.position = new Vector2(0,0);
    Start(); // restart the ball 
}

This method halts the ball’s movement by setting its rigidbody’s velocity to Vector2.zero (meaning its velocity will be (0, 0) – no movement on either axis). Then the ball’s position is changed to (0, 0), which is the centre of the screen. Then finally, Start() is called, which sets the game off again.

We will need to add more here so the game doesn’t just suddenly restart, but for now we’ll leave it as it is.

Now we need to populate that gameBall variable in the GameManager script.

Make sure your scripts are saved or Unity might not realise you’ve fixed the errors pertaining to the missing methods you’ve just added.
  • Select GameManager in Hierarchy.
  • Drag Ball from the Hierarchy over to the empty Game Ball field in the Game Manager Script.

Unity is clever enough to infer that we need a component of type BallScript, so it finds the BallScript in the Ball GameObject and adds it to the field. We now have Ball‘s instance of BallScript referenced in GameManagerScript.

 

GoalScript

Our goal script will be quite simple. It will detect when the ball comes into contact with the goal and then tell GameManagerScript that a goal has been scored. We also need to differentiate between the two different goals so we know who scored.

Open GoalScript and add the following variables:

[SerializeField]
int attackingPlayer; // which player scores into this goal
[SerializeField]
GameManagerScript gameMan; // this will hold a reference to the game manager script

Next, add some behaviour to detect when the ball collides with the goal. This is very similar to the collision detection we did on the paddles:

void OnCollisionEnter2D(Collision2D other)
{
    if(other.transform.name == "Ball")
    {
        gameMan.GoalScored(attackingPlayer);
    }
} 

When the ball collides with the goal, GoalScored() is called in GameManagerScript, and we pass which player scored by using attackingPlayer
as a parameter.

Reference GameManagerScript in the Goals

  • Save GoalScript
  • Select Player1Goal in the Hierarchy
  • Drag GameManager from the Hierarchy into the Game Man slot in the Goal Script section of the Inspector.
  • Repeat the above steps for Player2Goal.

Now, set the correct player values in the Attacking Player Inspector fields for each goal. Note that the goal behind player 1 needs the value 2 because player 2 will be scoring points in that goal (and vice versa).
Save the scene and the project, then playtest the game. Though we can’t yet see the score, when a player gets to three points the game will reset.

Recap

We did a lot of scripting in Part 7, and by now you should be getting used to attaching scripts to GameObjects and using public/serialized fields to add components and values to a script instance (like when we added Attacking Player values to the goals). We also got some practice at communicating between scripts by referencing a script and calling its public methods. We now have scoring system for our game!

Continue to Part 8 where we will work on our game’s user interface by adding a score display and a menu screen, all using Unity great GUI system.

13 thoughts on “Beginner’s Guide: Create a Pong Clone in Unity: Part 7

  1. Pingback: Beginner’s Guide: Create a Pong Clone in Unity: Part 6 « Unity for All Unity for All

  2. Pingback: Beginner’s Guide: Create a Pong Clone in Unity: Part 8 « Unity for All Unity for All

  3. Pingback: Complete Unity Pong Clone Tutorial for Beginners « Unity for All Unity for All

  4. Pingback: Beginner’s Guide: Create a Pong Clone in Unity: Part 6 | Unity for All

  5. Some Guy

    So i downloaded the scripts from the completed project and when i try to add the script it says it has to be fixed.

    Reply
    1. Damien Post author

      That will be because Unity had made some changes since this tutorial was written. Unity should be able to automatically fix everything. There are a couple of very minor changes to the way certain things are scripted, but none of the fundamentals have changed. I plan to update this tutorial series when the next major Unity update (5.4) is released.

      Reply
  6. Gemayel

    Hi Damien,
    I seem to be getting errors using your if statement for GoalScored so l can not process further. I have even cut and pasted it into the GameManager script but it has not worked. I am using Unity 5.5 1f1 personal. Unity is showing the following:
    1. Assets/Assets/Scripts/GameManager.cs(16,1): error CS1525: Unexpected symbol `public’
    2. Assets/Assets/Scripts/GameManager.cs(16,9): error CS1547: Keyword `void’ cannot be used in this context
    3. Assets/Assets/Scripts/GameManager.cs(16,23): error CS1525: Unexpected symbol `(‘
    4. Assets/Assets/Scripts/GameManager.cs(21,3): error CS1525: Unexpected symbol `else’
    5. Assets/Assets/Scripts/GameManager.cs(49,1): error CS1525: Unexpected symbol `void’

    Reply
    1. Damien Post author

      For errors like that there is most likely something missing, like a bracket or semi-colon.

      Double-check your script and make sure all brackets are correctly paired. You may have pasted over a bracket or semi-colon, or pasted in slightly the wrong place.

      Find the ‘public’ on line 16 – there is probably something missing just before that – a closing bracket for the previous method probably. It’s also possible that a semi-colon is missing from the end of a line.

      You could also download the finished project and check that part of the code.

      If you’re still stuck, paste your whole GameManager script, and I’ll see if I can find the issue.

      Reply
      1. Gemayel

        using System.Collections;
        using System.Collections.Generic;
        using UnityEngine;

        public class GameManager : MonoBehaviour {

        // Use this for initialization
        void Start () {

        int playerOneScore, playerTwoScore;

        playerOneScore = 0;
        playerTwoScore = 0;

        public void GoalScored(int playerNumber)
        {
        // increase the score for whichever player scored
        if(playerNumber == 1)
        playerOneScore++;
        else if (playerNumber ==2)
        playerTwoScore++;

        // now check if the player has won
        if(playerOneScore == 3)
        GameOver(1);
        else if (playerTwoScore ==3)
        GameOver(2);

        }

        [SerializeField]
        BallScript gameBall;

        void GameOver(int winner)
        {
        // this is called when a player reaches 3 points

        // reset the scores
        playerOneScore = 0;
        playerTwoScore = 0;
        gameBall.Reset ();
        }
        }

        // Update is called once per frame
        void Update () {

        }
        }

        Reply
        1. Damien Post author

          There is s close curly bracket ( ‘}’) missing at the end of the Start() method.

          Place a closing curly bracket after playerTwoScore = 0;

          Reply
  7. lily

    Hi Damien,
    Thank you so much for making this best tutorial ever , I followed your direction make my fist game,so far so good!
    I have one problem that is on part 7
    “Select GameManager in Hierarchy.
    Drag Ball from the Hierarchy over to the empty Game Ball field in the Game Manager Script.”
    I did not find “empty Game Ball field in the Game Manager Script”, so I stuck here , please help me.
    lily

    Reply
    1. Damien Post author

      Do you have this:

      [SerializeField]
      BallScript gameBall;

      In your GameManager script? If you don’t have the [SerializeField] part, the field will not be visible in the Inspector.

      Another possibility is that you have that in your script but Unity is unable to compile the script due to an error elsewhere. Make sure there is no error in the Console window.

      Reply

Leave a Reply

Your email address will not be published.