J2ME Game Optimization Secrets (part 1)

This article describes the role that code optimization plays in writing fast games for mobile devices. Using examples I will show how, when and why to optimize your code to squeeze every last drop of performance out of MIDP-compliant handsets. We will discuss why optimization is necessary and why it is often best NOT to optimize. I explain the difference between high-level and low-level optimization and we will see how to use the Profiler utility that ships with the J2ME Wireless Toolkit to discover where to optimize your code. Finally this article reveals lots of techniques for making your MIDlets move.


Why Optimize?
It is possible to divide video games into two broad categories; real-time and input-driven. Input-driven games display the current state of game play and wait indefinitely for user input before moving on. Card games fall into this category, as do most puzzle games, strategy games and text adventures. Real-time games, sometimes referred to as Skill and Action games do not wait for the player – they continue until the game is over.


Skill and Action games are often characterized by a great deal of on-screen movement (think of Galaga or Robotron). Refresh rates must be at least 10fps (frames per second) and there has to be enough action to keep gamers challenged. They require fast reflexes and good hand-eye co-ordination from the player, so compelling S&A games must also be extemely responsive to user input. Providing all that graphical activity at high framerates while responding quickly to key-presses is why code for real-time games has to be fast. The challenges are even greater when developing for J2ME.


Java 2 Micro Edition (J2ME) is a stripped-down version of Java, suitable for small devices with limited capabilities, such as cell phones and PDAs. J2ME devices have:


limited input capabilities (no keyboard!)
small display sizes
restricted storage memory and heap sizes
slow CPUs
Writing fast games for the J2ME platform further challenges developers to write code that will perform on CPUs far slower than those found on desktop computers.


When not to Optimize
If you’re not writing a Skill and Action game, there’s probably no need to optimize. If the player has pondered her next move for several seconds or minutes, she will probably not mind if your game’s response to her action takes more than a couple hundred milliseconds. An exception to this rule is if the game needs to perform a great deal of processing in order to determine its next move, such as searching through a million possible combinations of chess pieces. In this case, you might want to optimize your code so that the computer’s next move can be calculated in a few seconds, not minutes.


Even if you are writing this type of game, optimization can be perilous. Many of these techniques come with a price tag – they fly in the face of conventional notions of “Good” programming and can make your code harder to read. Some are a trade-off, and require the developer to significantly increase the size of the app in order to gain just a minor improvement in performance. J2ME developers are all too familiar with the challenges of keeping their JAR as small as possible. Here are a few more reasons not to optimize:


optimization is a good way to introduce bugs
some techniques decrease the portability of your code
you can expend a lot of effort for little or no results
optimization is hard
That last point needs some illumination. Optimization is a moving target, only more so on the Java platform, and even more so with J2ME because the execution environments vary so greatly. Your optimized code might run faster on an emulator, but slower on the actual device, or vice versa. Optimizing for one handset might actually decrease performance on another.


But there is hope. There are two passes you can make at optimization, a high-level and a low-level. The first is likely to increase execution performance on every platform, and even improve the overall quality of your code. The second pass is the one more likely to cause you headaches, but these low-level techniques are quite simple to introduce, and even simpler to omit if you don’t want to use them. At the very least, they’re very interesting to look at.


We will also use the System timer to profile your code on the actual device, which can help you to gauge exactly how effective ( or utterly ineffective ) these techniques can be on the hardware you’re targeting for deployment.


And one last bullet-point:
optimizing is fun
A Bad Example
Let’s take a look at a simple application that consists of two classes. First, the MIDlet…


import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
public class OptimizeMe extends MIDlet implements CommandListener {
  private static final boolean debug = false;
  private Display display;
  private OCanvas oCanvas;
  private Form form;
  private StringItem timeItem = new StringItem( “Time: “, “Unknown” );
  private StringItem resultItem =
                            new StringItem( “Result: “, “No results” );
  private Command cmdStart = new Command( “Start”, Command.SCREEN, 1 );
  private Command cmdExit = new Command( “Exit”, Command.EXIT, 2 );
  public boolean running = true;
  public OptimizeMe() {
    display = Display.getDisplay(this);
    form = new Form( “Optimize” );
    form.append( timeItem );
    form.append( resultItem );
    form.addCommand( cmdStart );
    form.addCommand( cmdExit );
    form.setCommandListener( this );
    oCanvas = new OCanvas( this );
  }
  public void startApp() throws MIDletStateChangeException {
    running = true;
    display.setCurrent( form );
  }
  public void pauseApp() {
    running = false;
  }
  public void exitCanvas(int status) {
    debug( “exitCanvas – status = ” + status );
    switch (status) {
      case OCanvas.USER_EXIT:
        timeItem.setText( “Aborted” );
        resultItem.setText( “Unknown” );
      break;
      case OCanvas.EXIT_DONE:
        timeItem.setText( oCanvas.elapsed+”ms” );
        resultItem.setText( String.valueOf( oCanvas.result ) );
      break;
    }
    display.setCurrent( form );
  }
  public void destroyApp(boolean unconditional)
                          throws MIDletStateChangeException {
    oCanvas = null;
    display.setCurrent ( null );
    display = null;
  }
  public void commandAction(Command c, Displayable d) {
    if ( c == cmdExit ) {
      oCanvas = null;
      display.setCurrent ( null );
      display = null;
      notifyDestroyed();
    }
    else {
      running = true;
      display.setCurrent( oCanvas );
      oCanvas.start();
    }
  }
  public static final void debug( String s ) {
    if (debug) System.out.println( s );
  }
}


Second, the OCanvas class that does most of the work in this example…


