Dev 134 – Build an application with J2ME using MIDP 2

We learn to write a stable application model for our J2ME applications, making full use of the tools offered by the MIDP2 profile.

The Java language, now supported by the majority of portable devices, has given new possibilities to the imagination of programmers. In recent years we are witnessing a gradual and inexorable growth of the videogame business, especially for mobile phones.

The technical innovations introduced with the version of MIDP 2.0 have greatly favored this development by providing the programmer with a very complete set of tools, compared to version 1.0. In the earlier, for example, it was only possible to play music files in your programs using the proprietary SDK supplied by the manufacturer of the device.

In addition we will not have to worry about finding the appropriate tools including commercial ones since we have the chance to get a very professional one free of charge. This is NetBeans, a very complete and powerful development environment that we will present in future articles.

Also the topics to be discussed about J2ME are not few, so we decided to start an in-depth series that will lead us to the realization of a complete game for mobile.

The main objective of this article, as the title says, is to define a standard structure for the code that will keep the whole of our applications standing.

We will also see how to obtain the timing for frames so that they are displayed on a constant and regular basis on all devices.

Introduction

J2ME is a framework that directly addresses devices with very limited capabilities. Many of them, such as cell phones and pagers, did not allow to install other software in addition to those preloaded during the production process. With his introduction J2ME brought these devices out of their original static nature.

Obviously, in order to work with low processing power and storage, it was necessary to submit to some compromises. For this reason, J2ME is nothing but a subset of the J2SE API. Therefore many of its features are no longer available such as, for example, the AWT (Abstract Window Toolkit) which cannot be used on displays that are too small.

Sun’s main idea was to create a platform common to all devices with a set of APIs aimed at providing a certain range of basic functionality. Through the profile MIDP1, in fact, it was possible only the implementation of very simple utility applications.

With the progress of technologies these terminals have evolved and now have sophisticated features. What could have been the couse of these progresses if not in direction of enhanced multimedia functions?

MIDP2 is oriented to provide support for the essential components of your games, ie, sprites, sounds, animations as well as the networking. So let’s see how to use it best.

What do we need

As already mentioned we want to produce a set of Java classes – as a basic template – to set up any video game. To reach this objective we need to establish a very generic and schematic structure of the applications that we will implement.

First of all, it is necessary to consider the method used to interface the application with the events generated by the host device. Given the wide range of devices for which J2ME has been designed the set of events that can be intercepted by the application are relatively few.

We will have to focus our interest on three messages that can be raised in any moment during the life cycle of J2ME programs.

The first situation occurs at the start of the application and every time the execution is restored after a pause. Another one occurs when the device warns that the application will be paused, perhaps due to an incoming call. The last is sent when requesting the program shutdown. The MIDlet class implements the necessary methods to perform these tasks and those will be studied shortly.

Now  instead, it is important to decide which features the second class – the Canvas – which handle the actual game will have to possess.

Some methods need to be added to control the program execution according to messages intercepted by the MIDlet class. Then it will be the turn of the timing mechanism responsible to regularize the program frame rate.

Finally, there will be a keyboard event handler and a method that will be called up at every step of the execution to draw the action frames on the display. In Figure 1 it is represented a schematization of the modules that we intend to realize and the respective internal components.

Figure 1 – This image schematizes the essential components of a video game oriented application

Prepare the system for development

We have come a step away from the code writing phase. Obviously it is necessary to have the appropriate tools to complete the development. First of all we must have installed on our PC the latest version of the J2SE SDK package – obtainable from the Sun website www.sun.com .

From this moment the PC is able to compile and execute almost all types of Java programs. I said almost because the second step is to download the J2ME Wireless Toolkit package from the same site.

Therefore, all the components necessary for specific mobile development including emulators will be installed to run the programs directly from the PC. 

Among the various utilities there is a small program named KToolbar which facilitates the most common operations that are needed with projects.

