In this project we will build a really simple but fun version of the classic Breakout game for Android. Often known as the “Brick breaker game” the history of the game is interesting and if you like nostalgia I urge you to read this history of Breakout. Breakout sometimes called Arkanoid was developed by Atari in the early 1970’s and was a kind of spiritual successor to Pong. What made it exciting at the time was that it had all the proven game mechanics of Pong, and some extras but was playable by a single player. Let’s build a Breakout/Arkanoid game.

  • The courses above are up to 95% off - only $19 using voucher code UDEMARCH. Limited time offer.

About this project

Skill level 1
Time to complete 1 hour

New Concepts:

  1. Making the game full screen
  2. A closer look at access specifiers
  3. A closer look at OOP with getters and setter methods
  4. Building classes to represent game objects
  5. Simple collision detection in practice
  6. Static methods

Recommended preparation tutorials

Let’s discuss our starting code. It is basically the same as our simple game engine code so take a look at that project if any of it looks unfamiliar. The only changes are that I have stripped out the variables we used to control Bob as well as the code to update and draw him. I have also left in the code to detect screen touches and releases but removed the code which triggered Bob to move. In addition I have made the inner class name more appropriate. So we now extend SurfaceView to create a class called BreakoutView and create an object of it called breakoutView. As before we set this object as the view for the screen.

So what we are left with is a BreakoutView who’s run method constantly calls update and draw whilst keeping track of the frame rate. There is a slight change in the run method as I have wrapped the call to update with an if(!paused) statement so the player can start the game with a touch of the screen. We will build classes to represent the player’s paddle, a single brick and of course a ball. We will then see how we can use these objects of these classes to make our game.

Create a new project in Android Studio, call it Breakout Game and name the Activity BreakoutGame then enter the slightly modified simple game engine code below that we have just discussed. You could run this code but all you will get is a blank screen.

Also, note that all the bonus downloads for this and every future tutorial is available on an exclusive download area for my Patreon subscribers.

patreon-medium-banner

import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class BreakoutGame extends Activity {

    // gameView will be the view of the game
    // It will also hold the logic of the game
    // and respond to screen touches as well
    BreakoutView breakoutView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Initialize gameView and set it as the view
        breakoutView = new BreakoutView(this);
        setContentView(breakoutView);

    }

    // Here is our implementation of GameView
    // It is an inner class.
    // Note how the final closing curly brace }
    // is inside SimpleGameEngine

    // Notice we implement runnable so we have
    // A thread and can override the run method.
    class BreakoutView extends SurfaceView implements Runnable {

        // This is our thread
        Thread gameThread = null;

        // This is new. We need a SurfaceHolder
        // When we use Paint and Canvas in a thread
        // We will see it in action in the draw method soon.
        SurfaceHolder ourHolder;

        // A boolean which we will set and unset
        // when the game is running- or not.
        volatile boolean playing;

        // Game is paused at the start
        boolean paused = true;

        // A Canvas and a Paint object
        Canvas canvas;
        Paint paint;

        // This variable tracks the game frame rate
        long fps;

        // This is used to help calculate the fps
        private long timeThisFrame;

        // When the we initialize (call new()) on gameView
        // This special constructor method runs
        public BreakoutView(Context context) {
            // The next line of code asks the
            // SurfaceView class to set up our object.
            // How kind.
            super(context);

            // Initialize ourHolder and paint objects
            ourHolder = getHolder();
            paint = new Paint();

        }

        @Override
        public void run() {
            while (playing) {

                // Capture the current time in milliseconds in startFrameTime
                long startFrameTime = System.currentTimeMillis();

                // Update the frame
                // Update the frame
                if(!paused){
                     update();
                }

                // Draw the frame
                draw();

                // Calculate the fps this frame
                // We can then use the result to
                // time animations and more.
                timeThisFrame = System.currentTimeMillis() - startFrameTime;
                if (timeThisFrame >= 1) {
                    fps = 1000 / timeThisFrame;
                }

            }

        }

        // Everything that needs to be updated goes in here
        // Movement, collision detection etc.
        public void update() {

        }

        // Draw the newly updated scene
        public void draw() {

            // Make sure our drawing surface is valid or we crash
            if (ourHolder.getSurface().isValid()) {
                // Lock the canvas ready to draw
                canvas = ourHolder.lockCanvas();

                // Draw the background color
                canvas.drawColor(Color.argb(255,  26, 128, 182));

                // Choose the brush color for drawing
                paint.setColor(Color.argb(255,  255, 255, 255));

                // Draw the paddle

                // Draw the ball

                // Draw the bricks

                // Draw the HUD

                // Draw everything to the screen
                ourHolder.unlockCanvasAndPost(canvas);
            }

        }

        // If SimpleGameEngine Activity is paused/stopped
        // shutdown our thread.
        public void pause() {
            playing = false;
            try {
                gameThread.join();
            } catch (InterruptedException e) {
                Log.e("Error:", "joining thread");
            }

        }

        // If SimpleGameEngine Activity is started theb
        // start our thread.
        public void resume() {
            playing = true;
            gameThread = new Thread(this);
            gameThread.start();
        }

        // The SurfaceView class implements onTouchListener
        // So we can override this method and detect screen touches.
        @Override
        public boolean onTouchEvent(MotionEvent motionEvent) {

            switch (motionEvent.getAction() & MotionEvent.ACTION_MASK) {

                // Player has touched the screen
                case MotionEvent.ACTION_DOWN:

                    break;

                // Player has removed finger from screen
                case MotionEvent.ACTION_UP:

                    break;
            }
            return true;
        }

    }
    // This is the end of our BreakoutView inner class

    // This method executes when the player starts the game
    @Override
    protected void onResume() {
        super.onResume();

        // Tell the gameView resume method to execute
        breakoutView.resume();
    }

    // This method executes when the player quits the game
    @Override
    protected void onPause() {
        super.onPause();

        // Tell the gameView pause method to execute
        breakoutView.pause();
    }

}
// This is the end of the BreakoutGame class