import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import java.util.Random;
public class OCanvas extends Canvas implements Runnable {
  public static final int USER_EXIT = 1;
  public static final int EXIT_DONE = 2;
  public static final int LOOP_COUNT = 100;
  public static final int DRAW_COUNT = 16;
  public static final int NUMBER_COUNT = 64;
  public static final int DIVISOR_COUNT = 8;
  public static final int WAIT_TIME = 50;
  public static final int COLOR_BG = 0x00FFFFFF;
  public static final int COLOR_FG = 0x00000000;
  public long elapsed = 0l;
  public int exitStatus;
  public int result;
  private Thread animationThread;
  private OptimizeMe midlet;
  private boolean finished;
  private long started;
  private long frameStarted;
  private long frameTime;
  private int[] numbers;
  private int loopCounter;
  private Random random = new Random( System.currentTimeMillis() );
  public OCanvas( OptimizeMe _o ) {
    midlet = _o;
    numbers = new int[ NUMBER_COUNT ];
    for ( int i = 0 ; i < numbers.length ; i++ ) {
      numbers[i] = i+1;
    }
  }
  public synchronized void start() {
    started = frameStarted = System.currentTimeMillis();
    loopCounter = result = 0;
    finished = false;
    exitStatus = EXIT_DONE;
    animationThread = new Thread( this );
    animationThread.start();
  }
  public void run() {
    Thread currentThread = Thread.currentThread();
    try {
      while ( animationThread == currentThread && midlet.running
                                               && !finished ) {
        frameTime = System.currentTimeMillis() – frameStarted;
        frameStarted = System.currentTimeMillis();
        result += work( numbers );
        repaint();
        synchronized(this) {
          wait( WAIT_TIME );
        }
        loopCounter++;
        finished = ( loopCounter > LOOP_COUNT );
      }
    }
    catch ( InterruptedException ie ) {
      OptimizeMe.debug( “interrupted” );
    }
    elapsed = System.currentTimeMillis() – started;
    midlet.exitCanvas( exitStatus );
  }
  public void paint(Graphics g) {
    g.setColor( COLOR_BG );
    g.fillRect( 0, 0, getWidth(), getHeight() );
    g.setColor( COLOR_FG );
    g.setFont( Font.getFont( Font.FACE_PROPORTIONAL,
         Font.STYLE_BOLD | Font.STYLE_ITALIC, Font.SIZE_SMALL ) );
    for ( int i  = 0 ; i < DRAW_COUNT ; i ++ ) {
      g.drawString( frameTime + ” ms per frame”,
                    getRandom( getWidth() ),
                    getRandom( getHeight() ),
                    Graphics.TOP | Graphics.HCENTER );
    }
  }
  private int divisor;
  private int r;
  public synchronized int work( int[] n ) {
    r = 0;
    for ( int j = 0 ; j < DIVISOR_COUNT ; j++ ) {
      for ( int i = 0 ; i < n.length ; i++ ) {
        divisor = getDivisor(j);
        r += workMore( n, i, divisor );
      }
    }
    return r;
  }
  private int a;
  public synchronized int getDivisor( int n ) {
    if ( n == 0 ) return 1;
    a = 1;
    for ( int i = 0 ; i < n ; i++ ) {
      a *= 2;
    }
    return a;
  }
  public synchronized int workMore( int[] n, int _i, int _d ) {
    return n[_i] * n[_i] / _d + n[_i];
  }
  public void keyReleased(int keyCode) {
    if ( System.currentTimeMillis() – started > 1000l ) {
      exitStatus = USER_EXIT;
      midlet.running = false;
    }
  }
  private int getRandom( int bound )
  {  // return a random, positive integer less than bound
    return Math.abs( random.nextInt() % bound );
  }
}


This app is a MIDlet that simulates a simple game loop:


work
draw
poll for user input
repeat



For fast games, this loop has to be as tight and fast as possible. Our loop continues for a finite number of iterations (LOOP_COUNT = 100) and uses the System timer to calculate how long in milliseconds the whole exercise took, so we can measure and improve its performance. The time and the results of the work are displayed on a simple Form. Use the Start command to begin the test. Pressing any key will exit the loop prematurely. The Exit command exits the application.


In most games, the work phase of the main game loop involves updating the state of the game world – moving all the actors, testing for and reacting to collisions, updating scores, etc. In this example, we’re not doing anything particularly useful. The code simply runs through an array of numbers and performs a few arithmetic operations on each, aggregating the results in a running total.


The run() method also calculates the amount of time it takes to execute each iteration of the loop. Every frame, the OCanvas.paint() method displays this value in milliseconds at 16 random locations on screen. Normally you would be drawing the graphical elements of your game in this method, but our code offers a reasonable facsimile of this process.


Regardless of how pointless this code may seem, it will allow us ample opportunity to improve its performance.


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

And here’s the code for Tumbleweed.java:


package net.frog_parrot.jump;


import java.util.Random;


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


/**
 * This class represents the tumbleweeds that the player
 * must jump over.
 *
 * @author Carol Hamer
 */
public class Tumbleweed extends Sprite {


  //———————————————————
  //   dimension fields


  /**
   * The width of the tumbleweed’s bounding square.
   */
  static int WIDTH = 16;


  //———————————————————
  //    instance fields


  /**
   * Random number generator to randomly decide when to appear.
   */
  Random myRandom = new Random();


  /**
   * whether or not this tumbleweed has been jumped over.
   * This is used to calculate the score.
   */
  boolean myJumpedOver;


  /**
   * whether or not this tumbleweed enters from the left.
   */
  boolean myLeft;


  /**
   * the Y coordinate of the tumbleweed.
   */
  int myY;


  //———————————————————
  //   initialization


  /**
   * constructor initializes the image and animation.
   * @param left whether or not this tumbleweed enters from the left.
   */
  public Tumbleweed(boolean left) throws Exception {
    super(Image.createImage(“/icons/tumbleweed.png”),
   WIDTH, WIDTH);
    myY = JumpManager.DISP_HEIGHT – WIDTH – 2;
    myLeft = left;
    if(!myLeft) {
      setTransform(TRANS_MIRROR);
    }
    myJumpedOver = false;
    setVisible(false);
  }


  //———————————————————
  //   graphics


  /**
   * move the tumbleweed back to its initial (inactive) state.
   */
  void reset() {
    setVisible(false);
    myJumpedOver = false;
  }


  /**
   * alter the tumbleweed image appropriately for this frame..
   * @param left whether or not the player is moving left
   * @return how much the score should change by after this
   *         advance.
   */
  int advance(Cowboy cowboy, int tickCount, boolean left,
       int currentLeftBound, int currentRightBound) {
    int retVal = 0;
    // if the tumbleweed goes outside of the display
    // region, set it to invisible since it is
    // no longer in use.
    if((getRefPixelX() + WIDTH <= currentLeftBound) ||
       (getRefPixelX() – WIDTH >= currentRightBound)) {
      setVisible(false);
    }
    // If the tumbleweed is no longer in use (i.e. invisible)
    // it is given a 1 in 100 chance (per game loop)
    // of coming back into play:
    if(!isVisible()) {
      int rand = getRandomInt(100);
      if(rand == 3) {
 // when the tumbleweed comes back into play,
 // we reset the values to what they should
 // be in the active state:
 myJumpedOver = false;
 setVisible(true);
 // set the tumbleweed’s position to the point
 // where it just barely appears on the screen
 // to that it can start approaching the cowboy:
 if(myLeft) {
   setRefPixelPosition(currentRightBound, myY);
   move(-1, 0);
 } else {
   setRefPixelPosition(currentLeftBound, myY);
   move(1, 0);
 }
      }
    } else {
      // when the tumbleweed is active, we advance the
      // rolling animation to the next frame and then
      // move the tumbleweed in the right direction across
      // the screen.
      if(tickCount % 2 == 0) { // slow the animation down a little
 nextFrame();
      }
      if(myLeft) {
 move(-3, 0);
 // if the cowboy just passed the tumbleweed
 // (without colliding with it) we increase the
 // cowboy’s score and set myJumpedOver to true
 // so that no further points will be awarded
 // for this tumbleweed until it goes offscreen
 // and then is later reactivated:
 if((! myJumpedOver) &&
    (getRefPixelX() < cowboy.getRefPixelX())) {
   myJumpedOver = true;
   retVal = cowboy.increaseScoreThisJump();
 }
      } else {
 move(3, 0);
 if((! myJumpedOver) &&
    (getRefPixelX() > cowboy.getRefPixelX() + Cowboy.WIDTH)) {
   myJumpedOver = true;
   retVal = cowboy.increaseScoreThisJump();
 }
      }
    }
    return(retVal);
  }