As shown in Figure 2 the functions provided by the tool consists in some to create projects, compile source code, run on the emulator, and finally generate the necessary packages to distribute our applications.

Figure 2 – A view of the KToolbar and its useful development menus

KToolbar keeps the projects in its installation directory; if you have installed the version of 2.2 of Wireless Toolkit the default location is C:/WTK22, while the subdirectory for projects is named apps.

Each project is organized in a series of sub-folders containing the various types of files that take part in the development of the program. For our purposes we just need to work inside the src, where all the sources are grouped, and store in the res all the various resource files such as images, sounds etc. Once the work is finished and compiled without errors in the bin directory we will find the JAR and JAD files.

To edit the source files the Wireless Toolkit does not have any editor so we will have to use other programs – those that best suits us. Now that we know everything is needed we can proceed with the creation of a new project called HelloWorld .

The main class: MIDlet

To write the MIDlet we must import the following packages into our project:

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

The first package contains the definition of the abstract class MIDlet which must be inherited from our HelloWorld class. The second, on the other hand, has the definitions of all the basic functionalities for managing the graphical interface .

After this operation we can proceed with the declaration of the class we intend to write:

public class HelloWorld extends MIDlet

The MIDlet class also defines some abstract methods

public void startApp();
public void pauseApp();
public void destroyApp(boolean unconditional);

which represent the entry point to the application by the system. They are called, as mentioned above, in relation to different events that can happen and during the life cycle of the MIDlet – as the start of the program, pausing and its termination.

Without the need to use the objects proper to the graphical user interface the MIDlet is reduced to the role of “bridge” between the system and our Canvas class. Analyzing the code shown in Listing 1 we can understand how such a system can be organized in a very simple way.

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

public class HelloWorld extends MIDlet 
{
    HelloWorldCanvas helloWorldCanvas;
	
    private boolean init = false;
    private boolean pausa = false;

    public HelloWorld() {	            
    } 
	
    public void startApp()  
    {	   
        if (init == false) init();

	try
        {                    
            if (pausa == false) {
	        helloWorldCanvas.start();
            } else {
                // Restarts the execution
                helloWorldCanvas.restart();
                pausa = false;
        }
            // Sets the canvas reference        
           Display.getDisplay(this)
                  .setCurrent(helloWorldCanvas);
        } catch(Exception e) {
            // Unable to start the application
        }        
    }
	
    public void pauseApp()
    {
        // Pause the execution
        helloWorldCanvas.pause();
        pausa = true;
    }
	
    public void destroyApp(boolean unconditional)
    {
	destroyApp(false);
	notifyDestroyed();
    }
			
    // Initialization
    private synchronized void init()
    {
        if (!init) {
            try {
                helloWorldCanvas = new HelloWorldCanvas(this);
                init = true;
            } catch (Exception e) {
                init = false;
            }
        }
    }
}

Since the startApp() method is launched at launch of the program and each time it is restored from a pause condition, it is necessary to insert a control to determine which of the two situations corresponds to this call. This check is performed by testing the value of a private variable that acts as a flag to indicate whether the Canvas class has previously been paused or not .

Depending on the state of the pause variable, the program decides whether to wake up the Canvas or launch the initialization.

The pauseApp() method only contains a call to the appropriate function of the Canvas class to suspend execution and an assignment to the variable that signals the successful pause.

The destroyApp() method notifies that the execution of the application is complete and that the garbage collector can free the memory from all the allocated objects.

The last method init() serves to create an instance of the Canvas class and to notify the successful initialization of the program.

The core of the application is: the Canvas class

If the MIDlet class represents the application’s interface to the device, the Canvas class is the operational core of this one. It inherits from the GameCanvas the basic functions like the off-screen drawing, the detection of events arising from the keyboard and the pointer – if supported by the device.

Therefore in the HelloWorldCanvas.java file we will write all the game management code; the Listing 2 presents the complete source that we comment promptly.

