Upcoming Events
Unite 2010
11/10 - 11/12 @ Montréal, Canada

GDC China
12/5 - 12/7 @ Shanghai, China

Asia Game Show 2010
12/24 - 12/27  

GDC 2011
2/28 - 3/4 @ San Francisco, CA

More events...
Quick Stats
94 people currently visiting GDNet.
2406 articles in the reference section.

Help us fight cancer!
Join SETI Team GDNet!
Link to us Events 4 Gamers
Intel sponsors gamedev.net search:

First MIDlet

To write our first MIDlet we will use the J2ME Wireless Toolkit and the tools it provides. The most important tool is the KToolbar, from within the KToolbar you can create and manage your MIDlet projects. It has features to compile, package, run and even obfuscate your MIDlet.

Using the KToolbar to manage your MIDlet build process will enable you to quickly get into the development of your first MIDlet. You wont have to worry about doing all the compiling and packaging on the command line, but rather you can save this for later when you are comfortable with the environment. As your projects grow in size your needs for a tailored build process will increase and you will most probably need more control! (More on this in part 2)

Setting up the project

When you start the KToolbar, you will see the following window. Take a close look, this is your new friend!

To create your project, click (you guessed it) the New project button.

In the first field, enter the name of your Project. The second field "MIDlet Class Name" is where you define the name of your MIDlets main class. This class will be the one that extends MIDlet and is instantiated by the AMS. The actual name you give it is not important, but it must be a valid Java Class name. For now lets call it Startup. The next window holds all the properties that must be present in your MIDlets Jad and Manifest file.

  • MIDlet-Jar-Size - This Jad attribute must always reflect the exact size in bytes of your MIDlets Jar file. When using the KToolbar to package your MIDlet this is handled automatically. (If the jad file holds a wrong value for your Jar files size, it will fail to install to the target device!)
  • MIDlet-Jar-URL - The url to the Jar file of your MIDlet. (Typically just the filename of the jar file, this is more important in conjunction with delivery of the MIDlet via over the air downloads)
  • MIDlet-Name - The name of your MIDlet, which appears in the list of MIDlets in the devices AMS.
  • MIDlet-Vendor - The name of the MIDlets vendor, you or your company.
  • MIDlet-Version - The current version of the MIDlet.
  • MicroEdition-Configuration - Mandatory field to identify which configuration the MIDlet uses
  • MicroEdition-Profile - Mandatory field to identify which profile the MIDlet uses

When the new project wizard is complete you will be prompted with the following (or similar):

Place Java source files in "c:\j2mewtk\apps\MyGame\src"
Place Application resource files in "c:\j2mewtk\apps\MyGame\res"

What happened now is that the KToolbar has created a directory structure for your project. KToolbar projects are placed in their own subdirectory in the "apps" folder which is located in the folder where you chose to install the WTK.

As the KToolbar states, we should place all our source files in the newly created "src" folder.

Basic MIDlet

Ok, we have the project set up and are ready to go. In the "src" folder, create a file called Startup.java (case sensitive). This will be the source file for our MIDlets main class. The Startup Class will extend javax.microedition.midlet.MIDlet which has 3 abstract methods that need implementing. These are the all important methods that control the lifecycle of our MIDlets.

Below is the source for our first and most basic MIDlet

import javax.microedition.midlet.*;

public class Startup 
   extends MIDlet
{
   /*
    * Default constructor used by AMS to create an instance 
    * of our main MIDlet class.
    */
   public Startup()
   {
      //Print message to console when Startup is constructed
      System.out.println("Constructor: Startup()");
   }
   
   /*
    * startApp() is called by the AMS after it has successfully created
    * an instance of our MIDlet class. startApp() causes our MIDlet to
    * go into a "Active" state. 
    */
   protected void startApp() 
      throws MIDletStateChangeException
   {
      //Print message to console when startApp() is called
      System.out.println("startApp()");
   }
   
   /*
   * destroyApp() is called by the AMS when the MIDlet is to be destroyed
   */
   protected void destroyApp( boolean unconditional ) 
      throws MIDletStateChangeException
   {

   }
   
   /*
   * pauseApp() is called by the AMS when the MIDlet should enter a paused 
   * state. This is not a typical "game" pause, but rater an environment pause.
   * The most common example is an incoming phone call on the device, 
   * which will cause the pauseApp() method to be called. This allows
   * us to perform the needed actions within our MIDlet
   */
   protected void pauseApp()
   {
   }
}

