MIDP 2.0 Games: a Step-by-Step Tutorial with Code Samples (Step 4)

Now that I’ve discussed the special functions of a GameCanvas, I’d like to go over the main function of a Canvas in general, namely painting the screen. This usually takes place in the method paint(Graphics g) which you can override. The Graphics object can be queried for screen dimensions and then can be used to draw strings, images, and simple geometric objects such as rectangles. The Graphics object has a rich selection methods for drawing things on the screen. (It’s similar to the java.awt.graphics package except that since javax.microedition.lcdui.Graphics is intended for small devices it saves memory by having each shape be drawn by a simple method call instead of creating individual objects.) It would take several pages for me to go over all of the things that you can draw with the Graphics object, so here I will stick to discussing the parts of the class that I used. Plus the JavaDoc for javax.microedition.lcdui.Graphics is quite thorough and clear, so you should definitely look it over when writing your game.


In my example game “Tumbleweed” I need to draw a cowboy walking through a prairie jumping over tumbleweeds. The screen during the game looks like this:



As you can see I’ve put the score on the bottom and the time remaining on the top. (To simplify the game I just have it end when the player runs out of time.) As the cowboy is walking along, I would like his background to scroll to the right or to the left (otherwise he won’t have very far to go on such a small screen…) but I would like the time and the score to stay in place. To accomplish this I have my JumpCanvas class take care of painting the stable strip on the top and the bottom of the screen and I delegate the interesting graphics to the LayerManager (more details on that in the next section).


Looking in the paint(Graphics g) method, you see that the first step is to use the graphics object to get the screen dimensions and then use that information to calculate where the objects should be placed. If you’re interested in maintaining Java’s “write once, run anywhere” philosophy, it is obviously better to base the screen layout on the (dynamically determined) dimensions of the current screen rather than basing the dimensions on fixed constants. Even so your game will likely look strange on a screen that is significantly larger or smaller than the one you wrote the game on. You may want to throw an Exception if the screen size is outside of a reasonable max or min.


At the risk of belaboring the obvious, I’ll point out that in the paint(Graphics g) method, after calculating the appropriate sizes for the top and bottom regions, I paint the top one white and the bottom one green with g.fillRect and then I use g.drawString to add the time and the score. (Don’t ask me why my prairie has both green grass AND tumbleweeds: my only excuse is that I know more about Java than I know about the wild west…) Then I calculate the size of the region between them and pass it along to my subclass of LayerManager.


The LayerManager Class


The interesting graphical objects in a J2ME game are usually represented by subclasses of the javax.microedition.lcdui.game.Layer class. The background layers could be instances of javax.microedition.lcdui.game.TiledLayer and the player (and his enemies) would likely be instances of javax.microedition.lcdui.game.Sprite, both of which are subclasses of Layer. The LayerManager class helps you to organize all of these graphical layers. The order in which you append your Layers to your LayerManager determines the order in which they will be painted. (The first one appended is the last one painted.) The top layers will cover the lower layers although you can allow parts of the lower layers to show through by creating image files that have transparent regions.


Probably the most useful aspect of the LayerManager class is that you can create a graphical painting that is much larger than the screen and then choose which section of it will appear on the screen. Imagine drawing a huge and elaborate drawing and then covering it with a piece of paper that has a small rectangular hole that you can move around. The whole drawing represents what you can stock into the LayerManager, and the hole is the window showing the part that appears on the screen at any given time. Allowing the possibility of a virtual screen that is much larger than the actual screen is extremely helpful for games on devices with very small screens. It will save you huge amounts of time and effort if for example your game involves a player exploring an elaborate dungeon. The confusing part is that this means that you have to deal with two separate coordinate systems. The Graphics object of the GameCanvas has one coordinate system, but the various Layers need to be placed in the LayerManager according to the LayerManager’s coordinate system. So keep in mind that the method LayerManager.paint(Graphics g, int x, int y) paints the layer on the screen according to the coordinates of the GameCanvas whereas the method LayerManager.setViewWindow(int x, int y, int width, int height) sets the visible rectangle of the LayerManager in terms of the LayerManager’s coordinate system.


In my example I have a very simple background (it’s just a repeating series of patches of grass), but I would like the cowboy to stay in the middle of the screen as he goes to the right and left, so I need to continuously change which part of the LayerManager’s graphical area is visible. I do this by calling the method setViewWindow(int x, int y, int width, int height) from the paint(Graphics g) method of my subclass of LayerManager (called JumpManager). More precisely, what happens is the following: The main loop in the GameThread calls JumpCanvas.checkKeys() which queries the key states and tells the JumpManager class whether the cowboy should be walking to the right or to the left and whether he should be jumping. JumpCanvas passes this information along to JumpManager by calling the methods setLeft(boolean left) or jump(). If the message is to jump, the JumpManager calls jump() on the cowboy Sprite. If the message is that the cowboy is going to the left (or similarly to the right), then when the GameThread calls the JumpCanvas to tell the JumpManager to advance (in the next step of the loop), the JumpManager tells the cowboy sprite to move one pixel to the left and compensates by moving the view window one pixel to the right to keep the cowboy in the center of the screen. These two actions are accomplished by incrementing the field myCurrentLeftX (which is the x-coordinate that is sent to the method setViewWindow(int x, int y, int width, int height)) and then calling myCowboy.advance(gameTicks, myLeft). Of course I could keep the cowboy centered by not moving him and not appending him to the LayerManager but rather painting him separately afterwards, but it’s easier to keep track of everything by putting all of the moving graphics on one set of layers and then keeping the view window focused on the cowboy Sprite. While telling the cowboy to advance his position, I also have the tumbleweed Sprites advance their positions and I have the grass TiledLayer advance its animation and then I check if the cowboy has collided with any tumbleweeds, but I will go into more detail on those steps in the following sections. After moving the game pieces around, the JumpManager calls the method wrap() to see if the view window has reached the edge of the background, and if so, to move all of the game objects so that the background appears to continue indefinitely in both directions. Then the JumpCanvas repaints everything and then the game loop begins again.


