Click here to Skip to main content
15,867,756 members
Articles / Programming Languages / Java / Java SE / J2ME

Mobile Game Programming for Beginners: Part 2 of 4

Rate me:
Please Sign up or sign in to vote.
4.94/5 (31 votes)
17 May 2009CPOL12 min read 72.7K   2.2K   43   39
Second part of four in a beginners' guide to J2ME game programming.

BrickBreaker

Introduction

This article is the second part in my Mobile Game Programming for Beginners series. The series started out with a super-simple game, but will continue to show how to implement a variety of different game types and the techniques used to code them.

This is a four part series, where I'll go through the following games:

  • Basics
  • Break Out style
  • Top Down Scroller
  • 3D Space Game

This part will show you how to implement a break out style game.

In this game, the goal is to bounce a ball off a paddle and have the ball collide with "bricks"; if the player fails to block the ball with the paddle, the game is over. When the ball collides with a brick, the brick is removed. When all bricks are removed, the player has beaten the game. In this part, I'll discuss the following topics:

  • Menus and in-game menus
  • AI or computer controlled players
  • Collision detection

This game, even though it's still a very simple game, is far more complicated to write, and that's why this example consists of 20 classes rather than 2 as in the previous part of this series. Most of the classes, however, are generic, and can be reused in other projects, so it's not as complicated as it looks. I won't show code extracts for all the classes in this article, but I've commented the classes that are available for download, so for classes or concepts that I don't discuss in this article, please refer to the code comments instead.

Class Overview

The most important classes in this example are:

  • MainCanvas: Initializes the game's screens, and controls the updates, rendering, and transitions between the screens.
  • BrickBreakerScreen: The class that contains the game logic.
  • Ball: Controls the ball's movement, collision detection, and rendering.
  • Brick: Representation of a single brick.
  • BrickField: Representation of all the bricks in the game.
  • Paddle: Representation paddle; note that this class relies on a PaddleController to actually control the paddle.
  • PaddleController: Interface that's implemented by ComputerPaddleController and HumanPaddleController in order to allow either a human or a computer AI to control the paddle.
  • MenuScreen: Implementation of a simple, generic game menu.

Menus

I love writing games, but there's one thing I don't like about it, and that's writing all the little bits and pieces that aren't actually part of the game play, such as menus and in-game dialog boxes. It it, however, quite important to take the time to implement those parts as well; otherwise, the game won't feel polished and there's going to be no way for the user to configure game settings.

That's why in this part I'll spend some time showing you how to implement a game menu and how to handle transitions from rendering the menu to rendering the game. As the game implemented in this part of the series is a simple break out style game, there aren't really that many menu options, but it's still important to have a menu in order to make the game feel more complete.

Screen Transitions

In order to go from rendering the menu to rendering the actual game, the game must be able to handle different types of screens. The MIDlet could be used to swap between different Canvases, but I prefer to always use just one GameCanvas that maintains its set of GameScreens.

GameScreen

The GameScreen class is an abstract class that exposes a few methods required by my main MIDlet class in order to control and update the GameScreen's state and request it to render itself.

Java
package com.bornander.games.utils;

import javax.microedition.lcdui.Graphics;

public abstract class GameScreen {

    protected Background background;
    protected int width;
    protected int height;

    public GameScreen(int width, int height) {
        this.width = width;
        this.height = height;
        this.background = null;
    }

    public void setBackground(Background background) {
        this.background = background;
    }

    protected void paintBackground(Graphics graphics) {
        if (background != null)
            background.paint(graphics);
    }

    public abstract void activated(GameScreen previousScreen);

    public abstract void paint(Graphics graphics);

    public abstract int doUpdate();

    public abstract void keyPressed(int keyCode, int gameAction);

    public abstract void keyReleased(int keyCode, int gameAction);
}

By using a construct such as this GameScreen class, it's easy to write a quite simple Canvas implementation (I call mine the main canvas) that can initialize the game's required screens and then handle the transitions between them depending on the logic local to the active screen. The game starts up with the menu screen as the active screen, and that screen can request the main canvas to change the screen to the actual game screen when the relevant menu option is selected. That way, the actual implementation of the menu GameScreen can be made completely generic, and it can be re-used for other games. Something which I really like, because as I said, I don't like writing the menus very much. In this example, there's a utility class called MenuScreen that is a generic implementation of a menu, and I'll be reusing that for my other two parts in this series.