example01.zip

Compiling, preverifying and running

To compile all the src files in your project (currently just Startup.java) press "Build".

What the KToolbar does now is to compile your source code against the MIDP and CLDC APIs (and any libraries in the /lib folder of the currently selected emulator) into a folder called tmpclasses. The command it executes would be similar to (relative to project folder):

javac -d tmpclasses -bootclasspath %wtk%\lib\midpapi.zip -classpath tmpclasses;classes src\*.java

The next step the KToolbar takes is to preverify your java Classes and place them in the "classes" folder in your project. Class files must be preverified before they can be run on a MIDP device.

The command line approach for preverifying: (preverify.exe is a tool provided with the WTK, located in the WTK's \bin folder)

preverify -classpath %wtk%\lib\midpapi.zip tmpclasses -d classes

Now that your MIDlet is compiled and preverified you can run it by pressing "Run". As the only thing we are doing in our MIDlet is to print 2 strings to the console nothing will actually happen in the display of the emulator, but you should see the following in the WTK console:

Building "MyGame"
Build complete
Constructor: Startup()
startApp()

This shows us that the Startup class was constructed through its default constructor and then startApp() was called. Not very exciting, but important for our MIDlet to start :)

Using Forms and Commands

Forms and Commands are high level UI components that come in handy for building menus and showing instructions in games. The Form class is a subclass of Displayable, this means that we can directly display it on the devices Display.

The devices current Displayable can be set by help from the Display class. To get a reference to a Display object for our MIDlet we call the static method getDisplay() on the Display class. The method takes one parameter which is a reference to an instance of our MIDlet class:

Display display = Display.getDisplay( midletInstance );

we can now set the display to any Object that extends Displayable by calling:

display.setCurrent(nextDisplayable );

Now that we know this, lets try and make a Form and display it!

First we need to create a Form, the most basic form we can create is an empty form with a title;

Form basicForm = new Form("Form Title");

Let's add a String to the Form as well, this is done by appending it :

basicForm.append("My basic Form");

So we now have a Display and a Form ( which extends Displayable ), so we have all we need to proceed in showing the Form;

display.setCurrent( basicForm );

Put all this in our startApp() method and this will be the first thing that happens when our MIDlet launches.

Form and Display are both Classes in the javax.microedition.lcdui package so we must remember to import this in our source file.

example02.zip

Build and run!

Now that we have the Form, lets add a Command so that we can get some high-level input from the user. Lets make a Command that allows the user to generate a random number and append it to the Form.

A Command is a component that triggers an action in our MIDlet. To capture the event triggered when the user activates the Command we need to use the CommandListener interface. The CommandListener interface defines one method that we must implement:

commandAction( Command c, Displayable d )

When the user triggers the Command, the implementation will call the commandAction method on the associated CommandListener. The CommandListener is set by the setCommandListener() method on the Displayable where the Command has been added.

appendCommand = new Command( "Random", Command.SCREEN, 0 );

The Command constructor takes 3 parameters, the String associated with the Command, the type of Command (indicates to the implementation what type of action this Command will perform, sometimes affects the positioning of the Command on the screen) and the Commands priority or display order if you wish. And add it to our Form (addCommand() is inherited from Displayable, so all Displayables can have Commands):

basicForm.addCommand( appendCommand );

We now have a Command added to our Form, the next step would be to set the CommandListener of our Form. Lets make our Startup class implement CommandListener and override the commandAction() method to perform our actions.

basicForm.setCommandListener( this );

As we wanted to generate a random number we will also need to add a Random number generator to our app. For this we must import java.util.Random and create a new generator :

generator = new Random( System.currentTimeMillis() );

This creates a new Random generator and seeds it with the current time in milliseconds.

/* 
 * Callback method for the CommandListener, notifies us of Command action events
 */
public void commandAction( Command c, Displayable d )
{
   //check if the commandAction event is triggerd by our appendCommand 
   if( c == appendCommand )
   {
      //append a String to the form with a random number between 0 and 50.
      basicForm.append("Random: " + ( Math.abs( generator.nextInt() ) %50 ) + "\n");
   }
}