I’ll just add a few words here about the method wrap(). The class LayerManager unfortunately does not have a built in wrapping capability for the case in which you have a simple background that you would like to have repeat indefinitely. The LayerManager’s graphical area will appear to wrap when the coordinates sent to setViewWindow(int x, int y, int width, int height) exceed the value Integer.MAX_VALUE, but that is unlikely to help you. Thus you have to write your own functions to prevent the player Sprite from leaving the region that contains background graphics. In my example, the background grass repeats after the number of pixels given by Grass.TILE_WIDTH*Grass.CYCLE. So whenever the x-coordinate of the view window (myCurrentLeftX) is an integer multiple of the length of the background, I move the view window back to the center and also move all of the Sprites in the same direction which seamlessly prevents the player from reaching the edge.


Here’s the code for JumpManager.java:


package net.frog_parrot.jump;


import javax.microedition.lcdui.*;
import javax.microedition.lcdui.game.*;


/**
 * This handles the graphics objects.
 *
 * @author Carol Hamer
 */
public class JumpManager extends javax.microedition.lcdui.game.LayerManager {


  //———————————————————
  //   dimension fields
  //  (constant after initialization)


  /**
   * The x-coordinate of the place on the game canvas where
   * the LayerManager window should appear, in terms of the
   * coordiantes of the game canvas.
   */
  static int CANVAS_X;


  /**
   * The y-coordinate of the place on the game canvas where
   * the LayerManager window should appear, in terms of the
   * coordiantes of the game canvas.
   */
  static int CANVAS_Y;


  /**
   * The width of the display window.
   */
  static int DISP_WIDTH;


  /**
   * The height of this object’s graphical region. This is
   * the same as the height of the visible part because
   * in this game the layer manager’s visible part scrolls
   * only left and right but not up and down.
   */
  static int DISP_HEIGHT;


  //———————————————————
  //   game object fields


  /**
   * the player’s object.
   */
  Cowboy myCowboy;


  /**
   * the tumbleweeds that enter from the left.
   */
  Tumbleweed[] myLeftTumbleweeds;


  /**
   * the tumbleweeds that enter from the right.
   */
  Tumbleweed[] myRightTumbleweeds;


  /**
   * the object representing the grass in the background..
   */
  Grass myGrass;


  /**
   * Whether or not the player is currently going left.
   */
  boolean myLeft;


  /**
   * The leftmost x-coordinate that should be visible on the
   * screen in terms of this objects internal coordinates.
   */
  int myCurrentLeftX;


  //—————————————————–
  //    gets/sets


  /**
   * This tells the player to turn left or right.
   * @param left whether or not the turn is towards the left..
   */
  void setLeft(boolean left) {
    myLeft = left;
  }


  //—————————————————–
  //    initialization and game state changes


  /**
   * Constructor merely sets the data.
   * @param x The x-coordinate of the place on the game canvas where
   * the LayerManager window should appear, in terms of the
   * coordiantes of the game canvas.
   * @param y The y-coordinate of the place on the game canvas where
   * the LayerManager window should appear, in terms of the
   * coordiantes of the game canvas.
   * @param width the width of the region that is to be
   * occupied by the LayoutManager.
   * @param height the height of the region that is to be
   * occupied by the LayoutManager.
   */
  public JumpManager(int x, int y, int width, int height) {
    CANVAS_X = x;
    CANVAS_Y = y;
    DISP_WIDTH = width;
    DISP_HEIGHT = height;
    myCurrentLeftX = Grass.CYCLE*Grass.TILE_WIDTH;
    setViewWindow(0, 0, DISP_WIDTH, DISP_HEIGHT);
  }


  /**
   * sets all variables back to their initial positions.
   */
  void reset() {
    if(myGrass != null) {
      myGrass.reset();
    }
    if(myCowboy != null) {
      myCowboy.reset();
    }
    if(myLeftTumbleweeds != null) {
      for(int i = 0; i < myLeftTumbleweeds.length; i++) {
 myLeftTumbleweeds[i].reset();
      }
    }
    if(myRightTumbleweeds != null) {
      for(int i = 0; i < myRightTumbleweeds.length; i++) {
 myRightTumbleweeds[i].reset();
      }
    }
    myLeft = false;
    myCurrentLeftX = Grass.CYCLE*Grass.TILE_WIDTH;
  }


  //——————————————————-
  //  graphics methods


  /**
   * paint the game graphic on the screen.
   * initialization code is included here because some
   * of the screen dimensions are required for initialization.
   */
  public void paint(Graphics g) throws Exception {
    // create the player:
    if(myCowboy == null) {
      myCowboy = new Cowboy(myCurrentLeftX + DISP_WIDTH/2,
       DISP_HEIGHT – Cowboy.HEIGHT – 2);
      append(myCowboy);
    }
    // create the tumbleweeds to jump over:
    if(myLeftTumbleweeds == null) {
      myLeftTumbleweeds = new Tumbleweed[2];
      for(int i = 0; i < myLeftTumbleweeds.length; i++) {
 myLeftTumbleweeds[i] = new Tumbleweed(true);
 append(myLeftTumbleweeds[i]);
      }
    }
    if(myRightTumbleweeds == null) {
      myRightTumbleweeds = new Tumbleweed[2];
      for(int i = 0; i < myRightTumbleweeds.length; i++) {
 myRightTumbleweeds[i] = new Tumbleweed(false);
 append(myRightTumbleweeds[i]);
      }
    }
    // create the background object:
    if(myGrass == null) {
      myGrass = new Grass();
      append(myGrass);
    }
    // this is the main part of the method:
    // we indicate which rectangular region of the LayerManager
    // should be painted on the screen and then we paint
    // it where it belongs.  The call to paint() below
    // prompts all of the appended layers to repaint themselves.
    setViewWindow(myCurrentLeftX, 0, DISP_WIDTH, DISP_HEIGHT);
    paint(g, CANVAS_X, CANVAS_Y);
  }