  /**
   * Gets a random int between
   * zero and the param upper.
   */
  public int getRandomInt(int upper) {
    int retVal = myRandom.nextInt() % upper;
    if(retVal < 0) {
      retVal += upper;
    }
    return(retVal);
  }


}


The TiledLayer Class


As mentioned above, the TiledLayer class is similar to the Sprite class except that a TiledLayer can be composed of multiple cells, each of which is painted with an individually set image frame. The other differences between TiledLayer and Sprite are mostly related to extra functionality missing from TiledLayer: TiledLayer has no transforms, reference pixel, or frame sequence.


Of course the mere fact that you’re simultaneously managing multiple images complicates things a bit. I’ll explain it by going over my subclass of TiledLayer which I have called Grass. This class represents a row of grass in the background which waves back and forth as the game is being played. To make it more interesting, some of the cells in my TiledLayer have animated grasses and others have no tall grasses and hence just consist of a green line representing the ground at the bottom of the cell. Here’s the corresponding image file:


 
WARNING: the tile index for Sprite starts with 0, but the tile index for TiledLayer starts with 1! This is a little confusing (it caused me to get an IndexOutOfBoundsException the first time I made a Sprite because I had assumed that the Sprite images were numbered like the TiledLayer images…). Yet the system is completely logical. In a TiledLayer, the tile index 0 indicates a blank tile (i.e. paint nothing in the cell if the cell’s tile index is set to zero). A Sprite, however, is composed of only one cell, so if you want that cell to be blank, then you can just call setVisible(false), meaning that Sprite doesn’t need to reserve a special index to indicate a blank tile. This little confusion in the indices should not pose a big problem, but it’s something to keep in mind if you can’t figure out why your animation appears to be displaying the wrong images.. Aside from this point, the image file is divided into individual frames or tiles in TiledLayer just as in Sprite, explained above.


The first step in creating your TiledLayer is to decide how many rows and columns of cells you will need. If you don’t want your layer to be rectangular it isn’t a problem because any unused cells are by default set to being blank which prevents them from getting in the way of other images. In my example I have only one row, and I calculate the number of columns based on the width of the screen.


Once you’ve set how many rows and columns you’ll be using, you can fill each cell with a tile using the method setCell(int col, int row, int tileIndex). The tileIndex argument is as explained in the Sprite section and the warning paragraph above. If you would like some of the cells to be filled with animated images, you need to create an animated tile by calling createAnimatedTile(int staticTileIndex), which returns the tile index that has been allotted to your new animated tile. You can make as many animated tiles as you want, but remember that each animated tile can be used in multiple cells if you want the cells to display the same animation simultaneously. In my case, I create only one animated tile and reuse it because I want all of my animated grass to be waving in sync. The cells are set in the constructor of Grass below. To advance the animation you don’t get built-in frame-sequence functionality as in Sprite, so you have to set the frames with the method setAnimatedTile(int animatedTileIndex, int staticTileIndex). This sets the current frame of the given animated tile. Thus all of the cells that have been set to contain the animated tile corresponding to animatedTileIndex will change to the image given by the argument staticTileIndex. To simplify the animation updates, it’s easy to add your own frame-sequence functionality: see the method Grass.advance(int tickCount) for an idea of how to do it.


Here’s the code for the last class, Grass.java:


package net.frog_parrot.jump;


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


/**
 * This class draws the background grass.
 *
 * @author Carol Hamer
 */
public class Grass extends TiledLayer {


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


  /**
   * The width of the square tiles that make up this layer..
   */
  static int TILE_WIDTH = 20;


  /**
   * This is the order that the frames should be displayed
   * for the animation.
   */
  static int[] FRAME_SEQUENCE = { 2, 3, 2, 4 };


  /**
   * This gives the number of squares of grass to put along
   * the bottom of the screen.
   */
  static int COLUMNS;


  /**
   * After how many tiles does the background repeat.
   */
  static int CYCLE = 5;


  /**
   * the fixed Y coordinate of the strip of grass.
   */
  static int TOP_Y;


  //———————————————————
  //    instance fields


  /**
   * Which tile we are currently on in the frame sequence.
   */
  int mySequenceIndex = 0;


  /**
   * The index to use in the static tiles array to get the
   * animated tile..
   */
  int myAnimatedTileIndex;


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


  /**
   * Takes the width of the screen and sets my columns
   * to the correct corresponding number
   */
  static int setColumns(int screenWidth) {
    COLUMNS = ((screenWidth / 20) + 1)*3;
    return(COLUMNS);
  }


  //———————————————————
  //   initialization


  /**
   * constructor initializes the image and animation.
   */
  public Grass() throws Exception {
    super(setColumns(JumpCanvas.DISP_WIDTH), 1,
   Image.createImage(“/icons/grass.png”),
   TILE_WIDTH, TILE_WIDTH);
    TOP_Y = JumpManager.DISP_HEIGHT – TILE_WIDTH;
    setPosition(0, TOP_Y);
    myAnimatedTileIndex = createAnimatedTile(2);
    for(int i = 0; i < COLUMNS; i++) {
      if((i % CYCLE == 0) || (i % CYCLE == 2)) {
 setCell(i, 0, myAnimatedTileIndex);
      } else {
 setCell(i, 0, 1);
      }
    }
  }


  //———————————————————
  //   graphics


  /**
   * sets the grass back to its initial position..
   */
  void reset() {
    setPosition(-(TILE_WIDTH*CYCLE), TOP_Y);
    mySequenceIndex = 0;
    setAnimatedTile(myAnimatedTileIndex, FRAME_SEQUENCE[mySequenceIndex]);
  }


  /**
   * alter the background image appropriately for this frame..
   */
  void advance(int tickCount) {
    if(tickCount % 2 == 0) { // slow the animation down a little
      mySequenceIndex++;
      mySequenceIndex %= 4;
      setAnimatedTile(myAnimatedTileIndex, FRAME_SEQUENCE[mySequenceIndex]);
    }
  }


}


Putting it Together


Now that you have all to the relevant classes, the example needs to be built just as in the “Hello World” example. The classes should be compiled, preverified, and jarred. (Preverification is an extra step that makes bytecode verification easier for J2ME applications; it’s a necessary step but one that is performed by standard utility programs so you don’t need to worry too much about it.) Be sure that all of your resources have been correctly placed in the jar. In my example I need to place the files cowboy.png, tumbleweed.png, and grass.png in a top-level folder called icons in my jar since I constructed my Layers with images created by calls to Image.createImage(“/icons/grass.png”), etc. Also I need to place the correct MANIFEST.MF file in the jar. Here’s the one I used:


MIDlet-1: Hello World, /icons/hello.png, net.frog_parrot.hello.Hello
MIDlet-2: Tumbleweed, /icons/boot.png, net.frog_parrot.jump.Jump
MMIDlet-Description: Example games for MIDP
MIDlet-Name: Example Games
MIDlet-Permissions:
MIDlet-Vendor: frog-parrot.net
MIDlet-Version: 2.0
MicroEdition-Configuration: CLDC-1.0
MicroEdition-Profile: MIDP-2.0


