Bounce

Bounce

START
1
Boing!
  • 2.1.1 Add a ring entity and get it moving to the right
  • 2.1.2 Detect collision with the right wall
  • 2.1.3 Move the ring out of the wall and update velocity to bounce

Add a new orange ring entity to the scene. Make it move to the right at a constant speed. When it reaches the right edge of the screen, make it bounce back to the left.

Important! The rigidbody component is the hook into Unity's built-in physics engine. We are not using that in this assignment (doing it ourselves one time), so there should be no rigidbody components in your project.

Collision detection and response

Collision detection is the process of determining if any elements in the game are touching each other. In this simple case, we just want to know if the ring is touching the right wall, which is a very simple calculation for a circle and a vertical line.

During the update when the circle touches the wall, collision response involves two actions:

  1. Move the ring so that it is not overlapping with the wall. Because movement is not continuous, you'll never have an update where the ring is exactly touching the wall like it would in real life. Instead, you detect when it's gone "into" the wall, and correct that by nudging it back out (update transform.position).
  2. Update the velocity. In a perfectly elastic collision where the incoming velocity is perpendicular to the surface, you just flip it the other way.

To do all this, you need to know the position of the wall, the position of the ring, and the size (radius) of the ring sprite.

Relevant Unity elements

Script component: Add a new MonoBehaviour script component called Bounce to the entity that controls its movement by setting position on each Update. Movement should be controlled by a velocity vector which you integrate over time to get the new position. As we saw in the last assignment, velocity can be updated a number of ways, including based on user input. In this case, you're just setting it to the right at some constant speed.

Camera.main.ViewportToWorldPoint: This Unity method converts viewport coordinates to world coordinates. The viewport uses normalized coordinates (0, 0) to (1, 1). Converting (1, 1) will tell you the world coordinates of the top right corner of the visible game area.

GetComponent<SpriteRenderer>(): This Unity method is available to every MonoBehaviour to access the SpriteRenderer component on your game object. The SpriteRenderer has a property sprite.bounds.extents that gives you the half-width and half-height of the sprite, useful for collision detection calculations.

2
I'm the man in the box
  • 2.2.1 Random starting direction, fixed speed
  • 2.2.2 Complete collision detection for all four walls

Instead of hardcoding the starting direction, generate a random direction vector each time the game starts. Extend your single-wall collision detection and response to check all four walls. There's nothing fancy about that, you just check all four.

You will need to update your collision response to deal with collisions that are not exactly perpendicular to the wall. Fortunately, this is simple with horizontal and vertical walls (called axis-aligned). A surface can only extert force in the perpendicular direction, so a vertical wall can only change the x component of the velocity, and a horizontal wall can only change the y component. On collision, you flip the affected component (x or y) and keep the other one the same.

Relevant Unity elements

Random.Range: Unity's built-in method for generating random numbers. Use Random.Range(-1f, 1f) to create random x and y velocity components.

Vector2.normalized: Vector normalization maintains the direction but changes the magnitude to be 1.0f. Apply this after creating a random vector to get a unit vector (length 1.0f) in a random direction. That direction vector can then be multiplied by a constant speed.

Save Point
SAVE POINT
commit your work
🪙 5
3
A little help from my friends
  • 2.3 Add two more bouncing rings to the scene

All code should generlize over data. When you have multiple entities that behave the same way, the same script should work for all of them. Components are classes. When the game starts and an entity is created, it instantiates a new object for each component class attached to it. Like any object, they have their own private copies of all their internal variables.

In this case, we want to create three rings, each with the same script component, moving in different directions. This demonstrates a common game design pattern that Unity uses: component-based design. The same script component can be attached to multiple game objects, and each will maintain its own state (position, velocity, etc.) while sharing the same behavior logic.

Relevant Unity elements

Prefabs: Instead of manually creating three entities and attaching the script to each, use Unity's template system for creating reusable game objects. Create a prefab by dragging your configured circle (with sprite and script) from the scene into the Project window. You can then drag the prefab back into the scene to create identical copies, each with their own independent behavior. Prefab elements in the scene are linked back to the prefab so you can make changes to them all at once.

4
Ninja!
  • 2.4.1 Disable and parent the rings to a RingPool outside the camera view
  • 2.4.2 Create a Spawner script and a timer
  • 2.4.3 Spawn a ring every 3 seconds at a random location

Make the three rings appear at different times in random places. To do this, we're going to start them off-screen with updating disabled. Every 3 seconds, activate a ring by moving it into the visible area and enabling update.

This is a common technique called pooling. We could dynamically create each ring when it is needed, but creating and destroying objects is expensive and can lead to uneven performance during gameplay (i.e. lagging). Most games use loading time to pre-create entities and move them in and out as needed.

Relevant Unity elements

Parenting: In the scene hierarchy on the right, entities can be parented to each other (drag and drop) creating a tree. This is useful for positioning and movement relative to your parent, for keeping track of a set of entities (the children of a certain entity), and for keeping the hierarchy neat. Create an empty entity called RingPool, position it offscreen, and parent your three rings to it. Notice that they don't move in the scene when parented. Instead, their transform.position changes relative to the location of their parent. (Try move the pool around and see.) Set all their starting positions to (0,0,0) and they'll move to the parent.

MonoBehaviour.enabled: You can enable/disable a script using the checkbox in the inspector pane in the Unity editor. Programmatically, you GetComponent<Bounce> to get the script and set its .enabled to True or False. Disabling means that Update doesn't run. You can also enable/disable the entire entity using SetActive.

Note: enabling a script does not run Start, it runs only once which is fine in this case. There are other methods to override such as OnEnable if you need to run something on every enable. See documentation here.

Timer: Timers are ubiquitous in games. It's just a variable that you update using Time.deltaTime every frame until it reaches a value. You can count up or down. Add a Spawner script component to your RingPool and set up a timer to print a debug message every 3 seconds.

Transform.childCount / Transform.GetChild: Parenting is actually done through the Transform component rather than the entity itself. These properties and methods let you access and manage the list of children through your transform. When your timer goes off, you can check if you have any more child rings and if so, activate the next one.

Transform.parent: You can programmatically change the parent of a Transform by setting it to another Transform or to null (no parent). e.g. transform.parent = null; or transform.parent = someEntity.transform;.

Recap

To spawn the next ring from the Spawner script on RingPool:

  1. Get it from the list of children
  2. Unparent it
  3. Change its position to be in a random spot in the gameplay area
  4. Enable the Bounce script component
Save Point
SAVE POINT
commit your work