Now we will do something else new.

Making the game full-screen landscape

We want to use every pixel that the device has to offer so we will make changes to the app’s AndroidManifest.xml configuration file.

  1. In the project explorer pane in Android Studio double click on the manifests folder, this will open up the AndroidManifest.xml file in the code editor.
  2. In the AndroidManifest.xml file find the following line of code,  android:name=".BreakoutGame"
  3. Immediately below type or copy and paste these two lines to make the game run full screen and lock it in the landscape orientation.
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
android:screenOrientation="landscape"

That’s it. If you run the game it will be full-screen and landscape. Note however that some tablets don’t allow true full-screen.

Getting the screen resolution

In this project, we are going to make a much fuller system than in our simple game engine. Having said that we will still not be making a fully featured engine; we will progress steadily in complexity with each game project. One improvement this game will have is that we will detect and respond to the individual device resolutions that our Breakout game might run on. Add two variables to hold the horizontal and vertical resolution of the device just after the declaration of timeThisFrame. For more context, you can see the entire listing of BreakoutGame.java at the end of this page.

// The size of the screen in pixels
int screenX;
int screenY;

Now we can initialize those variables via an object of the type Point using a Display object in next block of code. Add this code into the BreakoutView constructor just after we initialize paint.

// Get a Display object to access screen details
Display display = getWindowManager().getDefaultDisplay();
// Load the resolution into a Point object
Point size = new Point();
display.getSize(size);

screenX = size.x;
screenY = size.y;

Creating the player paddle

Right- click the java folder in the Android Studio project explorer as shown in the next image.

Creating a new Java class called Paddle

Creating a new Java class called Paddle

Now select New|Java class then select …\app\source\main\java and click OK. Now enter Paddle as the name for our new class and click OK. We have now created a new Java class in a separate file called Paddle. The code is not amongst the rest of our code but we can still access it as we will soon see. This helps us to compartmentalize our code and keep it organized into logical areas. We will do the same for our ball and for a brick.

The code for Paddle class

Let’s code this class in its entirety then we can switch back to the BreakoutGame class and its inner BreakoutView class and put our paddle to work.

Take a look through the entire code listing for the Paddle class. Be sure to read all the comments for clarification we will talk about it afterward. Copy and paste this code below the package declaration in your Paddle.java file.

  • The courses above are up to 95% off - only $19 using voucher code UDEMARCH. Limited time offer.

import android.graphics.RectF;

public class Paddle {

    // RectF is an object that holds four coordinates - just what we need
    private RectF rect;

    // How long and high our paddle will be
    private float length;
    private float height;

    // X is the far left of the rectangle which forms our paddle
    private float x;

    // Y is the top coordinate
    private float y;

