HomeDigital EditionSys-Con RadioSearch Java Cd
Advanced Java AWT Book Reviews/Excerpts Client Server Corba Editorials Embedded Java Enterprise Java IDE's Industry Watch Integration Interviews Java Applet Java & Databases Java & Web Services Java Fundamentals Java Native Interface Java Servlets Java Beans J2ME Libraries .NET Object Orientation Observations/IMHO Product Reviews Scalability & Performance Security Server Side Source Code Straight Talking Swing Threads Using Java with others Wireless XML

The Mobile Information Device Profile (MIDP) 1.0 was designed to make it easier to design software for mobile devices (the applications are called MIDlets). The idea was great but Java's mantra,"Write Once, Run Everywhere," didn't quite come true since MIDP 1.0 was very restricted. Manufacturers wanted to offer more interfaces and features to developers and include proprietary interfaces such as messaging and graphics.

The new version of the profile, MIDP 2.0, has added many new and exciting features. One of the most interesting new features is the Game API, but there are others as well. The Media API allows playing tones (tone sequences or single tones) or wav files. An enhanced user interface allows a better layout and self-made custom items. The Communication API has new interfaces for new protocols (such as serial ports, sockets, and HTTPS), and it also enables inbound connections. Inbound connections mean that server applications can push information to MIDlets.

In MIDP 1.0 there was only a recommended specification for OTA provisioning (over-the-air loading of applications to the device). Now the OTA provisioning is a part of the specification and has been changed slightly by the removal of the cookie support requirement.

Why is the Game API one of the most important new features? Because it offers small companies and even individuals the opportunity to succeed if they make a good MIDlet. Imagine writing a program, for example, a game that half of the mobile phone users in the world would pay you $1 for. If you look at www.midletcentral.com, there are at least 188 applications and 338 games listed (at the time of writing). So games are obviously one of the most tempting areas for developers as well.

The possibility of making a financially successful game is not the only reason the Game API is important. Developing games was not a particularly easy job in MIDP 1.0 since there were only a few classes for graphics and there was no floating point support. For example, rotating an object was not easy to do.

MIDP 2.0 doesn't automatically solve the floating point issue, since floats were not supported in CLDC 1.0 (the configuration that MIDP was built on). However, there's a new version of CLDC on the way (CLDC 1.1) that supports floats.

For graphics manipulation, MIDP 2.0 offers a great deal. The whole API is based on the concept of layers and how to manage them. A typical game consists of several layers, such as the background, roads, walls, buildings, and the characters (if any). The game area can also be larger than the screen, and scrolling the game area and managing all the game activities in the code in MIDP 1.0 was a tough job. Not only that, it also increased the size of the JAR file. The size of the JAR file is important for several reasons: some devices might have limitations for loading and running the application, and the user can store more applications if they're smaller. By moving most of the basic gaming functions to the MIDP core or even to the underlying platform, the size of the JAR file gets a lot smaller and the code gets simpler. MIDP 2.0 does all this by offering a simple API for most of the basic 2D gaming operations.

The Game API
The Game API has five classes (see Figure 1). Four of the classes (Layer, LayerManager, Sprite, and TiledLayer) are directly related to layers or manage them. The fifth class, GameCanvas, is a subclass of Canvas. Along with everything inherited from the Canvas, GameCanvas has two major features: built-in double-buffering and the ability to get the states of the device's keys.

Figure 1

Double-buffering is an old practice used in game programming to avoid flickering while updating the screen. The idea is to use a temporary image to construct everything off the screen and then flush the temporary image to the screen. With Canvas (in MIDP 1.0), drawing to the screen was done by implementing the paint(Graphics) method. With GameCanvas, you don't have to implement the paint(Graphics) method at all. Instead, obtain the Graphics object with the getGraphics() method, do your drawing, and call one of the flushGraphics() methods.

Key states are for obtaining information about which keys are pressed. The getKeyStates() method returns an integer that holds the states bit coded. Each bit in the integer represents a certain key in the device. The keys are up, down, right, left, fire, and four game keys (game a, b, c, d), which might not be supported by the device. The following code shows how to read the states of the left and right keys from the integer.

protected void keyPressed(int keyCode) {
int keyState = getKeyStates();

if ((keyState & LEFT_PRESSED) != 0) {
// move the ship left
}

if ((keyState & RIGHT_PRESSED) != 0) {
// move the ship right
}

if ((keyState & FIRE_PRESSED) != 0) {
// fire the arms
}
}

Setting the Background - Using Layers
The remaining classes - LayerManager, Layer, TiledLayer, and Sprite - are all related to managing layers. As can be seen in Figure 1, TiledLayer and Sprite are subclasses of Layer, which is an abstract class that represents a visual element of the game. LayerManager is responsible for managing instances of TiledLayer and Sprite. This might sound a bit confusing at first but a simple example will show how easy it is.

