© Lee Stemkoski 2018
Lee StemkoskiJava Game Development with LibGDXhttps://doi.org/10.1007/978-1-4842-3324-5_8

8. Bouncing and Collision Games

Lee Stemkoski
(1)
DEPT OF MATH & CS, ADELPHI UNIVERSITY DEPT OF MATH & CS, Garden City, New York, USA
 
In this chapter, you will create a ball-bouncing, brick-breaking game called Rectangle Destroyer, shown in Figure 8-1, inspired by arcade and early console games such as Breakout and Arkanoid. New features that will be implemented in this game include bouncing off of objects and power-up items.
A352797_2_En_8_Fig1_HTML.jpg
Figure 8-1.
The Rectangle Destroyer Game

Game Project: Rectangle Destroyer

Rectangle Destroyer is an action game in which the player controls a paddle that moves from side to side along the bottom of the screen and uses it to bounce a ball toward the rectangular brick-like objects above to destroy them. Destroying each brick awards points to the player, and if all the bricks are destroyed, the player wins the game. If the ball falls past the bottom edge of the screen, the ball is lost. If there are any balls in reserve (the player begins with three in reserve), a new ball will be spawned above the paddle. When the ball collides with the paddle, the angle at which it bounces off is determined by the point of collision: hitting the left side of the paddle causes the ball to bounce to the left, hitting the right side causes the ball to bounce to the right, and hitting the center causes the ball to bounce straight up.
Occasionally, a destroyed brick will release an item that will drift toward the bottom of the screen. Items can have various effects on gameplay, such as changing the size of the paddle or altering the speed of the ball. The effect of the item is indicated by its image. If the paddle collects the item (by colliding with it), then the item’s effect is activated. There are some items that make the game more difficult, such as the paddle-shrinking item , which the player may wish to avoid. Occasionally, collecting a beneficial item also includes the risk of missing the ball as it falls toward the bottom of the screen; the player must make decisions carefully.
The player controls the paddle by moving the mouse left and right. At the start of the game, and every time the ball is respawned, the ball will be locked to the top of the paddle and will be released when the player clicks a mouse button.
The user interface contains a few text displays. Along the top of the screen, the player’s score and the number of balls in reserve are displayed. In the center of the screen, text will appear informing the player that they should click a mouse button to start the game, and a message will appear when the player has won or lost the game.
The game has a simple line-art style and has upbeat background music to match the pace of the game. Short sound effects are played when game-world objects interact: when the ball bounces off of the paddle, bricks, or walls, and when items appear and are collected.
Beginning this project requires the same steps as in previous projects: creating a new project, creating an assets folder and a +libs folder (the latter not being necessary if you have set up the userlib directory), copying the custom framework files you created in the first part of this book (BaseGame.java, BaseScreen.java, BaseActor.java), and copying the graphics and audio for this project into your assets folder. As described in the previous chapter, a BlueJ project named Framework has been created for your convenience that contains these items (except for the project-specific assets), as well as a launcher class and extensions of the BaseGame and BaseScreen classes. For efficiency, these files will be used as a starting point. To begin this project :
  • Download the source code files for this chapter.
  • Make a copy of the downloaded Framework folder (and its contents) and rename it to Rectangle Destroyer.
  • Copy all the contents of the downloaded Rectangle Destroyer project assets folder into your newly created Rectangle Destroyer assets folder
  • Open the BlueJ project in your Rectangle Destroyer folder.
  • In the CustomGame class, change the name of the class to RectangleGame (BlueJ will then rename the source code file to RectangleGame.java).
  • In the Launcher class, change the contents of the main method to the following:
    Game myGame = new RectangleGame();
    LwjglApplication launcher = new LwjglApplication(
            myGame, "Rectangle Destroyer", 800, 600 );
At this point, you are ready to begin creating the classes for the game-specific objects and the code for the gameplay itself.

Creating the Game Objects