Image 2

MenuScreen

The MenuScreen class is a fairly simple implementation of the GameScreen class. It allows the programmer to define a set of menu options, and then renders these based on key presses. It handles both simple items and "multiple choice" items (such as Sound on/off). The MenuScreen itself has no knowledge of the actual game, so it can't directly configure it, but it is possible for the game to retrieve the settings from the previous screen as that is passed as an argument when a screen transition occurs.

In-game Dialogs

Sometimes there's a need to interrupt the game and present the player with information and/or options during the game. In these scenarios, it's neater to present an in-game dialog rather than taking the user back to a full fledged menu screen. That means that the actual game screen handles the transitions between playing the game and showing the dialog, and the main canvas has nothing to do with this type of transition.

One such situation is when the user pauses the game, which in this example is done by clicking the Fire button. When the game enters the paused state, it stops updating the paddle's and ball's positions. It still renders them, but it also renders a dialog box in the foreground. The game state changes from running to paused, also changes how the input is handled; in running mode, pressing Up or Down adjusts the sound volume, but in paused state, it resumes or returns to the main menu instead.

Image 3

The different states that the BrickBreakerScreen class uses are:

  • Starting: When the game is starting up, this gives the player a bit of time to prepare.
  • Running: When the game is in play.
  • Stopped: The game enters this state when it's game over.
  • Paused: The user has paused the game.

These states are defined as simple ints in BrickBreakerScreen:

Java
public class BrickBreakerScreen extends GameScreen {

    ...

    // The different game states
    private final static int GAME_STATE_STARTING = 0;
    private final static int GAME_STATE_RUNNING = 1;
    private final static int GAME_STATE_STOPPED = 2;
    private final static int GAME_STATE_PAUSED = 3;

    ...
}

AI

An important part of most games is computer opponents that behave intelligently, and while a break out game probably isn't the first game that comes to mind when discussing AI, I think it's a good place to start as the actual AI is easy to implement (the paddle can only move right or left). It will also allow me to show how using a well abstracted class design not only makes it easy to create a computer controlled entity in the game, it can also make it easier to debug and to add network support.

Abstracting the Controller

The AI in this game is simply the computer trying to control the paddle, and like I said, this is a fairly easy task as there are only a few things the paddle can do:

  • Move left
  • Move right
  • Don't move at all

The information the AI uses to decide which of these options to go for are:

  • Position of the ball
  • Position and size of the paddle