Build and run!

example03.zip

This was a brief introduction to the High-level components of the MIDP API. They provide a generic portable way of displaying information and getting high-level user input. Next is where the heart of your game will take place, so get ready to paint() that Canvas!

Using the Canvas to draw and handle input

Maybe the most important thing in a game is being able to draw stuff to the screen, be it characters, items or backgrounds. To do this in MIDP we will be using the Canvas, Graphics and Image classes, these are the main classes you will be using for your low-level graphics handling.

The Canvas is an abstract class and we must therefore subclass it to be able to use it, lets make a new Class called GameScreen that extends Canvas. As we have seen before the Canvas class defines the abstract paint( Graphics g ) method, in our GameScreen class we will override this method which will allow us to draw to the Graphics object passed to the paint() method by the Virtual Machine.

This leaves us with the following source for our GameScreen class:

import javax.microedition.lcdui.*;

public class GameScreen 
   extends Canvas
{
   //Default constructor for our GameScreen class.
   public GameScreen()
   {
      
   }
   
   /* 
    * called when the Canvas is to be painted
    */
   protected void paint( Graphics g )
   {
      
   }
}

Now we have the basics we need to draw to the screen, lets get things set up to receive key events from the user. The Canvas class defines 3 methods that handle key events, keyPressed(), keyReleased() and keyRepeated(). The canvas class has empty implementations of these methods, so it is up to us to override them and handle the events as we see fit.

/* 
* called when a key is pressed and this Canvas is the
* current Displayable 
*/
protected void keyPressed( int keyCode )
{
}

/* 
* called when a key is released  and this Canvas is the
* current Displayable 
*/
protected void keyReleased( int keyCode )
{
}

As you see we have only implemented keyPressed() and keyReleased() not keyRepeated(). You should try not to rely on keyRepeated() events as the frequency of the calls to keyRepeated() varies a lot from device to device. And using the behaviour keyRepeated() provides us is not the optimal way to check whether the user has held the key down or not.

Ok so we are now ready to receive input and draw to the screen, before we go any further lets make sure we know how to get the Canvas we have made displayed on the screen. Remember the Startup class we made earlier? Lets change this class so that its sole purpose is to serve as an entry point to our game and create and display a new instance of our GameScreen class.

protected void startApp() 
   throws MIDletStateChangeException
{
   Display display = Display.getDisplay( this );
   
   //GameScreen extends Canvas which extends Displayable so it can
   // be displayed directly
   display.setCurrent( new GameScreen() );
}

So now we are creating a new GameScreen and displaying it. Now we can try out some of the primitive drawing methods available from the Graphics class.

//set the current colour of the Graphics context to a darkish blue
// 0xRRGGBB
g.setColor( 0x000088 );
//draw a filled rectangle at x,y coordinates 0, 0 with a width
//and height equal to that of the Canvas itself
g.fillRect( 0, 0, this.getWidth(), this.getHeight() );

By setting the colour via setColor( int rgbColor ) we will affect all subsequent rendering operations to this Graphics context. Hence our call to fillRect( x, y, width, height ) will draw a filled rectangle in our desired colour. This also introduces 2 quite important methods of the Canvas class, getWidth() and getHeight() you will use these methods to obtain the total available for you to draw to on the Canvas. These are important values when targeting multiple devices with varying screen sizes. Always obtain the values via getWidth() and getHeight() don't be tempted to hardcode the values as you will create a lot of extra work for yourself when you want to port your game. Try to make all your draws to the screen (where possible) relative to the width and height of the Canvas.

Build and run!

example04.zip

Input handling

Just to get the hang of handling key events lets make the colour of our filled rectangle change when the user presses a key. For fun we can make the rectangle red when the user presses the LEFT key, green when for RIGHT, black for UP, white for DOWN and blue for FIRE.

