Retro Revolution: Building a Pong Clone in Unity

Vincent Quarles
Share

Before starting you can view the game at itch.io

Analyzing Pong

Pong was one of the first video games ever made and was the first successful commercial game. When Pong was first created the developers most likely struggled with the logic for the code, however, nowadays you can make a simple two player Pong with one method call, colliders, and sprites. Pong gets harder to create once the decision to make a one-player Pong is made. In this tutorial, we will create the base gameplay for Pong and break down a very simple AI alternative that still adds to gameplay value.

We must ask, what are the core elements of Pong gameplay? Here is a list with the answer to that question:

  1. Player Input – We want the player to be able to move their paddle up and down so that they can hit the ball.
  2. Ball Collision – When the ball hits the paddle or boundaries it can’t be allowed to lose any speed.
  3. Boundary Collision – The ball has to be able to bounce off of the top and bottom part of the screen so that it doesn’t leave the play area.
  4. Enemy AI – The gameplay value of the game would be next to zero if the enemy sat on the opposite end of the screen and didn’t move.
  5. Spawning the Ball – When the ball hits one of the boundaries behind the paddles we need it to respawn so that we can continue the game.
  6. Ball to Paddle Hit Area Detection – This allows for the ball to bounce off the paddle at unique angles so that we are able to to better aim the ball when it is hit with the paddle.

With this list we can beginning programming the game.

Note that any numbers used relating to a game object’s location, rotation, scale, etc are relative and may need to be changed for your specific setup.

Setting up the Game

Now that we’ve analyzed the fundamentals of Pong we can start setting up the game. Go ahead and open Unity and create a new 2D project. Once the editor opens, set the game screen’s aspect ratio to 4:3. We are using 4:3 since this is one of the most common screen ratios and is one of the closest ratios to being a standard. Inside of the Assets pane create four folders named Scripts, Sprites, Prefabs, and Materials. These folders will be used to hold all of our game assets.

Adding folders

Download the appropriate images for the game and add them to the Sprites folder (this can be done via drag and drop). The images we just added will be the sprites, interactive game objects, used in the game.

Gray circle sprite Blue square sprite Red square sprite

We are going to need to change the pixels per unit for the sprites so that they conform to a standard. I generally use 64 pixels per unit as this gives most sprites a crisp clean look, and keeps their relative size. You can think of pixels per unit as the density of pixels allocated in a 1×1 space in Unity’s editor.

Let’s set the squares’ pixels per unit to 64 and the circle’s pixels per unit can be set to 128. We can go on and add the three images to the hierarchy pane.

Hierarchy pane

Now we need to give each asset a name and set their initial properties and tags. You can name the blue block “Player” and set the player’s x-position to 6 and its x-scale to .2.

We are going to need to create a tag to separate the paddle game objects from other game objects. In a broad sense, you can think of Tags as categories for game objects. Click on “Untagged” (located under the player’s name) and select “Add Tag”. Create a new tag named “Paddle”, and re-select the player game object and set its tag to Paddle.

Name the red block “Enemy”. Set the enemy’s x-position to -6 and its x-scale to .2. Make the enemy game object’s tag Paddle.

Name the grey circle “Ball” and go on and create a new tag named “Ball”. Make sure to set the ball game object’s tag to Ball.

Adding Player Input

In the Assets pane, open the Scripts folder and create a new C# script named “PlayerController”. Open the PlayerController script in your IDE and type:

	//speed of player
	public float speed = 10;

	//bounds of player
	public float topBound = 4.5F;
	public float bottomBound = -4.5F;

	// Use this for initialization
	void Start () {
		Time.timeScale = 0;
	}

	void Update(){

		//pauses or plays game when player hits p
		if(Input.GetKeyDown(KeyCode.P) && Time.timeScale == 0){
			Time.timeScale = 1;
		} else if(Input.GetKeyDown(KeyCode.P) && Time.timeScale == 1){
			Time.timeScale = 0;
		}
	}

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

		//get player input and set speed
		float movementSpeedY = speed * Input.GetAxis("Vertical") * Time.deltaTime;
		transform.Translate(0, movementSpeedY, 0);

		//set bounds of player
		if(transform.position.y > topBound){
			transform.position = new Vector3(transform.position.x, topBound, 0);
		} else if(transform.position.y < bottomBound){
			transform.position = new Vector3(transform.position.x, bottomBound, 0);
		}
	}

Add the PlayerController script to the Player game object in the hierarchy pane by dragging the script onto the game object. Select the Player game object and add a Box Collider 2D to the game object by clicking on “Add Component” at the bottom of the Inspector pane.

Ball Collision

Open the Materials folder inside of the Assets pane. You can create a new Physics2D Material by right-clicking and selecting Create -> Physics2D Material (as in image below).

Creating a new Physics2D material

Go ahead and name the new material “Bounce”, and set its bounciness to 1 and its friction to 0.

Friction and bounciness settings

Select the Ball game object in the hierarchy pane. Add a Circle Collider 2D to the Ball game object. Drag the bounce material to the collider’s Material property in order to set the material to bounce. By giving the Ball game object the bounce material we make it so that when the Ball game object hits the boundaries it won’t lose any speed and will bounce off in the correct angle. We also need to add a Rigidbody 2D to the Ball game object and set gravity scale to 0, then check fixed angle.

Ball settings

Open the Scripts folder inside of the Assets pane and create a script named “BallController”. Open the BallController script inside of your IDE and type:

//speed of the ball
	public float speed = 3.5F;

	//the initial direction of the ball
	private Vector2 spawnDir;

	//ball's components
	Rigidbody2D rig2D;
	// Use this for initialization
	void Start () {
		//setting balls Rigidbody 2D
		rig2D = this.gameObject.GetComponent<Rigidbody2D>();

		//generating random number based on possible initial directions
		int rand = Random.Range(1,4);

		//setting initial direction
		if(rand == 1){
			spawnDir = new Vector2(1,1);
		} else if(rand == 2){
			spawnDir = new Vector2(1,-1);
		} else if(rand == 3){
			spawnDir = new Vector2(-1,-1);
		} else if(rand == 4){
			spawnDir = new Vector2(-1,1);
		}

		//moving ball in initial direction and adding speed
		rig2D.velocity = (spawnDir*speed);

	}

Boundary Collision

Inside of the hierarchy pane, create two empty game objects (right-click -> create empty), and name the new game objects “RightBound” and “TopBound”. Set the RightBound’s x-position to 7 and add a Box Collider 2D to the RightBound game object. In the Inspector pane check “Is Trigger” and set the collider’s y-size to 20. Duplicate the RightBound game object (select it and Command+D). Name the duplicate “LeftBound” and set its y-position to -7.

Next, select the TopBound game object and set its y-position to 5.5, and add a Box Collider 2D to the TopBound game object. Set the collider’s x-size to 20. Duplicate the TopBound game object and name the duplicate “BottomBound”. Set the BottomBound game object’s y-position to -5.5.

Pong wireframe

We want the left and right bounds to destroy the ball when the ball gets past one of the paddles. Inside of the Assets pane open the Scripts folder and create a new C# script named “BoundController”. Open BoundController inside of your favorite IDE and type:

void OnTriggerEnter2D(Collider2D other){

		//Destroys gameobject if its tag is Ball
		if(other.gameObject.tag == "Ball"){
			Destroy(other.gameObject);
		}
}

Add the BoundController script to the LeftBound game object and the RightBound game object.

Enemy AI

Select the Enemy game object and add a Box Collider 2D. Inside of the Assets pane open the Scripts folder. Create a new C# script named “EnemyController” and type:

//Speed of the enemy
	public float speed = 1.75F;

	//the ball
	Transform ball;

	//the ball's rigidbody 2D
	Rigidbody2D ballRig2D;

	//bounds of enemy
	public float topBound = 4.5F;
	public float bottomBound = -4.5F;

	// Use this for initialization
	void Start () {

	}

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

		//finding the ball
		ball = GameObject.FindGameObjectWithTag("Ball").transform;

		//setting the ball's rigidbody to a variable
		ballRig2D = ball.GetComponent<Rigidbody2D>();

		//checking x direction of the ball
		if(ballRig2D.velocity.x < 0){

			//checking y direction of ball
			if(ball.position.y < this.transform.position.y){
				//move ball down if lower than paddle
				transform.Translate(Vector3.down*speed*Time.deltaTime);
			} else if(ball.position.y > this.transform.position.y){
				//move ball up if higher than paddle
				transform.Translate(Vector3.up*speed*Time.deltaTime);
			}

		}

		//set bounds of enemy
		if(transform.position.y > topBound){
			transform.position = new Vector3(transform.position.x, topBound, 0);
		} else if(transform.position.y < bottomBound){
			transform.position = new Vector3(transform.position.x, bottomBound, 0);
		}
	}

Note: 1.75 is used for the enemy’s speed solely because it makes the enemy slow enough not to be able to get to every ball that comes its direction.

Another Note: We used fixed update because this method is better for movement since it updates the game object at a fixed rate.

Spawning the Ball

Create an empty game object inside of the hierarchy pane and name it “BallSpawner”. Next, add the Ball game object to the Prefabs folder inside of the Assets pane. We can delete the Ball game object inside of the hierarchy pane.

Open the Scripts folder inside of the Assets pane and create a new C# script named “BallSpawnerController”. Open BallSpawnerController inside of your IDE and type:

public GameObject ball;

	// Use this for initialization
	void Start () {
		GameObject ballClone;
		ballClone = Instantiate(ball, this.transform.position, this.transform.rotation) as GameObject;
		ballClone.transform.SetParent(this.transform);
	}

	// Update is called once per frame
	void Update () {
		if(transform.childCount == 0){
			GameObject ballClone;
			ballClone = Instantiate(ball, this.transform.position, this.transform.rotation) as GameObject;
			ballClone.transform.SetParent(this.transform);
		}
	}

Add the BallSpawnerController script to the BallSpawner game object. Now we can drag the Ball game object from the Prefabs folder into the Ball value of the BallSpawnerController.

enter image description here

Adding Basic Text

We need to add some text to the screen telling players to hit P to play and pause the game so that players know how to start and pause the game.

We can create a text game object (in the hierarchy pane right-click -> UI -> Text), and name the text game object PlayText. Next let’s set its Text value to “Hit P to play or pause” and center align the text. Now we can align the game object to the top of the screen and then anchor it. Let’s set its width to 200 so that all of the text is visible. Finally, set the text color to white.

Save the scene and add it to the build settings (by dragging or checking add current scene) and you’re done!

Conclusion

You have now officially created a basic one-player Pong clone in Unity2D. For more practice try to think of ways to update the game and make it better – add accelleration to the ball (the more hits, the faster the ball), add inertia to paddles, add difficulty levels by increasing the enemy speed, etc.

You can download the project on GitHub