Keep in mind that if you want to put a custom MANIFEST.MF file in your jar instead of an auto-generated one, you need to use the m option when creating your jar.


Once you have your jar file, you need a jad file. Here’s the one I used, which I called jump.jad:


MIDlet-1: Hello, /icons/hello.png, net.frog_parrot.hello.Hello
MIDlet-2: Tumbleweed, /icons/boot.png, net.frog_parrot.jump.Jump
MMIDlet-Description: Example games for MIDP
MIDlet-Jar-URL: jump.jar
MIDlet-Name: Example Games
MIDlet-Permissions:
MIDlet-Vendor: frog-parrot.net
MIDlet-Version: 2.0
MicroEdition-Configuration: CLDC-1.0
MicroEdition-Profile: MIDP-2.0
MIDlet-Jar-Size: 17259


Don’t forget to verify that the MIDlet-Jar-Size is correct if you’re creating the jad file by hand. Also note that this jad file indicates that your jar file needs to contain the icon /icons/boot.png which is used in the game selection menu and looks like this:


 
Once the jar and jad files are ready, you can run the program. I ran it from the command line with the runmidlet script that came with the WTK2.0 toolkit.


Good luck with your own game!


Appendix A: Script Hints


Running the midlet in the emulator from the command line is very simple. All I did was go to the bin directory under the toolkit’s WTK2.0 directory. From there I typed ./bin/emulator followed by an option giving the name of the jad descriptor file corresponding to the MIDlet I wanted to run. For example in my case this was the following line:


./bin/emulator -Xdescriptor:/home/carol/j2me/book/ch02/bin/games.jad


Other emulator options are listed in the toolkit documentation in section D. Command Line Utilities under the heading of Running the Emulator.


The following build script will automatically fix the MIDlet-Jar-Size property in your jad file after rebuilding as long as the MIDlet-Jar-Size property is the last line of the jad file, as it is in the examples I provided.


Note that this script assumes that you have a file tree configured as follows: Under a main directory you must have four subdirectories: bin (which contains this script as well as the jad and MANIFEST.MF files), a tmpclasses directory (which may be empty), a classes directory (containing a subdirectory called images which contains all of your image files), and a src directory which contains the directory tree for the source code of the MIDlet you would like to compile.


# This script builds and preverifies the code
# for the tumbleweed game.


# reset this variable to the path to the correct javac
# command on your system:
JAVA4_HOME=/usr/java/j2sdk1.4.0_01/bin
# reset this variable to the corresct path to the WTK2.0
# directory of the WTK2.0 toolkit that you downloaded:
WTK2_HOME=../../../../WTK2.0