As you might have noticed, a key pressed event is represented by a int value reflecting the key code of the key the user pressed. This key code can be treated in 2 separate ways, either via its actual value (KEY_NUM0 to KEY_NUM9, KEY_STAR or KEY_POUND which make up a standard telephone keypad) or its game action value (UP, DOWN, LEFT, RIGHT, FIRE, GAME_A, GAME_B,GAME_C,GAME_D). Why have 2 approaches you ask? As there are so many different keypad layouts and configuration, using the game action value of a key code will allow us to identify keys by their game action in a portable fashion. To retrieve the game action mapped to a key code we use the getGameAction( int keyCode ) method of the Canvas class.

/* 
 * called when the Canvas is to be painted
 */
protected void paint( Graphics g )
{
   //set the current color of the Graphics context to the specified RRGGBB colour
   g.setColor( colour );
   //draw a filled rectangle at x,y coordinates 0, 0 with a width
   // and height equal to that of the Canvas itself
   g.fillRect( 0, 0, this.getWidth(), this.getHeight() );      
}

/* 
* called when a key is pressed and this Canvas is the
* current Displayable 
*/
protected void keyPressed( int keyCode )
{
   //get the game action from the passed keyCode
   int gameAction = getGameAction( keyCode );
   switch( gameAction )
   {
      case LEFT:
      //set current colour to red
      colour = 0xFF0000; 
      break;
      
      case RIGHT:
      //set current colour to green
      colour = 0x00FF00;
      break;
      
      case UP:
      //set current colour to black
      colour = 0x000000;
      break;
      
      case DOWN:
      //set current colour to white
      colour = 0xFFFFFF;
      break;
      
      case FIRE:
      //set current colour to blue
      colour = 0x0000FF;
      break;
   }
   //schedule a repaint of the Canvas after each key press as
   //currently do not have any main game loop to do this for us.
   repaint();
}

Build and run!

example05.zip

If you look at the last line of the keyPressed() method you will see a call to repaint(). This schedules a repaint of the Canvas. Normally we would not do this from within the keyPressed() method, but at the end of our game loop. So now is a good time to get that main game loop going!

Game loop

The Thread class will be used to spawn our game thread so we can use this for our main loop. Threads can be created in 2 different ways. Either by subclassing Thread and overriding the run() method of the Thread class. But as mutliple inheritance is not possible in Java (our GameScreen class is already extending Canvas) we will use the second approach which is to implement the Runnable interface and implement the run() method of that interface. This means we can spawn a new Thread by passing the instance of our GameScreen class (which implements Runnable) to the Threads constructor.

//Default constructor for our GameScreen class.
public GameScreen()
{
   //create a new Thread on this Runnable and start it immediately
   new Thread( this ).start();		
}

/*
 * run() method defined in the Runnable interface, called by the 
 * Virtual machine when a Thread is started.
 */
public void run()
{
}

Now when we construct our GameScreen it will create and start a new Thread which triggers a call to our run() method.

We want our main loop to be called at a fixed rate, for this example lets set the rate to 15 times per second. (Although it is impossible to give an exact performance figure that will apply to all games, 15fps is a reasonably indicative average to start off with)

Within the run() method of our class we implement the timing logic for our main loop:

/*
 * run() method defined in the Runnable interface, called by the 
 * Virtual machine when a Thread is started.
 */
public void run()
{
   //set wanted loop delay to 15th of a second
   int loopDelay = 1000 / 15;
   while( true )
   {
      //get the time at the start of the loop
      long loopStartTime = System.currentTimeMillis();
      //call our tick() fucntion which will be our games heartbeat
      tick();
      //get time at end of loop
      long loopEndTime  = System.currentTimeMillis();
      //caluclate the difference in time from start til end of loop
      int loopTime = (int)(loopEndTime - loopStartTime); 
      //if the difference is less than what we want
      if( loopTime < loopdelay )
      {
         try
         {
            //then sleep for the time needed to fullfill our wanted rate
            thread.sleep( loopdelay - looptime );
         }
         catch( exception e )
         {
         }
         
      }
   }
}

/*
 * our games main loop, called at a fixed rate by our game thread
 */
public void tick()
{
}

To test our main loop lets make it change the colour of the background to a random colour every frame. We can use this opportunity to move the repaint() call to within our game loop.

/*
 * Our games main loop, called at a fixed rate by our game Thread
 */
public void tick()
{
   //get a random number within the RRGGBB colour range
   colour = generator.nextInt() & 0xFFFFFF; 
   
   //schedule a repaint of the Canvas 
   repaint();
   //forces any pending repaints to be serviced, and blocks until 
   //paint() has returned
   serviceRepaints();
}

Build and run! (don't stare to long at the screen, you will be mesmerized!)

example06.zip

Using Images

Images in MIDP are very easy to create. The easiest way is to call the static method of the Image class, createImage( String name ). The String passed to the method is the location of the image within the MIDlets jar file. So the first thing we need to to is make a image to use in our game. Generic MIDP only supports PNG images. When using the KToolbar to build our projects, all we need to to is place the image file in the "res" folder of our project. Create a image and called "sprite.png" and put it in the res folder. Normally you should keep your images files as small as possible, there are several tricks, the obvious ones are to save them as indexed mode PNGs. To gain an extra few bytes you can optimise them with for example XAT image optimiser or pngcrush, these tools on average save you 30% of your original image size! Note: for transparency to work in the default WTK emulators your files must be saved as 24bit pngs, this is not true for most of the actual devices or device specific emulators.

try
{
   myImage = Image.createImage("/sprite.png");
}
catch( Exception e )
{
   e.printStackTrace();
}

There you go, image created. Images take time to create and require a lot of runtime memory, so keep your image creation within controlled areas of your game. Actual memory usage varies between implementations, bear in mind graphic modes are not palette based, a typical implementation will use 2 bytes of memory per pixel.

To draw the created Image to the screen, use the drawImage() method of the Graphics class.

g.drawImage( myImage, x, y, Graphics.TOP | Graphics.LEFT );

The Graphics.TOP | Graphics.LEFT parameter is called an anchor. This defines how the Image should be drawn relative to the x and y coordinates. The Graphics.TOP constant causes the TOP of the image to be at the y coordinate, and the Graphics.LEFT constant causes the LEFT of the image to be at the x coordinate. So if you wanted to draw an image at the center of the screen, the quickest way to do it is to set the anchor to the vertical and horizontal center of the Image:

g.drawImage( myImage, this.getWidth()/2, this.getHeight()/2, Graphics.VCENTER | Graphics.HCENTER );

Build and run! (i suggest we get rid of the random colour thingy before it causes any permanent damage to our brain..)

example07.zip

Double buffering

To avoid flickering when drawing to the screen we will need to use well known double buffering techniques, where everything is rendered to an off screen buffer then later drawn to the visible screen. Some implementations will actually do the double buffering for us! Whether a device does so or not can be queried at runtime via the isDoubleBuffered() method on the Canvas. The advantage of not having to do the double buffering yourself is that it saves us the runtime memory needed to store the off screen buffer. We can easily write our code to automatically check and cater for devices that need us to implement double buffering ourselves.

At the same time as we load and create all our needed Images we can create our off screen buffer if needed.

/*
 * Creates all the needed images, called upon creation
 * of our GameScreen class 
 */   
public void createImages()
{
   try
   {
      //if device doesnt do automatic double buffering
      if( !isDoubleBuffered() )
      {
         //create offscreen Image
         bufferImage = Image.createImage( getWidth(), getHeight() );
         //get a Graphics context to we can render onto the bufferImage
         buffer = bufferImage.getGraphics();
      }
      myImage = Image.createImage("/sprite.png");
   }
   catch( Exception e )
   {
      e.printStackTrace();
   }
}

We create a new empty Image by calling Image.createImage( width, height ). The Image should be the exact same size as the Canvas's viewable area. In MIDP there are Mutable and Immutable Images. The difference being that Immutable Images, the ones created from image files/data, cannot be modified once created. A mutable Image, normally created through Image.createImage( width, height) can be modified by obtaining a Graphics context that will render to the Image itself. This is done by calling getGraphics() on the Image. This is what we have done for our back buffer!

With a small modification to our paint() method we can accommodate for those devices that do not do the buffering for us.

/* 
 * called when the Canvas is to be painted
 */
protected void paint( Graphics g )
{
   //cache a reference to the original Graphics context
   Graphics original = g;
   //if device doesn't do automatic double buffering
   if( !isDoubleBuffered() )
   {
      //change the g object reference to the back buffer Graphics context
      g = buffer;
   }
   //set the current color of the Graphics context to the specified RRGGBB colour
   g.setColor( colour );
   //draw a filled rectangle at x,y coordinates 0, 0 with a width
   // and height equal to that of the Canvas itself
   g.fillRect( 0, 0, this.getWidth(), this.getHeight() );
   
   //draw an image to the centre of the screen
   g.drawImage( myImage, this.getWidth()/2, this.getHeight()/2, Graphics.VCENTER | Graphics.HCENTER );
   
   if( !isDoubleBuffered() )
   {
      //draw the off screen Image to the original graphics context 
      original.drawImage( bufferImage, 0, 0, Graphics.TOP | Graphics.LEFT );
   }      
}

This might be a little confusing at first, at the top of the paint() method we keep a reference to the original Graphics context that was passed as a parameter to the method. We then check whether we need to perform the double buffering, if so we change the Graphics context that the g variable references to the Graphics context obtained from the buffer Image. At the end of the paint method we again check if we needed to perform the double buffering, and draw the buffer Image to the original Graphics context we kept earlier.

Build and run!

Example08.zip

To wrap up, lets change our input handling so we can move our image around the screen by pressing the keys.

/* 
 * called when a key is pressed and this Canvas is the
 * current Displayable 
 */
protected void keyPressed( int keyCode )
{
   //get the game action from the passed keyCode
   int gameAction = getGameAction( keyCode );
   switch( gameAction )
   {
      case LEFT:
      //move image left
      imageDirection = LEFT; 
      break;
      
      case RIGHT:
      //move image right
      imageDirection = RIGHT;
      break;
      
      case UP:
      //move image up
      imageDirection = UP;
      break;
      
      case DOWN:
      //move image down
      imageDirection = DOWN;
      break;
      
      case FIRE:
      //set current to a random colour
      colour = generator.nextInt()&0xFFFFFF;
      break;
   }
}

/*
 * Our games main loop, called at a fixed rate by our game Thread
 */
public void tick()
{
   int myImageSpeed = 4;
   switch( imageDirection )
   {
      case LEFT:
      myImageX-=myImageSpeed;
      break;
      
      case RIGHT:
      myImageX+=myImageSpeed;
      break;
      
      case UP:
      myImageY-=myImageSpeed;
      break;
      
      case DOWN:
      myImageY+=myImageSpeed;
      break;
   }
   //schedule a repaint of the Canvas 
   repaint();
   //forces any pending repaints to be serviced, and blocks until 
   //paint() has returned
   serviceRepaints();
}

Build and run!

Example09.zip
all.zip

That sums up the first part of the article, you should now be equipped with the tools and knowledge you need to make your first MIDP game. You know how to display and capture high level information, you can create and draw to a Canvas, you can even set up a game loop and handle key events, so now it is all up to you and your imagination!

The next part of the article will among other things focus on specific devices and their APIs, optimising the size of your MIDlet and look into ways you can more easily target multiple devices.

Resources

Below is a random list of sites related to J2ME and mobile gaming, enjoy!

http://java.sun.com/j2me/ - Sun's J2ME website
http://midlet.org - a huge repository of MIDlets and the chance to make your game publicly available
http://www.billday.com/j2me/ - nice resource with mixed info on J2mE
http://www.midlet-review.com - Mobile game review site, see what games are around and how they rate
http://games.macrospace.com - excellent collection of commercial games
http://www.microjava.com - a good resource for J2ME related news, tutorials and articles
http://www.mophun.com - The biggest mophun resource around
http://wireless.ign.com - IGN's wireless gaming section
http://www.qualcomm.com/brew - All the info you will need to get started with Brew
http://www.kobjects.org/devicedb - list of devices and device specs
http://wireless.java.sun.com/device/ - Another detailed list of java devices
http://home.rochester.rr.com/ohommes/MathFP/ - easy to use publicly available fixed point library for J2ME
http://www.forum.nokia.com/main.html - Nokia's developer site, lots of news, tools and developer forums
http://www.motocoder.com - Motorola's developer site
http://archives.java.sun.com/archives/kvm-interest.html - Sun's KVM mailing list




Contents
  Introduction
  MIDP the API
  First MIDlet

  Printable version
  Discuss this article

The Series
  Part 1