Over the course of this section, you will create classes for the four main game objects: the paddle, the walls, the bricks, and the ball. After creating each class, you’ll add code to the screen class that adds the objects to the game and enables them to interact with each other.

Paddle

First, you will create a class that represents the paddle object controlled by the player, which will be the simplest object to set up. Create a new class called Paddle that contains the following code:
import com.badlogic.gdx.scenes.scene2d.Stage;
public class Paddle extends BaseActor
{
    public Paddle(float x, float y, Stage s)
    {
        super(x,y,s);
        loadTexture("assets/paddle.png");
    }
}
Then, in the LevelScreen class, add the following import statement (which will enable you to get the position of the mouse later):
import com.badlogic.gdx.Gdx;
Then, add the following variable declaration to the class:
Paddle paddle;
In the initialize method , set up a background image and the paddle by adding the following code:
BaseActor background = new BaseActor(0,0, mainStage);
background.loadTexture("assets/space.png");
BaseActor.setWorldBounds(background);
paddle = new Paddle(320, 32, mainStage);
Then, in the update method, add the following code, which will keep the paddle horizontally aligned with the mouse (by adjusting its x-coordinate), while also keeping the paddle completely on the screen. (If the mouse is off-screen, however, this code will have no effect.)
float mouseX = Gdx.input.getX();
paddle.setX( mouseX - paddle.getWidth()/2 );
paddle.boundToWorld();
This is a good point at which to test your game (by running the main method from the Launcher class); you should be able to control the position of the paddle by moving the mouse; at this point, your game should appear as shown in Figure 8-2.
A352797_2_En_8_Fig2_HTML.jpg
Figure 8-2.
The game with the background image and paddle added

Walls

Next, you will create a class for the wall objects. Walls will be positioned along the left, right, and top edges of the screen, and the ball will bounce off of them. Since walls may have different sizes, the constructor of the Wall class will also take the width and the height of the wall as parameters. To this end, create a new class called Wall that contains the following code:
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.graphics.Color;
public class Wall extends BaseActor
{
    public Wall(float x, float y, float width, float height, Stage s)
    {
        super(x,y,s);
        loadTexture("assets/white-square.png");
        setSize(width, height);
        setColor( Color.GRAY );
        setBoundaryRectangle();
    }
}
If desired, the color can be set to something other than Color.GRAY. Note that since the boundary polygon is set to match the size of the texture that is loaded, changing the object size with the setSize method should be followed by calling the setBoundaryRectangle method to update this data as well.
Next, in the initialize method of the LevelScreen class, add the following code:
new Wall(  0,0, 20,600, mainStage); // left wall
new Wall(780,0, 20,600, mainStage); // right wall
new Wall(0,550, 800,50, mainStage); // top wall
Variables to store the Wall objects are not needed, since a list of Brick objects can be easily obtained later on using the getList method of the BaseActor class. Note in particular that the top wall has a large height; this will be to provide space for labels in the user interface, implemented later in this chapter. If desired, you can test the program again at this point to visually inspect that the walls are placed correctly.

Bricks