echo “clear directories”
# it’s better not to leave old class files lying
# around where they might accidentally get picked up
# and create errors…
rm ../tmpclasses/net/frog_parrot/hello/*.class
rm ../classes/net/frog_parrot/hello/*.class
rm ../tmpclasses/net/frog_parrot/jump/*.class
rm ../classes/net/frog_parrot/jump/*.class


echo “Compiling source files”


$JAVA4_HOME/javac -bootclasspath $WTK2_HOME/lib/midpapi.zip –
d ../tmpclasses -classpath ../tmpclasses ../src/net/frog_parrot/hello/*.java
../src/net/frog_parrot/jump/*.java


echo “Preverifying class files”


$WTK2_HOME/bin/preverify -classpath $WTK2_HOME/lib/midpapi.zip:
../tmpclasses -d ../classes ../tmpclasses


echo “Jarring preverified class files”
$JAVA4_HOME/jar cmf MANIFEST.MF jump.jar -C ../classes .


echo “Updating JAR size info in JAD file…”


NB=`wc -l jump.jad | awk ‘{print $1}’`
head –lines=$(($NB-1)) jump.jad > jump.jad1
echo “MIDlet-Jar-Size:” `stat -c ‘%s’ jump.jar`>> jump.jad1
cp jump.jad1 jump.jad


There’s one more little hint I would like to pass along to you. I noticed that at the end of the runmidlet script all of the messages generated by the runmidlet program are redirected to /dev/null. If you would like to see your messages for debugging purposes, you should remove the > /dev/null from the end of the script.


Appendix B: Images and the Gimp


If you’re wondering where all of the image files in this example came from, I drew them myself with a free program called the gimp, which you can download from http://www.gimp.org/download.html. If you decide to draw your image files with the gimp and you would like them to have transparent backgrounds, be sure to select a transparent background when you first create a new image file. Then make sure that you select “save background color” when you save the file. Also, you should save it with the .png extension so that the gimp will save it in the right format to be used by a J2ME game.


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

The Sprite Class


A Sprite is a graphical object represented by one image (at a time). The fact that a Sprite is composed of only one image is the principal difference between a Sprite and a TiledLayer, which is a region that is covered with images that can be manipulated. (The Sprite class has a few extra features, but the fact that it uses one image rather than filling an area with images is the most obvious difference.) So a Sprite is generally used for small, active game objects (such as your spaceship and the asteroids that are coming to crash into it) and a TiledLayer would be more likely to be used for an animated background.

One cool feature of Sprite is that even though a Sprite is represented by only one image at a time, it can be easily represented by different images under different circumstances, including by a series of images that make up an animation. In my example game the cowboy has three different images in which he is walking and one in which he is jumping. All of the images used for a given Sprite need to be stored together in a single image file. (To indicate where to find the image file to use, send the address of the image within the jar in exactly the same format that is used to find resources in the method Class.getResource(); see the Putting it Together section for more details.) The fact that multiple frames are stored in a single Image object is a convenience that means you don’t have to manipulate multiple Image objects to determine which face your Sprite is wearing at any given time. Here’s the image file for the cowboy:

 


And here is the image file for the tumbleweed which is animated to appear to be rolling:

 
The way to select which frame is shown at any given time is very intuitive. First, if your image file is composed of multiple images (as these two are), you should construct the Sprite with the constructor that specifies the width and height (in pixels) that you would like your Sprite to be. The width and height of the Image should be integer multiples of the width and height that you send to the constructor. In other words, the computer should be able to divide up your image file evenly into rectangles of the size you specify. As you can see from the examples above, the sub-images may be arranged horizontally or vertically. They can even be arranged in a grid with multiple rows and columns. Then to identify the individual frames, they are numbered starting with zero at the top left corner and continuing to the right and then continuing on to the lower rows, in exactly the same order in which you are reading the letters on this page. To select which frame is currently displayed, use the method setFrame(int sequenceIndex) sending the frame number as an argument.

The Sprite class has some added support for animation by allowing you to define a frame sequence with the method setFrameSequence(int[] sequence). As you can see in the code below, I’ve set a frame sequence of { 1, 2, 3, 2 } for my cowboy and { 0, 1, 2 } for my tumbleweed. To advance your Sprite’s animation from one frame to the next, you use the method nextFrame() (or if you prefer prevFrame()). This is very convenient in cases like my tumbleweed where all of the available frames are used in the animation. It’s slightly less convenient in cases like my cowboy that have an image or images that fall outside of the frame sequence of the animation. This is because once a frame sequence has been set, the argument to the method setFrame(int sequenceIndex) gives the index of an entry in the frame sequence instead of giving the index of the frame itself. What that means is that once I’ve set my cowboy’s frame sequence to { 1, 2, 3, 2 }, if I call setFrame(0) it will show frame number 1, setFrame(1) will show frame number 2, setFrame(2) will show frame number 3, and setFrame(3) will show frame number 2. But when the cowboy is jumping, I would like it to show frame number 0 which is no longer accessible. So when my cowboy jumps, I have to set my frame sequence to null before calling setFrame(0), and then I have to set my frame sequence back to the animation sequence { 1, 2, 3, 2 } afterwards. This is illustrated in the code below in the methods jump() and advance(int tickCount, boolean left).

In addition to changing your Sprite’s appearance by changing frames, you can change it by applying simple transforms such as rotations or mirror images. Both my cowboy and my tumbleweed below can be going either left or right, so of course I need to use the mirror image transform to change from one direction to the other. Once you start applying transforms, you need to keep track of the Sprite’s reference pixel. This is because when you transform your Sprite, the reference pixel is the pixel that does not move. You might expect that if your Sprite’s image is square then after a transformation the Sprite’s image will continue to occupy the same square of area on the screen. This is not the case. The best way to illustrate what happens is to imagine an example Sprite of a standing person facing left whose reference pixel has been defined to be the tip of his toe. Then after applying a 90-degree rotation, your person will be in the spot he would be in if he had tripped and fallen forward. Clearly this has its applications if your Sprite has a special pixel (such as the tip of an arrow) that should stay put after a transformation. But if you want your Sprite to continue to occupy the same space on the screen after a transformation, then you should first call defineReferencePixel(int x, int y) and set your Sprite’s reference pixel to the center of the Sprite, as I did in the Cowboy constructor below. Be aware that the coordinates in defineReferencePixel(int x, int y) are relative to the top corner of the Sprite whereas the coordinates sent to setRefPixelPosition(int x, int y) tell where to place the Sprite’s reference pixel on the screen in terms of the screen’s coordinates. To be more precise, the coordinates sent to setRefPixelPosition(int x, int y) refer to the coordinate system of the Canvas if the Sprite is painted directly onto the Canvas, but if the Sprite is painted by a LayerManager, these coordinates should be given in terms of the LayerManager’s coordinate system. (How these coordinate systems fit together is explained in the LayerManager section above.) The coordinates in the various methods to set and get the position of the reference pixel or to set and get the position of the top left corner pixel refer to the coordinates of the appropriate Canvas or LayerManager. Also note that if you perform multiple transformations, the later trasformations are applied to the original image and not to its current state. In other words, if I apply setTransform(TRANS_MIRROR) twice in a row, the second transform will not mirror my image back to its original position, it will just repeat the action of setting the Image to being a mirror image of the original Image. If you want to set a transformed Sprite back to normal, use setTransform(TRANS_NONE). This is illustrated in the top of the Cowboy.advance(int tickCount, boolean left) method.

Another great feature of the Layer class (including both Sprites and TiledLayers) is the support it gives you for placing your objects in relative terms instead of in absolute terms. If your Sprite needs to move over three pixels regardless of where it currently is, you can just call move(int x, int y) sending it the x and y distances it should move from its current position, as opposed to calling setRefPixelPosition(int x, int y) with the absolute coordinates of the Sprite’s new location. Even more useful is the set of collidesWith() methods. This allows you to check if a Sprite is occupying the same space as another Sprite or TiledLayer or even an Image. It’s easy to see that this saves you quite a number of comparisons, especially since when you send the pixelLevel argument as true, it will only consider the two Layers as having collided if their opaque pixels overlap.

In my “Tumbleweed” game, after advancing all of the Sprites, I check if the cowboy has collided with any tumbleweeds. (This happens in the Cowboy.checkCollision(Tumbleweed tumbleweed) method which is called from JumpManager.advance(int gameTicks).) I check the collisions between the cowboy and all of the tumbleweeds each time because it automatically returns false for any tumbleweeds that aren’t currently visible anyway, so I’m not really being wasteful by checking the cowboy against tunbleweeds that are not currently in use. In many cases, however, you can save some effort by only checking for collisions that you know are possible rather than checking all of the Sprites against each other. Note that in my example I don’t bother to check if the tumbleweeds collide with each other or if anything collides with the background grass because that is irrelevant. If you’re checking for pixel-level collisions, you will want to be sure that your images have a transparent background. (This is also helpful in general so that your Sprite doesn’t paint an ugly rectangle of background color over another Sprite or Image.) Some discussion of creating the image files correctly is included in Appendix B.

Here’s the code for Cowboy.java:

package net.frog_parrot.jump;

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

/**
 * This class represents the player.
 *
 * @author Carol Hamer
 */
public class Cowboy extends Sprite {

  //———————————————————
  //    dimension fields

  /**
   * The width of the cowboy’s bounding rectangle.
   */
  static int WIDTH = 32;

  /**
   * The height of the cowboy’s bounding rectangle.
   */
  static int HEIGHT = 48;

  /**
   * This is the order that the frames should be displayed
   * for the animation.
   */
  static int[] FRAME_SEQUENCE = { 3, 2, 1, 2 };

  //———————————————————
  //    instance fields

  /**
   * the X coordinate of the cowboy where the cowboy starts
   * the game.
   */
  int myInitialX;

  /**
   * the Y coordinate of the cowboy when not jumping.
   */
  int myInitialY;

  /**
   * The jump index that indicates that no jump is
   * currently in progress..
   */
  int myNoJumpInt = -6;

  /**
   * Where the cowboy is in the jump sequence.
   */
  int myIsJumping = myNoJumpInt;

  /**
   * If the cowboy is currently jumping, this keeps track
   * of how many points have been scored so far during
   * the jump.  This helps the calculation of bonus points since
   * the points being scored depend on how many tumbleweeds
   * are jumped in a single jump.
   */
  int myScoreThisJump = 0;

  //———————————————————
  //   initialization

  /**
   * constructor initializes the image and animation.
   */
  public Cowboy(int initialX, int initialY) throws Exception {
    super(Image.createImage(“/icons/cowboy.png”),
   WIDTH, HEIGHT);
    myInitialX = initialX;
    myInitialY = initialY;
    // we define the reference pixel to be in the middle
    // of the cowboy image so that when the cowboy turns
    // from right to left (and vice versa) he does not
    // appear to move to a different location.
    defineReferencePixel(WIDTH/2, 0);
    setRefPixelPosition(myInitialX, myInitialY);
    setFrameSequence(FRAME_SEQUENCE);
  }

  //———————————————————
  //   game methods