  /**
   * If the cowboy gets to the end of the graphical region,
   * move all of the pieces so that the screen appears to wrap.
   */
  void wrap() {
    if(myCurrentLeftX % (Grass.TILE_WIDTH*Grass.CYCLE) == 0) {
      if(myLeft) {
 myCowboy.move(Grass.TILE_WIDTH*Grass.CYCLE, 0);
 myCurrentLeftX += (Grass.TILE_WIDTH*Grass.CYCLE);
 for(int i = 0; i < myLeftTumbleweeds.length; i++) {
   myLeftTumbleweeds[i].move(Grass.TILE_WIDTH*Grass.CYCLE, 0);
 }
 for(int i = 0; i < myRightTumbleweeds.length; i++) {
   myRightTumbleweeds[i].move(Grass.TILE_WIDTH*Grass.CYCLE, 0);
 }
      } else {
 myCowboy.move(-(Grass.TILE_WIDTH*Grass.CYCLE), 0);
 myCurrentLeftX -= (Grass.TILE_WIDTH*Grass.CYCLE);
 for(int i = 0; i < myLeftTumbleweeds.length; i++) {
   myLeftTumbleweeds[i].move(-Grass.TILE_WIDTH*Grass.CYCLE, 0);
 }
 for(int i = 0; i < myRightTumbleweeds.length; i++) {
   myRightTumbleweeds[i].move(-Grass.TILE_WIDTH*Grass.CYCLE, 0);
 }
      }
    }
  }


  //——————————————————-
  //  game movements


  /**
   * Tell all of the moving components to advance.
   * @param gameTicks the remainaing number of times that
   *        the main loop of the game will be executed
   *        before the game ends.
   * @return the change in the score after the pieces
   *         have advanced.
   */
  int advance(int gameTicks) {
    int retVal = 0;
    // first we move the view window
    // (so we are showing a slightly different view of
    // the manager’s graphical area.)
    if(myLeft) {
      myCurrentLeftX–;
    } else {
      myCurrentLeftX++;
    }
    // now we tell the game objects to move accordingly.
    myGrass.advance(gameTicks);
    myCowboy.advance(gameTicks, myLeft);
    for(int i = 0; i < myLeftTumbleweeds.length; i++) {
      retVal += myLeftTumbleweeds[i].advance(myCowboy, gameTicks,
      myLeft, myCurrentLeftX, myCurrentLeftX + DISP_WIDTH);
      retVal -= myCowboy.checkCollision(myLeftTumbleweeds[i]);
    }
    for(int i = 0; i < myLeftTumbleweeds.length; i++) {
      retVal += myRightTumbleweeds[i].advance(myCowboy, gameTicks,
           myLeft, myCurrentLeftX, myCurrentLeftX + DISP_WIDTH);
      retVal -= myCowboy.checkCollision(myRightTumbleweeds[i]);
    }
    // now we check if we have reached an edge of the viewable
    // area, and if so we move the view area and all of the
    // game objects so that the game appears to wrap.
    wrap();
    return(retVal);
  }


  /**
   * Tell the cowboy to jump..
   */
  void jump() {
    myCowboy.jump();
  }


}


 

MIDP 2.0 Games: a Step-by-Step Tutorial with Code Samples (Step 3)

The GameCanvas Class


The GameCanvas class represents the area of the screen that the device has allotted to your game. The javax.microedition.lcdui.game.GameCanvas class differs from its superclass javax.microedition.lcdui.Canvas in two important ways: graphics buffering and the ability to query key states. Both of these changes give the game developer enhanced control over precisely when the program deals with events such as keystrokes and repainting the screen.


The graphics buffering allows all of the graphical objects to be created behind the scenes and then flushed to the screen all at once when they’re ready. This makes animation smoother. I’ve illustrated how to use it in the method advance() in the code below. (The method advance() is called from the main loop of my GameThread object.) Notice that all you need to do is call paint(getGraphics()) and then call flushGraphics(). To make your program more efficient there is even a version of the flushGraphics() method which allows you to repaint just a subset of the screen if you know that only part has changed. As an experiment I tried replacing the calls to paint(getGraphics()) and flushGraphics() with calls to repaint() and then serviceRepaints() as you might if your class extended Canvas instead of GameCanvas. In my simple examples it didn’t make much difference, but if your game has a lot of complicated graphics the GameCanvas version will undoubtedly make a big difference.


If you’re following along in the code below, you’ll notice that after flushing the graphics (still in the method advance()), I have the thread wait one millisecond. This is partially to be sure that the freshly painted graphics stay on the screen for an instant before the next paint, but it is also useful to help the keystroke query work correctly. As I mentioned above, the ability to query key states is one of the differences between GameCanvas and Canvas. With Canvas, if you want to know about keystroke events you must implement the keyPressed(int keyCode) method which the enveloping Java program will call when it wants to tell your program that a key has been pressed. With GameCanvas, you can call the method getKeyStates() whenever your program wants to know which keys have been pressed. Of course the value returned by getKeyStates() (telling you what key(s) have been pressed), is still updated on another thread, so it’s necessary to put a short wait inside your game loop to make sure that the key states value is updated in a timely fashion allowing your game to respond immediately when the user presses a key. Even a millisecond will do the trick. (I earlier wrote a racecar game in which I neglected to put a wait in the main game loop, and I found that the car would go halfway around the track between the time I pressed the lane change key and the time the car actually changed lanes…).