Next, you will add the rectangular bricks to the game. Create a new class called Brick that contains the following code:
import com.badlogic.gdx.scenes.scene2d.Stage;
public class Brick extends BaseActor
{
    public Brick(float x, float y, Stage s)
    {
        super(x,y,s);
        loadTexture("assets/brick-gray.png");
    }
}
If desired, you can use the setColor method in the constructor to tint the bricks to a particular color.
Next, you will set up a rectangular grid of bricks on the main screen. The easiest way to do this is with a nested for loop. The coordinates of each brick can be calculated in terms of the row and column position and width and height of each brick. To determine the width and height of the bricks, a single Brick object is created and removed after its size is stored. To position the grid of bricks near the center of the screen, margins are also calculated and taken into account. To implement this, in the initialize method of the LevelScreen class, add the following code:
Brick tempBrick = new Brick(0,0,mainStage);
float brickWidth = tempBrick.getWidth();
float brickHeight = tempBrick.getHeight();
tempBrick.remove();
int totalRows = 10;
int totalCols = 10;
float marginX = (800 - totalCols * brickWidth) / 2;
float marginY = (600 - totalRows * brickHeight) - 120;
for (int rowNum = 0; rowNum < totalRows; rowNum++)
{
    for (int colNum = 0; colNum < totalCols; colNum++)
    {
        float x = marginX + brickWidth  * colNum;
        float y = marginY + brickHeight * rowNum;
        new Brick( x, y, mainStage );
    }
}
Once again, you can test the project at this point to verify that the bricks appear as desired. The preceding code produces the arrangement shown in Figure 8-3; you can adjust the number of bricks in each row and column if desired by changing the values of totalRows and totalCols, or even add some spacing between the bricks by adding a small value to brickHeight and brickWidth.
A352797_2_En_8_Fig3_HTML.jpg
Figure 8-3.
Adding a rectangular grid of bricks
Some games in this genre will add visual interest by creating a pixel art–style picture out of bricks of different colors. While technically possible at this point, it is not really worth the effort involved. Designing and loading a pattern of colored bricks with the assistance of a third-party program for tilemap editing will be the topic of a future chapter.

Ball

Next, you will implement the ball object. The ball will make use of the physics functionality in the BaseActor class: it will travel at a constant velocity. A small amount of gravitational force will be simulated so that the ball never gets “stuck” at a given vertical position, moving directly from the left wall to the right wall and back again. At the beginning of the game, the ball also needs to be stationary until the user clicks a mouse button. For this reason, the Ball class will include a Boolean variable called paused (and associated methods setPaused and isPaused) that controls whether the physics will be applied in the act method. To begin, create a new class called Ball with the following code:
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.math.Vector2;
public class Ball extends BaseActor
{
    public boolean paused;
    public Ball(float x, float y, Stage s)
    {
        super(x,y,s);
        loadTexture("assets/ball.png");
        setSpeed(400);
        setMotionAngle(90);
        setBoundaryPolygon(12);
        setPaused(true);
    }
    public boolean isPaused()
    {
        return paused;
    }
    public void setPaused(boolean b)
    {
        paused = b;
    }
    public void act(float dt)
    {
        super.act(dt);
        if ( !isPaused() )
        {
            // simulate gravity
            setAcceleration(10);
            accelerateAtAngle(270);
            applyPhysics(dt);
        }
    }
}
In addition, the ball needs to “bounce” off of the bricks and the walls. This is accomplished with the help of the vector returned by the preventOverlap method in the BaseActor class, which represents the direction in which the actor was moved so that the actors involved no longer overlap. By comparing the x and y components of the vector, you can estimate whether the overlap was mostly along the x-direction (in which case, the ball should bounce horizontally) or the y-direction (in which case, the ball should bounce vertically). Bouncing in either direction involves reversing the velocity in that direction, which is accomplished by multiplying the corresponding component by -1. For this reason, change the velocityVec field in the BaseActor class to
protected Vector2 velocityVec;
After making this change, the Ball class can access and modify this variable. To implement this, in the Ball class, add the following method:
public void bounceOff(BaseActor other)
{
    Vector2 v = this.preventOverlap(other);
    if ( Math.abs(v.x) >= Math.abs(v.y) ) // horizontal bounce
        this.velocityVec.x *= -1;
    else // vertical bounce
        this.velocityVec.y *= -1;
}
To add a ball to the game , in the LevelScreen class, add the following variable declaration:
Ball ball;
In the initialize method, add the following line of code:
ball = new Ball(0,0, mainStage);
To lock the ball into place above the center of the paddle when the ball is paused, add the following block of code to the update method:
if ( ball.isPaused() )
{
    ball.setX( paddle.getX() + paddle.getWidth()/2  - ball.getWidth()/2 );
    ball.setY( paddle.getY() + paddle.getHeight()/2 + ball.getHeight()/2 );
}
Finally, to release the ball when the user clicks the mouse button, add the following method to the LevelScreen class:
public boolean touchDown(int screenX, int screenY, int pointer, int button)
{
    if ( ball.isPaused() )
    {
        ball.setPaused(false);
    }
    return false;  
}
At this point, you can test the game once again. The ball should appear centered above the paddle as it moves back and forth, and if you click a mouse button, the ball will fly straight up toward (and past) the top edge of the screen. In the next section, you will add code to make the ball bounce off of the various game objects .