  /**
   * If the cowboy has landed on a tumbleweed, we decrease
   * the score.
   */
  int checkCollision(Tumbleweed tumbleweed) {
    int retVal = 0;
    if(collidesWith(tumbleweed, true)) {
      retVal = 1;
      // once the cowboy has collided with the tumbleweed,
      // that tumbleweed is done for now, so we call reset
      // which makes it invisible and ready to be reused.
      tumbleweed.reset();
    }
    return(retVal);
  }

  /**
   * set the cowboy back to its initial position.
   */
  void reset() {
    myIsJumping = myNoJumpInt;
    setRefPixelPosition(myInitialX, myInitialY);
    setFrameSequence(FRAME_SEQUENCE);
    myScoreThisJump = 0;
    // at first the cowboy faces right:
    setTransform(TRANS_NONE);
  }

  //———————————————————
  //   graphics

  /**
   * alter the cowboy image appropriately for this frame..
   */
  void advance(int tickCount, boolean left) {
    if(left) {
      // use the mirror image of the cowboy graphic when
      // the cowboy is going towards the left.
      setTransform(TRANS_MIRROR);
      move(-1, 0);
    } else {
      // use the (normal, untransformed) image of the cowboy
      // graphic when the cowboy is going towards the right.
      setTransform(TRANS_NONE);
      move(1, 0);
    }
    // this section advances the animation:
    // every third time through the loop, the cowboy
    // image is changed to the next image in the walking
    // animation sequence:
    if(tickCount % 3 == 0) { // slow the animation down a little
      if(myIsJumping == myNoJumpInt) {
 // if he’s not jumping, set the image to the next
 // frame in the walking animation:
 nextFrame();
      } else {
 // if he’s jumping, advance the jump:
 // the jump continues for several passes through
 // the main game loop, and myIsJumping keeps track
 // of where we are in the jump:
 myIsJumping++;
 if(myIsJumping < 0) {
   // myIsJumping starts negative, and while it’s
   // still negative, the cowboy is going up. 
   // here we use a shift to make the cowboy go up a
   // lot in the beginning of the jump, and ascend
   // more and more slowly as he reaches his highest
   // position:
   setRefPixelPosition(getRefPixelX(),
         getRefPixelY() – (2 << (-myIsJumping)));
 } else {
   // once myIsJumping is negative, the cowboy starts
   // going back down until he reaches the end of the
   // jump sequence:
   if(myIsJumping != -myNoJumpInt – 1) {
     setRefPixelPosition(getRefPixelX(),
    getRefPixelY() + (2 << myIsJumping));
   } else {
     // once the jump is done, we reset the cowboy to
     // his non-jumping position:
     myIsJumping = myNoJumpInt;
     setRefPixelPosition(getRefPixelX(), myInitialY);
     // we set the image back to being the walking
     // animation sequence rather than the jumping image:
     setFrameSequence(FRAME_SEQUENCE);
     // myScoreThisJump keeps track of how many points
     // were scored during the current jump (to keep
     // track of the bonus points earned for jumping
     // multiple tumbleweeds).  Once the current jump is done,
     // we set it back to zero. 
     myScoreThisJump = 0;
   }
 }
      }
    }
  }

  /**
   * makes the cowboy jump.
   */
  void jump() {
    if(myIsJumping == myNoJumpInt) {
      myIsJumping++;
      // switch the cowboy to use the jumping image
      // rather than the walking animation images:
      setFrameSequence(null);
      setFrame(0);
    }
  }

  /**
   * This is called whenever the cowboy clears a tumbleweed
   * so that more points are scored when more tumbleweeds
   * are cleared in a single jump.
   */
  int increaseScoreThisJump() {
    if(myScoreThisJump == 0) {
      myScoreThisJump++;
    } else {
      myScoreThisJump *= 2;
    }
    return(myScoreThisJump);
  }

}

 

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.

mysql backup shell script

mysql backup shell script
#!/bin/sh
# mysql_backup.sh: backup mysql databases and keep newest 5 days backup.
#
# Last updated: 2006.9.6
# ———————————————————————-
# This is a free shell script under GNU GPL version
# Feedback/comment/suggestions : http://www.strongd.net
# ———————————————————————-


# your mysql login information
# db_user is mysql username
# db_passwd is mysql password
# db_host is mysql host
# —————————–
db_user=”root”
db_passwd=”passwd”
db_host=”localhost”


# the directory for story your backup file.
backup_dir=”/backup”


# date format for backup file (dd-mm-yyyy)
time=”$(date +”%d-%m-%Y”)”


# mysql, mysqldump and some other bin’s path
MYSQL=”$(which mysql)”
MYSQLDUMP=”$(which mysqldump)”
MKDIR=”$(which mkdir)”
RM=”$(which rm)”
MV=”$(which mv)”
GZIP=”$(which gzip)”


# check the directory for store backup is writeable
test ! -w $backup_dir && echo “Error: $backup_dir is un-writeable.” && exit 0


# the directory for story the newest backup
test ! -d “$backup_dir/backup.0/” && $MKDIR “$backup_dir/backup.0/”


# get all databases
all_db=”$($MYSQL -u $db_user -h $db_host -p$db_passwd -Bse ‘show databases’)”


for db in $all_db
do
    $MYSQLDUMP -u $db_user -h $db_host -p$db_passwd $db | $GZIP -9 > “$backup_dir/backup.0/$time.$db.gz”
done


# delete the oldest backup
test -d “$backup_dir/backup.5/” && $RM -rf “$backup_dir/backup.5”


# rotate backup directory
for int in 4 3 2 1 0
do
    if(test -d “$backup_dir”/backup.”$int”)
    then
        next_int=`expr $int + 1`
        $MV “$backup_dir”/backup.”$int” “$backup_dir”/backup.”$next_int”
    fi
done


exit 0;


How to write a shell script

How to write a shell script
Introduction
A shell is a command line interpretor. It takes commands and executes them. As such, it implements a programming language. The Bourne shell is used to create shell scripts — ie. programs that are interpreted/executed by the shell. You can write shell scripts with the C-shell; however, this is not covered here.


Creating a Script
Suppose you often type the command
    find . -name file -print


and you’d rather type a simple command, say
    sfind file


Create a shell script
    % cd ~/bin
    % emacs sfind
    % page sfind
    find . -name $1 -print
    % chmod a+x sfind
    % rehash
    % cd /usr/local/bin
    % sfind tcsh
    ./shells/tcsh


Observations
This quick example is far from adequate but some observations:
Shell scripts are simple text files created with an editor.
Shell scripts are marked as executeable
    %chmod a+x sfind


Should be located in your search path and ~/bin should be in your search path.
You likely need to rehash if you’re a Csh (tcsh) user (but not again when you login).
Arguments are passed from the command line and referenced. For example, as $1.
#!/bin/sh
All Bourne Shell scripts should begin with the sequence
    #!/bin/sh


From the man page for exec(2):
“On the first line of an interpreter script, following the “#!”, is the name of a program which should be used to interpret the contents of the file. For instance, if the first line contains “#! /bin/sh”, then the con- tents of the file are executed as a shell script.”