    // This will hold the pixels per second speed that the paddle will move
    private float paddleSpeed;

    // Which ways can the paddle move
    public final int STOPPED = 0;
    public final int LEFT = 1;
    public final int RIGHT = 2;

    // Is the paddle moving and in which direction
    private int paddleMoving = STOPPED;

    // This the the constructor method
    // When we create an object from this class we will pass
    // in the screen width and height
    public Paddle(int screenX, int screenY){
        // 130 pixels wide and 20 pixels high
        length = 130;
        height = 20;

        // Start paddle in roughly the sceen centre
        x = screenX / 2;
        y = screenY - 20;

        rect = new RectF(x, y, x + length, y + height);

        // How fast is the paddle in pixels per second
        paddleSpeed = 350;
    }

    // This is a getter method to make the rectangle that
    // defines our paddle available in BreakoutView class
    public RectF getRect(){
        return rect;
    }

    // This method will be used to change/set if the paddle is going left, right or nowhere
    public void setMovementState(int state){
        paddleMoving = state;
    }

    // This update method will be called from update in BreakoutView
    // It determines if the paddle needs to move and changes the coordinates
    // contained in rect if necessary
    public void update(long fps){
        if(paddleMoving == LEFT){
            x = x - paddleSpeed / fps;
        }

        if(paddleMoving == RIGHT){
            x = x + paddleSpeed / fps;
        }

        rect.left = x;
        rect.right = x + length;
    }

}

Variables, access specifiers, and constants

First of all, before the class declaration we import a class called RectF. We will see RectF in action soon. It basically allows us to create a rectangle based on four float coordinates. This is perfect for representing the coordinates that we need to draw and as we will see when we handle collision detection the RectF class has a method to do all the hard work for us.

Next, we declare a whole bunch of member variables which are self-explanatory apart from one thing which we have not seen before but we did talk about in the Understanding OOP for Java games tutorial. All of the variables have an access specifier; either private or public. The private specifier means that the variable can only be read or changed by within this ( Paddle) class and public means that any class that has an object of type Paddle can access it.

The private variables protect themselves from being changed in ways that they were not intended. By doing things this way we improve our code because we control how it operates to within the scope of just this class. It is called encapsulation and as our code gets longer and longer and we add more classes it is invaluable for keeping our code manageable. But if these variables are private, how on earth do we make use of them? We will see how very soon.

Before we do, it also raises the question of why are three of our variables  STOPPED, LEFT and RIGHT declared as public. They are not technically variables, they are constants, as defined by the final keyword. They cannot be changed. What they do is they make obvious to classes that use the Paddle class exactly what three states a Paddle object can be in. They can then refer to those states without worrying about how they are used internally in the Paddle class. We will see exactly how they work later in this class and also back in the BreakoutView class when we handle the player’s input.

The Paddle class constructor

Next, we  have the constructor method that starts public void Paddle(...). This is the method that sets up the object when we initialize it. When we initialize an object of the type Paddle we need to pass in values that match those in the signature. In this case, two int variables, screenX and screenY. These variables will hold the screen’s horizontal and vertical resolution of pixels.

Inside the constructor, we then initialize the length and height variables for our future paddle, divide the screen width by two and subtract 20 from the height to get the approximate center bottom of the screen as a starting place for the paddle.

Then we initialized our RectF object by passing in four coordinates x, y, x+ length, y + height. This object now holds four coordinates that start with a top left corner of ( x,y)and completes with a bottom right corner of  x+ length, y + height. The constructor will now alter the length and height variables to cope with positioning the paddle almost centre bottom on the screen regardless of the device’s screen resolution.

Finally, in the constructor, we set the speed of the paddle to 350 pixels per second by initializing paddleSpeed to 350.

Accessing private variables with getters and setters

The next method is getRect. It is called a getter because it gets the value of variables and returns them. Notice it is public. This means that our BreakoutView class will be able to use it. And look what it does. Just one thing. It returns a copy of rect. BreakoutView will now be able to use this method to get the coordinates of the paddle so it can draw it to the screen or detect if it has hit the ball.

The next method setMovementState does just one thing. It receives a  int parameter and assigns it to the private paddleMoving variable. In BreakoutView soon we will use the constant variables  STOPPED, LEFT and RIGHT in conjunction with this method to alter the paddleMoving variable. When we do so it will control what happens when the next public method we will see is called.

The Paddle update method explained

