Click here to Skip to main content
11,481,265 members (64,480 online)
Click here to Skip to main content

Programming 2D Games in J2ME

, 23 Apr 2009 CPOL 167.5K 15.3K 62
Rate this:
Please Sign up or sign in to vote.
It is easy to run your own game on your own mobile
game_screen.jpg

Introduction

J2ME is an interesting environment for games. With basic knowledge of Java, preinstalled NetBeans and J2ME Wireless Toolkit, you can make simple, funny 2D games that are capable of running on your own mobile devices.

This article will make use of the 5-class Game API composed in the package javax.microedition.lcdui.game.

Background

This thread assumes that you have basic knowledge of Java, are familiar with NetBeans and have gone through the thread “Introduction to Java ME Programming”. Game making also requires certain knowledge of physics including Newton’s Law of motion, Motions, collisions, …etc.

Recently, I attend a project named Mobile Application Development Intensive Programs organized by my University and its alliances. The project is financed by Uramus, a program to encourage student exchange between European countries. We started with J2ME and it is the result of this thread. So, in this thread, I will use some of the material taken from the project.

By using the GameBuilder, the game making process is much easier. However, I’m not going to cover it in this thread. Details of game making by using GameBuilder can be found here.

As a student, I probably do not have enough experience to apply the best practice. That's why I warmly welcome any comments or suggestions to make it a better guide.

Using the Code  

The MainMidlet 

As a Midlet, the MainMidlet must extend the abstract class Midlet that can be found in the package javax.microedition.midlet. The Midlet requires override of three methods: 

  • startApp() called to start the game
  • pauseApp() called to temporarily stop the application, i.e., receiving a call. Application should stop the animation and release resources that are not needed. Application can be resumed by calling resumeMIDlet() 
  • destroyApp(boolean unconditional) called when exiting the application. In order to terminate, the MIDlet can call notifyDestroyed()

(These methods are automatically created by creating a Visual Midlet in NetBeans.)

We are, however, only needed to implement the startApp() methods by creating an instance of our GameCanvas and adding a CommandListener to exit the Midlet. It is, of course, not a good programming habit but until this step, we probably only want the application to run. Current display can be set to the GameCanvas at the end or inside the GameCanvas by the method setCurrent. This method accepts any Displayable objects as an argument. 

public class MainMidlet extends MIDlet implements CommandListener {
    private SSGameCanvas gameCanvas ;
    private Command exitCommand ;
    public void startApp() {
        try {
           //create new game thread
            gameCanvas = new SSGameCanvas();
            gameCanvas.start(); // start game thread
            exitCommand = new Command("Exit",Command.EXIT,1);
            gameCanvas.addCommand(exitCommand);
            gameCanvas.setCommandListener(this);
            Display.getDisplay(this).setCurrent(gameCanvas);
        }
        catch (java.io.IOException e) { e.printStackTrace();}
    }
public void pauseApp() {} 
public void destroyApp(boolean unconditional) {}
public void commandAction(Command command, Displayable displayable) {
        if (command == exitCommand) { 
            destroyApp(true); 
            notifyDestroyed();
        } 
    } 
}

The GameCanvas

As an element of low level UI engine, when combined with Graphics, GameCanvas provides us flexible tools to set up our own game screen. With Graphics, you basically can draw things that you can normally do in Java 2D, including drawing shapes, strings or images. GameCanvas is an extension to the original Canvas with more control over the painting, and the rate at which keys are delivered into the game.

Using GameCanvas, you can notice its capabilities including the off-screen buffering. When you are drawing things, you probably are drawing into the off-screen and by calling the flushGraphics() method, the buffer is quickly written into the screen.

GameCanvas also simplifies the process of getting input by allowing us to query the key status using the getKeyState() method. Processing key status, however, is left to the GameManager for easier management. 

The origin of the game coordinate system is located in the top left corner of the screen as shown.

gamescreen.jpg

In the render method, the off-screen is cleared and graphics are rendered by calling paint method of the GameManager.  

public void render(Graphics g) {

        ……..
       // Clear the Canvas.
        g.setColor(0, 0, 50);
        g.fillRect(0,0,WIDTH-1,HEIGHT-1);
        ….….
        gameManager.paint(g);
    }

In this example, the SSGameCanvas implements Runnable interface, which leads to the creation of the run() methods in which we create a game loop running until we reach certain ending condition.

game_states.jpg

Game Loops

  public void run() {

 
        while (running) {
            // draw graphics 
            render(getGraphics());
            // advance to the next tick 
            advance(tick++); 
            // display 
            flushGraphics();
            try { Thread.sleep(mDelay); } 
            catch (InterruptedException ie) {} 
        }
    }