You can get away without this, but you shouldn’t. All good scripts state the interpretor explicitly. Long ago there was just one (the Bourne Shell) but these days there are many interpretors — Csh, Ksh, Bash, and others.


Comments
Comments are any text beginning with the pound (#) sign. A comment can start anywhere on a line and continue until the end of the line.
Search Path
All shell scripts should include a search path specifica- tion:
    PATH=/usr/ucb:/usr/bin:/bin; export PATH


A PATH specification is recommended — often times a script will fail for some people because they have a different or incomplete search path.
The Bourne Shell does not export environment variables to children unless explicitly instructed to do so by using the export command.


Argument Checking
A good shell script should verify that the arguments sup- plied (if any) are correct.


    if [ $# -ne 3 ]; then
         echo 1>&2 Usage: $0 19 Oct 91
         exit 127
    fi


This script requires three arguments and gripes accordingly.


Exit status
All Unix utilities should return an exit status.
    # is the year out of range for me?


    if [ $year -lt 1901  -o  $year -gt 2099 ]; then
         echo 1>&2 Year \”$year\” out of range
         exit 127
    fi


    etc…


    # All done, exit ok


    exit 0


A non-zero exit status indicates an error condition of some sort while a zero exit status indicates things worked as expected.
On BSD systems there’s been an attempt to categorize some of the more common exit status codes. See /usr/include/sysexits.h.


Using exit status
Exit codes are important for those who use your code. Many constructs test on the exit status of a command.
The conditional construct is:


    if command; then
         command
    fi


For example,
    if tty -s; then
         echo Enter text end with \^D
    fi


Your code should be written with the expectation that others will use it. Making sure you return a meaningful exit status will help.
Stdin, Stdout, Stderr
Standard input, output, and error are file descriptors 0, 1, and 2. Each has a particular role and should be used accordingly:
    # is the year out of range for me?


    if [ $year -lt 1901  -o  $year -gt 2099 ]; then
         echo 1>&2 Year \”$year\” out of my range
         exit 127
    fi


    etc…


    # ok, you have the number of days since Jan 1, …


    case `expr $days % 7` in
    0)
         echo Mon$$
    1)
         echo Tue$$


    etc…


Error messages should appear on stderr not on stdout! Output should appear on stdout. As for input/output dialogue:
    # give the fellow a chance to quit


    if tty -s ; then
         echo This will remove all files in $* since …
         echo $n Ok to procede? $c;      read ans
         case “$ans” in
              n*|N*)
    echo File purge abandoned;
    exit 0   $$
         esac
         RM=”rm -rfi”
    else
         RM=”rm -rf”
    fi


Note: this code behaves differently if there’s a user to communicate with (ie. if the standard input is a tty rather than a pipe, or file, or etc. See tty(1)).
Language Constructs
For loop iteration
Substitute values for variable and perform task:


    for variable in word …
    do
         command
    done


For example:
    for i in `cat $LOGS`
    do
            mv $i $i.$TODAY
            cp /dev/null $i
            chmod 664 $i
    done


Alternatively you may see:
    for variable in word …; do command; done


Case
Switch to statements depending on pattern match


    case word in
    [ pattern [ | pattern … ] )
         command $$ ] …
    esac


For example:


    case “$year” in


    [0-9][0-9])
            year=19${year}
            years=`expr $year – 1901`
            $$
    [0-9][0-9][0-9][0-9])
            years=`expr $year – 1901`
            $$
    *)
            echo 1>&2 Year \”$year\” out of range …
            exit 127
            $$
    esac


Conditional Execution
Test exit status of command and branch


    if command
    then
         command
    [ else
         command ]
    fi


