Java Micro Edition for Mobile Devices

From Gamedev.org wiki

Hi there. Welcome to what will in time be a tutorial for how to get started making your own game in Java which can run on any J2ME equipped mobile phone. There is quite a bit of info on the internet, especially on the java website, but not a lot of it is aimed at people who dont have a lot of Java experience, so I'm going to consolidate what I have learned into one document. Plus you also get my renowned witty and easy to read and understand commentary. You might want to put some prog rock on at this point (something really horrible like Dream Theatre), I find it helps.

Why is J2ME of interest to me personally? Well for a start, I just cant put in the hours to make a huge 3D MMORPG for the PC. To be honest, few people can. But I can roll out a 2D platform or strategy game. Only problem is, no-one plays games like that any more (apart from all those flash/java game websites, but I detest them). I think the best way to get my mates to play my game is to bluetooth it to them at lectures (and anyone else who has left their bluetooth on), the pub, in traffic jams etc. Call it viral marketing if you like, but there is no better way of getting your name about than by making a game for a phone. You can even make money because if your game is any good you could sell it to Jamster or one of the other rip-off merchant phone clubs for a premium.

So anyway, here we go. I am assuming you can program basic stuff in java, and have a general grasp of games programming, but if things are going to fast let me know! (For reference I am using Java version 1.60) If you want to take this tutorial further, I would recommend the O'Reilly book "J2ME in a Nutshell".

First thing to know about J2ME is that its not nice and standard like J2SE! Whereas you know exactly what you are getting with J2SE in terms of packages, J2ME is a bit different. Because of the wide variety of devices avaliable there are lots of different varieties of J2ME divided into two device groups, and then lots of different "profiles". I am not going to go into the different groups, but merely point you to the one which we will be using for mobile phones (CLDC group, MIDP profile). For this tutorial we will be using eclipse. If you do not already have the installed, go and google it and get it working, and prehaps make a quick test program or two to make sure you understand how the IDE works.