The update method of the Paddle class is separate from the update method of our BreakoutView class. Once per frame, the update method of the BreakoutView class will call the update method of the Paddle class. In this update method, the paddle will be assigned new coordinates if necessary, ready for when the draw method is called.

In the update method, we check for two possible conditions paddleMoving == LEFT and paddleMoving == RIGHT. We then change the x coordinate by the speed per second divided by the frames per second passed into the method. After the two if statements we update the starting and finishing ( rect.left and rect.right) coordinates of rect. Notice that if paddleMoving == STOPPED is true then nothing would change that frame.

Now we have coded and discussed the Paddle class we can put it to use.

Using the Paddle class

We will do this in a few steps. As we need to insert code in various different places within our game engine (BreakoutGame.java) I will explain as clearly as possible where each bit of code goes. For absolute clarity check the code listing at the very end of the article where the finished code for the BreakoutGame class including the BreakoutView inner class is listed in its entirety.

First of all, we declare an object of the type Paddle called paddle. You can add this code anywhere after the BreakoutView declaration but before the constructor; I added it after the declaration of screenX and screenY.


// The player's paddle

Paddle paddle;

Now we need to instantiate it. As we need to pass in the screen’s horizontal and vertical resolution. The ideal place to do this is in the BreakoutView constructor after we initialized screenX and screenY.

paddle = new Paddle(screenX, screenY);

Now we simply add a call to the paddle object’s update method. This code goes, as we might expect in the update method of the BreakoutView class.

// Move the paddle if required
paddle.update(fps);

Then, in the draw method, we can draw the rectangle that represents the paddle. Notice that when we use drawRect, compared to the Drawing graphics demo project, we replace the actual rectangle coordinates with a call to paddle.getRect. As we discussed this method returns whatever the current coordinates of the paddle are. Add this code to the draw method just after the call to paint.setColor.

// Draw the paddle
canvas.drawRect(paddle.getRect(), paint);

One more step before we can see our paddle in action.

Detecting input and moving the paddle.

Here we will add code to the onTouchEvent method. All we need to do is get the coordinates that the touch occurred at within the ACTION_DOWN case with motionEvent.getX(), we can then determine which half of the screen was pressed by determining if the coordinate is smaller or larger than screenX divided by two. If it’s larger we call setMovementState(paddle.RIGHT), if not we use paddle.LEFT. Also in the,  ACTION_DOWN case we set paused to false which will start our game engine updating because we wrapped a  if(!paused) statement around the call to update in the run method.

Also, notice that for the,  ACTION_UP case it doesn’t matter about the coordinates where the action happened we just call setMovementState(paddle.STOPPED).  Here is the entire code for the onTouchEvent method with the new code we just discussed it as well.

// The SurfaceView class implements onTouchListener
        // So we can override this method and detect screen touches.
        @Override
        public boolean onTouchEvent(MotionEvent motionEvent) {

            switch (motionEvent.getAction() & MotionEvent.ACTION_MASK) {

                // Player has touched the screen
                case MotionEvent.ACTION_DOWN:

                    paused = false;

                    if(motionEvent.getX() > screenX / 2){
                        paddle.setMovementState(paddle.RIGHT);
                    }
                    else{
                        paddle.setMovementState(paddle.LEFT);
                    }

                    break;

                // Player has removed finger from screen
                case MotionEvent.ACTION_UP:

                    paddle.setMovementState(paddle.STOPPED);
                    break;
            }
            return true;
        }

We now have full control over the paddle and can start the game. Run the game, tap the screen to un-pause and move the paddle left and right by holding and releasing the appropriate half of the screen.

android_breakout_moving_paddle

Let’s make a ball.

Creating the ball

As we did for our Paddle class, right click the java folder in the Android Studio project explorer and select New|Java class then select …\app\source\main\java and click OK. Now enter Ball as the name for our new class and click OK. We have now created a new Java class in a separate file called Ball.