For example:
    if [ $# -ne 3 ]; then
            echo 1>&2 Usage: $0 19 Oct 91
            exit 127
    fi


Alternatively you may see:
    if command; then command; [ else command; ] fi


While/Until Iteration
Repeat task while command returns good exit status.


    {while | until} command
    do
         command
    done


For example:
    # for each argument mentioned, purge that directory


    while [ $# -ge 1 ]; do
            _purge $1
            shift
    done


Alternatively you may see:
    while command; do command; done


Variables
Variables are sequences of letters, digits, or underscores beginning with a letter or underscore. To get the contents of a variable you must prepend the name with a $.


Numeric variables (eg. like $1, etc.) are positional vari- ables for argument communication.



Variable Assignment
Assign a value to a variable by variable=value. For example:


    PATH=/usr/ucb:/usr/bin:/bin; export PATH


or
    TODAY=`(set \`date\`; echo $1)`


Exporting Variables
Variables are not exported to children unless explicitly marked.


    # We MUST have a DISPLAY environment variable


    if [ “$DISPLAY” = “” ]; then
            if tty -s ; then
     echo “DISPLAY (`hostname`:0.0)? \c”;
     read DISPLAY
            fi
            if [ “$DISPLAY” = “” ]; then
     DISPLAY=`hostname`:0.0
            fi
            export DISPLAY
    fi


Likewise, for variables like the PRINTER which you want hon- ored by lpr(1). From a user’s .profile:
    PRINTER=PostScript; export PRINTER


Note: that the Cshell exports all environment variables.


Referencing Variables
Use $variable (or, if necessary, ${variable}) to reference the value.


    # Most user’s have a /bin of their own


    if [ “$USER” != “root” ]; then
            PATH=$HOME/bin:$PATH
    else
            PATH=/etc:/usr/etc:$PATH
    fi


The braces are required for concatenation constructs.
$p_01


The value of the variable “p_01”.
${p}_01


The value of the variable “p” with “_01” pasted onto the end.


Conditional Reference


${variable-word}


If the variable has been set, use it’s value, else use word.
POSTSCRIPT=${POSTSCRIPT-PostScript};
export POSTSCRIPT


${variable:-word}


If the variable has been set and is not null, use it’s value, else use word.
These are useful constructions for honoring the user envi- ronment. Ie. the user of the script can override variable assignments. Cf. programs like lpr(1) honor the PRINTER environment variable, you can do the same trick with your shell scripts.


${variable:?word}


If variable is set use it’s value, else print out word and exit. Useful for bailing out.


Arguments
Command line arguments to shell scripts are positional vari- ables:


$0, $1, …


The command and arguments. With $0 the command and the rest the arguments.
$#


The number of arguments.
$*, $@


All the arguments as a blank separated string. Watch out for “$*” vs. “$@”.
And, some commands:
shift


Shift the postional variables down one and decrement number of arguments.
set arg arg …


Set the positional variables to the argument list.
Command line parsing uses shift:


    # parse argument list


    while [ $# -ge 1 ]; do
            case $1 in
         process arguments…
            esac
            shift
    done


A use of the set command:
    # figure out what day it is


    TODAY=`(set \`date\`; echo $1)`


    cd $SPOOL


    for i in `cat $LOGS`
    do
            mv $i $i.$TODAY
            cp /dev/null $i
            chmod 664 $i
    done


Special Variables
$$


Current process id. This is very useful for constructing temporary files.
         tmp=/tmp/cal0$$
         trap “rm -f $tmp /tmp/cal1$$ /tmp/cal2$$”
         trap exit 1 2 13 15
         /usr/lib/calprog >$tmp


$?


The exit status of the last command.
         $command
         # Run target file if no errors and …


         if [ $? -eq 0 ]
         then
  etc…
         fi



Quotes/Special Characters
Special characters to terminate words:


      ; & ( ) | ^ < > new-line space tab


These are for command sequences, background jobs, etc. To quote any of these use a backslash (\) or bracket with quote marks (“” or ”).
Single Quotes


Within single quotes all characters are quoted — including the backslash. The result is one word.



         grep :${gid}: /etc/group | awk -F: ‘{print $1}’


Double Quotes
Within double quotes you have variable subsitution (ie. the dollar sign is interpreted) but no file name generation (ie. * and ? are quoted). The result is one word.


         if [ ! “${parent}” ]; then
           parent=${people}/${group}/${user}
         fi


Back Quotes
Back quotes mean run the command and substitute the output.



         if [ “`echo -n`” = “-n” ]; then
   n=””
   c=”\c”
         else
   n=”-n”
   c=””
         fi


and
         TODAY=`(set \`date\`; echo $1)`


Functions
Functions are a powerful feature that aren’t used often enough. Syntax is


    name ()
    {
         commands
    }


For example:


    # Purge a directory


    _purge()
    {
            # there had better be a directory


            if [ ! -d $1 ]; then
     echo $1: No such directory 1>&2
     return
            fi


         etc…
    }


Within a function the positional parmeters $0, $1, etc. are the arguments to the function (not the arguments to the script).
Within a function use return instead of exit.


Functions are good for encapsulations. You can pipe, redi- rect input, etc. to functions. For example:


    # deal with a file, add people one at a time


    do_file()
    {
            while parse_one


            etc…
    }


    etc…


    # take standard input (or a specified file) and do it.


    if [ “$1” != “” ]; then
            cat $1 | do_file
    else
            do_file
    fi


Sourcing commands
You can execute shell scripts from within shell scripts. A couple of choices:


sh command


This runs the shell script as a separate shell. For example, on Sun machines in /etc/rc:


         sh /etc/rc.local


. command
This runs the shell script from within the current shell script. For example:


         # Read in configuration information
         .  /etc/hostconfig


What are the virtues of each? What’s the difference? The second form is useful for configuration files where environment variable are set for the script. For example:
    for HOST in $HOSTS; do


      # is there a config file for this host?


      if [ -r ${BACKUPHOME}/${HOST} ]; then
.  ${BACKUPHOME}/${HOST}
      fi
    etc…


Using configuration files in this manner makes it possible to write scripts that are automatically tailored for differ- ent situations.
Some Tricks
Test
The most powerful command is test(1).


    if test expression; then


         etc…


and (note the matching bracket argument)
    if [ expression ]; then


         etc…


On System V machines this is a builtin (check out the com- mand /bin/test).
On BSD systems (like the Suns) compare the command /usr/bin/test with /usr/bin/[.


Useful expressions are:


test { -w, -r, -x, -s, … } filename


is file writeable, readable, executeable, empty, etc?
test n1 { -eq, -ne, -gt, … } n2


are numbers equal, not equal, greater than, etc.?
test s1 { =, != } s2


Are strings the same or different?
test cond1 { -o, -a } cond2


Binary or; binary and; use ! for unary negation.
For example


    if [ $year -lt 1901  -o  $year -gt 2099 ]; then
         echo 1>&2 Year \”$year\” out of range
         exit 127
    fi


Learn this command inside out! It does a lot for you.


String matching
The test command provides limited string matching tests. A more powerful trick is to match strings with the case switch.


    # parse argument list


    while [ $# -ge 1 ]; do
            case $1 in
            -c*)    rate=`echo $1 | cut -c3-`$$
            -c)     shift;  rate=$1 $$
            -p*)    prefix=`echo $1 | cut -c3-`$$
            -p)     shift;  prefix=$1 $$
            -*)     echo $Usage; exit 1 $$
            *)      disks=$*;       break   $$
            esac


            shift


    done


Of course getopt would work much better.


SysV vs BSD echo
On BSD systems to get a prompt you’d say:


    echo -n Ok to procede?;  read ans


On SysV systems you’d say:
    echo Ok to procede? \c; read ans


In an effort to produce portable code we’ve been using:
    # figure out what kind of echo to use


    if [ “`echo -n`” = “-n” ]; then
            n=””;  c=”\c”
    else
            n=”-n”;     c=””
    fi


    etc…


    echo $n Ok to procede? $c; read ans


Is there a person?
The Unix tradition is that programs should execute as qui- etly as possible. Especially for pipelines, cron jobs, etc.


User prompts aren’t required if there’s no user.


    # If there’s a person out there, prod him a bit.


    if tty -s; then
         echo Enter text end with \^D
    fi


The tradition also extends to output.
    # If the output is to a terminal, be verbose


    if tty -s <&1; then
         verbose=true
    else
         verbose=false
    fi


Beware: just because stdin is a tty that doesn’t mean that stdout is too. User prompts should be directed to the user terminal.
    # If there’s a person out there, prod him a bit.


    if tty -s; then
         echo Enter text end with \^D >&0
    fi


Have you ever had a program stop waiting for keyboard input when the output is directed elsewhere?


Creating Input
We’re familiar with redirecting input. For example:


    # take standard input (or a specified file) and do it.


    if [ “$1” != “” ]; then
            cat $1 | do_file
    else
            do_file
    fi


alternatively, redirection from a file:
    # take standard input (or a specified file) and do it.


    if [ “$1” != “” ]; then
            do_file < $1
    else
            do_file
    fi


You can also construct files on the fly.
    rmail bsmtp <
    rcpt to:
    data
    from: <[email protected]>
    to:
    Subject: Signon $2


    subscribe $2 Usenet Feeder at UWO
    .
    quit
    EOF


Note: that variables are expanded in the input.


String Manipulations
One of the more common things you’ll need to do is parse strings. Some tricks



    TIME=`date | cut -c12-19`


    TIME=`date | sed ‘s/.* .* .* \(.*\) .* .*/\1/’`


    TIME=`date | awk ‘{print $4}’`


    TIME=`set \`date\`; echo $4`


    TIME=`date | (read u v w x y z; echo $x)`


With some care, redefining the input field separators can help.


    #!/bin/sh
    # convert IP number to in-addr.arpa name


    name()
    {    set `IFS=”.”;echo $1`
         echo $4.$3.$2.$1.in-addr.arpa
    }


    if [ $# -ne 1 ]; then
         echo 1>&2 Usage: bynum IP-address
         exit 127
    fi


    add=`name $1`


    nslookup < < EOF | grep “$add” | sed ‘s/.*= //’
    set type=any
    $add
    EOF


Debugging
The shell has a number of flags that make debugging easier:


sh -n command


Read the shell script but don’t execute the commands. IE. check syntax.


sh -x command


Display commands and arguments as they’re executed. In a lot of my shell scripts you’ll see


    # Uncomment the next line for testing
    # set -x