The following steps detail how to set up your PC for J2ME development with eclipse (if you dont want to use eclipse then you can use the Wireless Toolkit on its own, or use NetBeans with the Mobility Pack)

  1. Goto Java Wireless Toolit for CLDC (http://java.sun.com/products/sjwtoolkit/download-2_5_1.html) and download and install the Wireless Toolkit for CLDC (Connected Limited Device Configuration). If you want, click on the "Run MIDP Application" shortcut on the Start bar and try out some of the example programs. They will run in a mobile phone emulator on your PC screen. You should also be able to go into the folder on your hard drive and using bluetooth or a cable, send these to your phone.
  2. Goto EclipseME (http://eclipseme.org/) and download the latest version. This is a plug in for eclipse which allows you to connect the build to the Wireless Toolkit. It will come as a zip file which you need to extract to the base eclipse directory.
  3. Open eclipse. Click File->New Project->J2ME->J2ME Midlet Suite and enter a suitable project name.
  4. On the next screen click the "Manage Devices" button.
  5. Click the "Import..." button on the right hand side.
  6. Click the "Browse" button and browse to the base folder of the Java Wireless Toolkit (default c:\WTK2.01)
  7. Click the refresh button
  8. Select the "DefaultColorPhone" device
  9. Click OK a few times, then "Finish"

We now have a blank J2ME project! (it will be quicker the next time you make one)

The thing to remember with J2ME is that we don't have all the classes frmo the regular Java library. We do still have quite a few to play with, although notable absentees are the collections such as ArrayList, Set etc, and the i/o is much restricted. From a security perspective it is much simpler too, as there is no JNI. There are several different levels of trust that your program can fall under, but don't worry too much about all that, as we will be working within the basic "sandbox".

Your application will be a *.jar file like any other program. However, the main class must extend the class javax.microedition.midlet.MIDlet. It follows then that a J2ME program is a MIDlet (as opposed to an applet?). MIDlet has three abstract methods which you must override, these correspond to start, pause and end (fairly self explanatory). To begin with, we will just override the startApp method.

protected void startApp() throws MIDletStateChangeException
{
Display display = Display.getDisplay(this);
Displayable d = new TestCanvas();
display.setCurrent(d);

}

As you can see, we have created a Display object, and assigned it to refer to the current MIDlet display object (Display.getDisplay is a static method).A Display is capable of displaying (naturally) a Displayable object (note careful use of capitals). Conveniently, J2ME provides us with a very helpful class called javax.microedition.lcdui.game.GameCanvas, which incoorperates double buffering and input handling. If you dont understand the following code, I suggest you go and read the first five chapters of "Killer Game Programming in Java". They are avaliable for free here [1] (http://fivedots.coe.psu.ac.th/~ad/jg/) but I would recommend buying the book if you are serious about making games in Java.

Important things to note:

  1. There are no colour classes in J2ME, just use the hex equivalents (like in html).
  2. The GameCanvas automatically gives us the graphics context of the off screen buffer, and then when you call flushGraphics() it flips it onto the screen.
  3. There is no InterruptedException in J2ME so I have just used plain old Exception around the Thread.sleep call. Important because this try block also includes the rest of the game loop, so we could miss exceptions if we are not careful!

Now we should be ready to run our MIDlet for the first time. In eclipse click Run -> Run... to bring up the run configuration dialog. Double click "Wireless Toolkit Emulator" on the left hand side to create a new run configuration. Select our project, and then in the "Executable" frame, select our class "TestMIDlet". In the emulation tab you may need to select DefaultColorPhone as our device, and the Wireless Toolkit as the Device Group. Click "RUN".

If all went well, you should be staring at a mobile phone emulator with a green screen. If not, have another try, or post on the forums, as its highly possible I have done something wrong.

Images

Its obviously going to be important for our game to be able to load images. For this, we use the javax.microedition.lcdui.Image class, which incoorperates a nice Image loader feature. Simply put the image files in a folder within the eclipse project call "images" nd then use the following line to load them.

Image image = Image.createImage("/images/tile.PNG");

The Image class must support loading PNG images, but *may* also support GIF and BMP. I find it best to just stick with the Portable Network Graphic format, because it has everything we need and is open source. The Graphics class has a few differences to its J2SE counterpart. For example, the drawImage method takes the following parameters (Image i, int x, int y, int anchor). The anchor determines the placement of the image which the coordinate (x,y) refers to (for example Graphics.TOP | Graphics.LEFT). This means that you dont have to do as much tedius calculation to place your images. Use your initiative to figure out the rest of the code to display an image. I will talk about a couple more topics, and then do a complete mini game code section.

Images can support an alpha layer, which is obviously useful for creating transparency. It is simple to manipulate the image data bytes if you dont want to embed the alpha layer in your editor. For example:

private static Image setTransparentColor(Image im, int color)
{
int[] data = new int[im.getWidth()*im.getHeight()];
im.getRGB(data, 0, im.getWidth(), 0, 0, im.getWidth(), im.getHeight());
for (int i=0; i < (im.getWidth()*im.getHeight()); i++)
{
if (data[i] == color)
{
data[i] = 0x00000000; // alpha = 0.0 transparent
}
}
return Image.createRGBImage(data, im.getWidth(), im.getHeight(), true);

}

This method takes an image and sets the specified color to be transparent. Note the last parameter of the createRGBImage method turns on the alpha channel for the new Image.

Sprites

All 2D games need sprites, and the great thing about J2ME is that it has a built in Sprite class (javax.microedition.game.Sprite) which handles animation, collision detection, and basic transformations. What more could you want?! Personally I like many others have my own custom sprite class which works in the way that I like. But when you think about it, it makes a lot of sense to use this built in class, because you want your packaged MIDlet to be as small as possible. Have a poke about on the API get used to how this baby works. [2] (http://java.sun.com/javame/reference/apis/jsr118/javax/microedition/lcdui/game/Sprite.html) If you have all of your animation cells in seperate files you will need to combine them into one image for the Sprite class to split up (I don't know why it can't just take an array of Images). I use the following code:

private static Image mergeImages(Image[] images)
{
int width = images[0].getWidth();
int height = images[0].getHeight();
int[] pixels = new int[ images.length * width * height ];
for (int i=0; i<height;i++)
{
for (int j=0; j<images.length; j++)
{
if (images[0].getWidth() != width || images[0].getHeight() != height)
throw new IllegalArgumentException();
images[j].getRGB(pixels, (width * j) + (width * i *images.length), width, 0, i, width, 1);
}
}

Image newImage = Image.createRGBImage(pixels, width * images.length, height, true);
return newImage;

}
TiledLayers

Another helpful thing Java have done for us is create a tile manager which simplifies loading and arranging tile based layouts on the screen. The way that this works is you tell the TiledLayer class a file containing all of your tiles, the tile size, and your desired layout size, and TiledLayer will chop the image up and arrange them accordingly. It even handles tile animations, so you can have pretty water tiles! This class is very handy, because when you dont have much memory to play with, tiles are our friend! Check out the API here:

[3] (http://java.sun.com/javame/reference/apis/jsr118/javax/microedition/lcdui/game/TiledLayer.html)
Commands and Keypresses

J2ME is interesting in that rather than give you access to the keypad of the device(which could be wildly different from device to device), it only really allows you to use the following buttons: Up, Down, Left, Right, Fire, and the digits 0-9, as well as four "GAME" buttons which may or may not be supported on an individual device (so you cant use them for anything critical or unreachable by another method). The Canvas class normally delivers key events to its KeyPressed and KeyReleased methods which you can override. Note that you have to translate the delivered keyCode to one of the above keys (enumerated in Canvas) by using the getGameAction(int keyCode) method). GameCanvas allows you to switch off key event delivery and poll the device for the current state if you want (and if you are using a seperate rendering thread this makes sense).

If you want more actions than these keys can hold (likely) then you can use the phones "soft-keys". This is a nice feature. You can create Command objects and add them to the current Displayable, and the phone will automatically assign these to the softkeys and create a menu if necessary. For example:
displayable.add(new Command("Buy", Command.ITEM, 1));
displayable.add(new Command("Info", Command.ITEM, 1));
displayable.add(new Command("Back", Command.BACK, 1));

If there were two softkeys, Back would be assigned to one of them, and a menu on the other created and Buy and Info added to it. See the Command API here [4] (http://java.sun.com/javame/reference/apis/jsr118/javax/microedition/lcdui/Command.html). You will need to add a CommandListener to the Displayable class in order to recieve these key events. It is best to create the Commands you want to use as static final fields as they are immutable.

Tips and Tricks

A couple of things which are worth noting now. Firstly java.util has been stripped down to a bare minimum for the MID profile, all of Collections is gone! the only data structures remaining are Vector, Hashmap and Stack, so it can be worth spending time planning the data structures your game is going to use! There are no generics in J2ME either, so if you want to use a J2SE Collection class, you need to get it from the 1.4SE release (source available from java.sun.com). I wouldn't advise importing data structures like this, because there is so much inheritance in the Java Collections classes that you end up importing a large chunk of files. Vector can be adapted to suit most purposes, but if you really need a linked list then it would be best to implement your own.

Secondly, something to note is that sometimes you will get an Exception on one phone but not another. This is one of the quirks of J2ME! It doesnt happen too often though thankfully, because its usually a very subtle bug or synchronization (concurrency) problem. You will notice that System.out.println() has no effect on a mobil phone! This can be quite annoying for debugging purposes, so I will draw your attention to the java.microedition.lcdui.Alert class, which can be used to display text without requiring a Graphics context or anything else which could go wrong. Simply assign it to your Display object using setDisplay(alert). I tend to use a static Display variable in my main MIDlet class so that I can have a static function called debugAlert or something which I can call from anywhere in my program to display a String, even if everything else is falling apart!