Bouncing Around

In this section, you will add the code that enables the ball to bounce off the walls, the bricks, and the paddle. The simplest of these is bouncing off the walls . In the update method of the LevelScreen class, add the following code:
for (BaseActor wall : BaseActor.getList(mainStage, "Wall"))
{  
    if ( ball.overlaps(wall) )
    {
        ball.bounceOff(wall);
    }
}
Only slightly more difficult is bouncing off bricks, as these will also be destroyed when hit. In the update method of the LevelScreen class, add the following code:
for (BaseActor brick : BaseActor.getList(mainStage, "Brick"))
{  
    if ( ball.overlaps(brick) )
    {
        ball.bounceOff(brick);
        brick.remove();
    }
}
Finally, you will add the code that enables the ball to bounce off the paddle. As described at the beginning of this chapter, the angle of bounce is dependent on the part of the paddle that is hit, and so the Ball class bounceOff method will not be used here. Instead, you determine the x-coordinate of the center of the ball and calculate how far along the paddle it lies, as a percentage: 0.00 represents the leftmost point, 0.50 represents the exact center, and 1.00 represents the rightmost point. The percentage is then used to (linearly) interpolate the angle of bounce using the MathUtils class lerp method: a percentage of 0.00 will correspond to an angle of 150 degrees (which points toward the left), a percentage of 1.00 will correspond to an angle of 30 degrees (which points towards the right), and percentages in between are interpolated correspondingly using a linear function (the equation of the line connecting these two data points). To implement this, add the following import statement to the LevelScreen class:
import com.badlogic.gdx.math.MathUtils;
Then, in the update method, add the following code.
if ( ball.overlaps(paddle) )
{
    float ballCenterX = ball.getX() + ball.getWidth()/2;
    float paddlePercentHit = (ballCenterX - paddle.getX()) / paddle.getWidth();
    float bounceAngle = MathUtils.lerp( 150, 30, paddlePercentHit );
    ball.setMotionAngle( bounceAngle );
}
At this point, you should test the game and verify that the ball bounces off the walls, bricks, and paddle as expected. It is important to note that there are two possible strange behaviors that may arise in gameplay. First, if the walls are too thin and the ball is moving too quickly, it is possible for the ball to pass through the wall in a single iteration of the game loop. If this happens while you are playtesting, you may want to cap the speed of the ball or increase the size of the walls. Also, the ball may appear to pass through bricks instead of bouncing off of them; this can happen if the ball happens to overlap two bricks at the same time; the bounceOff method is called twice, and reversing the velocity twice (for the same component) will result in the ball continuing in the same direction. There seems to be no simple or elegant approach to eliminating this behavior, but it happens infrequently in practice, so it will not be addressed here.

User Interface