After everything, we learned while coding the Paddle class our Ball class will be quite straight forward. Let’s get an overview of what we need to do.

  • Our ball will be a square so it will need an RectF object and a getRect method to share the coordinates with BreakoutView.
  • Our ball needs to move around in all four directions so it will need an x and a y velocity. In order to be able to update its coordinates as it moves around, it will need its own update method.
  • Furthermore when our ball bumps into something it will need to reverse its velocity in the appropriate direction. The ball itself does not ‘know’ when it bumps into something so it will need public methods to reverse velocity; one for x and one for y.
  • At the start of a game, the ball will need to reset its coordinates to a sensible starting situation sitting just above the bat moving upwards.
  • Also, because we are doing a very rudimentary implementation of the physics of bouncing we want the ball when it hits the paddle to bounce back with a random horizontal direction. We will need a method to create this randomness.
  • Finally, from experience, I know that the ball will sometimes get stuck. For example, if we detect a collision when the ball is a couple of pixels into another object and reverse its velocity it is possible that it will remain stuck for eternity, constantly going back and forth. We need to be able to clear obstacles and we will write methods that achieve this for both the x and y axis.

With these requirements in mind, we will need the following methods.

  • A simple Ball constructor to give our ball its shape.
  • A getRect method to pass the coordinates to BreakoutView
  • update method to move our ball around based on its velocities.
  • A reverseXVelocity and reverseYVelocity methods.
  • A reset method to set the ball into its starting state each game.
  • A setRandomXVelocity method to randomly choose which way the ball heads after hitting the paddle.
  • And for when it gets stuck, clearObstacleX and clearObstacleY methods will do the job.

Here is the full code listing for the Ball class. We will look at each method in more depth, be sure to read the comments for clarification.

import android.graphics.RectF;

import java.util.Random;

public class Ball {
    RectF rect;
    float xVelocity;
    float yVelocity;
    float ballWidth = 10;
    float ballHeight = 10;

    public Ball(int screenX, int screenY){

        // Start the ball travelling straight up at 100 pixels per second
        xVelocity = 200;
        yVelocity = -400;

        // Place the ball in the centre of the screen at the bottom
        // Make it a 10 pixel x 10 pixel square
        rect = new RectF();

    }

    public RectF getRect(){
        return rect;
    }

    public void update(long fps){
        rect.left = rect.left + (xVelocity / fps);
        rect.top = rect.top + (yVelocity / fps);
        rect.right = rect.left + ballWidth;
        rect.bottom = rect.top - ballHeight;
    }

    public void reverseYVelocity(){
        yVelocity = -yVelocity;
    }

    public void reverseXVelocity(){
        xVelocity = - xVelocity;
    }

    public void setRandomXVelocity(){
        Random generator = new Random();
        int answer = generator.nextInt(2);

        if(answer == 0){
            reverseXVelocity();
        }
    }

    public void clearObstacleY(float y){
            rect.bottom = y;
            rect.top = y - ballHeight;
    }

    public void clearObstacleX(float x){
        rect.left = x;
        rect.right = x + ballWidth;
    }

    public void reset(int x, int y){
        rect.left = x / 2;
        rect.top = y - 20;
        rect.right = x / 2 + ballWidth;
        rect.bottom = y - 20 - ballHeight;
    }

}

First, we declare some variables to hold the coordinates, velocity, and size of the ball and in the constructor initialize the velocities and the rect object. Note that we have not yet assigned coordinates to the rect object, just initialized it with zero for each corner.

In update we change the coordinates of the ball with the same formula we used in paddle only without the conditions ( paddleMoving) and this time on both the x and y coordinates.

In getRect we return rect as we did for our Paddle class. In reverseYVelocity we make velocityY equal to -velocityY reversing whatever its current sign (+-) is and hence reversing the direction of the ball on the y-axis. In reverseXVelocity we do the same but for the x axes of movement.

In setRandomXVelocity we use an object of type Random and generate a number with two possible values. If it is the first we reverse velocityX in the usual way if it is the second possible number we do nothing so velocityX remains the same.

To be clear, when the ball hits the left or right wall we will always reverse the velocity but when the ball hits the paddle we will randomly either change it or maintain it. This gives an effect of differing types of impact with the paddle. It is scientifically inaccurate but is nice and simple. We will do some more advanced physics in the next full game project.

The next methods we implemented were clearObstacleY and clearObstacleX where we respectively amend the vertical and horizontal coordinates of the ball. We will see soon where we use this but it is to avoid the ball getting stuck.

We can now go and play with our ball.

Using a ball object

Declare an object of type Ball below where we declared paddle.

// A ball
Ball ball;

Initialize ball right after the initialization of paddle.

// Create a ball
ball = new Ball(screenX, screenY);

Call the ball.update method below where we called the paddle.update method in the BreakoutView update method.

ball.update(fps);

