Mobile Game Development

Published on March 2017 | Categories: Documents | Downloads: 29 | Comments: 0 | Views: 220
of 31
Download PDF   Embed   Report

Comments

Content



Download Basics - 59.88 KB

Introduction
This article is the first part in my Mobile Game Programing for Beginners series. The series start 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. The series is a four part series where I'll go through the following games:
• • • •

Basics BreakOut Top Down Scroller 3D Space Game

This part will show you how to implement a very simple game, which tools you'll need and how to actually try it out on your handset. In this game, the goal is to navigate your avatar to a target, no obstacles and the target isn't even moving. There's no way to "die", no way to loose, but it's a good starting point to show the basics of game programming. The areas I'll cover in this first part are:
• • •

Tools of the Trade, the applications required (or at least the ones I like to use). Creating a MIDlet, an application that can run on your mobile phone. Basic Game Loop, the foundation of any game.



Reading user input.

Tools of the Trade
All the games in this series have been developed using three software packages:
• • •

Java 6 SDK NetBeans Paint.NET

Java 6 SDK
The Java SDK (or JDK, Java Development Kit) is a collection of class libaries, tools and documentation used when developing Java applications. It is not to be confused with a JRE, Java Runtime Environment, which is something that is used to run Java based application. I downloaded my JDK from here. You'll need a JDK installed to use the next tool on the list, NetBeans, so make sure this is the first thing you download.

NetBeans
NetBeans is an Integrated Development Environment, IDE, mainly for Java development. It is free, which is good, but the best thing about it is that it truly rocks. According to me it's by far the best Java IDE around right now. NetBeans is the tool that you'll use to implement and test your game as it comes bundles with a neat emulator so that you don't have to install the game on your mobile phone before you know it works. Go here and download the Mobility pack and get that installed. The Mobility pack includes the J2ME JDK (Java 2 Mobile Edition), which contains the class libraries required to write a MIDlet (which is a java application that can run on a mobile device), and the tools required (such as emulators for testing the game without having to install it on an actual handset).