It’s easy to see how these two enhancements in GameCanvas improve control over the order of execution of painting and keystroke-related updates. Going back to my GameThread class, notice that the main game loop first tells my GameCanvas subclass (called JumpCanvas) to query the key states (see the method JumpCanvas.checkKeys() below for details). Then once the key events have been dealt with, the main loop of the GameThread class calls JumpCanvas.advance() which tells the LayerManager to make appropriate updates in the graphics (more on that in the next sections) and then paints the screen and then waits as explained above.


Here’s the code for JumpCanvas.java:


package net.frog_parrot.jump;


import javax.microedition.lcdui.*;
import javax.microedition.lcdui.game.*;


/**
 * This class is the display of the game.
 *
 * @author Carol Hamer
 */
public class JumpCanvas extends javax.microedition.lcdui.game.GameCanvas {


  //———————————————————
  //   dimension fields
  //  (constant after initialization)


  /**
   * the height of the green region below the ground.
   */
  static int GROUND_HEIGHT = 32;


  /**
   * a screen dimension.
   */
  static int CORNER_X;


  /**
   * a screen dimension.
   */
  static int CORNER_Y;


  /**
   * a screen dimension.
   */
  static int DISP_WIDTH;


  /**
   * a screen dimension.
   */
  static int DISP_HEIGHT;


  /**
   * a font dimension.
   */
  static int FONT_HEIGHT;


  /**
   * the default font.
   */
  static Font FONT;


  /**
   * a font dimension.
   */
  static int SCORE_WIDTH;


  /**
   * The width of the string that displays the time,
   * saved for placement of time display.
   */
  static int TIME_WIDTH;


  //———————————————————
  //   game object fields


  /**
   * a handle to the display.
   */
  Display myDisplay;


  /**
   * a handle to the MIDlet object (to keep track of buttons).
   */
  Jump myJump;


  /**
   * the LayerManager that handles the game graphics.
   */
  JumpManager myManager;


  /**
   * whether or not the game has ended.
   */
  static boolean myGameOver;


  /**
   * the player’s score.
   */
  int myScore = 0;


  /**
   * How many ticks we start with.
   */
  int myInitialGameTicks = 950;


  /**
   * this is saved to determine if the time string needs
   * to be recomputed.
   */
  int myOldGameTicks = myInitialGameTicks;


  /**
   * the number of game ticks that have passed.
   */
  int myGameTicks = myOldGameTicks;


  /**
   * whether or not this has been painted once.
   */
  boolean myInitialized;


  /**
   * The initial time string.
   */
  static String myInitialString = “1:00”;


  /**
   * we save the time string to avoid recreating it
   * unnecessarily.
   */
  String myTimeString = myInitialString;


  //—————————————————–
  //    gets/sets


  /**
   * This is called when the game ends.
   */
  static void setGameOver() {
    myGameOver = true;
    GameThread.requestStop();
  }


  /**
   * Find out if the game has ended.
   */
  static boolean getGameOver() {
    return(myGameOver);
  }


  //—————————————————–
  //    initialization and game state changes


  /**
   * Constructor sets the data.
   */
  public JumpCanvas(Jump midlet) {
    super(false);
    myDisplay = Display.getDisplay(midlet);
    myJump = midlet;
  }


  /**
   * This is called as soon as the application begins.
   */
  void start() {
    myGameOver = false;
    myDisplay.setCurrent(this);
    repaint();
  }


  /**
   * sets all variables back to their initial positions.
   */
  void reset() {
    myManager.reset();
    myScore = 0;
    myGameOver = false;
    myGameTicks = myInitialGameTicks;
    myOldGameTicks = myInitialGameTicks;
    repaint();
  }


  /**
   * clears the key states.
   */
  void flushKeys() {
    getKeyStates();
  }


  //——————————————————-
  //  graphics methods


  /**
   * paint the game graphics on the screen.
   */
  public void paint(Graphics g) {
    // perform the calculations if necessary:
    if(!myInitialized) {
      CORNER_X = g.getClipX();
      CORNER_Y = g.getClipY();
      DISP_WIDTH = g.getClipWidth();
      DISP_HEIGHT = g.getClipHeight();
      FONT = g.getFont();
      FONT_HEIGHT = FONT.getHeight();
      SCORE_WIDTH = FONT.stringWidth(“Score: 000”);
      TIME_WIDTH = FONT.stringWidth(“Time: ” + myInitialString);
      myInitialized = true;
    }
    // clear the screen:
    g.setColor(0xffffff);
    g.fillRect(CORNER_X, CORNER_Y, DISP_WIDTH, DISP_HEIGHT);
    g.setColor(0x0000ff00);
    g.fillRect(CORNER_X, CORNER_Y + DISP_HEIGHT – GROUND_HEIGHT,
        DISP_WIDTH, DISP_HEIGHT);
    // create (if necessary) then paint the layer manager:
    try {
      if(myManager == null) {
 myManager = new JumpManager(CORNER_X, CORNER_Y + FONT_HEIGHT*2,
      DISP_WIDTH, DISP_HEIGHT – FONT_HEIGHT*2 – GROUND_HEIGHT);
      }
      myManager.paint(g);
    } catch(Exception e) {
      errorMsg(g, e);
    }
    // draw the time and score
    g.setColor(0);
    g.setFont(FONT);
    g.drawString(“Score: ” + myScore,
   (DISP_WIDTH – SCORE_WIDTH)/2,
   DISP_HEIGHT + 5 – GROUND_HEIGHT, g.TOP|g.LEFT);
    g.drawString(“Time: ” + formatTime(),
     (DISP_WIDTH – TIME_WIDTH)/2,
     CORNER_Y + FONT_HEIGHT, g.TOP|g.LEFT);
    // write game over if the game is over
    if(myGameOver) {
      myJump.setNewCommand();
      // clear the top region:
      g.setColor(0xffffff);
      g.fillRect(CORNER_X, CORNER_Y, DISP_WIDTH, FONT_HEIGHT*2 + 1);
      int goWidth = FONT.stringWidth(“Game Over”);
      g.setColor(0);
      g.setFont(FONT);
      g.drawString(“Game Over”, (DISP_WIDTH – goWidth)/2,
           CORNER_Y + FONT_HEIGHT, g.TOP|g.LEFT);
    }
  }