Use drawRect in exactly the same way as we did for the paddle but use the getRect method on ball. Add the code right after the code to draw the paddle.

// Draw the ball
canvas.drawRect(ball.getRect(), paint);

Now we need to create a new method which we will call each time a new game is started. We will add more code to this later in the project but for now, we just call ball.reset. Add the createBricksAndRestart method right after the constructor in BreakoutView. The method could actually go anywhere within the BreakoutView class but this seems like a logical place for it.

public void createBricksAndRestart(){

     // Put the ball back to the start
     ball.reset(screenX, screenY);

}

Finally, for this part of the project, we want to call this new method as the very last line of code in the BreakoutView constructor.

createBricksAndRestart();

Now we can run the game, tap the screen to start and watch the ball fly off into oblivion.

Creating some bricks

As we have done before to create a new class select right click the java folder and select  New|Java class then select …\app\source\main\java and click OK. Now enter Brick as the name for our new class and click OK. We have now created a new Java class in a separate file called Brick.

Our Brick class is the simplest of them all. All that a brick needs to do is sit there looking brick-like until it is bashed by a ball at which time it should disappear. Knowing this we can work our that a brick will need an RectF object and a boolean variable so our game engine knows if it is destroyed or visible.


import android.graphics.RectF;

public class Brick {

    private RectF rect;

    private boolean isVisible;

    public Brick(int row, int column, int width, int height){

        isVisible = true;

        int padding = 1;

        rect = new RectF(column * width + padding,
                row * height + padding,
                column * width + width - padding,
                row * height + height - padding);
    }

    public RectF getRect(){
        return this.rect;
    }

    public void setInvisible(){
        isVisible = false;
    }

    public boolean getVisibility(){
        return isVisible;
    }
}

In the class above we can see that we have an RectF as expected and a boolean isVisible. In the constructor, we can see that we pass in a row number, column number and a width and height for the brick. Next, in the constructor, we initialize the location of the brick using the row number, column number,  width height and one pixel of padding so we can distinguish each individual brick from the others.

As we have come to expect  rect and isVisible are private so we need a getRect method a setInvisible method for when a brick is hit by the ball and a getVisibility method which will let us check on the current visibility status of a brick.

Now we will see how we draw all our bricks.

Drawing the bricks

In order to manage our bricks, it will be really tidy to keep them in an array. Declare an array called bricks and a variable to keep track of its size called numBricks. Add this code after the ball declaration in the BreakoutView class.

// Up to 200 bricks
Brick[] bricks = new Brick[200];
int numBricks = 0;

Perhaps unsurprisingly we will create our bricks in the createBricksAndRestart method. First, we define the size of the bricks based on the resolution of the screen and initialize numBricks to zero so we can keep track of the number of bricks that we draw. We will use this variable in a number of places as we will see.

Next, we set a nested for loop an outer one for each column and the inner one for each row. The code below loops through 3 rows with 8 columns in each. In the centre of the two loops we create a new brick based on the column number, row number, width and height. And of course, our Brick class will use this information to set the coordinates of each accordingly. Each Brick object is neatly tucked away in our bricks array. After we declare and initialize each brick we increment numBricks to keep a track of the number of bricks in the array. You can easily alter the number of rows and columns as well as the size of the bricks and the code will still work. Add this code after the code to reset the ball.

int brickWidth = screenX / 8;
int brickHeight = screenY / 10;

// Build a wall of bricks
numBricks = 0;

for(int column = 0; column < 8; column ++ ){
     for(int row = 0; row < 3; row ++ ){
          bricks[numBricks] = new Brick(row, column, brickWidth, brickHeight);
          numBricks ++;
     }
}

Now we will draw all our bricks, again using a for loop to loop through the bricks array. Just before we do so we change the color of the brush to make our bricks a different color than the paddle and ball. Add this code in the draw method just after we draw the ball. Notice that as we loop through the bricks array we first check if getVisibility returns true. Only if it does will the brick be drawn. We make sure we don’t access an empty slot in the array by using i < numBricks as the condition of the for loop.

// Change the brush color for drawing
paint.setColor(Color.argb(255,  249, 129, 0));

// Draw the bricks if visible
for(int i = 0; i < numBricks; i++){
     if(bricks[i].getVisibility()) {
          canvas.drawRect(bricks[i].getRect(), paint);
     }
}

We can now run the game and see the bricks.