We can argue that the velocity of the ball should be an input as well, but for simplicity's sake, I'll ignore that for now. This information is the same information a human would use. The difference is that the human needs to input the choice using the keys. Therefore, if an interface was created that would allow the above information to be consumed, and the output (left, right, don't move) produced as well, taking into account the key presses, then that interface would apply for both the human controller and the computer controller. I decided to call the interface PaddleController:

Java
public interface PaddleController {

    public static final int COMMAND_NOTHING = 0;
    public static final int COMMAND_MOVE_LEFT = 1;
    public static final int COMMAND_MOVE_RIGHT = 2;

    void initialize();

    void updatePaddleData(int x, int y, int width);

    void updateBall(int x, int y, int deltaX, int deltaY);

    void keyPressed(int keyCode, int gameAction);

    void keyReleased(int keyCode, int gameAction);

    int getCommand();
}

Information about the ball and paddle are provided to the controller using the updatePaddleData and updateBall methods; information about key presses are captured using keyPressed and keyReleased, and the getCommand method returns the action.

The version of the controller used for a human player then looks like:

Java
public class HumanPaddleController implements PaddleController {

    private boolean leftPressed = false;
    private boolean rightPressed = false;

    public HumanPaddleController() {
    }

    public void initialize() {
    }

    public void updatePaddleData(int x, int y, int width) {
    }

    public void updateBall(int x, int y, int deltaX, int deltaY) {
    }

    public void keyPressed(int keyCode, int gameAction) {
        switch(gameAction) {
            case Canvas.LEFT: leftPressed = true; break;
            case Canvas.RIGHT: rightPressed = true; break;
        }
    }

    public void keyReleased(int keyCode, int gameAction) {
        switch(gameAction) {
            case Canvas.LEFT: leftPressed = false; break;
            case Canvas.RIGHT: rightPressed = false; break;
        }
    }

    public int getCommand() {
        if (leftPressed)
            return PaddleController.COMMAND_MOVE_LEFT;
        if (rightPressed)
            return PaddleController.COMMAND_MOVE_RIGHT;

        return PaddleController.COMMAND_NOTHING;
    }
}

The computer controlled version is implemented like this:

Java
public class ComputerPaddleController implements PaddleController {

    private int width;
    private int height;

    private int ballX = 0;
    private int ballY = 0;
    private int ballDeltaX = 0;
    private int ballDeltaY = 0;

    private int paddleX = 0;
    private int paddleY = 0;
    private int paddleWidth = 0;

    public ComputerPaddleController(int width, int height) {
        this.width = width;
        this.height = height;
    }

    public void updatePaddleData(int x, int y, int width) {
        paddleX = x;
        paddleY = y;
        paddleWidth = width;
    }

    public void updateBall(int x, int y, int deltaX, int deltaY) {
        ballX = x;
        ballY = y;
        ballDeltaX = deltaX;
        ballDeltaY = deltaY;
    }

    public void keyReleased(int keyCode, int gameAction) {
    }

    public void keyPressed(int keyCode, int gameAction) {
    }

    public void initialize() {
    }

    public int getCommand() {
        if (ballDeltaY < 0) {
            // If ball is moving up, place paddle in center
            int targetDifferance = paddleX + (paddleWidth / 2) - width / 2;
            if (Math.abs(targetDifferance) > paddleWidth / 10) {
                if (targetDifferance < 0)
                    return PaddleController.COMMAND_MOVE_RIGHT;
                if (targetDifferance > 0)
                    return PaddleController.COMMAND_MOVE_LEFT;
            }
        }
        else {
            // If ball is coming down, move towards it
            int targetDifference = paddleX + (paddleWidth / 2) - ballX;
            if (Math.abs(targetDifference) > paddleWidth / 12) {
                if (targetDifference < 0)
                    return PaddleController.COMMAND_MOVE_RIGHT;
                if (targetDifference > 0)
                    return PaddleController.COMMAND_MOVE_LEFT;
            }
        }
        return PaddleController.COMMAND_NOTHING;
    }
}

We could argue that the key input methods shouldn't really be part of this interface, and that the HumanPaddleController should read the input in some other way. While that would make the interface cleaner and a better abstraction of a paddle's controller, I've gone with this approach for simplicity.

A neat thing with abstracting the controller in this way is that it allows for easy debugging; the computer will always play in exactly the same way (provided that the starting conditions are the same), and that means that the programmer won't have to play the game manually to test things like the game over state that kicks in when the game has been beaten. It's also possible to write a computer controller that plays according to a set of instructions recorded when a human controller was used. This is a very good thing to incorporate into simple games as it's a convenient way to get back to a state where a bug was found. The abstraction also hints at a way of creating a network controller so that multiplayer games can be created. The network controller would simply connect to a client controller that would receive the input and provide the commands, and it would be transparent to the implementation that one player is a remote player (not that this example is very applicable to a break out style game).

Collision Detection

Collision detection is an important part of a lot of different games, whether it is to find out when a ball hits a paddle, or when a character walks into a wall. Depending on the type of game, collision detection can be implemented in different ways. One approach is to check if two objects have collided and if they have moved apart. That's the approach I've gone for in this example, but I've structured it so that from the Ball's point of view, the movement is altered before the ball is actually inside one of the bricks or the paddle.

Bounding Box

Central to my collision detection is the BoundingBox class, it represents a rectangle on which collisions can be tested. The concept of a bounding shape is central to a lot of game collision detection and there are many variations such as bounding sphere, circle and cylinders.

The BoundingBox contains the position and size of the rectangle, and using this information, it's easy to determine if a point lies outside or inside (collision) the box.

Java
public class BoundingBox {

    ...

    private int x;
    private int y;
    private int width;
    private int height;

    public BoundingBox(int x, int y, int width, int height) {
        this.x = x;
        this.y = y;
        this.width = width;
        this.height = height;
    }

    public boolean isInside(int px, int py) {
        if (px < x || px > x + width)
            return false;
        if (py < y || py > y + height)
            return false;

        return true;
    }
    
    ...
}

But just determining if a collision has occurred is not enough. In my experience (which, granted, is quite limited in this area), it's often easy to figure out if a collision has occurred. What is difficult to compute is the Collision Response and the information required for that. In this example game, the ball needs to bounce off walls, bricks, and the paddle in a correct way. It won't do to just reverse the direction of the ball, because it's always coming in at an angle. Therefore, it's important to calculate which side of the paddle, wall, or brick the ball collided with.

In the case of a simple break out game, the collision response is fairly simple; if the ball collides with a horizontal edge, negate the vertical velocity, and vice versa. The problem is how to figure out with which edge the ball collided. I've used this method:

  1. Check if the Ball's current position plus its current velocity would place it inside a BoundingBox.
  2. If a collision will occur, use the current velocity and position relative to the BoundingBox's position to figure out which edge the Ball collided with.
  3. Use the edge information to cap the movement of the Ball so that it only travels to the edge of the BoundingBox.
  4. Negate either the vertical or the horizontal velocity depending on the edge collided with.

I came up with a system of regions, and based on which region the ball is in (the regions relate to the edges of the BoundingBox), there are only two possible edges that the Ball could have collided with.

Example

Image 4

In this example, the Ball is in region 8, and is moving with a horizontal velocity of 2 units per second and a vertical velocity of 1 unit per second. The only two edges the Ball can collide with from region 8 are the bottom and right edges. But because the horizontal velocity is greater than the vertical, the colliding edge will be the bottom one.

It doesn't take a genius to see that this method has its flaws and will in some cases pick the wrong edge as the colliding one, but it's close enough for this example. (To correct the method, you'd need to extend a vector out from the closest corner in the direction of the negative velocity of the Ball and then determine which side of that vector the Ball is on; I haven't got the energy to implement that in this example though, I'm saving all the vector math to part 4. :)

The BoundingBox returns a CollisionInformation object with information about where on the edge the collision would have occurred; this information is then used by the Ball to adjust its position and velocity.

Java
public class Ball {

    ...

    public int processCollisions(BrickField brickField) {
        int numberOfRemovedBricks = 0;

        // For each brick in the brick field...
        for(int row = 0; row < brickField.getNumberOfRows(); ++row) {
            for(int column = 0; column < brickField.getNumberOfColumns(); ++column) {
                Brick brick = brickField.getBrick(row, column);

                // If the brick isn't already removed...
                if (!brick.isRemoved()) {
                    BoundingBox brickBoundingBox = brick.getBoundingBox();

                    for (int i = 0; i < xOffset.length; ++i) {
                        int ox = x + xOffset[i];
                        int oy = y + yOffset[i];

                        // Check if there will be a collision
                        BoundingBox.CollisionInfo collisionInfo = 
                          brickBoundingBox.willCollide(ox, oy, deltaX, deltaY);
                        if (collisionInfo != null) {
                            // If there is a collision, remove the brick...
                            brick.remove();
                            ++numberOfRemovedBricks;

                            // ...correct the position to the edge of the brick...
                            x += collisionInfo.CorrectionOffsetX;
                            y += collisionInfo.CorrectionOffsetY;

                            // ...and alter the ball's velocity
                            // depending on the edge collided with.
                            switch(collisionInfo.Edge) {
                                case BoundingBox.EDGE_BOTTOM:
                                case BoundingBox.EDGE_TOP:
                                    deltaY = -deltaY;
                                    break;
                                case BoundingBox.EDGE_LEFT:
                                case BoundingBox.EDGE_RIGHT:
                                    deltaX = -deltaX;
                            }
                        }
                    }
                }
            }
        }
        return numberOfRemovedBricks;
    }

Similar methods are used for the paddle and the walls.

Points of Interest

Generated Resources

Graphics

In this example, you'll notice that there are no images or sprites included, that's because all the graphics in this game is generated on the fly. That's easy to do if the game has simple graphics, by using the different draw methods on the Graphics object. The bricks, ball, and paddle are all generated using the current width and height of the screen. That means that the game will look OK even if the aspect ratio or screen size changes.

This screenshot shows the game running under emulators with different screen sizes:

Image 5

Sounds

The sounds in this game are also generated; check out the SoundManager class to see how you can get the device to play simple notes. That class also knows how to render its volume state to the screen similar to what you'd see on the TV screen when changing volume.

Next Part

In the next part, I'll discuss J2ME's TiledLayer when I demonstrate how to write a top down scroller. I'll also cover how to load resources such as a game level, and continue to show how to use off-screens, menu-screens, and AI controllers.

Image 6

As always, any comments on the article or the code are most welcome.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Sweden Sweden
Article videos
Oakmead Apps Android Games

21 Feb 2014: Best VB.NET Article of January 2014 - Second Prize
18 Oct 2013: Best VB.NET article of September 2013
23 Jun 2012: Best C++ article of May 2012
20 Apr 2012: Best VB.NET article of March 2012
22 Feb 2010: Best overall article of January 2010
22 Feb 2010: Best C# article of January 2010

Comments and Discussions

 
QuestionKnowledge requirements Pin
Islam Hamdane30-Jun-21 5:08
Islam Hamdane30-Jun-21 5:08 
AnswerRe: Knowledge requirements Pin
Fredrik Bornander1-Jul-21 5:27
professionalFredrik Bornander1-Jul-21 5:27 
QuestionHello Pin
Thor Choon Feng13-Jun-13 15:31
Thor Choon Feng13-Jun-13 15:31 
AnswerRe: Hello Pin
Fredrik Bornander13-Jun-13 21:06
professionalFredrik Bornander13-Jun-13 21:06 
GeneralRe: Hello Pin
Thor Choon Feng14-Jun-13 22:07
Thor Choon Feng14-Jun-13 22:07 
GeneralRe: Hello Pin
Fredrik Bornander15-Jun-13 19:36
professionalFredrik Bornander15-Jun-13 19:36 
GeneralRe: Hello Pin
Thor Choon Feng19-Jun-13 15:43
Thor Choon Feng19-Jun-13 15:43 
QuestionDude, Sir, Monsieur ... Pin
venusboysatish26-Feb-12 3:15
venusboysatish26-Feb-12 3:15 
AnswerRe: Dude, Sir, Monsieur ... Pin
Fredrik Bornander26-Feb-12 3:41
professionalFredrik Bornander26-Feb-12 3:41 
QuestionThanks Pin
walterdixon3-Jan-12 7:42
walterdixon3-Jan-12 7:42 
AnswerRe: Thanks Pin
Fredrik Bornander3-Jan-12 7:50
professionalFredrik Bornander3-Jan-12 7:50 
GeneralThanks! Please publish the next one. Pin
klixon7-Mar-11 1:57
klixon7-Mar-11 1:57 
AnswerRe: Thanks! Please publish the next one. Pin
Fredrik Bornander7-Mar-11 2:07
professionalFredrik Bornander7-Mar-11 2:07 
GeneralRe: Thanks! Please publish the next one. Pin
klixon7-Mar-11 3:38
klixon7-Mar-11 3:38 
GeneralRe: Thanks! Please publish the next one. Pin
Fredrik Bornander7-Mar-11 4:09
professionalFredrik Bornander7-Mar-11 4:09 
GeneralRe: Thanks! Please publish the next one. Pin
klixon7-Mar-11 4:30
klixon7-Mar-11 4:30 
AnswerRe: Thanks! Please publish the next one. Pin
Fredrik Bornander7-Mar-11 6:11
professionalFredrik Bornander7-Mar-11 6:11 
GeneralRe: Thanks! Please publish the next one. Pin
klixon8-Mar-11 6:17
klixon8-Mar-11 6:17 
GeneralRe: Thanks! Please publish the next one. Pin
Fredrik Bornander18-Jun-13 4:51
professionalFredrik Bornander18-Jun-13 4:51 
GeneralMy vote of 5 Pin
nhim_nhan17-Feb-11 20:37
nhim_nhan17-Feb-11 20:37 
GeneralRe: My vote of 5 Pin
Fredrik Bornander25-Apr-11 21:32
professionalFredrik Bornander25-Apr-11 21:32 
Generalpretty good article Pin
kush857116-Jul-10 2:03
kush857116-Jul-10 2:03 
GeneralRe: pretty good article Pin
Fredrik Bornander16-Jul-10 5:42
professionalFredrik Bornander16-Jul-10 5:42 
Hi,

Glad you liked it and thank you for taking the time to comment on it. It's always appreciated.

/Fredrik Bornander
GeneralPart 3 : Pin
ragnatelo10-Feb-10 0:58
ragnatelo10-Feb-10 0:58 
AnswerRe: Part 3 : Pin
Fredrik Bornander10-Feb-10 1:24
professionalFredrik Bornander10-Feb-10 1:24 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.