  /**
   * a simple utility to make the number of ticks look like a time…
   */
  public String formatTime() {
    if((myGameTicks / 16) + 1 != myOldGameTicks) {
      myTimeString = “”;
      myOldGameTicks = (myGameTicks / 16) + 1;
      int smallPart = myOldGameTicks % 60;
      int bigPart = myOldGameTicks / 60;
      myTimeString += bigPart + “:”;
      if(smallPart / 10 < 1) {
 myTimeString += “0”;
      }
      myTimeString += smallPart;
    }
    return(myTimeString);
  }


  //——————————————————-
  //  game movements


  /**
   * Tell the layer manager to advance the layers and then
   * update the display.
   */
  void advance() {
    myGameTicks–;
    myScore += myManager.advance(myGameTicks);
    if(myGameTicks == 0) {
      setGameOver();
    }
    // paint the display
    try {
      paint(getGraphics());
      flushGraphics();
    } catch(Exception e) {
      errorMsg(e);
    }
    // we do a very short pause to allow the other thread
    // to update the information about which keys are pressed:
    synchronized(this) {
      try {
 wait(1);
      } catch(Exception e) {}
    }
  }


  /**
   * Respond to keystrokes.
   */
  public void checkKeys() {
    if(! myGameOver) {
      int keyState = getKeyStates();
      if((keyState & LEFT_PRESSED) != 0) {
 myManager.setLeft(true);
      }
      if((keyState & RIGHT_PRESSED) != 0) {
 myManager.setLeft(false);
      }
      if((keyState & UP_PRESSED) != 0) {
 myManager.jump();
      }
    }
  }


  //——————————————————-
  //  error methods


  /**
   * Converts an exception to a message and displays
   * the message..
   */
  void errorMsg(Exception e) {
    errorMsg(getGraphics(), e);
    flushGraphics();
  }


  /**
   * Converts an exception to a message and displays
   * the message..
   */
  void errorMsg(Graphics g, Exception e) {
    if(e.getMessage() == null) {
      errorMsg(g, e.getClass().getName());
    } else {
      errorMsg(g, e.getClass().getName() + “:” + e.getMessage());
    }
  }


  /**
   * Displays an error message if something goes wrong.
   */
  void errorMsg(Graphics g, String msg) {
    // clear the screen
    g.setColor(0xffffff);
    g.fillRect(CORNER_X, CORNER_Y, DISP_WIDTH, DISP_HEIGHT);
    int msgWidth = FONT.stringWidth(msg);
    // write the message in red
    g.setColor(0x00ff0000);
    g.setFont(FONT);
    g.drawString(msg, (DISP_WIDTH – msgWidth)/2,
   (DISP_HEIGHT – FONT_HEIGHT)/2, g.TOP|g.LEFT);
    myGameOver = true;
  }


}


 


MIDP 2.0 Games: a Step-by-Step Tutorial with Code Samples (Step 2)

The MIDlet Class

Now it’s time to get started on the real game. If you copy the code from this section and all of the following sections, it should compile together to form the example game “Tumbleweed.”

The MIDlet class controls some of the basic items that are common to all MIDlets (such as start and stop buttons) hence doesn’t need to vary much from one MIDlet to another. This MIDlet class (called Jump) resembles the MIDlet class from my “Hello World” example, but has a few additional items that I will explain before displaying the code. Remember that the MIDlet class is the class that needs to be mentioned in the MIDlet’s jad file.

This MIDlet has several different commands. The user can only pause the game when it’s unpaused, can only unpause the game when it’s paused, and can only start over when the game has ended, so only one of the commands “Go”, “Pause”, and “Play Again” appears at a time. Therefore each time I add one of these two commands to the screen, I remove the other two. Of course it is certainly possible to add several custom commands at a time to the MIDlet if your game needs them. The device’s JVM will deal with how to place them. For example the default emulator creates a menu of commands when there are more commands than available buttons. The additional arguments to the Command’s constructor help the device to decide how to map the Commands to physical buttons. Using static field codes such as Command.BACK or Command.HELP will ensure that standard types of commands will be mapped to their usual buttons on the device. The implementation of the priority argument may vary some from device to device, but in general those commands that have higher priority (indicated by a lower priority value) will be more easily accessible to the user.

The other thing to notice when reading this class is what happens when the method destroyApp() is called. This should be called just before exiting to free up any resources that your game may be using. This example is simple enough that it has no shared resources that it needs to let go of, but it is occupying memory (which is valuable on a small device!) so it’s a good idea to set your MIDlet’s object references to null so that any objects your game was using can be garbage collected. Calling notifyDestroyed() returns control of the device to the surrounding application, but it does not necessarily cause the JVM to exit (unless the user explicitly stops the Java functions and/or turns the device off). So in particular classes that were loaded by your MIDlet will remain in memory even after your game is done. It is especially important to keep this in mind when using static fields as they generally retain their values from one run to the next and they occupy space in memory when they’re no longer in use.