Next, you will implement a simple user interface for this game: three labels, two of which will appear near the top edge of the screen and display the number of points earned and the number of balls in reserve, as shown in Figure 8-1. A third label will appear in the center of the screen and contain the message “Click to start” while the ball is paused, and messages for winning and losing the game. To begin, in the LevelScreen class, add the following import statements:
import com.badlogic.gdx.scenes.scene2d.ui.Label;
import com.badlogic.gdx.graphics.Color;
Add the following variable declarations to the class :
int score;
int balls;
Label scoreLabel;
Label ballsLabel;
Label messageLabel;
To set up these variables, in the initialize method add the following code:
score = 0;
balls = 3;
scoreLabel = new Label( "Score: " + score, BaseGame.labelStyle );
ballsLabel = new Label( "Balls: " + balls, BaseGame.labelStyle );
messageLabel = new Label("click to start", BaseGame.labelStyle );
messageLabel.setColor( Color.CYAN );
To arrange the labels using uiTable , you will create a two-row, three-column table, the center column being empty to separate scoreLabel and ballsLabel. The second row will span all three columns and contain messageLabel so that it appears near the center of the screen. This is accomplished by adding the following code to the initialize method:
uiTable.pad(5);
uiTable.add(scoreLabel);
uiTable.add().expandX();
uiTable.add(ballsLabel);
uiTable.row();
uiTable.add(messageLabel).colspan(3).expandY();
First of all, the messageLabel should disappear when the player clicks a button. In the touchDown method , in the block of code that checks if the ball is paused, add the following line of code at the end of, but inside, the if statement:
messageLabel.setVisible(false);
Next, you will earn points when a brick is destroyed. In the update method , in the block of code that checks if the ball overlaps a brick (after brick.remove()), add the following two lines of code:
score += 100;
scoreLabel.setText("Score: " + score);
If all the bricks are destroyed, a “You win!” message should be displayed. This is accomplished by adding the following code to the update method:
if ( BaseActor.count(mainStage, "Brick") == 0)
{
    messageLabel.setText("You win!");
    messageLabel.setColor( Color.LIME );
    messageLabel.setVisible(true);
}
Next, you will set up the reserve ball and respawn mechanic. If the ball moves past the bottom edge of the screen, then it should be removed from the game. If there are any balls left, a new ball should be spawned, the number of balls should be decreased, and the message label should display “Click to start” again. If there are not any balls left, then a “Game Over” message should be displayed. Note that the condition also checks that there are bricks remaining, because these messages should not be displayed if the player has already won the game. Add the following code to the update method :
if ( ball.getY() < -50 && BaseActor.count(mainStage, "Brick") > 0 )
{
    ball.remove();
    if (balls > 0)
    {
        balls -= 1;
        ballsLabel.setText("Balls: " + balls);
        ball = new Ball(0,0,mainStage);
        messageLabel.setText("Click to start");
        messageLabel.setColor( Color.CYAN );
        messageLabel.setVisible(true);
    }
    else
    {
        messageLabel.setText("Game Over");
        messageLabel.setColor( Color.RED );
        messageLabel.setVisible(true);
    }
}
At this point, your game is ready for testing again! Make sure that the score and ball labels change as expected and that the correct text appears in the message label at the correct times.

Items

To add interest and variability to the gameplay, you will next implement items . To keep track of the different types of items, you will use an enumerated type .
Enumerated Types
An enumerated type is a special data type you can define that consists of a set of fixed values. This is particularly useful when you want a variable to store only a certain set of values.
For example, if you want to represent a compass direction, you could create an integer variable and a set of predefined constants, as follows:
final int NORTH = 0;
final int SOUTH = 1;
final int EAST = 2;
final int WEST = 3;
int direction;
Later on, you could write code such as:
direction = NORTH;
However, this code suffers from the drawback that any integer value could be assigned to the direction variable , including values that do not make sense, such as:
direction = 4;
Enumerated types eliminate this problem entirely. The same functionality just described could be implemented much more robustly. Using the enum keyword, you can define an enumerated type (named Direction) and corresponding variable as follows:
enum Direction { NORTH, SOUTH, EAST, WEST };
Direction direction;
In this example, Direction is now a user-defined data type, and direction is a variable of that type. The syntax for using the values of an enumerated type is similar to that for accessing static fields defined in a class. For example, to set the value of direction to the Direction value NORTH, you would enter:
direction = Direction.NORTH;
Finally, you can compare the value of an enumerated type variable using == or the equals method, and you can obtain an array containing the values with the values method.
In this section, you will create four different types of items: two that affect the size of the paddle (one expanding it, the other shrinking it) and two that affect the speed of the ball (one increasing it, the other decreasing it). The type will be specified with an enum (called Type). Each type of item will have a corresponding image, as shown in Figure 8-4.
A352797_2_En_8_Fig4_HTML.jpg
Figure 8-4.
Item images (paddle expand, paddle shrink, ball speed up, ball speed down)
When items are created, the following will happen:
  • A random type will be selected.
  • There will be an animated effect that causes the item to grow from a point to its full size.
  • The item will move toward the bottom of the screen at a constant speed.
  • If the item moves past the bottom edge of the screen, it will be removed from the game.