Timing of the game is controlled by an integer named tick. tick simplifies timer problem in the game, such as firing rate of a ship, or how long a star will flash, how long a sprite will step into the next frame. If timing is done in the GameCanvas by implementing Runnable, a tick means mDelay + time to complete one game cycle (milliseconds). If we create a Thread which takes care of tick, we will probably have tick = mDelay. We will probably need only 24 -30 fps, so we limit the mDelay accordingly to have the desired animation effect with less power consumption. At every cycle of the game, we call the method advance of the GameManager, which extends LayerManager to check the user input, collisions and paint the graphics.

public void advance(int ticks) {
       // advance to next game canvas
        gameManager.advance(ticks);
        this.paint(getGraphics());
    }
}

tick  may have a limitation: it limits the game playing time by the limit of the integer, which is about over 590 hours in a 32-bit system.

Sprite

Sprite acts as the actors of the games. It could be our Mario characters, ducks, and bullets in the Mario games or space ships in a star war game. As a basic visual element, it can be rendered to display continuous action with several frames stored in an Image. The Image file must pack all the frames of the Sprite in order to be displayed. All frames must have the same and predefined width and height.

public SpaceShip(Image image, int w, int h) throws java.io.IOException {
       super(image,w ,h);
        WIDTH = w;
        HEIGHT= h;
        setFrameSequence(SEQUENCE);
        defineReferencePixel(WIDTH/2,HEIGHT/2);
        setTransform(this.TRANS_MIRROR_ROT270);
    }

To initiate the Ship sprite, I call the constructor from the superclass Sprite: super(image, w, h); where w and h is the width and height of each frame. The image consists of 8 frames, so I set the frame sequence {0,1,2,3,4,5,6,7} using setFrameSequence method.

Next, I call the defineReferencePixel method to set the reference point to the middle of the frame. This reference pixel will be used to position the ship on the screen. And finally I rotate all the frames by the method setTransform.

public void advance(int ticks) {
        if ((ticks%RATE==0))
            nextFrame();
    }

The method advance will change the frame of the ship to create animation accordingly to RATE. By continuously calling nextFrame(), the screen will display the frame sequence from 0 to 8 and then come back to 0: 0,1,2…7,0,1,2…. The following methods moveLeft(), moveRight(), moveUp(), moveDown() to change the position of the ship on the screen depends on its speedX and speedY.

public void moveLeft () {
       if (this.getRefPixelX()>0)
            this.move(-speedX, 0);
    }
public void moveRight (int m) {
        if (this.getRefPixelX() < m)
            this.move(speedX, 0);
    }
public void moveUp () {
        if (this.getRefPixelY()>0)
            this.move(0, -speedY);
    } 
public void moveDown (int m) {
        if (this.getRefPixelY()<m)
            this.move(0, speedY);
    }

When the ship is commanded to shoot a bullet, we check whether the cool down is gone or not by comparing current time with the previous shot time.

public Bullet fire (int ticks) {
        if (ticks- fireTick > SHOOT_RATE) {
           fireTick = ticks;
           bullet.setSpeed(BULLET_SPEED);
           bullet.shot(this.getRefPixelX(), this.getRefPixelY()+HEIGHT/2);
            return bullet;
        }
        else
           return null;
   }

To check the collision between the spites, images or TitledLayer (will be mentioned later), we use the method collidesWith. I will make use of this method in the GameManager to check the collision between the ship and the asteroids to reduce HP of the ship and to check collision between the bullet and the asteroids to increase score and destroy both the bullet and the asteroid.

GameManager

As a subclass of LayerManager , the GameManager is capable of managing series of Layers, automatically renders each Layer in an appropriate order. Method append is called to add a specific Layer to the LayerManager.

private Image shipImage;
    private static final String SHIP_IMAGE    = "/resource/blue_ship.png";
    private static final int    SHIP_WIDTH    = 40;
    private static final int    SHIP_HEIGHT   = 33;
shipImage = Image.createImage( SHIP_IMAGE );
        // create space ship
        ship = new SpaceShip(shipImage, SHIP_WIDTH, SHIP_HEIGHT);
        // set it position
       ship.setRefPixelPosition(height/2, width/2);
        this.append(ship);

In order to respond to the user input, we query the key states with a referenced instance of our GameCanvas using the method getKeyStates(). For each key state, we react correspondingly by moving the ship to the desired direction.

int keyState = gameCanvas.getKeyStates();
 
// move right
if ((keyState & GameCanvas.RIGHT_PRESSED)!= 0){
       ship.moveRight(width);
} 