Here’s the code for Jump.java:

package net.frog_parrot.jump;

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;

/**
 * This is the main class of the tumbleweed game.
 *
 * @author Carol Hamer
 */
public class Jump extends MIDlet implements CommandListener {

  //———————————————————
  //   game object fields

  /**
   * the command to end the game.
   */
  private Command myExitCommand = new Command(“Exit”, Command.EXIT, 99);

  /**
   * the command to start moving when the game is paused.
   */
  private Command myGoCommand = new Command(“Go”, Command.SCREEN, 1);

  /**
   * the command to pause the game.
   */
  private Command myPauseCommand = new Command(“Pause”, Command.SCREEN, 1);

  /**
   * the command to start a new game.
   */
  private Command myNewCommand = new Command(“Play Again”, Command.SCREEN, 1);

  /**
   * the canvas that all of the game will be drawn on.
   */
  JumpCanvas myCanvas;

  /**
   * the thread that advances the cowboy.
   */
  GameThread myGameThread;

  //—————————————————–
  //    initialization and game state changes

  /**
   * Initialize the canvas and the commands.
   */
  public Jump() {
    myCanvas = new JumpCanvas(this);
    myCanvas.addCommand(myExitCommand);
    myCanvas.addCommand(myGoCommand);
    myCanvas.setCommandListener(this);
  }

  /**
   * Switch the command to the play again command.
   */
  void setNewCommand() {
    myCanvas.removeCommand(myPauseCommand);
    myCanvas.removeCommand(myGoCommand);
    myCanvas.addCommand(myNewCommand);
  }

  /**
   * Switch the command to the go command.
   */
  void setGoCommand() {
    myCanvas.removeCommand(myPauseCommand);
    myCanvas.removeCommand(myNewCommand);
    myCanvas.addCommand(myGoCommand);
  }

  /**
   * Switch the command to the pause command.
   */
  void setPauseCommand() {
    myCanvas.removeCommand(myNewCommand);
    myCanvas.removeCommand(myGoCommand);
    myCanvas.addCommand(myPauseCommand);
  }

  //—————————————————————-
  //  implementation of MIDlet

  /**
   * Start the application.
   */
  public void startApp() throws MIDletStateChangeException {
    myGameThread = new GameThread(myCanvas);
    myCanvas.start();
  }
 
  /**
   * stop and throw out the garbage.
   */
  public void destroyApp(boolean unconditional)
      throws MIDletStateChangeException {
    myGameThread.requestStop();
    myGameThread = null;
    myCanvas = null;
    System.gc();
  }

  /**
   * request the thread to pause.
   */
  public void pauseApp() {
    setGoCommand();
    myGameThread.pause();
  }

  //—————————————————————-
  //  implementation of CommandListener

  /*
   * Respond to a command issued on the Canvas.
   * (either reset or exit).
   */
  public void commandAction(Command c, Displayable s) {
    if(c == myGoCommand) {
      myCanvas.removeCommand(myGoCommand);
      myCanvas.addCommand(myPauseCommand);
      myGameThread.go();
    } else if(c == myPauseCommand) {
      myCanvas.removeCommand(myPauseCommand);
      myCanvas.addCommand(myGoCommand);
      myGameThread.go();
    } else if(c == myNewCommand) {
      myCanvas.removeCommand(myNewCommand);
      myCanvas.addCommand(myGoCommand);
      myGameThread.requestStop();
      myGameThread = new GameThread(myCanvas);
      System.gc();
      myCanvas.reset();
    } else if(c == myExitCommand) {
      try {
 destroyApp(false);
 notifyDestroyed();
      } catch (MIDletStateChangeException ex) {
      }
    }
  }
 
}

The Thread Class

The use of threads is not unique to MIDlets, and should be very familiar to any game developer. In this example the run() method contains the main loop of the game which prompts the GameCanvas to check which keys are pressed and then advance the graphical objects accordingly as long as the game is not paused or stopped.

Here’s the code for GameThread.java:

package net.frog_parrot.jump;

/**
 * This class contains the loop that keeps the game running.
 *
 * @author Carol Hamer
 */
public class GameThread extends Thread {

  //———————————————————
  //   fields

  /**
   * Whether or not the main thread would like this thread
   * to pause.
   */
  boolean myShouldPause;

  /**
   * Whether or not the main thread would like this thread
   * to stop.
   */
  static boolean myShouldStop;

  /**
   * Whether or not this thread has been started.
   */
  boolean myAlreadyStarted;

  /**
   * A handle back to the graphical components.
   */
  JumpCanvas myJumpCanvas;

  //———————————————————-
  //   initialization

  /**
   * standard constructor.
   */
  GameThread(JumpCanvas canvas) {
    myJumpCanvas = canvas;
  }

  //———————————————————-
  //   actions

  /**
   * start or pause or unpause the game.
   */
  void go() {
    if(!myAlreadyStarted) {
      myAlreadyStarted = true;
      start();
    } else {
      myShouldPause = !myShouldPause;
    }
  }

  /**
   * pause the game.
   */
  void pause() {
    myShouldPause = true;
  }

  /**
   * stops the game.
   */
  static void requestStop() {
    myShouldStop = true;
  }

  /**
   * start the game..
   */
  public void run() {
    // flush any keystrokes that occurred before the
    // game started:
    myJumpCanvas.flushKeys();
    myShouldStop = false;
    myShouldPause = false;
    while(true) {
      if(myShouldStop) {
 break;
      }
      if(!myShouldPause) {
 myJumpCanvas.checkKeys();
 myJumpCanvas.advance();
      }
    }
  }

}

 

MIDP 2.0 Games: a Step-by-Step Tutorial with Code Samples (Step 1)