To implement these features, create a new class named Item containing the following code. Observe that in the constructor, after changing the size of the image, the origin coordinates and the boundary shape need to be updated as well.
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.scenes.scene2d.actions.Actions;
public class Item extends BaseActor
{
    public enum Type { PADDLE_EXPAND, PADDLE_SHRINK,
                       BALL_SPEED_UP, BALL_SPEED_DOWN };
    private Type type;
    public Item(float x, float y, Stage s)
    {
        super(x,y,s);
        setRandomType();
        setSpeed(100);
        setMotionAngle(270);
        setSize(50,50);
        setOrigin(25,25);
        setBoundaryRectangle();
        setScale(0,0);
        addAction( Actions.scaleTo(1,1, 0.25f) );
    }
    public void setType(Type t)
    {   
        type = t;
        if (t == Type.PADDLE_EXPAND)
            loadTexture("assets/items/paddle-expand.png");
        else if (t == Type.PADDLE_SHRINK)
            loadTexture("assets/items/paddle-shrink.png");
        else if (t == Type.BALL_SPEED_UP)
            loadTexture("assets/items/ball-speed-up.png");
        else if (t == Type.BALL_SPEED_DOWN)
            loadTexture("assets/items/ball-speed-down.png");
        else
            loadTexture("assets/items/item-blank.png");        
    }
    public void setRandomType()
    {
        int randomIndex = MathUtils.random(0, Type.values().length - 1);
        Type randomType = Type.values()[randomIndex];
        setType(randomType);
    }
    public Type getType()
    {
        return type;
    }
    public void act(float dt)
    {
        super.act(dt);
        applyPhysics(dt);
        if (getY() < -50)
            remove();
    }
}
Items should be spawned occasionally when bricks are destroyed. To implement this, in the LevelScreen class update method, locate the block of code that is run if the ball overlaps a brick and add the following. (You can adjust the value of spawnProbability as desired to make items appear more or less frequently.)
float spawnProbability = 20;
if ( MathUtils.random(0, 100) < spawnProbability )
{
    Item i = new Item(0,0,mainStage);
    i.centerAtActor(brick);
}
To implement the item effects when the paddle overlaps the item, add the following code to the update method. Note that the BaseActor objects retrieved by the getList method must be cast to Item objects in order to be able to access the getType method. Also note that after changing the size of the paddle, its boundary shape must be updated as well.
for (BaseActor item : BaseActor.getList(mainStage, "Item"))
{  
    if ( paddle.overlaps(item) )
    {
        Item realItem = (Item)item;
        if (realItem.getType() == Item.Type.PADDLE_EXPAND)
            paddle.setWidth( paddle.getWidth() * 1.25f );
        else if (realItem.getType() == Item.Type.PADDLE_SHRINK)
            paddle.setWidth( paddle.getWidth() * 0.80f );
        else if (realItem.getType() == Item.Type.BALL_SPEED_UP)
            ball.setSpeed( ball.getSpeed() * 1.50f );
        else if (realItem.getType() == Item.Type.BALL_SPEED_DOWN)
            ball.setSpeed( ball.getSpeed() * 0.90f );
        paddle.setBoundaryRectangle();
        item.remove();
    }
}
Once again, it is a good opportunity to test your code, this time making sure that all the powerups work as expected. For testing purposes , you may want to temporarily increase the value of spawnProbability so that items spawn more frequently, giving you more opportunities to test the items, or you may want to set the item type after they are spawned if there is one item type in particular you would like to test.