Paint.NET
Almost all games needs some sort of graphics to make them enjoyable, I prefer to use Paint.NET to create the graphics for my games (when it's not generated by the game itself, but more on that in the second part of the series). The reason I'm not using Paint that comes bundles with windows is that it's simply not packed with all the nice features that Paint.NET have, such as support for transparent png files. Head on over to Paint.NET's download page and download it. Once you have these three tools installed you're almost ready to start coding.

Basic Game Loop
Almost all games rely on a central game loop, it is the loop that manages or control the game. As the games you write become more and more complicated the methods called from the game loop will have to contain more and more logic, but the actual game loop will still be a fairly simple loop. In its simplest form it might look something like this: Collapse
while(gameShouldStillBeRunning) { // Capture input readInput(); // Update the game state updateGameState(); // Present the game to the user, i. e. render or draw it to screen renderGameState(); // Check for game over checkGameState(); }

Reading Input
This method is responsible for reading or capturing the current state of the input. This includes for example checking which keys are pressed and how far the mouse has moved since last check. The method typically stores the input that is relevant to the game in a place where the updateGameState method can access it. Storing the game state can be as easy as setting a boolean value to true if a key is pressed and to false it it's not pressed. The reason for checking the input and storing it first, instead of checking it when you actually need it (i.e when updating the game state), is because it is often quite expensive CPU wise to read the input and it is best to get all the input required at once. Otherwise different parts of the code that updates the game state might query the OS several times for the same key input, which would be unecessary. Making the games run smooth and fast is a major part of game development and I will discuss this in a later part of this series. You'll notice that the game included in this article doesn't read the input state in the game loop, that is because it's relying on a built in functionality of the javax.microedition.lcdui.Canvas, more on this later.

Update Game State
Updating the game state include all the actual processing of the game logic, such as moving the players character according to the input captured, moving enemies according

to their AI, updating the environment, checking for game over state and many more things all depending on what kind of game is being developed. In this first part, updating the game state includes only moving the avatar and checking if the avatar has reached the goal.

Render Game State
When the game state is rendered the state of the game (i.e. location of the player, the map and for example the current score) is drawn to the screen. The Canvas class in the javax.microedition.lcdui package is used to render things to the screen in kind of the same way as java.awt or javax.swing components, by overriding the Canvas.paint(Graphics graphics). Note that the Graphics parameter isn't the java.awt.Graphics object that you might reconize from desktop development, it is a javax.microedition.lcdui.Graphics object, which is like a cut down version of the AWT one.

Check for Game Over
This is where the game primarily detects if the player has lost or beaten the game, but it will also detect and act on any major state changes. Such as completing a level and moving into a game state where the level summary is shown before the next level is started (yet another state).

Getting Started
Setting up the Project
The first thing to do to get started is setting up a project in NetBeans, it is important to pick the right type of project as the project type dictates the JDK that will be used (remember we need the J2ME JDK to get this to run on a mobile device). Create a MIDP Application Project In NetBeans, select File->New Project... and select project category Mobility and projec MIDP Application, then click Next.

Give your project a name, in this example it is called Basics.GameLoop. Then make sure to un-tick the Create Hello MIDlet as that will create a template designed to handle a Control based UI (with buttons and lists), and for a game all rendering is custom so no standard controls will be used. Then click Next.

The next screen shows configuration options, the settings picked here must match the target device's capabilities. That means that a game written for MIDP-2.1 will not run on a mobile phone that only supports MIDP-1.0 for example. For now leave this page with it's default settings and click Finish.

In your new project's source package create a package (as keeping classes in the root package is discouraged), you can call the package whatever you want, in the example application it is called com.bornander.games.basics. Create a class called BasicsMIDletin the package you just created, this will be the entry point for the application. This is the class that the Java runtime on the mobile device will instanciate when the the user selects your midlet. Make sure the BasicsMIDlet class extends javax.microedition.midlet.MIDlet, as this class defines the interface used by the runtime to control a midlet. The javax.microedition.midlet.MIDlet is an abstract class and there are three methods that must be overridden:
startApp()

This method is called by the runtime to start the midlet, either when a start is requested by the user by selecting the midlet in some menu or when a the mobile device decides to return control back to a previously paused midlet.
pauseApp()

This method is called by the runtime to indicate to an midlet that it will loose focus, possibly due to an incomming call. It is entirely up to the midlet to actually pause something, the example game included in this article will ignore this method which means the game would continue to run when a call comes in. If a midlet decides to pause itself is should, after taken steps to suspend itself in a controled manner, call resumeRequest() method to notify the framework that it is interested to know when it can resume processing.
destroyApp(boolean conditional)

This method is called when a midlet is being destroyed (closed), to allow it to clean up its resources in a safe and controlled way. Make sure your BasicsMIDlet implements these three, abstract methods, after which your class should look something like this:

Collapse
package com.bornander.games.basics; import javax.microedition.midlet.MIDlet; public class BasicsMIDlet extends MIDlet { public BasicsMIDlet() { } public void startApp() { } public void pauseApp() { } public void destroyApp(boolean unconditional) { } }

The next step is to add the the MIDlet to the Application Descriptor, which is a set of meta data that holds information about the mobile application. One mobile application can contain several MIDlets but for this example there's only going to be one. Right-click the Basics.GameLoop and click Properties, then select Application Descriptor, the MIDlets tab and click Add.... This will let you select a MIDlet class, give it a name and icon and add it to the descriptor. The dialog auto-detects classes that extends javax.microedition.midlet.MIDlet so just click Ok to accept the default proposal.

And that's it! The mobile application is created, click Run Main Project to try it out on the emulator, this will launch a mobile phone emulator from where the MIDlet just

created can be started. Note that since there's no actual implementation yet it won't do anything. Add a System.out.println("Hello, world!"); to the BasicMIDlet.startApp method and run the application again to see that it's actually started. The text will be printed to NetBeans IDE so do not expect to see it on the screen of the emulator.

Rendering to screen
When writing MIDlets there's a set of classes for labels, text fields, checkboxes and such that are used in a similar way to the corresponding classes in Swing or AWT. As this article is about game programming it won't discuss any of these controls as they're not very well suited for graphical games. The class used to render to screen in javax.microedition.lcdui.Canvas, by extending this class it is possible to override its paint method and implement custom rendering. Create a new class called MainCanvas and have it extend javax.microedition.lcdui.Canvas. Override paint to render red text on blue background:
package com.bornander.games.basics; import javax.microedition.lcdui.Canvas; import javax.microedition.lcdui.Font; import javax.microedition.lcdui.Graphics; public class MainCanvas extends Canvas implements Runnable { public MainCanvas() { } // The Graphics object is used to render images, lines, shapes and // text to the screen using different draw methods. protected void paint(Graphics graphics) { // Get the width and height of the screen in pixels int w = getWidth(); int h = getHeight(); filled // Set the current color to blue (hex RGB value ) and draw a // rectangle the size of the screen graphics.setColor(0x00007F); graphics.fillRect(0, 0, w, h); and // Set the current color to red, the font to the default font // draw a string to the center of the screen. graphics.setColor(0xFF0000); graphics.setFont(Font.getDefaultFont()); graphics.drawString("Hello, world!", w / 2, h / 2, Graphics.BASELINE | Graphics.HCENTER);

Collapse

}

}

In order to get the MIDlet to use this Canvas it has to be set as the current one for the display. Modify BasicMIDlet.startApp to create a MainCanvas and set it to the default Display:
public void startApp() { MainCanvas mainCanvas = new MainCanvas(); Display.getDisplay(this).setCurrent(mainCanvas); }

Collapse

Run the MIDlet again, this time is should look something like this:

Now it's time to start implementing the actual game.

Implementing the Game
Loading resources
Even though it would be possible to render all aspects of a game using the different draw methods on the javax.microedition.lcdui.Graphics it is more common to use

images created in a image editing program (such as Paint.NET). This game will use two different images, one for the player or avatar and one for the target. Avatar: Target: When creating images it is important to make use of transparent pixels otherwise the avatar image wouldn't appear to be round but square. By adding the resources to a Java package it is possible to load them into javax.microedition.lcdui.Image objects which can then be drawn onto the javax.microedition.lcdui.Graphics. A convienient way of loading resources is to use the Class.getResourceAsStream method as it allows for referencing resources using java package names (but with forward slashes instead of dots). Also, since the easiest way to get a Class instance is to call Object.getClass, there's always a simple way to reference resources in the same bundle as the current class. Image resource can therefore be placed in the normal java package structure:

By adding two javax.microedition.lcdui.Images as members to the MainCanvas, and loading the resources in the constructor the paint method can be modified to draw the avatar in the upper left corner and the target in the lower right. Notice that it's good practice not to use absolute coordinates (such as 100, 100), but instead calcualte the coordinates based on the current screen width and height. Obviously

0, 0 is always going to be the upper left corner, but the coordinates of the lower right will vary on different mobile devices. By finding the widht and height of the screen, and then subtracting the width and height of the image, the target image is guaranteed to always appear in the lower right corner, regardless of screeen resolution. Collapse
package com.bornander.games.basics; import import import import import java.io.IOException; javax.microedition.lcdui.Canvas; javax.microedition.lcdui.Font; javax.microedition.lcdui.Graphics; javax.microedition.lcdui.Image;

public class MainCanvas extends Canvas implements Runnable { // Declare members for the images private Image avatar; private Image target; public MainCanvas() throws IOException { // Load the image resources avatar = Image.createImage( getClass().getResourceAsStream("/com/bornander/games/basics /resources/avatar.png")); target = Image.createImage( getClass().getResourceAsStream("/com/bornander/games/basics /resources/target.png")); } protected void paint(Graphics graphics) { int w = getWidth(); int h = getHeight(); graphics.setColor(0x00007F); graphics.fillRect(0, 0, w, h); // Draw the images graphics.drawImage(avatar, 0, 0, 0); graphics.drawImage(target, w - target.getWidth(), h target.getHeight(); graphics.setColor(0xFF0000); graphics.setFont(Font.getDefaultFont()); graphics.drawString("Hello, world!", w / 2, h / 2, Graphics.BASELINE | Graphics.HCENTER); } }

Making the Avatar controllable

Always painting the Avatar in the upper left corner isn't going to make for a very interesting game, by declaring members for position and also travel direction it will be possible to control the Avatar using the keypad. Normally I'd prefer to store the location of a Avatar in 2D space as some sort of Point object, but as the J2ME libraries is a cut down version of the J2SE there is no such class. I could have written my own (as I have for the other games in this series), but decided to store the position as two seperate integers. I store the direction as four seperate booleans, for up, down, left and right. Collapse
public class MainCanvas extends Canvas implements Runnable { // The two images private Image avatar; private Image target; // The coordinates of the player. private int x = 0; private int y = 0; // Flags indicating which buttons are pressed. private boolean up = false; private boolean down = false; private boolean left = false; private boolean right = false; } ...

Moving the avatar (i.e. updating it's X and Y coordinates according to the directon flags) is delegated to a method that also does constraint checking. By constraint checking I mean the process in which the position is constained to a valid one. In this example it is invalid for the avatar to be outside the bounds of the screen.
public class MainCanvas extends Canvas implements Runnable { ... private void moveAvatar() { if (up) --y; if (down) ++y; if (left) --x; if (right) ++x; if (y < 0) y = 0; if (y > getHeight() - avatar.getHeight())

Collapse

y = getHeight() - avatar.getHeight(); if (x x if (x x } } < = > = 0) 0; getWidth() - avatar.getWidth()) getWidth() - avatar.getWidth();

Notice again the use of getWidth/getHeight and avatar.getWidth/avatar.getHeight to make the constraint checking indenpendent of screen size as well as image size. This method, MainCanvas.moveAvatar. will be called by the game loop for each iteration to update the position of the player. Obviously this also requires a small change to the MainCanvas.paint method, as the avatar is no longer drawn at (0, 0) but at (x, y).

Capturing the Input
By extending Canvas it's easy to capture input by simply overriding some methods. Canvas exposes three methods related to key input:
• • • void keyPressed(int keyCode) void keyReleased(int keyCode) void keyRepeated(int keyCode)

In this first example I'm only going to use the keyPressed and keyReleased, and to control the Avatar the only thing the methods needs to is to set the up, down, left and right according to which key was pressed or released. The keyCode parameter passed to the key handling can be converted to key codes better suited for game programming using the aptly named function Canvas.getGameAction, this will convert the key code into one that can be checked for buttons such as up and down. Collapse
/** * This gets called for us whenever a key is pressed. * @param key The pressed key. */ protected void keyPressed(int key) { int gameKey = getGameAction(key); switch(gameKey) { case Canvas.UP: up = true; break; case Canvas.DOWN: down = true; break; case Canvas.LEFT: left = true; break; case Canvas.RIGHT: right = true; break; case Canvas.FIRE: shouldRun = false; break; }

} /** * This gets called for us whenever a key is released. * @param key The released key. */ protected void keyReleased(int key) { int gameKey = getGameAction(key); switch(gameKey) { case Canvas.UP: up = false; break; case Canvas.DOWN: down = false; break; case Canvas.LEFT: left = false; break; case Canvas.RIGHT: right = false; break; } }

Checking for Game Over state
All games needs to check the Game Over state, this can occur either when the player loses the game or when he beats it, in any case it has to be checked or the game can neither be won or lost. In this first example Game Over can only occur when the player has beaten the game, yep, that's right; this is a game you can't fail at. You can choose not to win, but you can't lose. This implementation calls a method called isGameCompleted to check if the game is over, this method sets a member variable completed. To change the behaviour of the game when the Game Over state is reached, the run method looks at the completed flag set when the Avatar has reached the Goal and if it's true then the Avatar isn't moved anymore, regardless of key presses. Also, the paint message draws a Game Over message at the center of the screen.
/** * Detects if the game has been completed (i.e. the avatar has navigated to the target). * @return true if the game is completed. */ private boolean isGameCompleted() { return x == targetX && y == targetY; }

Collapse

This method is called in the run method. Collapse
protected void paint(Graphics graphics) { ... if (completed) { graphics.setColor(0xA0A0FF); graphics.setFont(Font.getDefaultFont());

graphics.drawString("Game Over", w / 2, h / 2, Graphics.BASELINE | Graphics.HCENTER); } } public void run() { while(shouldRun) { completed = isGameCompleted(); if (!completed) { moveAvatar(); } repaint(); try { Thread.sleep(20); } catch (InterruptedException ex) { } } owner.exit(); }

And that's it! That's the whole game, it's not the most addictive game in the world but it is enough to show how to set up a Netbeans project and the basics of a game.

Next Part
In the next part I'll cover menus, simple AI and basic collision detection as I demonstrate how to write a BreakOut style game.

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


Download BrickBreaker - 172.36 KB

Introduction
This article is the second part in my Mobile Game Programing for Beginners series. The series start 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. The series 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 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 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 movment, collision detection and rendering.
Brick

Representation of a single brick.
BrickField

Representation of all 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 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 render 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 it's of set of GameScreens.

GameScreen
The GameScreen class is an abstract class that exposes a few method required by my main MIDlet class in order to control and update the GameScreen's state and request it to render itself. Collapse
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 active screen, and that screen can request the main canvas to change 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 retrive the settings from the previous screen as that is passed as an argument when a screen transisition 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 a in-game dialog rather than taking the user back to a fully 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 the paused state it stops updating the paddle's and ball's position, it still renders them but it also renders a dialog box in the foreground. The game state change from running to paused also changes how 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 the user has paused the game.

This states are defined as simple ints in BrickBreakerScreen: Collapse
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 makes 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 these options to go for are:
• •

Position of the ball Position and size of the paddle

One 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 as 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: Collapse
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 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 return 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) { }

Collapse

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 private private private int int int int ballX = 0; ballY = 0; ballDeltaX = 0; ballDeltaY = 0;

Collapse

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; } }

One could argue that the key input method's 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 convinient 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 that example is very applicable to a break out style game).

Collision Detection
Collision detection is an important part of a lot of different games, wether 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 has collided, and if they have move them 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 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 private private private int int int int x; y; width; height;

Collapse

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 occured 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 occured, 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 calulate which side of the paddle, wall or brick the ball collided with. In the case of a simple break out game, the collision response if 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 it's 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's 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 is the bottom and right edge. 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 it's 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 closes corner in the direction of the negative velocity of the Ball and then determine which side of that vector that the Ball is on, I haven't got the energy to implement that in this example though, I'm saving all the vector maths to part 4 :). The BoundingBox returns a CollisionInformation object with information about where on the edge the collision would have occured, this information is then used by the Ball to adjust it's position and velocity. Collapse
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 ractio or screen size changes. This screen shot 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 it's 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 dicuss 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 offsceens, menuscreens and AI controllers.

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)

Sponsor Documents

Or use your account on DocShare.tips

Hide

Forgot your password?

Or register your new account on DocShare.tips

Hide

Lost your password? Please enter your email address. You will receive a link to create a new password.

Back to log-in

Close