Writing games for small devices is easy and fun with the new MIDP 2.0 API. The biggest difference for the game developer between MIDP 1.0 and MIDP 2.0 is enhanced graphics capabilities. With MIDP 1.0 you can certainly write some fun games–those of us who remember the Atari 2600 will remember that the games were fun even if they weren’t always beautiful. The 2.0 version of the Mobile Information Device Profile doesn’t bring us quite to the level of the latest game cube, but it brings your cell phone at least to the realm of Super Mario Brothers or thereabouts.


In this tutorial I will go over the steps involved in writing a simple game MIDlet. (A MIDlet is like an applet except that it runs on a mobile device rather than in a browser.) I will assume that you know some Java but that you’re new to J2ME. My example is a game involving a cowboy walking through a prairie jumping over tumbleweeds. It’s kind of a silly game, but it illustrates most of the basics that you’ll need when writing more reasonable games.


All of the source code and resources for this example can be found here.


Getting Started


If you haven’t already downloaded and installed J2ME, you can get it at http://java.sun.com/j2me/download.html. The Java 2 Micro Edition Wireless Toolkit contains a MIDlet development environment, a cell-phone emulator, and a number of helpful demo applications with source code. I’m not going to go into too much detail here about the installation since it varies from system to system and the documentation bundled with the download is reasonably clear.


When you examine the demo applications, you’ll notice that they consist of a jar file and a jad file. The jar file contains the class files, the resources, and a manifest file. The jad file contains approximately the same descriptive information with the addition of two items that help the device load the MIDlets: the size and the URL of the MIDlet jar. Here is an example of the jad file I used for my Hello World application:


MIDlet-1: Hello World, /icons/hello.png, net.frog_parrot.hello.Hello
MMIDlet-Description: Hello World for MIDP
MIDlet-Jar-URL: hello.jar
MIDlet-Name: Hello World
MIDlet-Permissions:
MIDlet-Vendor: frog-parrot.net
MIDlet-Version: 2.0
MicroEdition-Configuration: CLDC-1.0
MicroEdition-Profile: MIDP-2.0
MIDlet-Jar-Size: 3201


The MIDlet-1 (and MIDlet-2, etc.) property gives the name of the MIDlet, the location of the MIDlet’s icon, and the MIDlet class to run. The first two items describe how the MIDlet will appear on the menu of MIDlets. The icon should be in the jar file, and its location should be given in the same format as is used by the method Class.getResource(). Thus in this example your jar file should contain a top level folder called icons which contains an icon called hello.png which looks like this:


 


The MIDlet-Jar-Size property gives the size of the corresponding jar file in bytes which you can find by looking at the properties or long listing of the jar file. Be aware that if you rebuild the demos or your own applications using the build script bundled with the toolkit, you must manually update the size of the jar file in the jad file. If the MIDlet-Jar-Size property in the jad file does not match the size of the jar file, the MIDlet will not run. (Since the size generally changes with every build and it’s annoying to open your jad file in a text editor with every build, I’ve included some build script modification suggestions in Appendix A at the end of this article.) The MIDlet-Jar-URL property gives the address of the MIDlet jar relative to the location of the jad file. If the jar file and the jad file are kept in the same directory, this is just the name of the jar file. The other properties are self-explanatory.


Hello World


To illustrate the components of a very basic MIDlet I’ve written a version of the classic “Hello World” application. The MIDlet will display the message “Hello World!” on the screen and remove it (or later put it back) when the “Toggle Msg” button is pressed. The “Exit” button will terminate the MIDlet. The application consists of two classes: the MIDlet class called Hello and the Canvas class called HelloCanvas. How it works is sufficiently simple that I will leave the explanations of the various steps in the comments.


Here’s the code for Hello.java:


package net.frog_parrot.hello;


import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;


/**
 * This is the main class of the hello world demo.
 *
 * @author Carol Hamer
 */
public class Hello extends MIDlet implements CommandListener {


  /**
   * The canvas is the region of the screen that has been allotted
   * to the game.
   */
  HelloCanvas myCanvas;


  /**
   * The Command objects appear as buttons.
   */
  private Command exitCommand = new Command(“Exit”, Command.EXIT, 99);


  /**
   * The Command objects appear as buttons.
   */
  private Command newCommand = new Command(“Toggle Msg”, Command.SCREEN, 1);


  /**
   * Initialize the canvas and the commands.
   */
  public Hello() {
    myCanvas = new HelloCanvas(Display.getDisplay(this));
    myCanvas.addCommand(exitCommand);
    myCanvas.addCommand(newCommand);
    myCanvas.setCommandListener(this);
  }


  //—————————————————————-
  //  implementation of MIDlet


  /**
   * Start the application.
   */
  public void startApp() throws MIDletStateChangeException {
    myCanvas.start();
  }
 
  /**
   * If the MIDlet was using resources, it should release
   * them in this method.
   */
  public void destroyApp(boolean unconditional)
      throws MIDletStateChangeException {
  }


  /**
   * This method is called to notify the MIDlet to enter a paused
   * state.  The MIDlet should use this opportunity to release
   * shared resources.
   */
  public void pauseApp() {
  }


  //—————————————————————-
  //  implementation of CommandListener


  /*
   * Respond to a command issued on the Canvas.
   * (either reset or exit).
   */
  public void commandAction(Command c, Displayable s) {
    if(c == newCommand) {
      myCanvas.newHello();
    } else if(c == exitCommand) {
      try {
 destroyApp(false);
 notifyDestroyed();
      } catch (MIDletStateChangeException ex) {
      }
    }
  }
 
}


Here’s the code for HelloCanvas.java:


package net.frog_parrot.hello;


import javax.microedition.lcdui.*;
import javax.microedition.lcdui.game.*;