import javax.microedition.lcdui.*;
import javax.microedition.lcdui.game.*;
import javax.microedition.media.*;
import java.io.*;
import java.util.*;

public class HelloWorldCanvas extends GameCanvas implements Runnable
{
    Graphics g;        
    HelloWorld           midlet_instance;

    private final int    MILLIS_PER_FRAME = 40;      
    private int     	 DISPLAY_WIDTH;
    private int          DISPLAY_HEIGHT;          
    private boolean      DISPLAY_COLOR;
    private int          COLOR_DEPTH;
    private int          ALPHA_DEPTH;    

    private volatile     Thread aniThread = null;
                        
    HelloWorldCanvas (HelloWorld HelloWorldMIDlet) {
        super(true);
                
        // Store the reference to the MIDlet class
        midlet_instance = MIDletInterface;
                		
        // Enables the full screen mode
        setFullScreenMode(true);
                
        // Gets the display size values
        DISPLAY_WIDTH = getWidth();
        DISPLAY_HEIGHT = getHeight();

        Display d = Display.getDisplay(istanza_midlet);
                
        // Determines if the display can show colors
        // or is monochromatic
        DISPLAY_COLOR = d.isColor();

        // Determines the number of colors the
        // display can show
        COLOR_DEPTH = d.numColors();

        // Determines the number of transparency 
        // levels that the app can use
        ALPHA_DEPTH = d.numAlphaLevels();

        g = getGraphics();
    }
        
    // Method called by the runtime when the 
    // application is started	        
    public void start() {
        // New thread creation
        aniThread = new Thread(this);
        aniThread.start();

        // Run the main loop
        run();
    }

    // Method called by the runtime when the 
    // application is restarted after an interruption
    public void restart()
    {
        aniThread.notify();
    }

    // Method called by the runtime when the 
    // application is suspended 
    public void pause()
    {
        try {
            aniThread.wait();
        } catch (Exception e) {
                    
        }
    }	

    // Method called by the runtime when the 
    // application is about to be killed
    public void stop()
    {
        aniThread = null;
    }
	
    public void run()
    {
        Thread cThread = Thread.currentThread();
		
	try {
	    while (cThread == aniThread) {
	        long startTime = System.currentTimeMillis();
				
	        if (isShown()) {	
                    keyboard();
		    frame();
		    draw();	
		    flushGraphics();
		}
				
		long endTime = System.currentTimeMillis() - startTime;
				
	        if (endTime < MILLIS_PER_FRAME) {
		    synchronized(this) {
	  	        wait(MILLIS_PER_FRAME - endTime);
		    }
		} else {
		    cThread.yield();
		}
	    }
	} catch (InterruptedException ex) {
	    // Nothing to do 
	}
    }
	
    void frame()
    {

    }
        
    void keyboard()
    {       
        int statoTastiera = getKeyStates();
            
        if ((statoTastiera & LEFT_PRESSED) != 0) {

        } else if ((statoTastiera & RIGHT_PRESSED) != 0) {

        } else if ((statoTastiera & FIRE_PRESSED) != 0) {

        } else if ((statoTastiera & UP_PRESSED) != 0) {

        } else if ((statoTastiera & DOWN_PRESSED) != 0) {

        } else if (statoTastiera == 0) {

        } else {

        }
    }
        
    private void draw()
    {            

    }
}

As in all Java applications the first lines of code to be written concern the inclusion of packages. In this case we have:

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

Since we do not use the standard graphical objects such as buttons the first inclusion is necessary essentially to access the functions for image management. The second one, on the other hand, includes all that is necessary for the creation of games elements such as sprites, layers and backgrounds in tilemapping.

The next line

public class HelloWorld Canvas extends GameCanvas implements Runnable

defines the Canvas class called HelloWorldCanvas. The constructor requires – as a parameter – the reference to a MIDlet class

HelloWorldCanvas(HelloWorld HelloWorldMIDlet)

Inside the constructor we see some instructions. The call setFullScreenMode activates or deactivates the full screen mode. This is a very interesting feature if we want to use the entire area of ​​the display. In this manner all the messages and/or informative images normally present will be hidden.

Note that by activating this mode, any elements added to the MIDlet class such as menus or function key captions will not be displayed.

If it is our intention that a program be launched on different types of devices we must be able to determine the characteristics of their display. In fact there are different classes of displays that differ in their pixel dimensions and the depth of color they are able to display.

For this purpose we use the getWidth() and getHeight() functions that will return the width and height, while the isColor() methods, numColors(), numAlphaLevels() of the Display class are used to determine whether is in colors or tones of gray, how many color levels can be displayed and how many levels of transparency can be managed.

We will now analyze how to interface with the MIDlet class and respond opportunely to the events mentioned above.

First we will provide an adequate implementation of the start() method

aniThread = new Thread(this);
aniThread.start();
run();

which does nothing else than create a new instance of a Thread object and start it. The call to the private method run() determines the start of the main game loop.

When the device requires that the program is paused simply will perform this operation, in the pause() method

try {
    aniThread.wait();
} catch (Exception e) {

}

In this way the Thread that is the main cycle is suspended until the next call of the function

aniThread.notify();

contained in the restart() method. When all operations are concluded, the stop() method

aniThread = null;

releases the Thread instance so it can be deallocated from the garbage collector.

What remains of the code represents the essence of our work: the four members of the class that are executed at each iteration of the main loop.

Take a look at the run() method, first called by the start() method, which is structured as follows

Thread cThread = Thread.currentThread();
		
try
{
    while (cThread == aniThread) {
        long startTime = System.currentTimeMillis();
		
	if (isShown()) {					
	    keyboard();
	    frame();
	    draw();	
	    flushGraphics();
	}
				
        long endTime = System.currentTimeMillis() - startTime;
		
	if (endTime < MILLIS_PER_FRAME) {
            synchronized(this) {
	        wait(MILLIS_PER_FRAME - endTime);
	    }
	} else {
	    cThread.yield();
	}
    }
}
catch (InterruptedException ex)
{

}

After checking that the currently running Thread is the same one we instantiated previously, we pass to the detection of the timer value storing it in the startTime variable. Then if our Canvas is visible on the display – isShown( ) – we follow the periodic operations of

  • Control of the keyboard state, that means to retrieve the pressed keys and which, via the keyboard() method
  • Execution of normal game management activities, with the frame() method
  • Drawing the output on the off-screen buffer, with the call to draw()
  • Final show of the off-screen buffer on the display via flushGraphics()

At this point the status of the timer is re-read and by subtracting the previously obtained value from it we can see if the operations carried out have required less time than that defined in the variable MILLIS_PER_FRAME. In this case we must ensure that the last produced frame remains visible for a period of time that is equal to the difference between the value indicated by us and the time taken.

To get this result just stop the program with the call to the wait () function, like this

wait (MILLIS_PER_FRAME - endTime);

The last operation to be performed is to allow the other Threads, running on the device, to operate by calling

cThread.yield();

Conclusions

That’s all. This is not a large amount of code and once set as a template we can create new fully functional programs simply by changing the class names and appropriately implementing the frame(), keyboard() and display() classes.

Figure 3 shows the output on the emulator of a simple little program that can be found on the FTP site.

Figure 3 – An image of the example program on the FTP site running on the default emulator.

I hope I have defined a good starting point to start exhaustively exploring this new world which is now in full evolution. Please remember, however, that for us your advices are very important then continue writing!

Bibliography

  1. Carol Hamer , ” J2ME Games with MIDP2 “, Apress, 2004 , ISBN 1-59059-382-0, www.apress.com
  2. John W. Muchow , ” J2ME Practical Guide to Programming Wireless Devices “, Mc Graw Hill , 2002, ISBN 88-386-4278-8 , website

Original article

Downloads

Leave a Reply

Your email address will not be published. Required fields are marked *