Screenshot_2015-05-21-10-47-30

The problem, of course, is that the ball just flies straight through them. What we need is to detect when things bump into each other. When these collision events occur we can then take appropriate action. As these actions include things like playing a sound, changing the score and restarting the game, let’s add some code to play sounds and handle the score then we will see how to detect the collision events.

Making some noise and keeping score

We will now add the ability to keep score and play sound FX. If you want to learn more details about how this sound code works have a look at the Playing sound FX tutorial. Let’s get started because we are not far away from a complete working Breakout game. Add this code to hold references to a bunch of sound FX and to keep track of the score and lives that the player has. The code goes right after the declaration of numBricks in the BreakoutView class.

// For sound FX
SoundPool soundPool;
int beep1ID = -1;
int beep2ID = -1;
int beep3ID = -1;
int loseLifeID = -1;
int explodeID = -1;

// The score
int score = 0;

// Lives
int lives = 3;

Next, in the BreakoutView constructor, just BEFORE the call to createBricksAndRestart add this code to load the sound FX from their files.

// Load the sounds

// This SoundPool is deprecated but don't worry
soundPool = new SoundPool(10, AudioManager.STREAM_MUSIC,0);

try{
     // Create objects of the 2 required classes
     AssetManager assetManager = context.getAssets();
     AssetFileDescriptor descriptor;

     // Load our fx in memory ready for use
     descriptor = assetManager.openFd("beep1.ogg");
     beep1ID = soundPool.load(descriptor, 0);

     descriptor = assetManager.openFd("beep2.ogg");
     beep2ID = soundPool.load(descriptor, 0);

     descriptor = assetManager.openFd("beep3.ogg");
     beep3ID = soundPool.load(descriptor, 0);

     descriptor = assetManager.openFd("loseLife.ogg");
     loseLifeID = soundPool.load(descriptor, 0);

     descriptor = assetManager.openFd("explode.ogg");
     explodeID = soundPool.load(descriptor, 0);

}catch(IOException e){
     // Print an error message to the console
     Log.e("error", "failed to load sound files");
}

Of course, we need to add the sound files to our project. You can create your own or download mine by right clicking on the files listed below. Just make sure you use exactly the same file names. You can also listen to each of the sounds by using the media controls below the links.

beep1
beep2
beep3
explode
loseLife

When you have your preferred sound FX, using your operating system’s file browser go to the app\src\main folder of the project and create a folder called assets. Add your sound file to this folder.

Now at the very end of the createBricksAndRestart method, we can initialize our score and lives variables like this.

// Reset scores and lives
score = 0;
lives = 3;
}

Now in the draw method, near the end, just after we draw the bricks add this code to draw the score, lives and announce either victory or defeat based on clearing all the bricks or losing all their lives.

// Draw the HUD
// Choose the brush color for drawing
paint.setColor(Color.argb(255,  255, 255, 255));

 // Draw the score
 paint.setTextSize(40);
 canvas.drawText("Score: " + score + "   Lives: " + lives, 10,50, paint);

// Has the player cleared the screen?
if(score == numBricks * 10){
     paint.setTextSize(90);
     canvas.drawText("YOU HAVE WON!", 10,screenY/2, paint);
}

// Has the player lost?
if(lives <= 0){
     paint.setTextSize(90);
     canvas.drawText("YOU HAVE LOST!", 10,screenY/2, paint);
}

Now we will see how we can detect collisions, play the sounds and change the score.

Collision detection

All of this code goes in the update method right after we update the paddle. I will present the code in manageable chunks in the order it should be entered. If at any time you are unsure of exactly where some code goes just jump to the end of this page and you can see the whole of the BreakoutGame class in a single listing.

Here we loop through the bricks array with a for loop. For each brick, we check if it is visible by calling getVisibility and if it is we use a method of the RectF class called intersects. The intersects method takes two RectF parameters so we pass in our ball and the brick currently being checked. If they intersect(have collided) it returns true and the code inside the if block will execute. Before we look at the code inside the if block you might notice the slightly strange looking call to intersects.