Sounds and Music

Finally, you will add some sounds and music to the game to help set the mood and to accentuate the interactions between the objects. In particular, there will be sound effects when the ball hits a wall, when the ball hits a brick, when the ball hits the paddle, when an item is spawned, and when an item is collected. The background music is called “Rollin at 5 1 and is an upbeat, jazzy piece appropriate to the pacing of this game . To begin, add the following import statements to the LevelScreen class:
import com.badlogic.gdx.audio.Sound;
import com.badlogic.gdx.audio.Music;
Next, add the following variable declarations to the same class:
Sound bounceSound;
Sound brickBumpSound;
Sound wallBumpSound;
Sound itemAppearSound;
Sound itemCollectSound;
Music backgroundMusic;
In the initialize method, add the following code:
bounceSound      = Gdx.audio.newSound(Gdx.files.internal("assets/boing.wav"));
brickBumpSound   = Gdx.audio.newSound(Gdx.files.internal("assets/bump.wav"));
wallBumpSound    = Gdx.audio.newSound(Gdx.files.internal("assets/bump-low.wav"));
itemAppearSound  = Gdx.audio.newSound(Gdx.files.internal("assets/swoosh.wav"));
itemCollectSound = Gdx.audio.newSound(Gdx.files.internal("assets/pop.wav"));
backgroundMusic = Gdx.audio.newMusic(Gdx.files.internal("assets/Rollin-at-5.mp3"));
backgroundMusic.setLooping(true);
backgroundMusic.setVolume(0.50f);
backgroundMusic.play();
Next, you will add the lines of code that play the sound effects at the appropriate moments, all of which occur in the update method. In the block of code following the conditional statement that checks if the ball overlaps the wall, directly after the line of code that makes the ball bounce off the wall, add the code:
wallBumpSound.play();
In the block of code following the conditional statement that checks if the ball overlaps a brick, directly after the line of code that makes the ball bounce off the brick, add the code:
brickBumpSound.play();
After the line of code that spawns a new Item object, add the code:
itemAppearSound.play();
In the block of code following the conditional statement that checks if the ball overlaps the paddle, directly after the line of code that sets the angle of motion after the ball, add the code:
bounceSound.play();
Finally, in the block of code following the conditional statement that checks if the paddle overlaps an item, directly after the line of code that removes the item from the stage, add the code:
itemCollectSound.play();
Test your project to verify that the music and sounds play as expected.
Congratulations—you have finished creating the Rectangle Destroyer game !

Summary and Next Steps

In this chapter, you created the ball-bouncing, brick-breaking action game Rectangle Destroyer. You learned how to simulate bouncing and how to use enumerated types in the process of creating collectible items that affect the gameplay.
As always, there are many features you could add to improve the quality of this game, such as a start menu that is displayed before the game begins. You could add more sounds corresponding to game-object interaction, such as a sound when the ball is lost, when the player wins the game, and when the player loses the game. You could add some solid, moving objects in the brick area that knock the ball around. Most of all, you may want to consider creating some additional powerups. Some ideas for extra item effects are:
  • bonus points added to score
  • an extra reserve ball
  • change the size of the ball (smaller or larger)
  • destroy a random brick
  • stop the paddle from moving for 1–3 seconds
In the next chapter, you will learn how to implement a completely different game mechanic: drag-and-drop functionality, a key aspect of card-based and puzzle-based games.
Footnotes
1
“Rollin at 5” was written by Kevin McLeod from http://incompetech.com and released under the Creative Commons Attribution 3.0 license.
 
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset