Breakout-3 renders bricks onto the screen. It implements bouncing behavior for the ball upon a collision with a brick. It also fixes our previous naive implementation of bouncing behavior between the ball and the paddle.
Important Algorithms
We want the player to control the trajectory of the ball based on where it hits the paddle. If the ball hits the center of the paddle, the ball should slow down and have a steeper angle. If the ball hits more towards the edges of the paddle, the ball should speed up and have a shallower angle. The code for this is explained in the next section.
For brick collision, we must check which edge of the ball is not inside the brick:
if left edge of ball is outside brick and dx is positive: trigger left-side collisionelse if right edge of ball is outside brick and dx is negative: trigger right-side collisionelse if top edge of ball is outside brick: trigger top-side collisionelse trigger bottom-side collisionThis is a fairly simple collision algorithm, so it is not the most accurate, particularly when faced with corner-cases, but it works 99% of the time. For a more robust solution, check out this alternative method.
Important Code
-
In
SpriteManager.jsyou’ll notice that we’ve added another method,generateBallSprites(), to extract the brick sprites from the sprite sheet. -
Brick.jscreates our brick class:constructor()initializes a brick. Importantly, we include aninPlayflag to serve as a signal for whether a brick is still in play or if it should disappear from the screen. In the context of this Breakout game, this is an effective shortcut, but do note that in larger programs, it would be better practice to free memory that is not being used instead of just hiding it from view.hit()hides a brick by toggling theinPlayflag tofalse.render()renders a brick to the screen.
-
PlayState.jsreferences a new class,LevelMaker.js, which encapsulates all the logic for generating new levels (i.e. different layouts for the bricks). It also checks for collisions between the ball and the bricks, hiding bricks as needed, and renders the “in play” bricks to the screen. -
To create a level,
LevelMaker.jsrandomly generates an array of bricks that can be rendered to the screen. Read through theLevelMaker.createMap()function carefully. -
In
Ball.js, we’ve added anhandlePaddleCollision()function to reflect the collision algorithms mentioned above:Ball.js const paddleBallDistance = paddle.x + paddle.width / 2 - this.x;const scaleFactor = 8;const minimumVelocity = 50;if (this.x < paddle.x + paddle.width / 2 && paddle.dx < 0) {this.dx = -minimumVelocity + -(scaleFactor * paddleBallDistance);} else if (this.x > paddle.x + paddle.width / 2 && paddle.dx > 0) {this.dx = minimumVelocity + scaleFactor * Math.abs(paddleBallDistance);}- First, we calculate the distance between the paddle and the ball to scale the ball’s
dx. scaleFactorandminimumVelocityare values that are used to control the ball’sdxdepending on where it hit the paddle.- Then perform this operation on either side of the paddle based on the paddle’s
dx. If on the right side, the differential will be negative, so we need to callMath.abs()to make it positive, then scale it by a positive amount sodxbecomes positive.
- First, we calculate the distance between the paddle and the ball to scale the ball’s
-
And below that, we’ve added a
handleBrickCollision()function based on the pseudocode in the section above. We slightly increase the ball’s velocity after a collision:Ball.js if (this.x < brick.x && this.dx > 0) {this.dx = -this.dx;} else if (this.x + this.width > brick.x + brick.width && this.dx < 0) {this.dx = -this.dx;} else if (this.y < brick.y) {this.dy = -this.dy;} else {this.dy = -this.dy;}this.dy *= 1.02;