// move left
if ((keyState & GameCanvas.LEFT_PRESSED)!= 0){
       ship.moveLeft();
}

// move up
if ((keyState & GameCanvas.UP_PRESSED) != 0){
        ship.moveUp();
} 

// move down
if ((keyState & GameCanvas.DOWN_PRESSED) != 0){
        ship.moveDown(height);
}

In the GameManager, we also check for collision between the ship, bullet and random created enemy (asteroids). If collision occurred, we change the game status accordingly to the collision.

private Obstacle checkCollisionWithAsteroids(Sprite t) {

        for (int i =0; i < MAX_OBS;i++) {
            if (obs[i]!=null)
                 if (obs[i].collidesWith(t, true)) {
                 return obs[i];
            }
        }
        return null;
    }

In case we reach the game ending condition, we display high score and stop the GameCanvas Thread using the method stop:

protected void endGame(Graphics g) {
        GameOver=true;
        gameCanvas.stop();
        Font f = g.getFont();
        int h = f.getHeight()+40;
        int w = f.stringWidth("High score")+40;       
        g.setColor(250,250,250);
        g.drawString("Score " + score,(width-w)/2,(height-h)/2,g.TOP | g.LEFT);
}

Points of Interest 

With the given knowledge, I believe that you can create simple games like: go fishing, frog crossing the road. I will cover TitledLayer, sound in the next updated of the post. They will surely increase the look and the feeling of the game.

fishing.jpg

frog.jpg

History

  • 23rd April, 2009: Published

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

hoangtuanbs
Other
Finland Finland
Programming languages: Java, C/C++, Objective-C, Assembly, Action script , PHP, Visual basic.

Second year student

Comments and Discussions

 
QuestionError in theGameManager.java Pin
suman259212-Jul-14 2:49
membersuman259212-Jul-14 2:49 
Questiongames Pin
Member 107949094-May-14 21:33
memberMember 107949094-May-14 21:33 
Questionbullet passes through the asteroid Pin
Member 1004315211-May-13 0:57
memberMember 1004315211-May-13 0:57 
QuestionWhat kind of j2me game is yours? Pin
supernorb29-Sep-12 12:26
membersupernorb29-Sep-12 12:26 
QuestionNeed help to run it Pin
Mac_xxx4-Aug-12 3:01
memberMac_xxx4-Aug-12 3:01 
AnswerRe: Need help to run it Pin
hoangtuanbs6-Aug-12 1:18
memberhoangtuanbs6-Aug-12 1:18 
QuestionBullet is not colliding with asteroid Pin
Ashz0077-Jul-12 11:19
memberAshz0077-Jul-12 11:19 
Questioncollision and score is not increasing Pin
hoaanhdao31127-May-12 17:51
memberhoaanhdao31127-May-12 17:51 
while running the program, not a collision between bullets and asteroids, the score does not increase.
Can you explain this case?