/**
 * This class represents the region of the screen that has been allotted
 * to the game.
 *
 * @author Carol Hamer
 */
public class HelloCanvas extends javax.microedition.lcdui.game.GameCanvas {


  //———————————————————
  //   fields


  /**
   * A handle to the screen of the device.
   */
  Display myDisplay;


  /**
   * whether or not the screen should currently display the
   * “hello world” message.
   */
  boolean mySayHello = true;


  //—————————————————–
  //    initialization and game state changes


  /**
   * Constructor merely sets the display.
   */
  public HelloCanvas(Display d) {
    super(false);
    myDisplay = d;
  }


  /**
   * This is called as soon as the application begins.
   */
  void start() {
    myDisplay.setCurrent(this);
    repaint();
  }


  /**
   * toggle the hello message.
   */
  void newHello() {
    mySayHello = !mySayHello;
    // paint the display
    repaint();
  }


  //——————————————————-
  //  graphics methods


  /**
   * clear the screen and display the hello world message if appropriate.
   */
  public void paint(Graphics g) {
    // x and y are the coordinates of the top corner of the
    // game’s display area:
    int x = g.getClipX();
    int y = g.getClipY();
    // w and h are the width and height of the display area:
    int w = g.getClipWidth();
    int h = g.getClipHeight();
    // clear the screen (paint it white):
    g.setColor(0xffffff);
    g.fillRect(x, y, w, h);
    // display the hello world message if appropriate:.
    if(mySayHello) {
      Font font = g.getFont();
      int fontHeight = font.getHeight();
      int fontWidth = font.stringWidth(“Hello World!”);
      // set the text color to red:
      g.setColor(0x00ff0000);
      g.setFont(font);
      // write the string in the center of the screen
      g.drawString(“Hello World!”, (w – fontWidth)/2,
     (h – fontHeight)/2,
     g.TOP|g.LEFT);
    }
  }


}


 


What is Android?



What is Android?


Android is a software stack for mobile devices that includes an operating system, middleware and key applications. This early look at the Android SDK provides the tools and APIs necessary to begin developing applications on the Android platform using the Java programming language.


Features



  • Application framework enabling reuse and replacement of components
  • Dalvik virtual machine optimized for mobile devices
  • Integrated browser based on the open source WebKit engine
  • Optimized graphics powered by a custom 2D graphics library; 3D graphics based on the OpenGL ES 1.0 specification (hardware acceleration optional)
  • SQLite for structured data storage
  • Media support for common audio, video, and still image formats (MPEG4, H.264, MP3, AAC, AMR, JPG, PNG, GIF)
  • GSM Telephony (hardware dependent)
  • Bluetooth, EDGE, 3G, and WiFi (hardware dependent)
  • Camera, GPS, compass, and accelerometer (hardware dependent)
  • Rich development environment including a device emulator, tools for debugging, memory and performance profiling, and a plugin for the Eclipse IDE

Android Architecture


The following diagram shows the major components of the Android operating system. Each section is described in more detail below.


Android System Architecture


Applications


Android will ship with a set of core applications including an email client, SMS program, calendar, maps, browser, contacts, and others. All applications are written using the Java programming language.


Application Framework


Developers have full access to the same framework APIs used by the core applications. The application architecture is designed to simplify the reuse of components; any application can publish its capabilities and any other application may then make use of those capabilities (subject to security constraints enforced by the framework). This same mechanism allows components to be replaced by the user.


Underlying all applications is a set of services and systems, including:


  • A rich and extensible set of Views that can be used to build an application, including lists, grids, text boxes, buttons, and even an embeddable web browser
  • Content Providers that enable applications to access data from other applications (such as Contacts), or to share their own data
  • A Resource Manager, providing access to non-code resources such as localized strings, graphics, and layout files
  • A Notification Manager that enables all applications to display custom alerts in the status bar
  • An Activity Manager that manages the lifecycle of applications and provides a common navigation backstack

For more details and a walkthrough of an application, see Writing an Android Application.


Libraries


Android includes a set of C/C++ libraries used by various components of the Android system. These capabilities are exposed to developers through the Android application framework. Some of the core libraries are listed below:



  • System C library – a BSD-derived implementation of the standard C system library (libc), tuned for embedded Linux-based devices
  • Media Libraries – based on PacketVideo’s OpenCORE; the libraries support playback and recording of many popular audio and video formats, as well as static image files, including MPEG4, H.264, MP3, AAC, AMR, JPG, and PNG
  • Surface Manager – manages access to the display subsystem and seamlessly composites 2D and 3D graphic layers from multiple applications
  • LibWebCore – a modern web browser engine which powers both the Android browser and an embeddable web view
  • SGL – the underlying 2D graphics engine
  • 3D libraries – an implementation based on OpenGL ES 1.0 APIs; the libraries use either hardware 3D acceleration (where available) or the included, highly optimized 3D software rasterizer
  • FreeType – bitmap and vector font rendering
  • SQLite – a powerful and lightweight relational database engine available to all applications

Android Runtime


Android includes a set of core libraries that provides most of the functionality available in the core libraries of the Java programming language.


Every Android application runs in its own process, with its own instance of the Dalvik virtual machine. Dalvik has been written so that a device can run multiple VMs efficiently. The Dalvik VM executes files in the Dalvik Executable (.dex) format which is optimized for minimal memory footprint. The VM is register-based, and runs classes compiled by a Java language compiler that have been transformed into the .dex format by the included “dx” tool.


The Dalvik VM relies on the Linux kernel for underlying functionality such as threading and low-level memory management.


Linux Kernel


Android relies on Linux version 2.6 for core system services such as security, memory management, process management, network stack, and driver model. The kernel also acts as an abstraction layer between the hardware and the rest of the software stack.