| Sign In/My Account | View Cart |
Your First Micro Java Game
Pages: 1, 2
The typical way of animating with MIDP is to use double-buffering:
repaint() on the MIDlet's main Canvas. Use the Thread.sleep() method within an endless loop to achieve slower or faster frame rates. |
Related Reading
Wireless Java |
Double-buffering in this way guarantees that your animation will occur at the same rate no matter which phone your game is running on.
Note that many phones automatically double-buffer your graphics for you. You can check whether double-buffering is supported within a Canvas using the isDoubleBuffered() method.
You may want to check the isDoubleBuffered() method and only create an offscreen image if double-buffering is not supported; otherwise you can leave it as null. You can then change the graphics context within your paint method appropriately:
if( offscreen != null ){
g = offscreen.getGraphics();
}
Rolling your own double-buffering can make for smoother animations, but copying images can very slow and memory-intensive. Also, some systems repaint the screen slower than the image can be copied -- these phones will show graphic flickering.
Be aware that the refresh rate of most mobile phone screens is much slower than you may be used to for PC game programming. Frame rates of 4 fps (frames per second) are common ... if you're lucky.
The best way to smoothly animate sprites is to create a game loop as a separate thread, and use the System.currentTimeMillis() method to adjust the sprite's position over real time; however, for the sake of simplicity, you can move your sprite in the Canvas' paint() method and call repaint() whenever something changes.
So we could make a spaceship game animate across the screen using the following code. Notice the keyPressed() method, which adjusts the sprite's x value depending on whether the player hits the phone's LEFT button or RIGHT button.
import javax.microedition.lcdui.*;
import javax.microedition.midlet.*;
public class SpaceGame extends MIDlet implements CommandListener
{
// The main display elements
private Display theDisplay;
private SpaceCanvas canvas;
// The exit command, so we can end the game
static final Command exitCommand = new Command("Exit", Command.STOP, 1);
public SpaceGame()
{
// Create the main Display
theDisplay = Display.getDisplay(this);
}
class SpaceCanvas extends Canvas
{
private Sprite shipSprite;
private int width;
private int height;
SpaceCanvas()
{
width = getWidth();
height = getHeight();
// Load the graphics
Image spaceship = null;
try
{
spaceship = Image.createImage("/ship.png");
shipSprite = new Sprite(spaceship);
// Put the sprite at (0,50)
shipSprite.setX(0);
shipSprite.setY(50);
}
catch (Exception ioe)
{
System.err.println("Problem loading image "+ioe);
}
}
public void paint(Graphics g)
{
// Paint a white background
g.setColor( 255, 255, 255 );
g.fillRect( 0, 0, width, height );
g.setColor( 255, 255, 255);
// Paint the sprite
shipSprite.paint(g);
}
public void keyPressed(int keyCode)
{
int key = getGameAction(keyCode);
// Move the sprite left or right.
if (key == LEFT)
{
int newx = shipSprite.getX()-5;
if (newx < 0)
newx = 0;
shipSprite.setX(newx);
}
else if (key == RIGHT)
{
int newx = shipSprite.getX()+5;
if (newx > (width-10))
newx = (width-10);
shipSprite.setX(newx);
}
// Repaint the canvas!
repaint();
}
}
protected void startApp() throws MIDletStateChangeException
{
canvas = new SpaceCanvas();
// Add the exit command
canvas.addCommand(exitCommand);
canvas.setCommandListener(this) ;
theDisplay.setCurrent(canvas);
}
protected void pauseApp() { }
public void destroyApp(boolean unconditional) { }
// Handle events
public void commandAction(Command c, Displayable d)
{
if (c == exitCommand)
{
destroyApp(false);
notifyDestroyed();
}
}
}

As you go forth into the world, coding games more complicated than our space "game"; be sure to remember that every byte counts! It's exceptionally easy to run out of memory on these phones. Try to avoid hashtables and vectors, and recycle any objects you no longer need. For example, instead of creating two buttons on two separate screens, try to merely change the label on an existing button.
Also, avoid using costly operations like string concatenations. Use a StringBuffer instead. As for interface design, remember your audience and the limitation of the device. Use few, large, simple components that require as few keypad presses as possible.
While your application is running, you can sniff out the memory using:
Runtime.getRuntime().freeMemory()
and
Runtime.getRuntime().totalMemory()
Remember to strategically garbage collect whenever resources fall too low, using:
Runtime.getRuntime().gc()
You should also use a code packer or obfuscator to compress your bytecode as much as possible. A good obfuscator such as IBM's jax can make your final application as much as 30 percent smaller!
For more articles, tips, and sample applications, visit the Micro Java Network. Bill Day's J2ME Archive also has tons of sample applications, links to IDEs and SDKs, and anything else J2ME related. Also, I'll shamelessly plug my book Micro Java Game Development, which discusses sprites, sprite managers, collisions, transparency, graphical effects, animations, custom extensions, and more.
David Fox is launching a game company in New York City. He's also the author of numerous books and articles about cyberculture and technology.
Return to ONJava.com.