AnswerRe: collision and score is not increasing Pin
hoangtuanbs27-May-12 19:37
memberhoangtuanbs27-May-12 19:37 
GeneralRe: collision and score is not increasing Pin
hoaanhdao31128-May-12 7:12
memberhoaanhdao31128-May-12 7:12 
QuestionRe: collision and score is not increasing Pin
hoaanhdao31128-May-12 17:54
memberhoaanhdao31128-May-12 17:54 
AnswerRe: collision and score is not increasing Pin
hoangtuanbs6-Aug-12 1:18
memberhoangtuanbs6-Aug-12 1:18 
QuestionHỏi Pin
hoaanhdao31126-May-12 18:15
memberhoaanhdao31126-May-12 18:15 
AnswerRe: Hỏi Pin
supernorb29-Sep-12 12:28
membersupernorb29-Sep-12 12:28 
QuestionRun in NetBean 7.01 :Platform home (platform.home property) is not set. Value of this property should be Sun Java(TM) Wireless Toolkit 2.5.2 for CLDC emulator home directory location Pin
qhai9-Sep-11 5:18
memberqhai9-Sep-11 5:18 
AnswerRe: Run in NetBean 7.01 :Platform home (platform.home property) is not set. Value of this property should be Sun Java(TM) Wireless Toolkit 2.5.2 for CLDC emulator home directory location Pin
hoangtuanbs6-Aug-12 1:20
memberhoangtuanbs6-Aug-12 1:20 
Questionplease... Pin
wawarock13-Apr-11 1:29
memberwawarock13-Apr-11 1:29 
AnswerRe: please... Pin
hoangtuanbs6-Aug-12 1:20
memberhoangtuanbs6-Aug-12 1:20 
Generalgive me suggestion Pin
abbie329020-Jan-11 2:23
memberabbie329020-Jan-11 2:23 
GeneralRe: give me suggestion Pin
hoangtuanbs20-Jan-11 6:32
memberhoangtuanbs20-Jan-11 6:32 
GeneralRe: give me suggestion Pin
abbie329020-Jan-11 8:26
memberabbie329020-Jan-11 8:26 
Generalcollision and score is not increasing Pin
vimalsudhan9-Nov-10 4:09
membervimalsudhan9-Nov-10 4:09 
GeneralRe: collision and score is not increasing Pin
hoangtuanbs9-Nov-10 8:04
memberhoangtuanbs9-Nov-10 8:04 
Generalj2me game Pin
sinha9916-Oct-10 23:55
membersinha9916-Oct-10 23:55 
GeneralException error. Pin
mport949116-Oct-10 2:37
membermport949116-Oct-10 2:37 
GeneralRe: Exception error. Pin
hoangtuanbs9-Nov-10 8:08
memberhoangtuanbs9-Nov-10 8:08 
GeneralDisgrace Pin
meo_beo16-Sep-10 16:41
membermeo_beo16-Sep-10 16:41 
GeneralRe: Disgrace Pin
hoangtuanbs16-Sep-10 22:41
memberhoangtuanbs16-Sep-10 22:41 
Ranterror Pin
jedmendoza12-Jul-10 19:05
memberjedmendoza12-Jul-10 19:05 
GeneralRe: error Pin
hoangtuanbs13-Jul-10 4:15
memberhoangtuanbs13-Jul-10 4:15 
GeneralInteresting Tuan! Pin
Thanh Dao10-Jul-10 19:02
memberThanh Dao10-Jul-10 19:02 
GeneralRe: Interesting Tuan! Pin
hoangtuanbs13-Jul-10 4:16
memberhoangtuanbs13-Jul-10 4:16 
GeneralRe: Interesting Tuan! Pin
sanya.fesak11-Oct-11 8:02
membersanya.fesak11-Oct-11 8:02 
Generalhelp Pin
dinjo18-Apr-10 13:47
memberdinjo18-Apr-10 13:47 
GeneralRe: help Pin
hoangtuanbs13-Jul-10 4:17
memberhoangtuanbs13-Jul-10 4:17 
GeneralThe ship isn't moving. Pin
kosiarz_ss26-Mar-10 14:09
memberkosiarz_ss26-Mar-10 14:09 
GeneralRe: The ship isn't moving. Pin
hoangtuanbs13-Jul-10 4:17
memberhoangtuanbs13-Jul-10 4:17 
GeneralMissing Resources Pin
Merve Akis11-Mar-10 4:14
memberMerve Akis11-Mar-10 4:14 
GeneralRe: Missing Resources Pin
nithin_in24-Mar-10 23:20
membernithin_in24-Mar-10 23:20 
GeneralException error when running the program Pin
Ghady Rayess6-Feb-10 13:56
memberGhady Rayess6-Feb-10 13:56 
GeneralRe: Exception error when running the program Pin
mport949116-Oct-10 2:36
membermport949116-Oct-10 2:36 
GeneralA little snippet to help speed up writing input checks Pin
batch program26-Aug-09 13:12
memberbatch program26-Aug-09 13:12 
GeneralRe: A little snippet to help speed up writing input checks Pin
hoangtuanbs1-Dec-09 7:09
memberhoangtuanbs1-Dec-09 7:09 
Generalout of memory error in simulator and in nokia 5610 Pin
v_srinu_26_f11-Jul-09 17:37
memberv_srinu_26_f11-Jul-09 17:37 
GeneralRe: out of memory error in simulator and in nokia 5610 Pin
hoangtuanbs17-Jul-09 14:15
memberhoangtuanbs17-Jul-09 14:15 
Questionhow to compile? Pin
v_srinu_26_f11-Jul-09 9:58
memberv_srinu_26_f11-Jul-09 9:58 
AnswerRe: how to compile? Pin
hoangtuanbs11-Jul-09 10:20
memberhoangtuanbs11-Jul-09 10:20 
Generalhow to run Pin
satish kumar6974-Dec-09 20:29
membersatish kumar6974-Dec-09 20:29 
GeneralNo source zip file!!! Pin
LexK1-May-09 5:19
memberLexK1-May-09 5:19 
GeneralRe: No source zip file!!! Pin
hoangtuanbs2-May-09 0:16
memberhoangtuanbs2-May-09 0:16 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.150520.1 | Last Updated 23 Apr 2009
Article Copyright 2009 by hoangtuanbs
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid