Yorkville High School Computer Science Department
Yorkville High School Computer Science Department on Facebook  Yorkville High School Computer Science Department Twitter Feed  Yorkville High School Computer Science Department on Instagram

Yorkville High School Computer Science

ASSIGNMENTS: No Current Assignments

Computer Programming II :: Lessons :: Unity Collisions

Rigidbody 2D

Rigidbodies

The ball you added in the last lesson doesn't move anywhere. Although a Unity game has built-in gravity, objects are not affected by physics unless they have a Rigidbody component attached.

  1. Open your Brick Breaker project in Unity.
  2. Select the ball object in the Level_01 scene.
  3. Click on Add Component>Physics 2D>Rigidbody 2D.

The ball should now fall when you start your game.

Colliders

The ball falls off the screen, but we want it to send the game to the Lose scene when that happens. We will need to create a collider to do this. A collider is an invisible component used to determine physical collisions that is typically, but not always, the same shape as the object.

Unity Circle Colliders
  1. Select the ball object.
  2. Click on Add Component>Physics 2D>Circle Collider 2D.

The circle collider is automatically aligned to the size of the ball. You can change the radius of the ball from the Inspector, but that isn't necessary unless you change the size of the ball itself. We need a second collider in order to determine if a collision has occurred so we will need to create an empty object to serve as the bottom of the screen.

  1. Go to Game Object>Create Empty.
  2. Call the empty object "Bottom Wall."
  3. Click on Add Component>Physics 2D>Box Collider 2D.
  4. Change the x value of the collider's size to "16" since it should take up 16 world units.
  5. Move the collider to the bottom of the game screen.

Collider Interactions

There are two different types of messages that can be passed in regards to collisions: a collision or a trigger. With a collision the physics engine will still respond while a trigger will simply send a message. These messages can be intercepted in a script to cause something to happen. Below are the different ways a collision or trigger message will be passed.

Collider Interactions in Unity

There are three types of colliders in Unity:

The ball is a Rigidbody collider while the bottom wall is a static collider. That means we will trigger a collision message when the ball hits the wall. The following methods are used to capture collisions:

void OnCollisionEnter2D(Collision2D collision)

void OnTriggerEnter2D(Collider2D collider)
  1. Select the bottom wall.
  2. Click on Add Component>New Script.
  3. Call the new script "BottomWallCollider."
  4. If you haven't already, copy the LevelManager object from the Start scene to your Level_01 scene.
  5. Open the new script and add the following code to it:
public class BottomWallCollider : MonoBehaviour {
    public LevelManager levelManager;
    
    public void OnCollisionEnter2D(Collision2D collision)
    {
    	levelManager.LoadLevel("Lose");
    }
}
  1. Go back to Unity and drag the LevelManager OBJECT to levelManager in the Inspector.

Your game should now end when the ball falls off the screen. Now let's add the paddle to the game so the ball has something to stop it.

  1. Add the brick sprite to your game and rename the object "Paddle."
  2. Set the Pixels Per Unit to "128" so the paddle takes up a single world unit in width.
  3. Add a Rigidbody 2D to the Paddle.
  4. Add a 2D Box Collider to the Paddle.
  5. Change the body type to "Kinematic" so the paddle will not be affected by gravity.

Bouncing Collisions

The next step is to get the ball to bounce. We will have the ball start on the paddle and start when the mouse button is clicked. We will need to use a Physics Material for this, which is used to adjust friction and bouncing effects for colliding objects.

Physics Material in Unity
  1. Create a new folder in your Assets called "Materials."
  2. In the new folder, go to Assets>Create>Physics2DMaterial.
  3. Call the new material "ball."
  4. Set the Friction of the material to "0" and the Bounciness to "1."
  5. Select your Ball object and drag the new material to the "Material" section of the Circle Collider.

The bounciness of a material is a value between 0 and 1. A value of 0 means no energy is conserved in a collision (meaning the object with the material will stop) while a value of 1 means there is no energy loss so the object with the material will bounce infinitely. To get to ball to bounce to half its height so need to use the coefficient of restitution formula to take the square root of the ratio between the bounce height and drop height. The square root of 0.5 gives us a value of .707.

Mouse Movement

We want the paddle to move left and right with the mouse and launch the ball when the mouse button is clicked.

  1. Add a new script component to the Paddle object and call it "Paddle."
  2. Add the following code to the Update method of this new script:
void Update() {
    Vector3 paddlePos = new Vector3(0.5f, this.transform.position.y, 0f);
    float mousePosInBlocks = Input.mousePosition.x / Screen.width * 16;
    
    paddlePos.x = Mathf.Clamp(mousePosInBlocks, 0.5f, 15.5f);
    this.transform.position = paddlePos;
}

The paddlePos variable stores the location of the paddle. This needs to be a 3D position vector because Unity stores all locations in three dimensions. The x value is 0.5 to set the paddle to the left of the game space, the y value is the current y value of the paddle, and the x value is 0.

The mousePosInBlocks stores the position of the mouse in terms of world units. The x location of the mouse is found using Input.mousePosition.x and it is divided by the width of the screen to find the current mouse position. Multiplying by 16 world units will find the current block containing the mouse cursor.

Ball on Paddle

The Mathf.Clamp method sets the x position of the paddle and makes sure it is between 0.5 and 15.5. These are the points where the left or right side of the paddle would be hitting the edge of the game space.

Finally, the last line sets the current position of the paddle to the paddlePos variable.

  1. Move the ball so it is touching the top of the paddle to start.
  2. Add a new script component to the Ball object and call it "Ball."
  3. Add a public Paddle variable to the top of the new script like so:
public class Ball : MonoBehaviour {
   public Paddle paddle;
   
   void Start () {
   
   }
   
   void Update () {
   
   }
}

Making a variable public means it is accessible in the Unity editor. We need the ball to be able to reference the paddle object so we need to create this Paddle variable.

  1. Select the Ball object in the Unity editor. Drag the Paddle object to the "paddle" section of the Inspector.
  2. Modify the Ball script to look like the following:
public class Ball : MonoBehaviour {
   public Paddle paddle;
   Vector3 paddleToBallVector;
   bool hasStarted = false;
   
   void Start () {
	paddleToBallVector = this.transform.position - paddle.transform.position;
   }
   
   void Update () {
	if (!hasStarted) {
        	this.transform.position = paddle.transform.position + paddleToBallVector;
            
            	if (Input.GetMouseButtonDown(0)) {
            		hasStarted = true;
                	this.gameObject.GetComponent<Rigidbody2D>().velocity = new Vector2(2f, 10f);
            	}
	}
   }
}	

The paddleToBallVector variable is the distance between the ball and the paddle before the ball starts moving. In the Update method, this is used to make sure the ball is always in that same position relative to the paddle if that game has not started.

The Input.GetMouseButtonDown call should make sense. The "0" represents the left mouse button. You can use "1" for the right button or "2" for the middle button. When the button is pressed down the hasStarted boolean is set to true so the code does not run again. The velocity vector of the ball is also set so the ball will start moving away from the paddle.

One problem that may occur before you press the mouse button is the ball may get disconnected from the paddle. This is because of the order the scripts are executed. You can change the script execution order by following the steps below.

Unity Script Execution Order
  1. Go to Edit>Project Settings>Script Execution Order.
  2. Hit the + Button and choose the Paddle.
  3. Hit the + Button again and choose the Ball.

The paddle script needs to execute before the ball since the ball will be moved to the paddle. You can drag the paddle script above the ball script if you need to.

Gravity Scale

The next part of this lesson is to get the ball to bounce off the sides and the top of the game space and move fast enough so it reaches the top wall. We will organize the Hierarchy a little bit to help with these final steps.

Hierarchy Management in Unity
  1. Create a new empty GameObject and call it "Game Space."
  2. Drag the "Background" object into the "Game Space" object to make the background a child of the Game Space.
  3. Create three more empty GameObjects for the left wall, right wall, and top wall and make them children of the Game Space.
  4. Set the Y value of the left wall's size to 12 (the height of the game space).
  5. Set the center of the left wall to (-0.5, 6) to move the gizmo to the lower-right of the wall.
  6. Set the position of the left wall to (0, 0, 0).
  7. Set up the top and right walls in a similar manner.
  8. Add a box collider to all three walls.

The final step is to get the ball moving faster. We could do this by changing the gravity scale of the ball, but then the ball will be affected less by gravity than other objects in the game. If you want to change the gravity for everything in the game there is a global gravity scale you can change.

  1. Go to Edit>Project Settings>Physics 2D.
  2. Change the Y value of the Gravity Scale to "-1" so the objects in the game are less affected by gravity.

Prefabs

A Unity prefab is a predefined GameObject with components that can be reused within a game. We can use a prefab to construct the bricks that we need to break to finish our game.

  1. Create a new Assets folder called "Prefabs."
  2. Select the Paddle in your Hierarchy and go to Edit>Duplicate.
  3. Rename the new paddle "Brick".
  4. Drag the brick to the "Prefabs" folder to create a prefab of it.

Prefabs in UnityWhen you drag the brick to the Prefabs folder you may notice the GameObject text changes to blue. This is to indicate that the object is based off of a prefab. You can change a GameObject so it is different from a prefab, but there is a new series of buttons in the Inspector for a prefab GameObject.

We can use the "Apply" button to make changes to the brick and apply it to the prefab. There are a few things we need to change because the brick is a little different from the paddle. You can also make changes directly to the prefab, but those changes are automatically applied to all of the objects based off the prefab.

  1. Remove the paddle script component from the brick.
  2. Remove the Rigidbody since the bricks will not move.
  3. Change the color of the brick to differentiate it from the paddle.
  4. Click the "Apply" button to apply these changes to the prefab.

You are going to have a lot of bricks in the game so it makes sense to have an organizing GameObject like the "Game Space" object. An organizing GameObject should always have a position of (0, 0, 0) so the objects within it line up with the play area.

  1. Create an empty GameObject and call it "Bricks."
  2. Set the position of the GameObject to (0, 0, 0).
  3. Make your brick GameObject a child of "Bricks."

Now we need to create a script to keep track of how many times a brick has been hit. When a brick has been hit a certain number of times it will be removed from the game.

  1. Select the brick PREFAB.
  2. Create a new script component for the brick called "Brick."
  3. Add the following code to the script:
public class Brick : MonoBehaviour {
    public int maxHits;
    int timesHit;
    
    void Start () {
    	timesHit = 0;
    }
    
    void Update () {
    	
    }
    
    void OnCollisionEnter2D(Collision2D ball)
    {
    	timesHit++;
    }
}

Since the maxHits variable is public we can change it from the editor. The rest of the code should be fairly self-explanatory. In order for the bricks to have a different number of hits, though, they need to be based off of different prefabs.

  1. Drag the Brick GameObject to the Prefabs folder to create two new prefabs and rename the prefabs "1-Hit Brick," "2-Hit Brick," and "3-Hit Brick."
  2. Drag the new prefabs into the scene.
  3. Change the maxHits variable for each prefab to match its name.
  4. Change the colors of the different prefabs.

Snap to Grid

You may have tried to move the bricks around and discovered it is hard to place them without overlapping bricks. You can fix this using Unity's snap to grid settings so objects will snap to your preset grid coordinates.

Unity Snap Settings
  1. Go to Edit>Snap Settings...
  2. Change the Move X value to "0.5" and the Move Y value to "0.32."

The settings of "0.5" and "0.32" mean objects will move half a world unit when dragged left and right and 1/3 of a world unit when dragged up and down. However, this only works while you are holding down the Ctrl key. The Snap All Axes button in the window will snap any selected object to the closest axes. You can select all of your bricks to do this all at once.

Counting and Deleting Objects

The final thing we want to do for Brick Breaker is destroy bricks and win the game. When bricks have been hit a certain number of times they should be destroyed. After all of the bricks are destroyed the player wins the game.

  1. Open the brick script.
  2. Add the following code in red to the OnCollisionEnter2D method:
void OnCollisionEnter2D (Collision2D collision) {
    timesHit++;
	
    if (timesHit >= maxHits)
    	Destroy(gameObject);
}

The bricks should now disappear once they have been hit the correct number of times. Now we need a variable that can be used to count the bricks to see when they have all been destroyed.

  1. Add the following code in red to the brick script:
public class Brick : MonoBehaviour {
    public int maxHits;
    int timesHit;
    public static int brickCount = 0;
    
    void Start () {
    	timesHit = 0;
        brickCount++;
    }
    
    void Update () {
    	
    }
    
    void OnCollisionEnter2D(Collision2D ball)
    {
    	timesHit++;
        if (timesHit >= maxHits)
        {
		brickCount--;
		Destroy(gameObject);
	}
    }
}

A static variable will persist across all instances of the Brick class so every brick will share the same variable. We increase the variable by one when a brick is first created and decrease it by one right before a brick is destroyed. Now we will create a method in the LevelManager script to see if all of the bricks have been destroyed.

  1. Open the LevelManager script.
  2. Add the following method to the script:
public void BrickDestroyed()
{
    if (Brick.brickCount <= 0)
        LoadLevel("Win");
}

A public static variable is available to any class by typing the name of the class that contains the static variable. Our last step is to call this new method from the brick script.

  1. Open the brick script.
  2. Add a public variable called "levelManager."
  3. Go to the editor and drag your LevelManager OBJECT to the levelManager variable.
  4. Add the following code in red to the OnCollisionEnter2D method:
void OnCollisionEnter2D(Collision2D ball)
{
	timesHit++;
	if (timesHit >= maxHits)
	{
		brickCount--;
        	levelManager.BrickDestroyed();
		Destroy(gameObject);
	}
}
Yorkville High School Computer Science Department on Facebook Yorkville High School Computer Science Department Twitter Feed Yorkville High School Computer Science Department on Instagram