The syntax is RectF.intersects(.... We are actually calling the method on the class, not on the object OF the class. This is a special type of method known as a static method. When a method is declared static it allows this functionality. Methods are often declared static when the action performed in the method is not actually on an individual object yet it makes sense for the functionality to be available with objects of that type.

Inside the if statement we set the brick to be invisible, reverse the balls y direction of travel, add ten to the score and play the explode sound.

// Check for ball colliding with a brick
for(int i = 0; i < numBricks; i++){

     if (bricks[i].getVisibility()){

          if(RectF.intersects(bricks[i].getRect(),ball.getRect())) {
               bricks[i].setInvisible();
               ball.reverseYVelocity();
               score = score + 10;
               soundPool.play(explodeID, 1, 1, 0, 0, 1);
          }
    }
}

In this next block of code, we again use the static intersects method to check for a collision between two objects of the type RectF. This time the paddle and the ball objects. If we have a collision we set a random x velocity so the ball could bounce off to either the left or the right and we reverse the y velocity so it heads back up the screen. Now we use our clearObstacleY method to make sure the ball doesn’t get stuck to the paddle and we pass in the position of the top of the paddle minus two pixels so the method will make sure that the ball resumes it’s upwards journey starting two pixels away from the top of the paddle. We play another beep and we are done here.

// Check for ball colliding with paddle
if(RectF.intersects(paddle.getRect(),ball.getRect())) {
     ball.setRandomXVelocity();
     ball.reverseYVelocity();
     ball.clearObstacleY(paddle.getRect().top - 2);
     soundPool.play(beep1ID, 1, 1, 0, 0, 1);
}

Now we handle a collision with the bottom of the screen. The bottom of the screen is obviously not a RectF so we can’t use the intersects method but we do know the exact coordinate of the bottom of the screen; it is screenY. So we just test for the bottom of the ball being greater than screenY. If it is the ball has hit the bottom. We then reverse the y velocity use the clearYObstacle to clear the bottom of the screen by two pixels, deduct a life and play a gloomy thud sound. Finally in the block of code below we test to see if the player has zero lives and if they do we pause the game and call createBricksAndRestart to build the game again from scratch.

// Bounce the ball back when it hits the bottom of screen
// And deduct a life
if(ball.getRect().bottom > screenY){
     ball.reverseYVelocity();
     ball.clearObstacleY(screenY - 2);

     // Lose a life
     lives --;
     soundPool.play(loseLifeID, 1, 1, 0, 0, 1);

     if(lives == 0){
          paused = true;
          createBricksAndRestart();
     }

}

The next block of code tests if the ball has hit the top of the screen. I love it when the ball bounces along the top of the blocks, I get a little dopamine rush and it reminds me of playing this game as a child. Yes, I am that old. Back to the code. We test for the top of the ball being less than zero and if so reverse the y velocity clear the top of the screen by 12 pixels and play a beep. The reason we clear the top of the screen by 12 pixels is because the clearObstacleY method works on the bottom of the ball and the ball is ten pixels high.

// Bounce the ball back when it hits the top of screen
if(ball.getRect().top < 0){
     ball.reverseYVelocity();
     ball.clearObstacleY(12);
     soundPool.play(beep2ID, 1, 1, 0, 0, 1);
}

The next block of code bounces the ball off of the left of the screen. All the code is self-explanatory at this point just note that we reverse the x velocity, not the y velocity.

// If the ball hits left wall bounce
if(ball.getRect().left < 0){
     ball.reverseXVelocity();
     ball.clearObstacleX(2);
     soundPool.play(beep3ID, 1, 1, 0, 0, 1);
}

The next block of code bounces the ball off of the right of the screen. All the self-explanatory at this point just again note that we reverse the x velocity not the y.

// If the ball hits right wall bounce
if(ball.getRect().right > screenX - 10){
     ball.reverseXVelocity();
     ball.clearObstacleX(screenX - 22);
     soundPool.play(beep3ID, 1, 1, 0, 0, 1);
}

Here we check if score equals numBricks multiplied by ten. This would indicate all the blocks have been cleared so we restart the game in the usual way.

// Pause if cleared screen
if(score == numBricks * 10){
     paused = true;
     createBricksAndRestart();
}

You can now run the game and play it for real.

The completed Breakout/Arkanoid game running on Android

The completed Breakout/Arkanoid game running on Android

What next

Congratulations for completing the game! There will be some more complete game projects really soon. In these projects, we will increase the number and variety of objects in our game and we will also significantly enhance the features of the game engine. A good next step would be to try the Android Space Invaders project.

And finally as promised here is the complete listing for the BreakoutGame class and its BreakoutView inner class.

Please visit the Android category of our game coding bookstore for beginners