Let's think about a simple space game in which a spaceship flies smoothly over a future city. The city with its streets and buildings is obviously the background and it can be done with an instance of a TiledLayer. The background consists of small building elements laid out in a grid. The image file in Figure 2 holds all the tiles (small images) used to construct the background. (The white lines and index numbers indicating the different tiles are not part of the image but are shown for clarity for the following discussion.)

Figure 2

The background is constructed with these small tiles by placing them in a grid. The grid (with white lines indicating the "invisible" grid) can be seen in Figure 3. The background is one layer and the spaceship another. The ship can be done as an instance of Sprite. Now, all we need in the game are the enemies (or at least some sort of challenge).

Figure 3

Constructing layers is easy. There are two methods in TiledLayer for that: setCell() that sets the content of a cell with a tile from the image and fillCells() that fills a region of cells. Listing 1 shows how a TiledLayer is created and how it's filled. The TiledLayer needs an image that has all the visual elements for building the background (or any other layer).

After the layer is ready, it needs to be added to an instance of LayerManager. LayerManager manages layers, and layers can be added to or removed from it. LayerManager also draws the contents of the layers, and scrolls the layers if the area is bigger than the screen. Adding new layers to LayerManager can be done with two methods: append() and insert(). Figure 4 demonstrates how LayerManager handles layers. When a new layer is added to the manager with the append() method, the layer is placed the furthest from the viewer. With the insert() method you can tell exactly where to place the layer. If all layers are the same size, only the closest will be drawn to the screen. If the layers are different sizes, the manager will render the graphics accordingly.

Figure 4

LayerManager also has a simple solution for handling game areas larger than the screen. With the setViewWindow() method you can set a so-called view window that represents a certain section of the game area. The size of the view window could be the size of the device's screen or you might want to set it a bit smaller to fit other info, like score. Figure 5 demonstrates the relationship between the game area and the view. The view can be scrolled as the user moves on the game area or the scrolling can be done automatically, for example, to create the effect of flying with a spaceship at a constant speed.

Figure 5

The Characters - Using Sprites
Sprites are visual elements on the screen, so a sprite could be a spaceship, a cloud, a bee, etc. There are two kinds of sprites, animated and nonanimated. A sprite can consist of one or several images. Setting the images to a certain sequence creates the frame sequence. The sequence is created with the setFrame() method and the animation is created by calling the nextFrame() method. With nonanimated sprites, the images can be used in different situations, for example, to demonstrate an explosion or a movement.

Developers often need different images, for example, for a sprite to move to the left or to the right. With the setTransform() method you can transform one image by rotating it or by taking a reflection of the image. This way a Pacman clone is easy to do with just two images - one with mouth open and one mouth closed - by transforming the images.

Figure 6 shows an image that contains four frames. You could use the frames to make the man walk in either direction and wave his hands.

Figure 6

Detecting collisions manually is also a tough job, but fortunately Sprite also offers methods for it. Collisions can be detected between the sprite and another sprite, an instance of TiledLayer, or an image. The collision can be detected on a pixel-by-pixel basis or by using simple bounds checking.

Conclusion
After going through all these features, it's time for some big questions: Is the API any good, and do we have to wait for the MIDP 2.0 or couldn't we just implement the API on MIDP 1.0? The answers are: yes it is good, and yes we could implement at least some of the features on MIDP 1.0. However, there are some features that can't be done easily or at all with MIDP 1.0. One of these is the collision detection in Sprite, which examines the collisions by reading the pixels of the images. The speed of the implementation and the size of the JAR file are also things that you can't change in MIDP 1.0. So the Game API is a welcome feature.

However, if you don't want to wait for MIDP 2.0 and want to start developing games on a similar basis right away, implement a subset of the API. If you make your own API similar to the Game API, your applications will port easily to MIDP 2.0 and can take full advantage of its new features.

Resources

  • Kontio, M. (2002). Professional Mobile Java with J2ME. IT Press.
  • http://wireless.java.sun.com
  • www.microjava.com
  • www.billday.com/j2me

    Author Bio
    Mikko Kontio is a technology manager for KPY eSolutions, a leading-edge software company in Finland. He holds an MSc in computer science and is the author and coauthor of several books, including Professional Mobile Java with J2ME (IT Press). [email protected]

    "The Game API"
    Vol. 8, Issue 5, p. 52

    	
    
    
    
    Listing 1
    
    
    try {
      tilesImage  = Image.createImage("/tiles.png");
    } catch (IOException e) {}
    
    // 4 columns wide with 10 rows, each tile 30x30 pixels
    tiles  = new TiledLayer(4, 10, tilesImage, 30,  30);
    // TILE_EMPTY is the last (7th) tile in the image file
    tiles.fillCells(0, 0, 4, 10, TILE_EMPTY);
    // TILE_LEFT is the last (2nd) tile in the image file
    tiles.setCell(0, 0, TILE_LEFT);
    
     
    

    All Rights Reserved
    Copyright ©  2004 SYS-CON Media, Inc.
      E-mail: [email protected]

    Java and Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. SYS-CON Publications, Inc. is independent of Sun Microsystems, Inc.