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 Canvas
es, but I prefer to always use just one GameCanvas
that maintains its set of GameScreen
s.
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.
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.
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.
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 int
s in BrickBreakerScreen
:
public class BrickBreakerScreen extends GameScreen {
...
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
:
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:
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:
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) {
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 {
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.
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:
- Check if the
Ball
's current position plus its current velocity would place it inside a BoundingBox
.
- 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.
- Use the edge information to cap the movement of the
Ball
so that it only travels to the edge of the BoundingBox
.
- 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
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.
public class Ball {
...
public int processCollisions(BrickField brickField) {
int numberOfRemovedBricks = 0;
for(int row = 0; row < brickField.getNumberOfRows(); ++row) {
for(int column = 0; column < brickField.getNumberOfColumns(); ++column) {
Brick brick = brickField.getBrick(row, column);
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];
BoundingBox.CollisionInfo collisionInfo =
brickBoundingBox.willCollide(ox, oy, deltaX, deltaY);
if (collisionInfo != null) {
brick.remove();
++numberOfRemovedBricks;
x += collisionInfo.CorrectionOffsetX;
y += collisionInfo.CorrectionOffsetY;
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:
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.
As always, any comments on the article or the code are most welcome.