netbean

179
So you want to make your own Java games for java enabled mobile phones. It's either that or you stumbled on this page by mistake. Just like I said in the introductory post, we'll be using Netbeans IDE on the tutorials that will be presented here. Here's a list of things you need and where to get them: Java SE Development Kit (JDK) 6.0 | Download Site NetBeans IDE 5.5.1/6.0 Beta 2 | Download Site Netbeans Mobility Pack 5.5.1 for CLDC/MIDP (only for NetBeans 5.5.1) | Download Site Note that you can download the NetBeans IDE 5.5 bundled with the JDK 6.0 from either site. If you're downloading NetBeans 6.0 Beta 2, you can get it bundled with Mobility pack. The difference between NetBeans 5.5 and 6.0 is that, in NetBeans 6.0 you get to try out the nifty Game Builder that helps you create maps, sprites, and sprite animation sequences. You also get the new syntax highlighting and other new stuff improved from the previous version. If you downloaded the bundle: Install JDK-Netbeans IDE bundle. Install Netbeans Mobility Pack If you downloaded them individually: Install JDK 6.0 Update 3. Install Netbeans IDE 5.5.1/6.0 Beta 2.

Upload: t-burn-cbz

Post on 01-Dec-2014

110 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: netbean

So you want to make your own Java games for java enabled mobile phones.

It's either that or you stumbled on this page by mistake.

Just like I said in the introductory post, we'll be using Netbeans IDE on the

tutorials that will be presented here.

Here's a list of things you need and where to get them:

← Java SE Development Kit (JDK) 6.0 | Download Site

← NetBeans IDE 5.5.1/6.0 Beta 2 | Download Site

← Netbeans Mobility Pack 5.5.1 for CLDC/MIDP (only for NetBeans 5.5.1) |

Download Site

Note that you can download the NetBeans IDE 5.5 bundled with the JDK 6.0

from either site. If you're downloading NetBeans 6.0 Beta 2, you can

get it bundled with Mobility pack.

The difference between NetBeans 5.5 and 6.0 is that, in NetBeans 6.0 you

get to try out the nifty Game Builder that helps you create maps, sprites, and

sprite animation sequences. You also get the new syntax highlighting and

other new stuff improved from the previous version.

If you downloaded the bundle:

← Install JDK-Netbeans IDE bundle.

← Install Netbeans Mobility Pack

If you downloaded them individually:

← Install JDK 6.0 Update 3.

← Install Netbeans IDE 5.5.1/6.0 Beta 2.

← Install Netbeans Mobility Pack for CLDC/Midp( only for NetBeans 5.5.1)

Page 2: netbean

That's it! You can now start making J2me applications for your java enabled

mobile phone.

There's one more thing we have to add to our checklist - a way to transfer

your game on your mobile phone. This depends on the phone you're working

on. Here's some options you might be able to choose from :

← Bluetooth - Use this if your phone is Bluetooth capable.

← Infrared Cable - Use this if you don't have Bluetooth.

← USB/Serial Cable - Use this if you have neither Bluetooth nor Infrared

← Card Reader - Use this if your phone has a memory card and you can't

use any of the above.

Aside from the cable/dongle you want to use, you might also need the

software and drivers to make them work. You can get these from your

phone's manufacturer site or the CD-Rom that came with your phone. An

example of the kind of software you might need is the Nokia PC Suite for

Nokia cellphones.

Obviously, you will also need an image editor for creating the graphics you

will use in your game. But for starters you can also find resources on the

internet that offer sprites, tiles and other graphic content for you to practice

on. Some of which I have added to the links section.

Alright, I think that's about it for our checklist. If you have some questions

place a comment under this post

As I've said in the Getting Started tutorial, we will be using NetBeans IDE in

making our applications for Java enabled mobile phones. If you prefer using

the J2me Toolkit you can skip this tutorial as it's a bit about familiarizing the

Page 3: netbean

user with the NetBeans IDE.

In this tutorial we will be creating our first, traditional "Hello World" MIDlet.

"So what's a MIDlet?", you say. Bah! Details...details. Who cares, right?

Seriously, in simple terms a MIDlet is a java program you can run on mobile

devices such as java enabled cellphones. A MIDlet is to your phone as an exe

is to pc (excuse the bad analogy heh).

If you haven't already, open the NetBeans IDE. You should see something

similar to this:

Choose "File" from the main menu and click on "New Project". Select

"Mobile" from the list of Categories and "Mobile Application" from the list of

Projects. Click on the "Next" button.

Page 4: netbean

On the next screen, you get to name your project and choose it's location on

your hard drive. As an example I typed "MyMidlet" in that box. Be sure to

place a check on both "Set as Main Project" and "Create Hello MIDlet"

options before you hit the "Next " button.

Page 5: netbean

Select an emulator platform. I recommend choosing the one with the highest

version. Leave the Device on "Default ColorPhone". Choose CLDC-1.0 from

Device Configuration and MIDP 2.0 for the Device Profile. There's more about

these settings later but for now hit the "Finish" button.

Page 6: netbean

If all went well, NetBeans would have created the MIDlet for you including

the whole "Hello World" program. You can view the source code of the

application by expanding the items of the treeview in the Projects panel.

Click on the Run icon from the toolbar, it's the one with the yellow and green

rectangles (back-to-backarrows?), or you can just press F6 from your

keyboard.

Page 7: netbean

After it has finished compiling and building the program the emulator window

will pop-up. You will see the list of MIDlets on the phone screen with your one

and only MIDlet already highlighted. You can click on the Select button or the

Softkey labeled with "Launch" to run the program or you can simply hit the

Enter key from your keyboard.

Page 8: netbean

Painless, wasn't it? Now about those "Platform Settings" we chose earlier. It really depends on the phone you want your program to run on. Most phones

Page 9: netbean

at the time of writing already supports MIDP 2.0 and this in turn allows us to use new libraries that were added to J2me specifically for game development. From here on, the rest of the tutorials you will find here will be focused on MIDP 2.0 phones.

The reason that I chose CLDC-1.0 is for compatibility. If you're targeting a specific phone model it's best to view the device specification for that phone from the manufacturers website. One of the main changes from CLDC-1.0 and CLDC-1.1 is that in the latter they have added support for real numbers or floats. Unless you really need to use floats in your code I suggest sticking to CLDC-1.0 for the time being. You can also find resources on the internet on how to deal with floats on the CLDC-1.0 platform.

Creating the Midlet

The last tutorial showed you how to use Netbeans IDE to create a basic MIDP

2.0 MIDlet, compile it, and preview your program in the emulator.

Unfortunately, the code generated for the Hello MIDlet is of little or no use

for making games. So in this tutorial you will be shown the basic code

needed for creating your games.

First off, open Netbeans and create a new mobile application project. What

we need is a blank project so remember to uncheck the "Create Hello MIDlet"

on the "Name and Location" screen.

Page 10: netbean

Now we need to create the MIDlet ourselves. There are several ways to go

about this.

← By pressing CTRL+N on your keyboard.

← By choosing New File from the File Menu

← By right-clicking on the project panel treeview and selecting New, then

Midlet

For those of you who used one of the first 2 ways, select MIDP from the

categories and MIDlet from the list of file types then click on the "Next"

button.

Page 11: netbean

On the next screen, type in midMain for MIDlet Name. The MIDlet Class

Name field will be automatically filled in for you. Leave the MIDlet icon blank

for now. As a rule of thumb, never create your files in the "default package".

Enter MyGame in the Package field. A folder with the same name will be

created inside the project source folder where all source code for your game

should go. Click on the "Finish" button.

Page 12: netbean

Congratulations!! You have succeeded on creating an empty MIDlet that does absolutely nothing. Next stop, Creating the Canvas...

We need to make a canvas based on the new GameCanvas class. The canvas

is where we draw all our stuff like images, sprites, maps, scores so it can be

shown on the phones screen. It also let's us know what keys were pressed on

the phone so we can respond to them.

Choose New File from the File menu. Select Java Classes from the list of

categories and Java Class from the list of file types then click on the "Next"

button. Alternatively, you can right-click on the package name then choose

New and Java Class.

Type in clsCanvas for the Class Name. Make sure to set the Package field to

the package name we set earlier for our MIDlet in the first part of this

tutorial. Click on the "Finish" button when you're done.

Page 13: netbean

You should see the source code of class file we just created in the editor

panel. If not, then navigate to the file from the project panel and double-click

on the filename. Except for the author's name and the file/class/package

name (if you chose your own), it should look similar to the code below.

** clsCanvas.java** Created on October 15, 2007, 7:13 PM** To change this template, choose Tools | Template Manager* and open the template in the editor.*/

package MyGame;

/**** @author devlin*/public class clsCanvas {     /** Creates a new instance of clsCanvas */

Page 14: netbean

   public clsCanvas() {   }  }

It's now time to turn this class into a GameCanvas class.

Modify this part of the code:

public class clsCanvas {

...into this:

public class clsCanvas extends GameCanvas implements Runnable {

Your code should now look like this:

(I will strip the comments from the code from now on for readability.)

package MyGame;

public class clsCanvas extends GameCanvas implements Runnable {     public clsCanvas() {   }}

Press Shift+ALT+F (short cut for Fix Imports) to make sure that all import

statements our code requires are detected and added automatically. (Do this

periodically or whenever we use a new class or code.)

package MyGame;

import javax.microedition.lcdui.game.GameCanvas;

Page 15: netbean

public class clsCanvas extends GameCanvas implements Runnable {     public clsCanvas() {   }}

Now it's time to get rid of those ugly red lines signifying errors in our code.

We first need to fulfill the requirements of the base class that we are

extending so modify the code further like so:

package MyGame;

import javax.microedition.lcdui.game.GameCanvas;

public class clsCanvas extends GameCanvas implements Runnable {     public clsCanvas() {       super(true);   }

   public void run() {   }

}

Tada! No more errors!

The superclass GameCanvas requires us to implement it's constructor which

has one boolean parameter - suppressKeyEvents. Passing the value true

stops key events like keyPressed, keyRepeated and keyReleased from being

called and optimizing your code. Then how do we respond to user input

then? We use the function getKeyStates() to find out what keys were

pressed whenever we need them. More on this later.

Our canvas class also implements the Runnable class which allows our game

to run on a separate thread. This requires us to implement the run()

Page 16: netbean

method. This will contain our main loop and most game related code.

Let's modify the code further to include the main loop for which we will be

using a while-loop construct. Modify your run() method like so:

    public void run() {       while(isRunning){

           flushGraphics();       }    }

Add this private variable under the main class declaration statement

public class clsCanvas extends GameCanvas implements Runnable {private boolean isRunning = true;

One feature of the GameCanvas is an off-screen buffer where everything you

draw is first rendered. The contents of this buffer is transferred to the screen

after the flushGraphics() function is called. This eliminates flicker and

makes your animations smoother. So flushGraphics() is called every time

at the end of each loop after you have drawn what you want to be shown on

the screen.

With the isRunning variable set to true, the while loop will run forever. We

must add a way for the loop to terminate and our program to end. Let's allow

the user to end our program when the Fire or 5 key is pressed on the phone.

This is where the getKeyStates() function comes in. Modify the run()

method like so:

    public void run() {       int iKey = 0;       while(isRunning){           iKey = getKeyStates();                      if ((iKey & GameCanvas.FIRE_PRESSED) != 0){

Page 17: netbean

               isRunning = false;           }

           flushGraphics();           try{               Thread.sleep(30);           } catch (Exception ex){                          }       }    }

We make the thread sleep or pause for 30 milliseconds after each loop. This

keeps the the thread from hogging the processing power of the phone and

the game from being unresponsive. Without it, the game will not be able to

respond to key presses at the moment it needs to. You can change this value

to what you want as long as it works. We will be adding frame limiting at

some point and that will dynamically adjust the sleep value and make the

frame rate of the game somewhat stable on different phones.

Let's add some drawing code so we can see what we've done later when we

run the application.

Add this code in our global variable declarations right under the isRunning

declaration:

private boolean isRunning = true;    private Graphics g;

Add this code in the run() method just before the while loop:

       g = getGraphics();       while(isRunning){

Page 18: netbean

Add this code inside the run() method just before the flushGraphics()

function call:

          //set drawing color to black           g.setColor(0x000000);           //fill the whole screen           g.fillRect(0, 0, getWidth(), getHeight());           // set drawing color to white           g.setColor(0xffffff);           //display the key code last pressed           g.drawString(Integer.toString(iKey), 2, 2, Graphics.TOP | Graphics.LEFT);

           flushGraphics();

Add this code at the end of the run() method:

       }       g = null;    }

Hit Shift+ALT+F to update the imports section and your code should

now look like this:

package MyGame;

import javax.microedition.lcdui.Graphics;import javax.microedition.lcdui.game.GameCanvas;

public class clsCanvas extends GameCanvas implements Runnable {private boolean isRunning = true;    private Graphics g;

    public clsCanvas() {        super(true);    }        public void run() {       int iKey = 0;

Page 19: netbean

       g = getGraphics();       while(isRunning){                      iKey = getKeyStates();                      if ((iKey & GameCanvas.FIRE_PRESSED) != 0){               isRunning = false;           }                      //set drawing color to black           g.setColor(0x000000);           //fill the whole screen           g.fillRect(0, 0, getWidth(), getHeight());           // set drawing color to white           g.setColor(0xffffff);           //display the key code last pressed           g.drawString(Integer.toString(iKey), 2, 2, Graphics.TOP | Graphics.LEFT);

           flushGraphics();                      try{               Thread.sleep(30);           } catch (Exception ex){                          }       }       g = null;    }}

Notice we declared a new Graphics object named g. The Graphics object contains the methods we must use to draw stuff on the GameCanvas. Just take note that the color passed when setColor() is called will apply to succeeding calls to drawing methods until setColor() is called again with a different color. The Graphics object is also declared as a global variable and is assigned to the actual object right before the main loop. This saves us from having to call the getGraphics() method of the GameCanvas over and over.

Page 20: netbean

Displaying the GameCanvas

In this last part of the tutorial entitled Basic Game Template, we will

modify the code of our MIDlet and Canvas classes to make the both ends

meet and the application to run. It is recommended that you start with Part

1 of this tutorial if you haven't done so.

First, we'll need to add a new global variable to reference the MIDlet from

the canvas class. Let's call it fParent. So add this code to clsCanvas like so:

private Graphics g;private midMain fParent;

Modify the contructor to accept the MIDlet class as a parameter and assign

the value to our global variable:

    public clsCanvas(midMain m) {        super(true);        fParent = m;    }

The canvas needs a way to notify the MIDlet that the main loop has ended

and the program needs to terminate. Add this code at the end of our run()

method:

       }       g = null;       fParent.destroyApp(false);       fParent = null;    }

We need a new method that the MIDlet can call to start the game thread.

Let's call it the start() method and add the code under the clsCanvas

constructor:

Page 21: netbean

    public void start(){        Thread runner = new Thread(this);        runner.start();    }

You should press Shift+ALT+F after editing the code...just in case.

Here's the completed clsCanvas source code:

package MyGame;

import javax.microedition.lcdui.Graphics;import javax.microedition.lcdui.game.GameCanvas;

public class clsCanvas extends GameCanvas implements Runnable {private boolean isRunning = true;    private Graphics g;private midMain fParent;

    public clsCanvas(midMain m) {        super(true);        fParent = m;    }        public void start(){        Thread runner = new Thread(this);        runner.start();    }

    public void run() {       int iKey = 0;        g = getGraphics();       while(isRunning){                      iKey = getKeyStates();                      if ((iKey & GameCanvas.FIRE_PRESSED) != 0){               isRunning = false;           }                      //set drawing color to black

Page 22: netbean

           g.setColor(0x000000);           //fill the whole screen           g.fillRect(0, 0, getWidth(), getHeight());           // set drawing color to white           g.setColor(0xffffff);           //display the key code last pressed           g.drawString(Integer.toString(iKey), 2, 2, Graphics.TOP | Graphics.LEFT);           flushGraphics();                      try{               Thread.sleep(30);           } catch (Exception ex){                          }       }       g = null;       fParent.destroyApp(false);       fParent = null;    }}

Now open the source code of our MIDlet. We will define a new global variable

called myCanvas that will allow the MIDlet to create and reference the

clsCanvas class. Place the code just under the midMain class declaration:

public class midMain extends MIDlet {    clsCanvas myCanvas;

Next, we will modify the startApp() method of our midlet so that it creates a

new instance of the clsCanvas class, starts it in a new thread, and finally

making the the canvas the current displayed item. Add this code to the

startApp() method:

    public void startApp() {        Display d = Display.getDisplay(this);        myCanvas = new clsCanvas(this);        myCanvas.start();

Page 23: netbean

        d.setCurrent(myCanvas);    }

The last code we will add is for making sure we release all the resources our

MIDlet has used and terminate gracefully when your game has ended. Place

this code in the destroyApp() method:

    public void destroyApp(boolean unconditional) {        myCanvas = null;        notifyDestroyed();    }

Here's the completed midMain source code:

package MyGame;

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

public class midMain extends MIDlet {    clsCanvas myCanvas;        public void startApp() {        Display d = Display.getDisplay(this);        myCanvas = new clsCanvas(this);        myCanvas.start();        d.setCurrent(myCanvas);    }        public void pauseApp() {    }        public void destroyApp(boolean unconditional) {        myCanvas = null;        notifyDestroyed();    }}

Page 24: netbean

Yay! We're done! You can now test your by pressing the F6 key on your keyboard or clicking on the Run Main Project icon on the toolbar. That ends our tutorial on creating a basic template for your game. I will also be using this template for the rest of the tutorials that will be posted here. If you have some comments, suggestions or having problem with the source code, feel free to leave a comment or use the ShoutBox. Have fun.

Making a FullScreen Canvas

Updated : 10/24/2007

In this rather short tutorial, we're going to make our canvas take up the

whole screen area of the phone. Open the project you created in the tutorial

Basic MIDP 2.0 Game Template. Open the file clsCanvas.java then

insert setFullScreenMode(true); at the end of the contructor:

public clsCanvas(midMain m) {   super(true);   fParent = m;   setFullScreenMode(true);}

That's all there is to it.

Update

As commented by our anonymous tipper, some of you might encounter a

bug while calling the setFullScreenMode() function from the constructor of

the GameCanvas class.

I decided to dig deeper on this and I was able to find a post at Sun's Java

Forums. One poster mentioned a problem with the SonyEricsson P900 and

P910 phones. You can read the whole thread here: Link to Thread.

I will try to find more information about bugs or other weird behaviors when

using the setFullScreenMode() function

Page 25: netbean

Loading Images Into Your Game

Updated : 10/22/2007

The title says it all. We're gonna load some graphics into your game. We're

going to build upon the project we made in the tutorial Basic MIDP 2.0

Game Template. So better do that part first if you haven't already. I will be

referring to that project as "project".

Image Format

The preferred image format is the PNG format. This is a good thing since PNG

supports alpha transparency which means that the images can be anti-

aliased and smoother looking. The bad news is not all phones can display

images with alpha transparent pixels correctly. It depends on your target

phone. But it's still better to avoid alpha transparent pixels as much as

possible.

Color Depth

Take note that different phones have different color depths. The color depth

of the phones today ranges from 8-bit(256colors) to 24-bit color (16,777,216

colors). What does this all mean? While your hi-res imagery looks great on

phones with higher depth, they would get dithered on phones with lower

color depth making the images look pixelated. Further loss of color might

even make your images unrecognizable given the small screen size the

game will be viewed from.

There's also only a small amount of memory available on mobile phones for

you to work with and pictures that use more colors eats up more memory. So

as a rule of thumb use less colors for your graphics.

Page 26: netbean

Introducing earth.png

For this tutorial I've prepared a quick-and-dirty 16x16 pixel version of your

home planet. W000t!! I drew a whole planet in less than 5 minutes!! It's a bit

large though coz' it used up 34 colors (502 bytes).

Actual Size of Earth (16x16)

Enlarged View of Earth (64x64)

Open an explorer window and navigate to the folder where you saved the

project and make a backup of the whole project folder just in case something

goes wrong (or for historical purposes). Better yet use a VCS like CVS or VSS.

NetBeans supports either (VSS via plug-in). I

After making a backup, go inside the the project folder and inside the src

folder.

So if the path to your project folder is:

c:\YourDocs\netbeans\BasicGameTemplate

...the path to the src folder should be:

c:\YourDocs\netbeans\BasicGameTemplate\src

Create a new folder inside the src folder named "images". The path to

which should look like this:

c:\YourDocs\netbeans\BasicGameTemplate\src\images

Page 27: netbean

Firefox users can right-click on the earth.png image and choose Save

Image As from the context menu. IE users can right-click on the earth.png

image and choose Save Picture As from the context menu. Save the PNG

file in the images folder you just created. You can save any of the images

above as they are the same file.

An even easier way to create the "images" folder is to:

1. Open the project in NetBeans.

2. Right-click on the project name in the Projects panel.

3. From the pop-up menu, choose New and then Folder.

4. Type "images" as the Folder Name in the resulting dialog box.

5. Make sure src is selected as the Parent Folder.

6. Click on the "Finish" button.

Loading the Image

Open NetBeans and press CTRL+Shift+O. This will bring about the Open

Project dialog box where you can choose the project folder. You can also

click on the purple/violet colored folder icon in the main toolbar. Don't forget

to set it as the main project by selecting the Open as Main Project at the

right side of the dialog box. If that was the same project you had open when

you closed NetBeans, the project would be automatically loaded the next

time you open NetBeans.

Page 28: netbean

Expand the nodes of the treeview in the Projects panel. You should now see

the images folder listed there and earth.png listed under that folder.

Page 29: netbean

You can now open the class file clsCanvas.java so we can start coding.

First we need to make a global variable to hold our image. Let's name it

"imgEarth". Declare imgEarth right under the fParent declaration like so:

private midMain fParent;private Image imgEarth;

Now would be a good time to press Shift+ALT+F to update the imports

section.

Let's make a new method named load() where we will place all the code to

initialize our game. This is where we actually load the image file. Place it

right after the start() method like so:

public void start(){   Thread runner = new Thread(this);   runner.start();}

   public void load(){   try{       // try to load the image file       imgEarth = Image.createImage("/images/earth.png");   }catch(Exception ex){       // exit the app if it fails to load the image       isRunning = false;       return;   }}

The createImage() method of the Image class is used to load the image

and assign the resulting Image object to our imgEarth variable. We used

the method inside an exception handling block so you can catch the

Page 30: netbean

exception error it generates when it fails to load the image. Just so you know,

NetBeans won't let you use it without exception handling.

Let's make an unload() method where we can place all the shutdown or

cleanup code our game needs. Place it after the newly created load()

method:

public void load(){   try{       // try to load the image file       imgEarth = Image.createImage("/images/earth.png");   }catch(Exception ex){       // exit the app if it fails to load the image       isRunning = false;       return;   }}

   public void unload(){   // make sure the object get's destroyed   imgEarth = null;}

Now it's time to call the new functions inside the run() method. Place the

load() method under the iKey variable declaration like so:

public void run() {  int iKey = 0;  load();  g = getGraphics();

...then call the unload() method after we assign null to the g variable:

Page 31: netbean

  }  g = null;  unload();  fParent.destroyApp(false);

When you're done, your clsCanvas code should look like this:

package MyGame;

import javax.microedition.lcdui.Graphics;import javax.microedition.lcdui.Image;import javax.microedition.lcdui.game.GameCanvas;

public class clsCanvas extends GameCanvas implements Runnable {private boolean isRunning = true;private Graphics g;private midMain fParent;private Image imgEarth;

public clsCanvas(midMain m) {    super(true);    fParent = m;    setFullScreenMode(true);}

public void start(){    Thread runner = new Thread(this);    runner.start();}

   public void load(){    try{        // try to load the image file        imgEarth = Image.createImage("/images/earth.png");    }catch(Exception ex){        // exit the app if it fails to load the image        isRunning = false;        return;    }}

Page 32: netbean

public void unload(){    // make sure the object get's destroyed    imgEarth = null;}

public void run() {   int iKey = 0;   load();   g = getGraphics();   while(isRunning){          iKey = getKeyStates();          if ((iKey & GameCanvas.FIRE_PRESSED) != 0){           isRunning = false;       }          //set drawing color to black       g.setColor(0x000000);       //fill the whole screen       g.fillRect(0, 0, getWidth(), getHeight());       // set drawing color to white       g.setColor(0xffffff);       //display the key code last pressed       g.drawString(Integer.toString(iKey), 2, 2, Graphics.TOP | Graphics.LEFT);       flushGraphics();          try{           Thread.sleep(30);       } catch (Exception ex){              }   }   g = null;   unload();   fParent.destroyApp(false);   fParent = null;}}

Drawing the Image

Page 33: netbean

To draw the image on the screen insert this line of code inside the run()

method just before the flushGraphics() function call:

               //draw the image        g.drawImage(imgEarth, 50, 50, Graphics.TOP | Graphics.LEFT);               flushGraphics();

The drawImage() method of the Graphics object g is used to draw the

image unto our canvas at the coordinates 50(X), 50(Y). The last parameter,

Graphics.Top | Graphics.Left, defines the anchor point or part of the

image that will reside at the given coordinates. In this example the top-left

part of the image will be positioned at coordinates 50,50 , X and Y

respectively.

The completed clsCanvas source code:

package MyGame;

import javax.microedition.lcdui.Graphics;import javax.microedition.lcdui.Image;import javax.microedition.lcdui.game.GameCanvas;

public class clsCanvas extends GameCanvas implements Runnable {private boolean isRunning = true; private Graphics g;private midMain fParent;private Image imgEarth;

 public clsCanvas(midMain m) {     super(true);     fParent = m;     setFullScreenMode(true); }

Page 34: netbean

 public void start(){     Thread runner = new Thread(this);     runner.start(); }

   public void load(){     try{         // try to load the image file         imgEarth = Image.createImage("/images/earth.png");     }catch(Exception ex){         // exit the app if it fails to load the image         isRunning = false;         return;     } }

 public void unload(){     // make sure the object get's destroyed     imgEarth = null; }

 public void run() {    int iKey = 0;      load();      g = getGraphics();    while(isRunning){             iKey = getKeyStates();             if ((iKey & GameCanvas.FIRE_PRESSED) != 0){            isRunning = false;        }             //set drawing color to black        g.setColor(0x000000);        //fill the whole screen        g.fillRect(0, 0, getWidth(), getHeight());        // set drawing color to white        g.setColor(0xffffff);        //display the key code last pressed        g.drawString(Integer.toString(iKey), 2, 2, Graphics.TOP | Graphics.LEFT);               //draw the image        g.drawImage(imgEarth, 50, 50, Graphics.TOP | Graphics.LEFT);

Page 35: netbean

               flushGraphics();             try{            Thread.sleep(30);        } catch (Exception ex){                 }    }    g = null;      unload();      fParent.destroyApp(false);    fParent = null; }}

Whenever your ready you can hit F6 on your keyboard and start the MIDlet

when the emulator pops up. You should see the something like the screeny

below.

Page 36: netbean

Got an error? Something could've been done better? --> Post a comment.

Page 37: netbean

Update

The code in this tutorial now runs in fullscreen mode. Details can be found

here : Making a FullScreen Canvas

Clipping Images or Displaying Only Parts of an Image

Sometimes you just need to display certain parts of an image. Like for

instance, when you want to show some animation, It would be better to

place all the animation frames inside a single image file rather than to store

them in several files. This will help save some precious phone memory since

you only have to load one image file into one Image object so there's no

extra overhead from using multiple Image object instances.

Once again we will just be using the project we last modified in the previous

tutorial : Loading Images Into Your Game.

I'm also writing this in preparation for the next tutorial which is about

graphical menus where clipping plays a major part.

Reintroducing Earth: Animated

In this tutorial we're going to show earth spinning on the phone screen like

the image displayed below:

Spinning Earth 16 x 16 pixels

Here is the image strip for that animation. Firefox users can right-click and

choose "Save Image As" from the context menu while IE users can choose

"Save Picture As" instead. Save the image inside the "images" folder of

the "src" folder of the project.

Spinning Earth animation with all frames in a single image.

Page 38: netbean

Dimensions: 144 x 16 pixels

Frame Size: 16 x 16 pixels

Just in case you're wondering, both images were made from using a satellite

image of earth which you can see here:

...which is a bit of an overkill.

Juggling Frames

What we're actually going to do is to draw one frame at a time from the

image strip you just downloaded. All the while keeping the frame we want to

draw inside the clipping rectangle. An illustration would be best to

demonstrate what I'm trying to say:

Page 39: netbean

The red rectangle represents the clipping rectangle and everything outside

that rectangle will not be visible on the screen. The dimmed portion of the

image strip represents the portion that will not be visible on the screen. The

frame number being displayed is the current frame that is inside the clipping

rectangle and is visible on the screen. We decrement the X of the image strip

by 16 pixels to skip to the beginning of each frame going from right to left.

Again the most important thing to remember about clipping rectangles is

that everything outside it is invisible and everything inside it is visible.

It's now time to start NetBeans and open the project. Upon opening the

project you should see the "earthstrip.png" file inside the images folder.

Navigate your way to clsCanvas.java so we can start editing some code. In

the load() method, change "earth.png" to "earthstrip.png".

       // try to load the image file       imgEarth = Image.createImage("/images/earthstrip.png");

Let's add some variables in the run() method under the iKey declaration:

public void run() {  int iKey = 0;

       int imgX = 50; // x coordinate of the image       int frameIndex = 0; // current frame to be drawn       long lDelay = 250; //time to pause between frames in milliseconds       long lStart = 0; //time we last changed frames in milliseconds       long lCurrTick = 0; // current system time in milliseconds;

Page 40: netbean

Add this code inside the main loop after we check for key presses and before

we call the drawing methods:

      if ((iKey & GameCanvas.FIRE_PRESSED) != 0){          isRunning = false;      }

                     lCurrTick = System.currentTimeMillis();           if ((lCurrTick-lStart) >= lDelay){               lStart = lCurrTick; // save the current time               if (frameIndex < 8) {                   frameIndex++; // skip to the next frame               } else {                   frameIndex = 0; // go back to first frame               }               imgX = 50 - (frameIndex * 16); // compute x relative to clip rect           }                      //restore the clipping rectangle to full screen           g.setClip(0, 0, getWidth(), getHeight());

          //set drawing color to black          g.setColor(0x000000);

There's a lot happening there. First, we store the current system time in

milliseconds to the lCurrTick variable. We use that to check if the time that

has passed since the last time we changed the frame, lStart, is greater than

the delay we set, lDelay. If so, then it's time to change frames. And since we

are changing frames, we will set lStart to the current time for our delay to

work on the next loop.

We have a total of 9 frames in that animation which we index starting at 0.

That means the last frame would have an index of 8. So we check if the

current frame index which is stored in frameIndex is the last index. If not,

then we increment it by 1 frame. If it is the last frame, we then return to the

first frame.

Page 41: netbean

After we have figured out if it's time to change frames and what frame to

display next, we can now compute the X coordinate of the image strip so the

frame we want to show will be drawn inside the clipping rectangle. Here's

the formula in pseudo code:

image_X = X_of_clipping_rectangle - (frame_index * frame_width);

setClip(), Not Scissors!!

You might have also noticed that we called a new method: setClip(). The

setClip() method of the Graphics class is what let's us define the position

and size of the clipping rectangle. The first two parameters defines the

position, x and y, and the last two parameters defines the width and height

of the clipping rectangle.

Let's draw some more.

Add this code in the main loop just before the call to drawImage():

      g.drawString("Frame : " + Integer.toString(frameIndex), 2, 68, Graphics.TOP | Graphics.LEFT);      g.drawString("X : " + Integer.toString(imgX), 2, 88, Graphics.TOP | Graphics.LEFT);       //clip the drawing area to a single frame      g.setClip(50, 50, 16, 16);

...and modify the drawImage() function call like so:

      g.drawImage(imgEarth, imgX, 50, Graphics.TOP | Graphics.LEFT);

Page 42: netbean

Notice that we placed two calls to the setClip() method of the Graphics

object g. The first call sets the clipping rectangles dimensions to match that

of the whole screen area of the phone. The second call sets the clipping

rectangle to where the animated globe will be drawn. This is because the

dimensions you pass to the setClip() method the last time you call it will be

in effect until you call setClip() again with a different set of parameters. This

means if you don't reset the clipping rectangle, you won't be able to draw

anywhere else on the screen. Anything you draw outside the clipping

rectangle will not show up on the phone screen. So it's good practice to call

the setClip() method with full screen dimensions at the top of your drawing

code.

Your clsCanvas source code should now look like this:

package MyGame;

import javax.microedition.lcdui.Graphics;import javax.microedition.lcdui.Image;import javax.microedition.lcdui.game.GameCanvas;

public class clsCanvas extends GameCanvas implements Runnable {private boolean isRunning = true;    private Graphics g;private midMain fParent;private Image imgEarth;

    public clsCanvas(midMain m) {        super(true);        fParent = m;        setFullScreenMode(true);    }        public void start(){        Thread runner = new Thread(this);        runner.start();    }        public void load(){

Page 43: netbean

        try{            // try to load the image file            imgEarth = Image.createImage("/images/earthstrip.png");        }catch(Exception ex){            // exit the app if it fails to load the image            isRunning = false;            return;        }    }        public void unload(){        // make sure the object get's destroyed        imgEarth = null;    }

    public void run() {       int iKey = 0;

       int imgX = 50; // x coordinate of the image       int frameIndex = 0; // current frame to be drawn       long lDelay = 250; //time to pause between frames in milliseconds       long lStart = 0; //time we last changed frames in milliseconds       long lCurrTick = 0; // current system time in milliseconds;             load();       g = getGraphics();       while(isRunning){                      iKey = getKeyStates();                      if ((iKey & GameCanvas.FIRE_PRESSED) != 0){               isRunning = false;           }                     lCurrTick = System.currentTimeMillis();           if ((lCurrTick-lStart) >= lDelay){               lStart = lCurrTick; // save the current time               if (frameIndex < 8) {                   frameIndex++; // skip to the next frame               } else {                   frameIndex = 0; // go back to first frame               }               imgX = 50 - (frameIndex * 16); // compute x

Page 44: netbean

relative to clip rect           }                      //restore the clipping rectangle to full screen           g.setClip(0, 0, getWidth(), getHeight());

           //set drawing color to black           g.setColor(0x000000);           //fill the whole screen           g.fillRect(0, 0, getWidth(), getHeight());           // set drawing color to white           g.setColor(0xffffff);           //display the key code last pressed           g.drawString(Integer.toString(iKey), 2, 2, Graphics.TOP | Graphics.LEFT);

           g.drawString("Frame : " + Integer.toString(frameIndex), 2, 68, Graphics.TOP | Graphics.LEFT);           g.drawString("X : " + Integer.toString(imgX), 2, 88, Graphics.TOP | Graphics.LEFT);                      //clip the drawing area to a single frame           g.setClip(50, 50, 16, 16);           //draw the image                      g.drawImage(imgEarth, imgX, 50, Graphics.TOP | Graphics.LEFT);                      flushGraphics();                      try{               Thread.sleep(30);           } catch (Exception ex){                          }       }       g = null;       unload();       fParent.destroyApp(false);       fParent = null;    }}

You can start up the MIDlet now. You should see a spinning globe with the

Page 45: netbean

current frame and x coordinate displayed below it. You can also change the

speed in which the globe rotates by increasing or decreasing the value of

iDelay in the run() method. Increasing iDelay will make the animation run

slower while setting it to a lower value will make the animation run faster.

Page 46: netbean

That's it for clipping. If you have a question or would like to point out a

mistake, feel free to post a comment. Have fun!!!

Thursday, October 25, 2007

Input Handling : Keypress with Repeat Rate

There are a lot of ways to deal with user input in your game. This tutorial will

show one of the solutions that you can use on certain parts of the game. Like

when your prompting the user to exit the game or not, while in the main

menu, or when you want the user to choose from some options on the

screen. As a warning, we're going to limit the code to using the

getKeyStates() method only.

The Problem : It's Like The Keys Get Stuck..

Continues key presses are good while playing the game because the game

can respond at the heat of the moment and match the users super reflexes.

But not during selection screens. Why so? Let's say your game has a frame

rate of 15 frames per second. That's also the speed at which it can act every

time the user presses a key on the phone.

Imagine that in your game the second item from the top of the list is the

View Instructions item and the current selected or highlighted item is the

New Game item which is the first item on the menu. The user, trying to get

to the Instructions item, presses the down button and the selection moves

down from one item to the other at an alarming rate of 15 times in one

second. The confused user will not be able to accurately pick items from the

menu.

Page 47: netbean

Better yet, imagine typing a document in a text editor and every time you hit

the keyboard you get 15 of each character you type in. It would be like

driving a car without breaks (No, I'm not drawing that for you!!!).

Making the Keys Toggle

In this tutorial we're going to add some interactivity to the spinning globe we

made in the last tutorial : Clipping Images or Displaying Only Parts of

an Image. We'll change the code so that the user can adjust the speed at

which the globe spins. So load the project in NetBeans and open the

clsCanvas code.

Let's declare some constants and variables. Insert this code under the

clsCanvas class declaration:

public class clsCanvas extends GameCanvas implements Runnable {

// key repeat rate in millisecondspublic static final int keyDelay = 250;

//key constantspublic static final int upKey = 0;public static final int leftKey = 1;

Page 48: netbean

public static final int downKey = 2;public static final int rightKey = 3;public static final int fireKey = 4;

//key states for up, left, down, right, and fire keyprivate boolean[] isDown = {false, false, false, false, false};

//last time the key changed stateprivate long[] keyTick = {0, 0, 0, 0, 0};

//lookup table for key constants :Pprivate int[] keyValue = {GameCanvas.UP_PRESSED, GameCanvas.LEFT_PRESSED,GameCanvas.DOWN_PRESSED, GameCanvas.RIGHT_PRESSED,GameCanvas.FIRE_PRESSED};

The value assigned to the constant keyDelay will determine how fast a key

state changes from being pressed to not pressed while holding down a key.

In other words, this will be the repeat rate. The higher the number, the

slower the repeat rate gets and vice versa.

The next five constants we declared are for the 5 standard game keys Up,

Left, Down, Right, and the Fire key. We just defined them so our code

won't become confusing, so instead of using numbers we can use

"meaningful words". Also, someone told me about 12-14 years ago that it

was good practice to define constants for numbers or other fixed values your

going to use repeatedly all over your code.

The boolean array isDown[] is going to hold the state for each of the

standard keys we've defined while the long array keyTick[] will hold the

time when a key last changed from being down to up.

The last array keyValue[], an integer array to hold the actual key codes for

Page 49: netbean

each key. We defined this so we can loop through each key in our key

detection code which you will see in the next code section.

We'll add a new method to our clsCanvas class called checkKeys(). Add

the code above the run() method:

   public void checkKeys(int iKey, long currTick){       long elapsedTick = 0;

       //loop through the keys       for (int i = 0; i < 5; i++){

           // by default, key not pressed by user           isDown[i] = false;

           // is user pressing the key           if ((iKey & keyValue[i]) != 0){               elapsedTick = currTick - keyTick[i];

               //is it time to toggle key state?               if (elapsedTick >= keyDelay){

                   // save the current time                   keyTick[i] = currTick;

                   // toggle the state to down or pressed                   isDown[i] = true;               }           }       }   }

public void run() {

The checkKeys() method takes the value returned by the getKeyStates()

method in the iKey parameter and the current time in milliseconds in the

currTick parameter. It then loops through all the keys in the keyValue[]

array too see if a certain key is being pressed. It then toggles the value of

the appropriate element in the isDown[] array depending on how long it's

Page 50: netbean

been since the key was in the pressed state. It also makes sure that the

isDown[] array is updated as to which keys are not being pressed.

Let's change our run() method so that when the user presses the left key

the spinning animation speeds up and when the user presses the right key

the animation slows down. First let's get rid of the old way we detect the fire

key so that it makes use of the new code we just added. So delete or

comment the following lines from the run() method, from inside the while

loop:

       /*       if ((iKey & GameCanvas.FIRE_PRESSED) != 0){           isRunning = false;       }       */

Next, insert the following code under the line where we store the current

time in lCurrTick so we can pass that value to the checkKeys() method:

       lCurrTick = System.currentTimeMillis();             checkKeys(iKey, lCurrTick);             if (isDown[leftKey]){              if (lDelay > 0){                  lDelay-=10;              }          } else if (isDown[rightKey]){              if (lDelay < 1000){                  lDelay+=10;              }          } else if (isDown[fireKey]){              isRunning = false;             }

Page 51: netbean

Add this next code just before the second call to setClip() so we can display

some feedback when the keys are pressed:

       g.drawString("lDelay : " + Long.toString(lDelay), 2, 108, Graphics.TOP | Graphics.LEFT);       if (isDown[upKey]) {         g.drawString("up key pressed", 2, 128, Graphics.TOP | Graphics.LEFT);       } else if (isDown[leftKey]) {         g.drawString("left key pressed", 2, 128, Graphics.TOP | Graphics.LEFT);       } else if (isDown[downKey]) {         g.drawString("down key pressed", 2, 128, Graphics.TOP | Graphics.LEFT);       } else if (isDown[rightKey]) {         g.drawString("right key pressed", 2, 128, Graphics.TOP | Graphics.LEFT);              }

       //clip the drawing area to a single frame       g.setClip(50, 50, 16, 16);

The last code we added displays the value of lDelay so we know if it's

actually getting changed. It also displays notification whenever one of the

directional keys are pressed. It doesn't display feedback for the Fire key

because when that is pressed the MIDlet is terminated.

Here's the completed clsCanvas source code:

package MyGame;

import javax.microedition.lcdui.Graphics;import javax.microedition.lcdui.Image;import javax.microedition.lcdui.game.GameCanvas;

public class clsCanvas extends GameCanvas implements Runnable {

Page 52: netbean

// key repeat rate in millisecondspublic static final int keyDelay = 250;  

//key constantspublic static final int upKey = 0;public static final int leftKey = 1;public static final int downKey = 2;public static final int rightKey = 3;public static final int fireKey = 4;

//key states for up, left, down, right, and fire keyprivate boolean[] isDown = {   false, false, false, false, false};

//last time the key changed stateprivate long[] keyTick = {   0, 0, 0, 0, 0};

//lookup table for key constants :Pprivate int[] keyValue = {   GameCanvas.UP_PRESSED, GameCanvas.LEFT_PRESSED,   GameCanvas.DOWN_PRESSED, GameCanvas.RIGHT_PRESSED,   GameCanvas.FIRE_PRESSED};

private boolean isRunning = true;   private Graphics g;private midMain fParent;private Image imgEarth;

   public clsCanvas(midMain m) {       super(true);       fParent = m;       setFullScreenMode(true);   }     public void start(){       Thread runner = new Thread(this);       runner.start();   }     public void load(){       try{           // try to load the image file           imgEarth =

Page 53: netbean

Image.createImage("/images/earthstrip.png");       }catch(Exception ex){           // exit the app if it fails to load the image           isRunning = false;           return;       }   }     public void unload(){       // make sure the object get's destroyed       imgEarth = null;   }     public void checkKeys(int iKey, long currTick){       long elapsedTick = 0;

       //loop through the keys       for (int i = 0; i < 5; i++){

           // by default, key not pressed by user           isDown[i] = false;

           // is user pressing the key           if ((iKey & keyValue[i]) != 0){               elapsedTick = currTick - keyTick[i];

               //is it time to toggle key state?               if (elapsedTick >= keyDelay){

                   // save the current time                   keyTick[i] = currTick;

                   // toggle the state to down or pressed                   isDown[i] = true;               }           }       }   }

   public void run() {      int iKey = 0;

      int imgX = 50; // x coordinate of the image      int frameIndex = 0; // current frame to be drawn      long lDelay = 250; //time to pause between frames in milliseconds      long lStart = 0; //time we last changed frames in

Page 54: netbean

milliseconds      long lCurrTick = 0; // current system time in milliseconds;           load();      g = getGraphics();      while(isRunning){                   iKey = getKeyStates();          /*          if ((iKey & GameCanvas.FIRE_PRESSED) != 0){              isRunning = false;          }          */          lCurrTick = System.currentTimeMillis();                   checkKeys(iKey, lCurrTick);                   if (isDown[leftKey]){              if (lDelay > 0){                  lDelay-=10;              }          } else if (isDown[rightKey]){              if (lDelay < 1000){                  lDelay+=10;              }          } else if (isDown[fireKey]){              isRunning = false;             }                   if ((lCurrTick-lStart) >= lDelay){              lStart = lCurrTick; // save the current time              if (frameIndex < 8) {                  frameIndex++; // skip to the next frame              } else {                  frameIndex = 0; // go back to first frame              }              imgX = 50 - (frameIndex * 16); // compute x relative to clip rect          }                   //restore the clipping rectangle to full screen          g.setClip(0, 0, getWidth(), getHeight());                   //set drawing color to black          g.setColor(0x000000);          //fill the whole screen          g.fillRect(0, 0, getWidth(), getHeight());

Page 55: netbean

          // set drawing color to white          g.setColor(0xffffff);          //display the key code last pressed          g.drawString(Integer.toString(iKey), 2, 2, Graphics.TOP | Graphics.LEFT);

          g.drawString("Frame : " + Integer.toString(frameIndex), 2, 68, Graphics.TOP | Graphics.LEFT);          g.drawString("X : " + Integer.toString(imgX), 2, 88, Graphics.TOP | Graphics.LEFT);                  g.drawString("lDelay : " + Long.toString(lDelay), 2, 108, Graphics.TOP | Graphics.LEFT);          if (isDown[upKey]) {            g.drawString("up key pressed", 2, 128, Graphics.TOP | Graphics.LEFT);          } else if (isDown[leftKey]) {            g.drawString("left key pressed", 2, 128, Graphics.TOP | Graphics.LEFT);          } else if (isDown[downKey]) {            g.drawString("down key pressed", 2, 128, Graphics.TOP | Graphics.LEFT);          } else if (isDown[rightKey]) {            g.drawString("right key pressed", 2, 128, Graphics.TOP | Graphics.LEFT);                       }                   //clip the drawing area to a single frame          g.setClip(50, 50, 16, 16);          //draw the image                    g.drawImage(imgEarth, imgX, 50, Graphics.TOP | Graphics.LEFT);                   flushGraphics();                   try{              Thread.sleep(30);          } catch (Exception ex){                       }      }      g = null;      unload();      fParent.destroyApp(false);      fParent = null;

Page 56: netbean

   }}

Great! Now you can hit F6 and view the result of your work.

Ponts to Ponder

There are a few more things you can do to the sample code presented in this

tutorial. For instance, you can add support for the other game keys or move

the checkKeys() method in it's own class along with the supporting

variables and even make it a static method. You can also change the value

of keyDelay to that which suits you. Finally, with a few modification you can

use the same technique when handling key codes from keyPressed() and

keyReleased() call back methods.

Although the sample code used here is quite usable, it doesn't mean you

Page 57: netbean

can't do it in your own way. What matters most is that you have a general

understanding of the problem and how to solve it in a simple but effective

manner.

Something is not working? I got it totally wrong? You got a question? Feel

free to post a comment. XD

Adding an Icon to Your MIDlet or Game

I was looking at my blog's search statistics at Google Webmaster Tools

when I found a few users using key words like "j2me icon MIDlet", "MIDP

icon bit depth", "MIDlet icon transparency". So I decided to write this

tutorial just for those people...how ever late it might seem to be.

If you we're following the series of tutorials presented here, you would recall

(or not) that in first part of the tutorial Creating a Basic MIDP 2.0 Game

Template you were prompted for an icon to associate with your MIDlet.

Page 58: netbean

We chose to ignore the field because it seemed trivial at the time. But I bear

great news to you! Because in NetBeans you can still add an icon to your

MIDlet through the Project Properties window!!! Oh, so you knew that

already. Well, for those of you who don't, you can access the Project

Properties from the File Menu and choosing the menu item labeled with

"Your-Project-Name" Properties, with the double quotes.

You can also reach the Project Properties by right-clicking on the project

name from the Projects Panel treeview and selecting Properties at the

bottom of the context menu.

Page 59: netbean

Before you go adding those uber cool icons one thing you have to remember

is that size does matter when it comes to MIDlet icons. Meaning the width

and height of the icon. If you also read the tutorial Loading Images Into

Your Game, it was mentioned that the number of colors used should be

kept at a minimum, preferably at the 8-bit depth of 256 colors max. You

should also avoid anti-aliased icons because some phones don't support

them and will display the anti-aliased icon with "dirty" edges or with random

pixels around the edges that are supposed to be semi-transparent. The PNG

format is also the preferred and widely used file format for a MIDlet icon.

Here are some icon sizes you may consider:

Phone/Model

Resolution128x128

128x160

176x208

208x176

208x208

240x320

320x240

352x416

416x352

BlackBerry

45x45

Motorola

15x1516x1632x32

Page 60: netbean

Nokia S40

16x16 18x1824x2429x29

n/a n/a 46x46 46x4842x29

n/a n/a n/a

Nokia S601st/2nd Edition

n/a n/a 42x2929x29

n/a n/a n/a n/a 76x76 n/a

Nokia S603rd Edition

n/a n/a 31x3142x29

37x37 37x37 53x5355x5564x64

52x5254x54

64x64 76x7684x58

Sagem 18x18Samsung

16x1629x2932x32

Sanyo 24x24Sharp 27x27Siemens

18x18

Sony Ericsson

16x1632x32

Note that most of the icon sizes for the Nokia mobile phones came from

the Using Icons in Midlets technical note at Forum Nokia. Some of the

additional sizes came from personally testing them on various phone models.

The icon sizes for the other brands of mobile phones came from other

developers experiences.

Mobile phones behave differently from one another on the way they try to

display an incorrectly sized icon. Some phones will scale your icons to the

right size. Other phones will display the icon as a small glyph at the upper-

left corner of the area where the full sized icon should appear. The glyph

displayed is so small you can barely see it. If that's the case, then you can

try the next larger icon size. Some phones will also clip the icon to fit the

area reserved for it like in the case of using 42x29 pixel icons on older Series

60 phones with a resolution of 176x208.

There are several ways to find out what icon size you can use. One sure way

to find the size that fits your target phone is by extracting the contents of the

Page 61: netbean

jar file of a game or application designed for that phone. Somehow that

didn't quite come out right...

If your phone supports themes or allows you to customize the skin, you can

usually download a theme editor or supplemental documents for creating

themes/skin for your phone and from there you can gather the information

you need.

Here are some tools you can use to make the PNG images:

← GIMP - Link to Site

← Paint.Net - Link to Site

← Adobe Fireworks - Link to Site

← Adobe Photoshop - Link to Site

← You can find a lot more tools you can use on the internet.

I won't go into details as to how to use those applications as such is beyond

the scope of this tutorial. I'll just give you a screenshot of the settings I use

in Fireworks. It should provide some insight on the settings you can use in

other applications.

These are the settings I use on icons with a transparent background. Notice

that there's only one transparent color in the pallet:

Page 62: netbean

These are the settings I use on icons with no transparency:

I have prepared a transparent icon for you to use in this tutorial. You can use

your own work of art if you want to.

42x29 pixels

Firefox users can right-click on the image and choose Save Image As

from the context menu while IE users can choose Save Picture As from the

context menu. Save the PNG file inside the images folder inside the projects

src. If you can't find the images folder then make one.

You can always just place the file inside the src folder and it will be listed

under the Default package. But then again I find it easier to work on the

game when all the images are kept inside a folder of their own.

Open the Project Properties in NetBeans and navigate your way to the

MIDlets section which is listed under Application Descriptor. You will find

your MIDlets listed here. Double-click on the MIDlet or select the MIDlet

and click on the Edit button to the right of the list.

Page 63: netbean

By now, NetBeans would have detected the new image you have saved

earlier and you can simply choose it from the images listed in the combo box

of the Edit MIDlet dialog box.

Click the "Ok" button on the Edit MIDlet dialog to confirm your selection

then click the "Ok" button again in the Project Properties window to

commit your changes. Press F6 to test your MIDlet in the emulator.

Page 64: netbean

When you run the emulator you will see the MIDlet brandishing your new

icon. Hold on a second?!! Why does our 42x29 icon look squashed and

squarish? That's only how the emulator displays your icon. You will only find

out what it actually looks like once you have tested your MIDlet on a real

phone.

I hope this tutorial have at least given you a clue of how to find your way

through the murky world of MIDlet icons.

Some humble words from Forrest Grumpy:

My momma always said, "MIDlet Icons was like a box of chocolates.

You never know what you're gonna get."

No Commands: Delicious Graphical Menus

Aesthetics plays an important part in the games that you make. Making the

main menu look like it fits the game is equally as important. If done right, it

can set the mood for the player even before actually playing the game.

Let's face it. Using Command Listeners and objects for your menus just

doesn't feel right. Command objects just don't look like they're part of the

game. On some phones, the game is hidden when the menu is activated.

Phones don't even display the label for the keys you have to press to

activate the menu when the game is in fullscreen mode. Graphical menus

solves these problems and makes the game look cool too.

Clipping is used extensively in this tutorial. If you need a quick study, turn to

this tutorial : Clipping Images or Displaying Only Parts of an Image.

Loading the Images

In this tutorial we will make a simple vertical menu in the middle of the

canvas. The MIDlet will be designed for mobile phones with 176x208 screen

resolution.

Page 65: netbean

I have prepared a clean project for you to start with. It includes the basic

game template and the input handling code from the tutorial Input

Handling : Keypress with Repeat Rate. It also includes the images we're

going to use for the menu. Download the one for the version of NetBeans you

will use:

← BasicGameTemplate4NB5.zip (39.04 KB) - project code for

NetBeans 5.5

← BasicGameTemplate4NB6.zip (45.03 KB) - project code for

NetBeans 6.0

When you open the project in NetBeans, you should see these 2 files inside

the images folder:

logo.png - 176x208 pixels

Page 66: netbean

menuitems.png - 82x80 pixels

First we'll have to load the images. So open the clsCanvas code and add

these variable declarations above the constructor:

private midMain fParent;

private Image imgBG;private Image imgMenu;

 public clsCanvas(midMain m) {

Add the actual image loading code in the load() method. Make sure they are

inside the try..catch or exception handling code:

 public void load(){     try{         // load the images here         imgBG = Image.createImage("/images/logo.png");         imgMenu = Image.createImage("/images/menuitems.png");

Page 67: netbean

              }catch(Exception ex){

Add these lines inside the unload() method to make sure the resources

reserved for those objects will get freed:

 public void unload(){     // make sure the object get's destroyed     imgMenu = null;     imgBG = null; }

The logo can now replace the call to fillRect() that we used to clear the

screen since the logo takes up the whole screen. Remove or comment the

lines from the main loop as indicated below:

        //restore the clipping rectangle to full screen        g.setClip(0, 0, getWidth(), getHeight());                /* start - delete lines        //set drawing color to black        g.setColor(0x000000);        //fill the whole screen        g.fillRect(0, 0, getWidth(), getHeight());        */ end - delete lines                // set drawing color to white        g.setColor(0xffffff);

Now we can draw the logo. Insert this code under the call to setClip() inside

the main loop :

        //restore the clipping rectangle to full screen        g.setClip(0, 0, getWidth(), getHeight());

        g.drawImage(imgBG, 0, 0, Graphics.TOP | Graphics.LEFT);

Page 68: netbean

Drawing the Menu

We'll need a variable to store the currently focused menu item. Let's call it

menuIndex and declare it under the other variables we declared earlier:

private Image imgMenu;

private int menuIndex = 0;

 public clsCanvas(midMain m) {

The drawing code for the menu will be placed on a new method name

drawMenu(). Add this code above the run() method:

 public void drawMenu(Graphics g){     int cy = 0;     for (int i = 0; i < 5; i++){         //compute the Y position of the menu item         cy = 64 + (i * 22);         //set the clipping rectangle to where the item will be drawn         g.setClip(47, cy, 82, 20);         if (menuIndex == i){           //draw the light button if the item is focused           g.drawImage(imgMenu, 47, cy - 20, Graphics.TOP | Graphics.LEFT);         } else {           //draw the dark button if the item is not focused           g.drawImage(imgMenu, 47, cy, Graphics.TOP | Graphics.LEFT);         }         //offset of the label is 6 pixels from the top of the button         cy += 6;         //set the clipping rectangle to where the label will be drawn         g.setClip(47, cy, 82, 8);

Page 69: netbean

         //draw the label so that it is inside the clipping rectangle         g.drawImage(imgMenu, 47, cy - (40 + (i * 8)), Graphics.TOP | Graphics.LEFT);     } }

 public void run() {

The menu will be drawn 64 pixels from the top of the screen and the menu

items are drawn at 22 pixel intervals and since each of the menu items is

only 20 pixels in height, a 2 pixel space will be left between the menu

items acting as a margin. Depending on the value of menuIndex, either a

light-blue or a dark-blue button will be drawn. Finally, the label of each menu

item is drawn 6 pixels lower than the menu items position.

To show the menu on the screen, simply insert the following lines of code at

the point after which the logo has been drawn:

        //restore the clipping rectangle to full screen        g.setClip(0, 0, getWidth(), getHeight());        //draw the logo        g.drawImage(imgBG, 0, 0, Graphics.TOP | Graphics.LEFT);               //draw the menu        drawMenu(g);        //restore the clipping rectangle to full screen again        g.setClip(0, 0, getWidth(), getHeight());               // set drawing color to white

Notice that we now have 2 calls to setClip() where the clipping rectangle is

reset to fullscreen. The second call is necessary because when the

drawMenu() is finished, the method would have resized and positioned the

clipping rectangle to that of the last label in the menu. Reseting the clipping

rectangle after drawMenu() is called allows us to draw more stuff in other

Page 70: netbean

parts of the screen.

The only thing left to do now is to make the menu respond to keypresses.

Modify the conditional statement under the call to the checkKeys() method

inside the main loop like so:

        checkKeys(iKey, lCurrTick);

        if (isDown[upKey]){            //move focus up            if (menuIndex > 0){                menuIndex--;            } else {                menuIndex = 4;            }        } else if (isDown[downKey]){            //move focus down            if (menuIndex < 4){                menuIndex++;            } else {                menuIndex = 0;            }        } else if (isDown[fireKey]){            //do action depending on the menu item selected            if (menuIndex == 4){                isRunning = false;            }        }

The last code allows us to move the highlight or focus from one menu item to

the other. Pressing the UP key should now move the focus one menu item

higher in the list. Pressing the DOWN key should move the focus one menu

item lower in the list. Pressing the FIRE key should select the focused menu

item and initiate an "action". So far, there is only one "action" defined in this

menu and that is for exiting the MIDlet from the EXIT menu item.

Here's the completed clsCanvas code so you can check if you missed

something:

Page 71: netbean

package MyGame;

import javax.microedition.lcdui.Graphics;import javax.microedition.lcdui.Image;import javax.microedition.lcdui.game.GameCanvas;

public class clsCanvas extends GameCanvas implements Runnable {// key repeat rate in millisecondspublic static final int keyDelay = 250;

//key constantspublic static final int upKey = 0;public static final int leftKey = 1;public static final int downKey = 2;public static final int rightKey = 3;public static final int fireKey = 4;

//key states for up, left, down, right, and fire keyprivate boolean[] isDown = { false, false, false, false, false};//last time the key changed stateprivate long[] keyTick = { 0, 0, 0, 0, 0};//lookup table for key constants :Pprivate int[] keyValue = { GameCanvas.UP_PRESSED, GameCanvas.LEFT_PRESSED, GameCanvas.DOWN_PRESSED, GameCanvas.RIGHT_PRESSED, GameCanvas.FIRE_PRESSED};

private boolean isRunning = true; private Graphics g;private midMain fParent;

private Image imgBG;private Image imgMenu;//stores the focused menu itemprivate int menuIndex = 0;

 public clsCanvas(midMain m) {     super(true);     fParent = m;     setFullScreenMode(true);

Page 72: netbean

 }

 public void start(){     Thread runner = new Thread(this);     runner.start(); }

 public void load(){     try{           // load the images here         imgBG = Image.createImage("/images/logo.png");         imgMenu = Image.createImage("/images/menuitems.png");             }catch(Exception ex){         // exit the app if it fails to load the image         isRunning = false;         return;     } }

 public void unload(){     // make sure the object get's destroyed     imgMenu = null;     imgBG = null; }

 public void checkKeys(int iKey, long currTick){     long elapsedTick = 0;     //loop through the keys     for (int i = 0; i < 5; i++){         // by default, key not pressed by user         isDown[i] = false;         // is user pressing the key         if ((iKey & keyValue[i]) != 0){             elapsedTick = currTick - keyTick[i];             //is it time to toggle key state?             if (elapsedTick >= keyDelay){                 // save the current time                 keyTick[i] = currTick;                 // toggle the state to down or pressed                 isDown[i] = true;             }         }     } }

 public void drawMenu(Graphics g){

Page 73: netbean

     int cy = 0;     for (int i = 0; i < 5; i++){         //compute the Y position of the menu item         cy = 64 + (i * 22);         //set the clipping rectangle to where the item will be drawn         g.setClip(47, cy, 82, 20);         if (menuIndex == i){           //draw the light button if the item is selected           g.drawImage(imgMenu, 47, cy - 20, Graphics.TOP | Graphics.LEFT);         } else {           //draw the dark button if the item is not selected           g.drawImage(imgMenu, 47, cy, Graphics.TOP | Graphics.LEFT);         }         //offset of the label is 6 pixels from the top of the button         cy += 6;         //set the clipping rectangle to where the label will be drawn         g.setClip(47, cy, 82, 8);         //draw the label so that it is inside the clipping rectangle         g.drawImage(imgMenu, 47, cy - (40 + (i * 8)), Graphics.TOP | Graphics.LEFT);     } }

 public void run() {    int iKey = 0;    long lCurrTick = 0; // current system time in milliseconds;     load();    g = getGraphics();    while(isRunning){             lCurrTick = System.currentTimeMillis();        iKey = getKeyStates();             checkKeys(iKey, lCurrTick);               if (isDown[upKey]){            //move focus up            if (menuIndex > 0){                menuIndex--;            } else {

Page 74: netbean

                menuIndex = 4;            }        } else if (isDown[downKey]){            //move focus down            if (menuIndex < 4){                menuIndex++;            } else {                menuIndex = 0;            }        } else if (isDown[fireKey]){            //do action depending on the menu item selected            if (menuIndex == 4){                isRunning = false;            }        }               //restore the clipping rectangle to full screen        g.setClip(0, 0, getWidth(), getHeight());

        //draw the logo        g.drawImage(imgBG, 0, 0, Graphics.TOP | Graphics.LEFT);             //draw the menu        drawMenu(g);        //restore the clipping rectangle to full screen again        g.setClip(0, 0, getWidth(), getHeight());               // set drawing color to white        g.setColor(0xffffff);        //display the key code last pressed        g.drawString(Integer.toString(iKey), 2, 2, Graphics.TOP | Graphics.LEFT);             flushGraphics();             try{            Thread.sleep(30);        } catch (Exception ex){                 }    }    g = null;    unload();    fParent.destroyApp(false);    fParent = null; }}

Page 75: netbean

Press the F6 key in NetBeans to see if it actually works.

Here's a link to a low-res flash video of the MIDlet running on an N70:

Click to View Video

The technique used here to draw the vertical menu is pretty old school. I've

been using it in DOS based games written in Turbo Pascal, DirectX games

written in VisualBasic 6.0, and in C# using XNA Game Studio Express. One

thing to remember is that you're not just limited to using vertical menus in

your game. I just used a vertical menu as an example because they are very

user friendly and easy to make. You can change the shape of the menu items

to anything you want, knowing that you can use transparent images or

change the colors to match your game. Here's a screenshot of a character

selection screen for a demo quiz game:

Page 76: netbean

Character sprites came from the MMORPG Trickster.

Using the graphics methods to make the menus for your game let's you use

your creativity and the design is only limited by your imagination. Just

remember that however unlimited your imagination may seem to be, the

phone on the other hand, has limited capabilities.

~John Constantime:

"There's always a catch...damn cellphones!"

Wednesday, November 7, 2007

Using Custom Fonts or Bitmap Fonts in MIDP 2.0 Part I

Jump to part: 1 | 2

Have you ever wondered how those other games display scores and text

with different styles and colors? You probably found out you can't do that

with drawString() and setFont() alone. The truth is that those numbers

and letters were drawn using images or what we call Bitmap Fonts.

Page 77: netbean

Ok, so that's really old news. But I'll have you know that about a decade and

a half ago, it was the only decent way to draw numbers and text on your

game screen. I think that makes it sound even much older...moving on...

Bitmap fonts also solves some portability issues in your game as different

phones sometimes have different font sizes and can really screw up your

design if your relying only on the drawString() method. Using bitmap fonts

ensure that those scores and text will look exactly the same on your target

phones.

In this tutorial you will be shown how to use bitmap fonts with a little help

from the setClip() method and type casting. If you need a jump starter on

clipping, you can follow the tutorial on Clipping Images or Displaying

Only Parts of an Image before you continue with this tutorial.

We'll be using the new project templates which you can find here:

Clean MIDP 2.0 Game Templates

The ASCII Table

Like I mentioned earlier, images are used to draw those numbers and letters.

Actually, it's an image strip where each frame contains one character. Here's

an example of a bitmap font image:

Bitmap font sample zoomed in 2x.

Frame size: 9x10 pixels.

Click on the image to see what it actually looks like.

Since it's an image, you have a higher degree of freedom when it comes to

customizing the way the font looks. Just remember that you can cause

someones eyes to bleed and ultimately delete your game from their phones

if you choose a repulsive set of colors and a barely readable font size. What

Page 78: netbean

am I saying?!!

The characters on the bitmap font image are arranged based on the

standard ASCII table :

This makes it easy for us to determine the location of a character on the

bitmap font image using this formula:

positionX = ((int)theCharacter) * frameWidth;

Type casting a char data type to an int gives you the ordinal value of the

Page 79: netbean

character, meaning it's Decimal value in the ASCII table.

You may have noticed that there are black and gray rectangles at the

beginning of the image. I placed them there as markers for non-printable

characters. Because the first 32 characters in the ASCII table are non-

printable, meaning they can't be drawn on the screen. Even the last

character in the table, the Delete character, cannot be drawn. So only the

characters whose Decimal value is in-between 31 and 127 can be drawn.

Creating the Bimap Font Writer : the clsFont Class

You can now open the project templates you just downloaded in NetBeans.

We will start by making a new class called "clsFont". A demo in how to

create a new class can be found in part II of the tutorial Basic MIDP 2.0

Game Template : Creating the GameCanvas. When your done, you

should now see something like this:

package MyGame;

public class clsFont {        /** Creates a new instance of clsFont */    public clsFont() {    }    }

Next, we'll declare some global variables under the class declaration:

public class clsFont {

    // additional space between characters    public int charS = 0;        // max clipping area

Page 80: netbean

    public int screenW = 176;    public int screenH = 208;        // flag: set to true to use the Graphics.drawString() method    // this is just used as a fail-safe    public boolean useDefault = false;        // height of characters    public int charH = 10;        // lookup table for character widths    public int[] charW = {    // first 32 characters        9, 9, 9, 9, 9, 9, 9, 9, 9, 9,     9, 9, 9, 9, 9, 9, 9, 9, 9, 9,     9, 9, 9, 9, 9, 9, 9, 9, 9, 9,     9, 9,    // space    9,    // everything else :P    3, 5, 8, 8, 7, 8, 3, 5, 5, 6,     7, 3, 7, 3, 9, 6, 4, 6, 6, 6,     6, 6, 6, 6, 6, 3, 3, 6, 6, 6,     6, 9, 6, 6, 6, 6, 6, 6, 6, 6,     3, 6, 6, 6, 9, 6, 6, 6, 6, 6,     6, 7, 6, 6, 9, 6, 6, 6, 5, 9,     5, 4, 6, 4, 6, 6, 6, 6, 6, 6,     6, 6, 3, 4, 6, 3, 9, 6, 6, 6,     6, 6, 6, 6, 6, 6, 9, 6, 6, 6,     5, 3, 5, 4,    // delete character    9};        // the bitmap font image    public Image imgFont;

Press ALT+Shift+F or choose "Fix Imports" from the "Source" menu so

NetBeans can automagically add those missing import statements.

The variable charS is used to define additional space between characters.

You can change it if you feel that the characters are too close together and

hard to read. It can also be useful if you want to do a spring effect animation

and make the characters bounce sideways.

Page 81: netbean

The screenW and screenH defines the maximum screen area we can draw

on. They are used to make sure that the clipping rectangle always falls in the

area defined. You will know more about that later.

Next we have the useDefault variable. This is purely optional and we'll use

that to signal if the image file failed to load. More on this later too.

The charH variable stores the maximum height of the characters and is used

to adjust the height of the clipping rectangle.

The integer array charW[] holds the pre-calculated widths of each

character. We use this to calculate the exact place where each character in a

given string should be drawn and how much space each of them should

occupy. This way, the string displayed will look more natural. It also helps

save some screen space since the space used by each character is adjusted

to only how much space is needed. Unlike monoblock style fonts where each

character uses the same width even if the characters themselves only take

up half as much space. One thing to note about the values stored in

charW[] is that they already include adequate character spacing. But you

can still use the charS variable to adjust the spacing if necessary.

The last variable imgFont will hold the actual bitmap font image.

We will now add the load() method for loading the image and the unload()

method for cleaning up when we're done using the class. Add these lines

after the class constructor:

    /** Creates a new instance of clsFont */    public clsFont() {    }       public boolean load(String imagePath){        useDefault = false;        try{            // load the bitmap font            if (imgFont != null){

Page 82: netbean

                imgFont = null;            }            imgFont = Image.createImage(imagePath);        } catch (Exception ex){            // oohh we got an error then use the fail-safe            useDefault = true;        }        return (!useDefault);    }        public void unload(){        // make sure the object get's destroyed        imgFont = null;    }

The load() method takes the string parameter imagePath which defines

the path to the bitmap font image that we want to load. If it fails to do so, the

useDefault variable will be set to true. You can check this variable in your

game so you know if the bitmap font got loaded or not and act accordingly.

The load() method also returns the inverse value of useDefault so you can

use the method in your condition statement to load the bitmap font and at

the same time check if it was loaded as in the example below:

    if (!myFont.load("/images/fonts.png")){       /*           ...do something to handle the error           when the image fails to load...           ...            ...       */    }

Next we will add the drawChar() method which will be used to draw a single

character on the screen. Add it beneath the unload() method:

Page 83: netbean

    public void drawChar(Graphics g, int cIndex, int x, int y, int w, int h){         // non printable characters don't need to be drawn        if (cIndex < 33){            return;        }

        // neither does the delete character         if (cIndex > 126){            return;        }

        // get the characters position        int cx = cIndex * 9;

        // reset the clipping rectangle        g.setClip(0, 0, screenW, screenH);

        // resize and reposition the clipping rectangle        // to where the character must be drawn        g.clipRect(x, y, w, h);

        // draw the character inside the clipping rectangle        g.drawImage(imgFont, x - cx, y, Graphics.TOP | Graphics.LEFT);    }

The drawChar() method takes a few parameters:

← Graphics g - the graphics object used to draw the image

← int cIndex - the ordinal value of the character to be drawn

← int x - the horizontal position of the character and clipping rectangle

on the screen

← int y - the vertical position of the character and clipping rectangle on

the screen

← int w - the width of the character and clipping rectangle

← int h - the height of the character and clipping rectangle

Page 84: netbean

The drawChar() method checks to see if the character supposed to be

drawn is a non-printable character by checking the value of cIndex. It then

computes the position of the character on the image and stores it in for later

use. The method resets the clipping rectangle to fullscreen using the

setClip() method and adjusts the clipping rectangle to exactly where the

character should be drawn and matches it's size by using the clipRect()

method. It then draws the character on the screen and inside the clipping

rectangle.

Using setClip() in conjunction with clipRect() is a good way of ensuring that

the resulting clipping rectangle is within the phones screen boundaries so

that the drawing methods will not draw anything outside the screen. Some

phones behave erratically when you try draw stuff outside the screen and

produce garbled output.

Let's add the final method for the clsFont class, the drawString() method.

This is the method we will call in our game to draw the strings, scores, and

other text. Add it under the drawChar() method:

    public void drawString(Graphics g, String sTxt, int x, int y){

        // get the strings length        int len = sTxt.length();

        // set the starting position        int cx = x;                // if nothing to draw return        if (len == 0) {            return;        }                // our fail-safe        if (useDefault){            g.drawString(sTxt, x, y, Graphics.TOP | Graphics.LEFT);            return;        }

Page 85: netbean

        // loop through all the characters in the string              for (int i = 0; i < len; i++){

           // get current character            char c = sTxt.charAt(i);

           // get ordinal value or ASCII equivalent           int cIndex = (int)c;

           // lookup the width of the character           int w = charW[cIndex];

           // draw the character           drawChar(g, cIndex, cx, y, w, charH);

           // go to the next drawing position           cx += (w + charS);        }    }

The drawString() method parameters:

← Graphics g - the Graphics object that will be used to draw the string

← String sTxt - the string to be drawn

← int x - the horizontal starting position of the string on the screen

← int y - the vertical position of the string on the screen

The drawString() method first gets the length of the string and the X

position where the first character will be drawn. It then checks the length of

the string to see if the string is not empty, otherwise it exits the method.

Now you will get to see what the useDefault is used for. If it's set to true,

meaning the bitmap font image wasn't loaded, the regular drawString()

method of the Graphics object is used to draw the string instead of using

the bitmap fonts.

Page 86: netbean

The drawString() method loops through each character of the string and

gets the ordinal value of each character. The ordinal values are used to get

each of the characters widths from the lookup table charW[]. It is also

passed to the drawChar() method to define the width of the clipping

rectangle. After the current character has been drawn, the width is added to

the current drawing position and the method proceeds with the next

character. If you put a value other than 0 in charS, it will also be added to

the current drawing position.

Here's the completed clsFont source code so you can check your work:

package MyGame;

import javax.microedition.lcdui.Graphics;import javax.microedition.lcdui.Image;

public class clsFont {    // additional space between characters    public int charS = 0;        // max clipping area    public int screenW = 176;    public int screenH = 208;        // flag: set to true to use the Graphics.drawString() method    // this is just used as a fail-safe    public boolean useDefault = false;        // height of characters    public int charH = 10;        // lookup table for character widths    public int[] charW = {    // first 32 characters        9, 9, 9, 9, 9, 9, 9, 9, 9, 9,     9, 9, 9, 9, 9, 9, 9, 9, 9, 9,     9, 9, 9, 9, 9, 9, 9, 9, 9, 9,     9, 9,    // space    9,    // everything else XD    3, 5, 8, 8, 7, 8, 3, 5, 5, 6,     7, 3, 7, 3, 9, 6, 4, 6, 6, 6,

Page 87: netbean

    6, 6, 6, 6, 6, 3, 3, 6, 6, 6,     6, 9, 6, 6, 6, 6, 6, 6, 6, 6,     3, 6, 6, 6, 9, 6, 6, 6, 6, 6,     6, 7, 6, 6, 9, 6, 6, 6, 5, 9,     5, 4, 6, 4, 6, 6, 6, 6, 6, 6,     6, 6, 3, 4, 6, 3, 9, 6, 6, 6,     6, 6, 6, 6, 6, 6, 9, 6, 6, 6,     5, 3, 5, 4,    // delete    9};        // the bitmap font image    private Image imgFont;        /** Creates a new instance of clsFont */    public clsFont() {    }        public boolean load(String imagePath){        useDefault = false;        try{            // load the bitmap font            if (imgFont != null){                imgFont = null;            }            imgFont = Image.createImage(imagePath);        } catch (Exception ex){            // oohh we got an error then use the fail-safe            useDefault = true;        }        return (!useDefault);    }        public void unload(){        // make sure the object get's destroyed        imgFont = null;    }        public void drawChar(Graphics g, int cIndex, int x, int y, int w, int h){         // non printable characters don't need to be drawn        if (cIndex < 33){            return;        }

        // neither does the delete character         if (cIndex > 126){

Page 88: netbean

            return;        }

        // get the characters position        int cx = cIndex * 9;

        // reset the clipping rectangle        g.setClip(0, 0, screenW, screenH);

        // resize and reposition the clipping rectangle        // to where the character must be drawn        g.clipRect(x, y, w, h);

        // draw the character inside the clipping rectangle        g.drawImage(imgFont, x - cx, y, Graphics.TOP | Graphics.LEFT);    }        public void drawString(Graphics g, String sTxt, int x, int y){        // get the strings length        int len = sTxt.length();

        // set the starting position        int cx = x;                // if nothing to draw return        if (len == 0) {            return;        }                // our fail-safe        if (useDefault){            g.drawString(sTxt, x, y, Graphics.TOP | Graphics.LEFT);            return;        }

        // loop through all the characters in the string              for (int i = 0; i < len; i++){

           // get current character            char c = sTxt.charAt(i);

           // get ordinal value or ASCII equivalent           int cIndex = (int)c;

Page 89: netbean

           // lookup the width of the character           int w = charW[cIndex];

           // draw the character           drawChar(g, cIndex, cx, y, w, charH);

           // go to the next drawing position           cx += (w + charS);        }    }    }

Press Shift+F11 or choose "Clean and Build Main Project" from the

"Build" menu to test-build your project and to see if anything is missing. You

can also choose to click on the toolbar icon with the broom glyph to clean

and build your project. If everything went well, we can move on to the next

step: Word-Wrap and More.

Using Custom Fonts or Bitmap Fonts Part II

Jump to part: 1 | 2

On the previous part of this tutorial, we created a clsFont class for drawing

bitmap fonts. Now we will use the class to see if it actualy works. So if you

haven't read the first part of this tutorial just click on this link: Using

Custom Fonts or Bitmap Fonts Part I. I have also added more methods

to the clsFont class that you may find useful so stick around a bit.

Using the Bitmap Font Writer

Open the clsCanvas code so we can begin. We will start by adding a new

global variable to hold an instance of the clsFont class. Let's call it

"myFont" and add it under the other variable declarations like so:

Page 90: netbean

private midMain fParent;

private clsFont myFont;

    /** Creates a new instance of clsCanvas */    public clsCanvas(midMain m) {

If you expand the images folder on the Projects panel, you should see the

image file "fonts.png" listed there. This is the bitmap font we're going to

pass to the clsFont.load() method. Add these lines in the end of the

clsCanvas.load():

        }               myFont = new clsFont();        myFont.load("/images/fonts.png");

    }

We will also call the clsFont.unload() method inside the

clsCanvas.unload() method like so:

    public void unload(){        // make sure the object get's destroyed

        myFont.unload();        myFont = null;

    }

Lastly, we will use the clsFont.drawString() method to draw some text on

Page 91: netbean

the screen. Add these lines above the call to flushGraphics() in the main

loop:

           g.fillRect(0, 0, screenW, screenH);                     myFont.drawString(g, "Hello Neo...", 10, 50);           myFont.drawString(g, "1234567890", 10, 70);           myFont.drawString(g, "ABCDEFG abcdefg", 10, 90);                     flushGraphics();

Test your MIDlet by pressing F6 on your keyboard and you should see

something like this:

I almost forgot to post the completed clsCanvas source code:

package MyGame;

import javax.microedition.lcdui.Graphics;import javax.microedition.lcdui.Image;import javax.microedition.lcdui.game.GameCanvas;

Page 92: netbean

public class clsCanvas extends GameCanvas implements Runnable {

// key repeat rate in millisecondspublic static final int keyDelay = 250;    

//key constantspublic static final int upKey = 0;public static final int leftKey = 1;public static final int downKey = 2;public static final int rightKey = 3;public static final int fireKey = 4;

//key states for up, left, down, right, and fire keyprivate boolean[] isDown = {    false, false, false, false, false};

//last time the key changed stateprivate long[] keyTick = {    0, 0, 0, 0, 0};

//lookup table for key constants :Pprivate int[] keyValue = {    GameCanvas.UP_PRESSED, GameCanvas.LEFT_PRESSED,    GameCanvas.DOWN_PRESSED, GameCanvas.RIGHT_PRESSED,     GameCanvas.FIRE_PRESSED};

private boolean isRunning = true;    private Graphics g;private midMain fParent;

private clsFont myFont;

    /** Creates a new instance of clsCanvas */    public clsCanvas(midMain m) {        super(true);        fParent = m;        setFullScreenMode(true);    }        public void start(){        Thread runner = new Thread(this);        runner.start();    }   

Page 93: netbean

    public void load(){        try{            // load the images here                    }catch(Exception ex){            // exit the app if it fails to load the image            isRunning = false;            return;        }               myFont = new clsFont();        myFont.load("/images/fonts.png");

    }        public void unload(){        // make sure the object gets destroyed

        myFont.unload();        myFont = null;

    }        public void checkKeys(int iKey, long currTick){        long elapsedTick = 0;        //loop through the keys        for (int i = 0; i < 5; i++){             // by default, key not pressed by user            isDown[i] = false;            // is user pressing the key            if ((iKey & keyValue[i]) != 0){                 elapsedTick = currTick - keyTick[i];                //is it time to toggle key state?                if (elapsedTick >= keyDelay){                     // save the current time                    keyTick[i] = currTick;                      // toggle the state to down or pressed                    isDown[i] = true;                 }            }        }    }

    public void run() {       int iKey = 0;       int screenW = getWidth();       int screenH = getHeight();

Page 94: netbean

       long lCurrTick = 0; // current system time in milliseconds;              load();       g = getGraphics();       while(isRunning){                      lCurrTick = System.currentTimeMillis();           iKey = getKeyStates();                      checkKeys(iKey, lCurrTick);                      if (isDown[fireKey]){               isRunning = false;               }                      //restore the clipping rectangle to full screen           g.setClip(0, 0, screenW, screenH);           //set drawing color to black           g.setColor(0x000000);           //fill the screen with blackness           g.fillRect(0, 0, screenW, screenH);                     myFont.drawString(g, "Hello Neo...", 10, 50);           myFont.drawString(g, "1234567890", 10, 70);           myFont.drawString(g, "ABCDEFG abcdefg", 10, 90);                     flushGraphics();                      try{               Thread.sleep(30);           } catch (Exception ex){                          }       }       g = null;       unload();       fParent.destroyApp(false);       fParent = null;    }}

Things to Consider

Page 95: netbean

You could also consider adding method wrappers in the clsFont class to

simplify drawing integer types like so:

    public void drawInt(Graphics g, int num, int x, int y){        drawString(g, Integer.toString(num), x, y);    }

    public void drawLong(Graphics g, long num, int x, int y){        drawString(g, Long.toString(num), x, y);    }

A variant of the drawString() that draws the string aligned to the right

could easily be made like the following lines:

    //draws string from right to left starting at x,y    public void drawStringRev(Graphics g, String sTxt, int x, int y){        // get the strings length        int len = sTxt.length();

        // set the starting position        int cx = x;                // if nothing to draw return        if (len == 0) {            return;        }                // our fail-safe        if (useDefault){            g.drawString(sTxt, x, y, Graphics.TOP | Graphics.RIGHT);            return;        }

        // loop through all the characters in the string              for (int i = (len - 1); i >= 0; i--){

           // get current character            char c = sTxt.charAt(i);

Page 96: netbean

           // get ordinal value or ASCII equivalent           int cIndex = (int)c;

           // lookup the width of the character           int w = charW[cIndex];

           // go to the next drawing position           cx -= (w + charS);

           // draw the character           drawChar(g, cIndex, cx, y, w, charH);        }    }

Then you can add more method wrappers like this:

    public void drawIntRev(Graphics g, int num, int x, int y){        drawStringRev(g, Integer.toString(num), x, y);    }

    public void drawLongRev(Graphics g, long num, int x, int y){        drawStringRev(g, Long.toString(num), x, y);    }

Those additional methods could be used in drawing scores and numbers that

are aligned to the right. Very useful if you want the score to grow leftwards

like in score boards.

Bitmap Fonts With Word Wrap

Here is an additional method that you can play with and use to emulate word

wrapping when using bitmap fonts:

    // space between lines in pixels    public int lineS = 2;    

Page 97: netbean

    // draws words that wrap between x and x1    public void drawStringWrap(Graphics g, String s, int x, int y, int x1){        int len = s.length();                // current x        int tx = x;                // current y        int ty = y;                /*           word buffer contents width -           I just thought it would be faster than            calling the String.length() method        */        int ww = 0;                 // word buffer        String sWord = "";                for (int i = 0; i < len; i++){            char c = s.charAt(i);            int cIndex = (int)c;            int cw = charW[cIndex];                        if ((cIndex > 32) && (cIndex < 127)){              //if not a space and the character is printable                               //add the character to the buffer              sWord += String.valueOf(c);                            //compute the length of the current word              ww += cw;            } else {               //if space or non-printable character                              // check if there is a word in the buffer                if (ww > 0) {                                      //check if the word goes past the right margin                   if ((tx + ww) > x1){                       // carrage return                       tx = x;                                              // line feed                       ty += (charH + lineS);

Page 98: netbean

                   }                                      // draw the contents of the word buffer                   drawString(g, sWord, tx, ty);               }                              //move to the next position               tx += (ww + cw);                // clear the word buffer               sWord = "";               // word buffer width to zero               ww = 0;            }        }

        // if there is a word remaining in the buffer then draw it        if (ww > 0) {            if ((tx + ww) > x1){                tx = x;                ty += (charH + lineS);            }            drawString(g, sWord, tx, ty);        }    }

The drawStringWrap() method parameters:

← Graphics g - the Graphics object used to draw the bitmapfont

← String s - the string to be drawn

← int x - the left margin and horizontal starting position

← int y - the vertical position where the first line is drawn

← int x1 - the right margin where the words will be wrapped

The drawStringWrap() method makes use of the drawString() method to

draw each word. The method considers the space and non-printable

characters as a word delimiter. You can change the line spacing by changing

the value of lineS.

Page 99: netbean

To test it, add this line before the call to flushGraphics() in main loop in

clsCanvas:

           myFont.drawString(g, "ABCDEFG abcdefg", 10, 90);

           myFont.drawStringWrap(g, "blah blah blah blah blah blah " +                   "blah blah blah blah blah blah blah blah blah " +                   "blah blah blah blah blah blah blah blah blah " +                   "blah blah blah blah blah blah blah blah blah " +                   "blah blah blah blah blah blah blah blah blah " +                   "blah ", 10, 100, 166);

           flushGraphics();

You should see this when you run it:

Page 100: netbean

I hope the comments in the code will make the methods inner workings clear

enough for you. If not, just post your questions in the comments. Here is the

completed clsFont class with the extended methods:

package MyGame;

import javax.microedition.lcdui.Graphics;import javax.microedition.lcdui.Image;

public class clsFont {    // additional space between characters    public int charS = 0;        // max clipping area    public int screenW = 176;    public int screenH = 208;        // flag: set to true to use the Graphics.drawString() method    // this is just used as a fail-safe    public boolean useDefault = false;        // height of characters    public int charH = 10;        // lookup table for character widths    public int[] charW = {    // first 32 characters        9, 9, 9, 9, 9, 9, 9, 9, 9, 9,     9, 9, 9, 9, 9, 9, 9, 9, 9, 9,     9, 9, 9, 9, 9, 9, 9, 9, 9, 9,     9, 9,    // space    9,    // everything else XD    3, 5, 8, 8, 7, 8, 3, 5, 5, 6,     7, 3, 7, 3, 9, 6, 4, 6, 6, 6,     6, 6, 6, 6, 6, 3, 3, 6, 6, 6,     6, 9, 6, 6, 6, 6, 6, 6, 6, 6,     3, 6, 6, 6, 9, 6, 6, 6, 6, 6,     6, 7, 6, 6, 9, 6, 6, 6, 5, 9,     5, 4, 6, 4, 6, 6, 6, 6, 6, 6,     6, 6, 3, 4, 6, 3, 9, 6, 6, 6,

Page 101: netbean

    6, 6, 6, 6, 6, 6, 9, 6, 6, 6,     5, 3, 5, 4,    // delete    9};        // the bitmap font image    private Image imgFont;        public clsFont() {    }        public boolean load(String imagePath){        useDefault = false;        try{            // load the bitmap font            if (imgFont != null){                imgFont = null;            }            imgFont = Image.createImage(imagePath);        } catch (Exception ex){            // oohh we got an error then use the fail-safe            useDefault = true;        }        return (!useDefault);    }        public void unload(){        // make sure the object gets destroyed        imgFont = null;    }        public void drawChar(Graphics g, int cIndex, int x, int y, int w, int h){         // non printable characters don't need to be drawn        if (cIndex < 33){            return;        }

        // neither does the delete character         if (cIndex > 126){            return;        }

        // get the characters position        int cx = cIndex * 9;

        // reset the clipping rectangle

Page 102: netbean

        g.setClip(0, 0, screenW, screenH);

        // resize and reposition the clipping rectangle        // to where the character must be drawn        g.clipRect(x, y, w, h);

        // draw the character inside the clipping rectangle        g.drawImage(imgFont, x - cx, y, Graphics.TOP | Graphics.LEFT);    }        public void drawString(Graphics g, String sTxt, int x, int y){        // get the strings length        int len = sTxt.length();

        // set the starting position        int cx = x;                // if nothing to draw return        if (len == 0) {            return;        }                // our fail-safe        if (useDefault){            g.drawString(sTxt, x, y, Graphics.TOP | Graphics.LEFT);            return;        }

        // loop through all the characters in the string              for (int i = 0; i < len; i++){

           // get current character            char c = sTxt.charAt(i);

           // get ordinal value or ASCII equivalent           int cIndex = (int)c;

           // lookup the width of the character           int w = charW[cIndex];

           // draw the character           drawChar(g, cIndex, cx, y, w, charH);

           // go to the next drawing position

Page 103: netbean

           cx += (w + charS);        }    }       // extended methods ***************************************

    public void drawInt(Graphics g, int num, int x, int y){        drawString(g, Integer.toString(num), x, y);    }

    public void drawLong(Graphics g, long num, int x, int y){        drawString(g, Long.toString(num), x, y);    }        // Right align methods ****************************************        //draws string from right to left starting at x,y    public void drawStringRev(Graphics g, String sTxt, int x, int y){        // get the strings length        int len = sTxt.length();

        // set the starting position        int cx = x;                // if nothing to draw return        if (len == 0) {            return;        }                // our fail-safe        if (useDefault){            g.drawString(sTxt, x, y, Graphics.TOP | Graphics.RIGHT);            return;        }

        // loop through all the characters in the string              for (int i = (len - 1); i >= 0; i--){

           // get current character            char c = sTxt.charAt(i);

           // get ordinal value or ASCII equivalent           int cIndex = (int)c;

Page 104: netbean

           // lookup the width of the character           int w = charW[cIndex];

           // go to the next drawing position           cx -= (w + charS);

           // draw the character           drawChar(g, cIndex, cx, y, w, charH);

        }    }

    public void drawIntRev(Graphics g, int num, int x, int y){        drawString(g, Integer.toString(num), x, y);    }

    public void drawLongRev(Graphics g, long num, int x, int y){        drawString(g, Long.toString(num), x, y);    }

    // Word wrap method ****************************************        // space between lines in pixels    public int lineS = 2;         // draws words that wrap between x and x1    public void drawStringWrap(Graphics g, String s, int x, int y, int x1){        int len = s.length();                // current x        int tx = x;                // current y        int ty = y;                /*           word buffer contents width -           I just thought it would be faster than            calling the String.length() method        */        int ww = 0;                 // word buffer        String sWord = "";                for (int i = 0; i < len; i++){

Page 105: netbean

            char c = s.charAt(i);            int cIndex = (int)c;            int cw = charW[cIndex];                        if ((cIndex > 32) && (cIndex < 127)){              //if not a space and the character is printable                               //add the character to the buffer              sWord += String.valueOf(c);                            //compute the length of the current word              ww += cw;            } else {               //if space or non-printable character                              // check if there is a word in the buffer                if (ww > 0) {                                      //check if it goes past the right margin                   if ((tx + ww) > x1){                       // carrage return                       tx = x;                                              // line feed                       ty += (charH + lineS);                   }                                      // draw the contents of the word buffer                   drawString(g, sWord, tx, ty);               }                              //move to the next position               tx += (ww + cw);                               // clear the word buffer               sWord = "";                              // word buffer width to zero               ww = 0;            }        }                // if there is a word remaining in the buffer then draw it        if (ww > 0) {            if ((tx + ww) > x1){                tx = x;

Page 106: netbean

                ty += (charH + lineS);            }            drawString(g, sWord, tx, ty);        }    }

}

There is a lot that could be done to make the clsFont class more functional.

For instance, you can add justification or optimize the already existing

methods. You can also add another set of characters to the bitmap with a

different style and modify the code so you can select which style to use.

Since all the building blocks are in place, I think I'll just leave the missing

parts to you.

Using the Sprite Class and Sprite Movement in MIDP 2.0

This short tutorial will show you how to use the built-in Sprite class and how

to move it about on the screen. This may come a bit late but I just thought I

should throw it in for variety.

We will be using the project templates which you can get from this post:

Clean Project Templates. Download and extract them to a folder of your

choice.

I have prepared a new image strip for us to play with. It's an 18x18 pixel

white van drawn in 4 directions from frame 0 to frame 3, UP, DOWN, LEFT,

and RIGHT.

White Van Image

Frame Size:18x18 pixels

I know the drawing is not politically correct so please bear with me on this

Page 107: netbean

one XD. Firefox users can right-click on the image and choose "Save Image

As" from the context menu and IE users can choose "Save Picture As"

instead. Save it inside the images folder, inside the src folder of the project

template you downloaded.

Fire up NetBeans to open your project and navigate your way to the

clsCanvas source code.

Let's add a couple of variables, one for the image and one for the sprite. Add

these lines above the clsCanvas constructor:

private Image imgVan;private Sprite Van;

    /** Creates a new instance of clsCanvas */    public clsCanvas(midMain m) {

Now we'll load the image and initialize the Sprite object so modify the

load() method like so:

    public void load(){        try{            // load the images here            imgVan = Image.createImage("/images/van.png");        }catch(Exception ex){            // exit the app if it fails to load the image            isRunning = false;            return;        }               // initialize the Sprite object        Van = new Sprite(imgVan, 18, 18);                // show the frame 1 - the second frame        Van.setFrame(1);                // move to 50, 50 (X, Y)

Page 108: netbean

        Van.setPosition(50, 50);    }

The Sprite() constructor we're using takes in 3 parameters:

1. Image image - The Image object that contains the frames/pictures

2. int frameWidth - the width of each frame in the image

3. int frameHeight - the height of each frame in the image

You must make sure that the width and height of the picture assigned to

the Image object must be exactly divisible by the frameWidth and

frameHeight that you defined. Otherwise, you will get an exception error at

runtime. In this example, each frame of the image is 18 pixels wide and 18

pixels high.

After initializing the sprite we use the setFrame() method of the Sprite

object to set the current visible frame to 1, which is the second frame

counting from left to right and starting with 0. We also set the initial position

to 50, 50 (X, Y) on the screen.

We will also add some code to the unload() method of clsCanvas to make

sure the objects we made gets destroyed:

    public void unload(){        // make sure the object gets destroyed

        Van = null;        imgVan = null;

    }

The next line of code must be placed before the call to flushGraphics():

Page 109: netbean

           // draw the sprite           Van.paint(g);                     flushGraphics();

To test the MIDlet, press F6 on your keyboard then press Enter when the

emulator shows up. You should see something like this:

For our next trick, we're going to make the van move in all 4 directions: Up,

Down, Left, Right. We'll begin by getting the current position of the sprite

and storing them in two variables. Add these lines in the run() method just

before the call to setClip():

           // get the current position of the van           int cx = Van.getX();           int cy = Van.getY();                     //restore the clipping rectangle to full screen           g.setClip(0, 0, screenW, screenH);

Page 110: netbean

We will change the value of those variables depending on what keys are

being pressed. Add the following lines beneath the lines you just added:

           // get the current position of the van           int cx = Van.getX();           int cy = Van.getY();                     if ((iKey & GameCanvas.UP_PRESSED) != 0){               // show the van facing up               Van.setFrame(0);               // move the van upwards               cy--;           } else if ((iKey & GameCanvas.DOWN_PRESSED) != 0){               // show the van facing down               Van.setFrame(1);               // move the van downwards               cy++;           } else if ((iKey & GameCanvas.LEFT_PRESSED) != 0){               // show the van facing left               Van.setFrame(2);               // move the van to the left               cx--;           } else if ((iKey & GameCanvas.RIGHT_PRESSED) != 0){               // show the van facing right               Van.setFrame(3);               // move the van to the right               cx++;           }                      // update the vans position           Van.setPosition(cx, cy);

           //restore the clipping rectangle to full screen           g.setClip(0, 0, screenW, screenH);

The code above checks which key is being pressed and changes the current

frame the sprite is showing to match the direction of it's movement. It then

computes for the next location the sprite should move to and updates the

sprites position.

Page 111: netbean

As a reference for beginners, here is a list of operations to do to move an

object in different directions:

← UP - decrease the value of the Y coordinate

← Down - increase the value of the Y coordinate

← Left - decrease the value of the X coordinate

← Right - increase the value of the X coordinate

If you run the project now, you should be able to control the van by pressing

the arrow/directional keys.

Here's the completed clsCanvas source code:

package MyGame;

import javax.microedition.lcdui.Graphics;import javax.microedition.lcdui.Image;import javax.microedition.lcdui.game.GameCanvas;import javax.microedition.lcdui.game.Sprite;

public class clsCanvas extends GameCanvas implements Runnable {// key repeat rate in millisecondspublic static final int keyDelay = 250;    

//key constantspublic static final int upKey = 0;public static final int leftKey = 1;public static final int downKey = 2;public static final int rightKey = 3;public static final int fireKey = 4;

//key states for up, left, down, right, and fire keyprivate boolean[] isDown = {    false, false, false, false, false};

//last time the key changed stateprivate long[] keyTick = {

Page 112: netbean

    0, 0, 0, 0, 0};

//lookup table for key constants :Pprivate int[] keyValue = {    GameCanvas.UP_PRESSED, GameCanvas.LEFT_PRESSED,    GameCanvas.DOWN_PRESSED, GameCanvas.RIGHT_PRESSED,     GameCanvas.FIRE_PRESSED};

private boolean isRunning = true;    private Graphics g;private midMain fParent;

private Image imgVan;private Sprite Van;

    /** Creates a new instance of clsCanvas */    public clsCanvas(midMain m) {        super(true);        fParent = m;        setFullScreenMode(true);    }        public void start(){        Thread runner = new Thread(this);        runner.start();    }        public void load(){        try{            // load the images here            imgVan = Image.createImage("/images/van.png");        }catch(Exception ex){            // exit the app if it fails to load the image            isRunning = false;            return;        }               // initialize the Sprite object        Van = new Sprite(imgVan, 18, 18);                // show the frame 1 - the second frame        Van.setFrame(1);                // move to 50, 50 (X, Y)        Van.setPosition(50, 50);

Page 113: netbean

    }        public void unload(){        // make sure the object gets destroyed        Van = null;        imgVan = null;    }        public void checkKeys(int iKey, long currTick){        long elapsedTick = 0;        //loop through the keys        for (int i = 0; i < 5; i++){             // by default, key not pressed by user            isDown[i] = false;            // is user pressing the key            if ((iKey & keyValue[i]) != 0){                 elapsedTick = currTick - keyTick[i];                //is it time to toggle key state?                if (elapsedTick >= keyDelay){                     // save the current time                    keyTick[i] = currTick;                      // toggle the state to down or pressed                    isDown[i] = true;                 }            }        }    }

    public void run() {       int iKey = 0;       int screenW = getWidth();       int screenH = getHeight();       long lCurrTick = 0; // current system time in milliseconds;              load();       g = getGraphics();       while(isRunning){                      lCurrTick = System.currentTimeMillis();           iKey = getKeyStates();                      checkKeys(iKey, lCurrTick);                      if (isDown[fireKey]){               isRunning = false;               }

Page 114: netbean

                     // get the current position of the van           int cx = Van.getX();           int cy = Van.getY();                      if ((iKey & GameCanvas.UP_PRESSED) != 0){               // show the van facing up               Van.setFrame(0);               // move the van upwards               cy--;           } else if ((iKey & GameCanvas.DOWN_PRESSED) != 0){               // show the van facing down               Van.setFrame(1);               // move the van downwards               cy++;           } else if ((iKey & GameCanvas.LEFT_PRESSED) != 0){               // show the van facing left               Van.setFrame(2);               // move the van to the left               cx--;           } else if ((iKey & GameCanvas.RIGHT_PRESSED) != 0){               // show the van facing right               Van.setFrame(3);               // move the van to the right               cx++;           }                      // update the vans position           Van.setPosition(cx, cy);                     //restore the clipping rectangle to full screen           g.setClip(0, 0, screenW, screenH);           //set drawing color to black           g.setColor(0x000000);           //fill the screen with blackness           g.fillRect(0, 0, screenW, screenH);                     // draw the sprite           Van.paint(g);                     flushGraphics();                      try{               Thread.sleep(30);           } catch (Exception ex){                          }

Page 115: netbean

       }       g = null;       unload();       fParent.destroyApp(false);       fParent = null;    }}

The code presented in this tutorial also shows the basics of how to make a

user controlled sprite or character. I hope it was of some help to you

Using the TiledLayer Class to Display Tile Maps

A tile map is the map/landscape drawn in the background that the character

walks on. Tile maps are usually seen in games like Diablo, Fallout, Command

& Conquer, StarCraft, Final Fantasy, Pokemon, most 2D RPGs/MMORPGs and

strategy games. Here's an example of a game where a player controlled

character and some monsters are standing on a tile map:

Screenshot of the game Herbarrio

It's called a tile map because the map is actually made out of smaller images

called tiles. The tiles are drawn repeatedly and mix-matched with one

another to form the whole map. The collection of different tiles is also called

Page 116: netbean

a tileset.

The whole point of using a tile map is so the game doesn't have to load a

huge image but instead loads a smaller image containing the tileset and

recreates the map in the game. The tilesets are usually made so you can use

them to make several different maps using the same tile images. This saves

a us lot of memory and makes the game load faster.

In this tutorial, we're going to make use of the project created in a previous

tutorial Using the Sprite Class and Sprite Movement. We're going to

place a map for the van to run on and place obstacles that blocks the vans

path. We're also going to take advantage of the TiledLayer class in MIDP

2.0 which was made specifically for drawing tile maps.

The map we'll be making will take up the whole screen of a 176x208 pixel

display. Here's an illustration to make you more confused:

The large green rectangle is the tile map and the dark green grid inside it

represents the tiles that makes up the map. Tile map dimensions are usually

measured by the number of tile columns and tile rows. In the example

above, the tile map is 11x13 tiles, columns and rows respectivly, and the

tiles themselves are 16x16 pixels. This makes a perfect fit for a 176x208

Page 117: netbean

pixel display screen.

Creating and Displaying the TiledLayer

I prepared a tileset image for us to use in drawing the map. Firefox users can

right-click on the image and choose "Save Image As" from the context

menu, while IE users can choose "Save Picture As" instead. Save the image

in the images folder, inside the src folder of the project.

Tileset Image

Dimensions: 160x16 pixels

Tile Size: 16x16 pixels

Here's a larger view:

Tileset Image zoomed in at 2x

If you're done saving the image, you can now open the project in NetBeans

and navigate your way to the clsCanvas code. We'll add some variables to

hold our TiledLayer objects and the tileset image before the clsCanvas

constructor:

private Image imgTileset;private TiledLayer roadMap;private TiledLayer blockMap;

    /** Creates a new instance of clsCanvas */    public clsCanvas(midMain m) {

You can press ALT+SHIFT+F now to add the missing export statements

automatically.

Page 118: netbean

The imgTileset will be used to hold the tileset image you downloaded. The

roadMap TiledLayer will be used to display the road which the van can run

on. The blockMap TileLayer will be used to display the blocks/obstacles

and will also be used to check for collision.

Next, we'll add a loadRoadMap() method for initializing the roadMap

TiledLayer. Add the following lines beneath the start() method:

    public void loadRoadMap(){        //initialize the roadMap        roadMap = new TiledLayer(11, 13, imgTileset, 16, 16);

        // Create a new Random for randomizing numbers        Random Rand = new Random();                //loop through all the map cells        for (int y = 0; y < 13; y++){            for (int x = 0; x < 11; x++){                // get a random tile index between 2 and 5                int index = (Math.abs(Rand.nextInt()>>>1) % (3)) + 2;

                // set the tile index for the current cell                roadMap.setCell(x, y, index);            }        }        // mark Rand for clean up        Rand = null;    }

Again, you can press ALT+SHIFT+F now to add the missing export

statements automatically.

The loadRoadMap() method creates a new TiledLayer object and defines

the number of columns, number of rows, the tileset image to be used, the

tile width, and the tile height. It then assigns a random tile index from 2-5 to

each cell of the map using the setCell() method.

Take note that the tile index of the tiles starts at 1 instead of 0. You can also

Page 119: netbean

leave a cell empty by assigning 0 to the cell, meaning that cell will not be

drawn.

We'll do the same for the blockMap TiledLayer and add a

loadBlockMap() method. Insert the following lines beneath the

loadRoadMap() method:

    public void loadBlockMap(){        // define the tile indexes to be used for each map cell        byte[][] blockData = {            {10, 8 , 7 , 6 , 10, 9 , 8 , 7 , 6 , 10, 9 },              {6 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 8 },              {7 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 7 },              {8 , 0 , 0 , 10, 6 , 0 , 0 , 7 , 0 , 0 , 6 },              {9 , 0 , 0 , 0 , 0 , 0 , 0 , 8 , 0 , 0 , 10},              {10, 0 , 0 , 0 , 0 , 0 , 0 , 9 , 0 , 0 , 9 },              {6 , 0 , 0 , 8 , 0 , 0 , 0 , 0 , 0 , 0 , 8 },              {7 , 0 , 0 , 7 , 0 , 0 , 0 , 0 , 0 , 0 , 7 },              {8 , 0 , 0 , 6 , 0 , 0 , 0 , 10, 0 , 0 , 6 },              {9 , 0 , 0 , 10, 0 , 0 , 7 , 6 , 0 , 0 , 10},              {10, 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 9 },              {6 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 8 },              {7 , 8 , 9 , 10, 6 , 7 , 8 , 9 , 10, 6 , 7 }        };                //initialize blockMap        blockMap = new TiledLayer(11, 13, imgTileset, 16, 16);                //loop through all the map cells        for (int y = 0; y < 13; y++){            for (int x = 0; x < 11; x++){                // set the tile index for the current cell                // take note of the reversed indexes for blockData                blockMap.setCell(x, y, blockData[y][x]);            }        }                blockData = null;    }

Page 120: netbean

The loadBlockMap() method does pretty much the same thing as what the

loadRoadMap() method does. Except this time, the tile index for each cell

is predefined in a byte array before being assigned to each map cell.

Now, we'll modify the load() method so it will load the tileset image and call

the methods we previously added for initializing the TiledLayer objects.

We'll also change the initial position of the van so that it's not already hitting

a block when the game starts. So modify the load() method like this:

    public void load(){        try{            // load the images here            imgVan = Image.createImage("/images/van.png");                        // load the tileset                  imgTileset = Image.createImage("/images/tileset1.png");        }catch(Exception ex){            // exit the app if it fails to load the image            isRunning = false;            return;        }                // initialize the Sprite object        Van = new Sprite(imgVan, 18, 18);                // show the frame 1 - the second frame        Van.setFrame(1);                // move to 16, 16 (X, Y)        Van.setPosition(16, 16);               //initialize the TiledLayers        loadRoadMap();        loadBlockMap();           }

Let's also add some clean up code to the unload() method:

Page 121: netbean

    public void unload(){        // make sure the object gets destroyed        blockMap = null;        roadMap = null;        Van = null;        imgTileset = null;        imgVan = null;    }

We can now draw the maps on the screen. Add these lines inside the run()

method before the van is drawn:

           //draw the road           roadMap.paint(g);                      //draw the blocks           blockMap.paint(g);                     // draw the sprite           Van.paint(g);                      flushGraphics();

Collision Detection

If you run the project now, you will notice that the van doesn't really stop

when you hit a block. The van just runs over them. Lucky for us, the

TiledLayer and Sprite objects already have methods for collision detection.

First, let's make it so it's easier to change the speed of the van. Add the

global variable vanSpeed under the Van Sprite declaration:

private Sprite Van;

Page 122: netbean

private int vanSpeed = 2;

private Image imgTileset;

Then, modify the run() method to make use of the vanSpeed variable like

so:

           if ((iKey & GameCanvas.UP_PRESSED) != 0){               // show the van facing up               Van.setFrame(0);

               // move the van upwards               cy -= vanSpeed;           } else if ((iKey & GameCanvas.DOWN_PRESSED) != 0){               // show the van facing down               Van.setFrame(1);

               // move the van downwards               cy += vanSpeed;           } else if ((iKey & GameCanvas.LEFT_PRESSED) != 0){               // show the van facing left               Van.setFrame(2);

               // move the van to the left               cx -= vanSpeed;           } else if ((iKey & GameCanvas.RIGHT_PRESSED) != 0){               // show the van facing right               Van.setFrame(3);

               // move the van to the right               cx += vanSpeed;           }

Now for the collision detection part. We'll first store the current position of

the van in new variables, tx and ty, so we can reset the vans position to

where it was before it collided with a block:

           // get the current position of the van

Page 123: netbean

           int cx = Van.getX();           int cy = Van.getY();                     // save the current position in temporary vars           // so we can restore it when it hits a block           int tx = cx;           int ty = cy;                     if ((iKey & GameCanvas.UP_PRESSED) != 0){

The following lines of code checks if the van hits a block and resets the vans

position to where it was before it hit the block. Add the code after the vans

position has been updated:

           // update the vans position           Van.setPosition(cx, cy);                     //check if the van hits a block           if (Van.collidesWith(blockMap, true)){             //reset the van to the original position               Van.setPosition(tx, ty);           }                     //restore the clipping rectangle to full screen           g.setClip(0, 0, screenW, screenH);

The Sprite.collidesWith() method is used to check if the van collides with

any part of the blockMap TiledLayer. The second parameter we passed,

the boolean value "true", indicates that it should check for collision at the

"pixel level". This means that the collidesWith() method will only return

true if the non-transparent parts of the vans image overlaps with non-

transparent parts of the blockMap TiledLayer. You can also use the

collidesWith() method to check for collision between Sprite objects.

Btw, since the map takes up the whole screen, it would probably be better to

Page 124: netbean

comment out or remove the code for filling the screen with blackness from

the run() method:

           //restore the clipping rectangle to full screen           g.setClip(0, 0, screenW, screenH);                     /* comment out or remove this code                       set drawing color to black           g.setColor(0x000000);                       fill the screen with blackness           g.fillRect(0, 0, screenW, screenH);

            */                     //draw the road           roadMap.paint(g);

When you run the project, you should see something like this:

Page 125: netbean

Here's the completed clsCanvas code so you can check your work:

package MyGame;

import java.util.Random;import javax.microedition.lcdui.Graphics;import javax.microedition.lcdui.Image;import javax.microedition.lcdui.game.GameCanvas;import javax.microedition.lcdui.game.Sprite;import javax.microedition.lcdui.game.TiledLayer;

public class clsCanvas extends GameCanvas implements Runnable {

// key repeat rate in millisecondspublic static final int keyDelay = 250;    

//key constantspublic static final int upKey = 0;public static final int leftKey = 1;public static final int downKey = 2;public static final int rightKey = 3;public static final int fireKey = 4;

//key states for up, left, down, right, and fire keyprivate boolean[] isDown = {    false, false, false, false, false};

//last time the key changed stateprivate long[] keyTick = {    0, 0, 0, 0, 0};

//lookup table for key constants :Pprivate int[] keyValue = {    GameCanvas.UP_PRESSED, GameCanvas.LEFT_PRESSED,    GameCanvas.DOWN_PRESSED, GameCanvas.RIGHT_PRESSED,     GameCanvas.FIRE_PRESSED};

private boolean isRunning = true;    private Graphics g;private midMain fParent;

Page 126: netbean

private Image imgVan;private Sprite Van;

private int vanSpeed = 2;

private Image imgTileset;private TiledLayer roadMap;private TiledLayer blockMap;

    public clsCanvas(midMain m) {        super(true);        fParent = m;        setFullScreenMode(true);    }        public void start(){        Thread runner = new Thread(this);        runner.start();    }       public void loadRoadMap(){        //initialize the roadMap        roadMap = new TiledLayer(11, 13, imgTileset, 16, 16);                // Create a new Random for randomizing numbers        Random Rand = new Random();                //loop through all the map cells        for (int y = 0; y < 13; y++){            for (int x = 0; x < 11; x++){                // get a random tile index between 2 and 5                int index = (Math.abs(Rand.nextInt()>>>1) % (3)) + 2;                                // set the tile index for the current cell                roadMap.setCell(x, y, index);            }        }                Rand = null;    }        public void loadBlockMap(){        // define the tile indexes to be used for each map cell        byte[][] blockData = {            {10, 8 , 7 , 6 , 10, 9 , 8 , 7 , 6 , 10, 9 },  

Page 127: netbean

            {6 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 8 },              {7 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 7 },              {8 , 0 , 0 , 10, 6 , 0 , 0 , 7 , 0 , 0 , 6 },              {9 , 0 , 0 , 0 , 0 , 0 , 0 , 8 , 0 , 0 , 10},              {10, 0 , 0 , 0 , 0 , 0 , 0 , 9 , 0 , 0 , 9 },              {6 , 0 , 0 , 8 , 0 , 0 , 0 , 0 , 0 , 0 , 8 },              {7 , 0 , 0 , 7 , 0 , 0 , 0 , 0 , 0 , 0 , 7 },              {8 , 0 , 0 , 6 , 0 , 0 , 0 , 10, 0 , 0 , 6 },              {9 , 0 , 0 , 10, 0 , 0 , 7 , 6 , 0 , 0 , 10},              {10, 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 9 },              {6 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 8 },              {7 , 8 , 9 , 10, 6 , 7 , 8 , 9 , 10, 6 , 7 }        };                //initialize blockMap        blockMap = new TiledLayer(11, 13, imgTileset, 16, 16);                //loop through all the map cells        for (int y = 0; y < 13; y++){            for (int x = 0; x < 11; x++){                // set the tile index for the current cell                // take note of the reversed indexes for blockData                blockMap.setCell(x, y, blockData[y][x]);            }        }                blockData = null;    }       public void load(){        try{            // load the images here            imgVan = Image.createImage("/images/van.png");

            imgTileset = Image.createImage("/images/tileset1.png");        }catch(Exception ex){            // exit the app if it fails to load the image            isRunning = false;            return;        }                // initialize the Sprite object        Van = new Sprite(imgVan, 18, 18);                // show the frame 1 - the second frame

Page 128: netbean

        Van.setFrame(1);               // move to 16, 16 (X, Y)        Van.setPosition(16, 16);                loadRoadMap();        loadBlockMap();           }        public void unload(){        // make sure the object gets destroyed        blockMap = null;        roadMap = null;        Van = null;        imgTileset = null;        imgVan = null;    }        public void checkKeys(int iKey, long currTick){        long elapsedTick = 0;        //loop through the keys        for (int i = 0; i < 5; i++){             // by default, key not pressed by user            isDown[i] = false;            // is user pressing the key            if ((iKey & keyValue[i]) != 0){                 elapsedTick = currTick - keyTick[i];                //is it time to toggle key state?                if (elapsedTick >= keyDelay){                     // save the current time                    keyTick[i] = currTick;                      // toggle the state to down or pressed                    isDown[i] = true;                 }            }        }    }

    public void run() {       int iKey = 0;       int screenW = getWidth();       int screenH = getHeight();       long lCurrTick = 0; // current system time in milliseconds;              load();

Page 129: netbean

       g = getGraphics();       while(isRunning){                      lCurrTick = System.currentTimeMillis();           iKey = getKeyStates();                      checkKeys(iKey, lCurrTick);                      if (isDown[fireKey]){               isRunning = false;               }                      // get the current position of the van           int cx = Van.getX();           int cy = Van.getY();                     // save the current position in temporary vars           // so we can restore it when we hit a block           int tx = cx;           int ty = cy;                     if ((iKey & GameCanvas.UP_PRESSED) != 0){               // show the van facing up               Van.setFrame(0);                             // move the van upwards               cy -= vanSpeed;           } else if ((iKey & GameCanvas.DOWN_PRESSED) != 0){               // show the van facing down               Van.setFrame(1);                             // move the van downwards               cy += vanSpeed;           } else if ((iKey & GameCanvas.LEFT_PRESSED) != 0){               // show the van facing left               Van.setFrame(2);                             // move the van to the left               cx -= vanSpeed;           } else if ((iKey & GameCanvas.RIGHT_PRESSED) != 0){               // show the van facing right               Van.setFrame(3);                             // move the van to the right               cx += vanSpeed;           }           

Page 130: netbean

           // update the vans position           Van.setPosition(cx, cy);                     //check if the van hits a block           if (Van.collidesWith(blockMap, true)){             //reset the van to the original position               Van.setPosition(tx, ty);           }                     //restore the clipping rectangle to full screen           g.setClip(0, 0, screenW, screenH);                     /* comment out or remove this code                       set drawing color to black           g.setColor(0x000000);                       fill the screen with blackness           g.fillRect(0, 0, screenW, screenH);

            */

           //draw the road           roadMap.paint(g);                      //draw the blocks           blockMap.paint(g);                     // draw the sprite           Van.paint(g);                      flushGraphics();                      try{               Thread.sleep(30);           } catch (Exception ex){                          }       }       g = null;       unload();       fParent.destroyApp(false);       fParent = null;    }}

Page 131: netbean

The sample code presented here was made to show a very simple way to

use the TiledLayer class, how to draw a Tile Map and check for collision. It

also shows a way to randomize numbers by using the Random class.

Finally, you can find a lot of information about tile maps on the internet.

Although most of them aren't related to MIDP 2.0 or Java, the concept is still

the same and can be useful to your game project.

Loading Tile Map Data From a File

In case you haven't seen it yet, there's a push-puzzle demo game in the

"Various Games"/"Various Games for MIDP 2.0" sample mobile project

included with The NetBeans Mobility Pack. The push-puzzle game demo also

shows how to load map data from text files. But the one thing I remember

most about that sample game is that the code confused me a lot, heh.

Here's my attempt to make things a bit easier for you so you won't have to

go through all that code jumping. But instead of loading map data from a

text file, we're going to pull the map data from a binary file.

In this tutorial, we're going to make use of the project code from the tutorial

Using the TiledLayer Class to Display Tile Maps. So head over there

first to get the code and to have a better understanding of what's going on.

That tutorial displays a user controlled sprite on a map with obstacles. We're

going to modify the code so that the obstacle layer data is loaded from a

map file.

You can get the map file here in SMP format:

samplemap.smp (155 bytes) - Download Link

Create a new folder called "maps" in the "src" folder of the project and save

the map file there.

Page 132: netbean

You can view or edit the contents of the map with the map editor which you

can get here: Simple Tile Map Editor Info. and Download Page.

The link includes instructions and some more info.

To preview the map in the map editor, Click on the "Open" icon and choose

the "samplemap.smp" file you saved earlier. In the next dialog, select the

"tileset1.png" image file inside the "images" folder within the "src" folder

of the project. You should see something like this:

When you're ready, open the project in NetBeans and navigate your way to

the clsCanvas code. Add the following code below the loadBlockMap()

method:

    public TiledLayer getMap(String fpath, Image ftiles){        TiledLayer tMap = null;        try {            // open the file            InputStream is = this.getClass().getResourceAsStream(fpath);            DataInputStream ds = new DataInputStream(is);            try {

Page 133: netbean

                // skip the descriptor                ds.skipBytes(8);                                // read map width                int mW = ds.readByte();                                // read map height                int mH = ds.readByte();                                // read tile width                int tW = ds.readByte();                                // read tile height                int tH = ds.readByte();                                // create a new tiled layer                tMap = new TiledLayer(mW, mH, ftiles, tW, tH);                                // loop through the map data                for (int rCtr = 0; rCtr < mH; rCtr++){                  for (int cCtr = 0; cCtr < mW; cCtr++){                    // read a tile index                     byte nB = ds.readByte();                                          // if tile index is non-zero                    // tile index 0 is usually a blank tile                    if (nB > 0) {                        // assign (tile index + 1) to the current cell                        // TiledLayer objects start tile index at 1                        // instead of 0                        tMap.setCell(cCtr, rCtr, nB + 1);                      }                  }                  }

           } catch (Exception ex) {               tMap = null;               System.err.println("map loading error : " + ex.getMessage());           }           // close the file           ds.close();           ds = null;           is = null;        } catch (Exception ex) {

Page 134: netbean

            tMap = null;            System.err.println("map loading error : " + ex.getMessage());        }                // return the newly created map or null if loading failed        return tMap;    }

Make sure to press ALT+Shift+F so NetBeans can add the missing import

statements.

The code we just added is the same getMap() method you will find the

Simple Tile Map Editor page. We will use it here to serve as an example of

it's usage.

We'll make a new method called loadBlockMapFile() that uses the

getMap() method to load the map data and initialize the blockMap

TiledLayer. This will let you preserve the previous loadBlockMap() method

code for future reference. Add the following code below the

loadBlockMap() method:

    public void loadBlockMapFile(){        //initialize blockMap        blockMap = getMap("/maps/samplemap.smp", imgTileset);    }

We now have to replace the call to loadBlockMap() inside the load()

method to call loadBlockMapFile() instead:

        loadRoadMap();        loadBlockMapFile();            }

Page 135: netbean

You should see something like this when you run the project:

There's not much difference between the output of the previous tutorial and

this one except for the position of a few blocks and their colors.

Here's the completed clsCanvas source code so you can check your work:

package MyGame;

import java.io.DataInputStream;import java.io.InputStream;import java.util.Random;import javax.microedition.lcdui.Graphics;import javax.microedition.lcdui.Image;import javax.microedition.lcdui.game.GameCanvas;import javax.microedition.lcdui.game.Sprite;import javax.microedition.lcdui.game.TiledLayer;

public class clsCanvas extends GameCanvas implements Runnable {// key repeat rate in milliseconds

Page 136: netbean

public static final int keyDelay = 250;    

//key constantspublic static final int upKey = 0;public static final int leftKey = 1;public static final int downKey = 2;public static final int rightKey = 3;public static final int fireKey = 4;

//key states for up, left, down, right, and fire keyprivate boolean[] isDown = {    false, false, false, false, false};//last time the key changed stateprivate long[] keyTick = {    0, 0, 0, 0, 0};//lookup table for key constants :Pprivate int[] keyValue = {    GameCanvas.UP_PRESSED, GameCanvas.LEFT_PRESSED,    GameCanvas.DOWN_PRESSED, GameCanvas.RIGHT_PRESSED,     GameCanvas.FIRE_PRESSED};

private boolean isRunning = true;    private Graphics g;private midMain fParent;

private Image imgVan;private Sprite Van;

private int vanSpeed = 2;

private Image imgTileset;private TiledLayer roadMap;private TiledLayer blockMap;

    /** Creates a new instance of clsCanvas */    public clsCanvas(midMain m) {        super(true);        fParent = m;        setFullScreenMode(true);    }        public void start(){        Thread runner = new Thread(this);        runner.start();

Page 137: netbean

    }        public void loadRoadMap(){        //initialize the roadMap        roadMap = new TiledLayer(11, 13, imgTileset, 16, 16);                // Create a new Random for randomizing numbers        Random Rand = new Random();                //loop through all the map cells        for (int y = 0; y < 13; y++){            for (int x = 0; x < 11; x++){                // get a random tile index between 2 and 5                int index = (Math.abs(Rand.nextInt()>>>1) % (3)) + 2;                                // set the tile index for the current cell                roadMap.setCell(x, y, index);            }        }                Rand = null;    }        public void loadBlockMap(){        // define the tile indexes to be used for each map cell        byte[][] blockData = {            {10, 8 , 7 , 6 , 10, 9 , 8 , 7 , 6 , 10, 9 },              {6 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 8 },              {7 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 7 },              {8 , 0 , 0 , 10, 6 , 0 , 0 , 7 , 0 , 0 , 6 },              {9 , 0 , 0 , 0 , 0 , 0 , 0 , 8 , 0 , 0 , 10},              {10, 0 , 0 , 0 , 0 , 0 , 0 , 9 , 0 , 0 , 9 },              {6 , 0 , 0 , 8 , 0 , 0 , 0 , 0 , 0 , 0 , 8 },              {7 , 0 , 0 , 7 , 0 , 0 , 0 , 0 , 0 , 0 , 7 },              {8 , 0 , 0 , 6 , 0 , 0 , 0 , 10, 0 , 0 , 6 },              {9 , 0 , 0 , 10, 0 , 0 , 7 , 6 , 0 , 0 , 10},              {10, 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 9 },              {6 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 8 },              {7 , 8 , 9 , 10, 6 , 7 , 8 , 9 , 10, 6 , 7 }        };                //initialize blockMap        blockMap = new TiledLayer(11, 13, imgTileset, 16, 16);                //loop through all the map cells        for (int y = 0; y < 13; y++){

Page 138: netbean

            for (int x = 0; x < 11; x++){                // set the tile index for the current cell                // take note of the reversed indexes for blockData                blockMap.setCell(x, y, blockData[y][x]);            }        }                blockData = null;    }       public void loadBlockMapFile(){        //initialize blockMap from a binary file        blockMap = getMap("/maps/samplemap.smp", imgTileset);    }

    public TiledLayer getMap(String fpath, Image ftiles){        TiledLayer tMap = null;        try {            // open the file            InputStream is = this.getClass().getResourceAsStream(fpath);            DataInputStream ds = new DataInputStream(is);            try {                // skip the descriptor                ds.skipBytes(8);                                // read map width                int mW = ds.readByte();                                // read map height                int mH = ds.readByte();                                // read tile width                int tW = ds.readByte();                                // read tile height                int tH = ds.readByte();                                // create a new tiled layer                tMap = new TiledLayer(mW, mH, ftiles, tW, tH);                                // loop through the map data                for (int rCtr = 0; rCtr < mH; rCtr++){                  for (int cCtr = 0; cCtr < mW; cCtr++){                    // read a tile index                     byte nB = ds.readByte();  

Page 139: netbean

                                        // if tile index is non-zero                    // tile index 0 is usually a blank tile                    if (nB > 0) {                        //assign (tile index + 1) to the current cell                        tMap.setCell(cCtr, rCtr, nB + 1);                      }                  }                  }

           } catch (Exception ex) {               tMap = null;               System.err.println("map loading error : " + ex.getMessage());           }           // close the file           ds.close();           ds = null;           is = null;        } catch (Exception ex) {            tMap = null;            System.err.println("map loading error : " + ex.getMessage());        }                // return the newly created map or null if loading failed        return tMap;    }           public void load(){        try{            // load the images here            imgVan = Image.createImage("/images/van.png");            imgTileset = Image.createImage("/images/tileset1.png");        }catch(Exception ex){            // exit the app if it fails to load the image            isRunning = false;            return;        }                // initialize the Sprite object        Van = new Sprite(imgVan, 18, 18);                // show the frame 1 - the second frame

Page 140: netbean

        Van.setFrame(1);                // move to 50, 50 (X, Y)        Van.setPosition(16, 16);                loadRoadMap();        loadBlockMapFile();            }        public void unload(){        // make sure the object gets destroyed        blockMap = null;        roadMap = null;        Van = null;        imgTileset = null;        imgVan = null;    }        public void checkKeys(int iKey, long currTick){        long elapsedTick = 0;        //loop through the keys        for (int i = 0; i < 5; i++){             // by default, key not pressed by user            isDown[i] = false;            // is user pressing the key            if ((iKey & keyValue[i]) != 0){                 elapsedTick = currTick - keyTick[i];                //is it time to toggle key state?                if (elapsedTick >= keyDelay){                     // save the current time                    keyTick[i] = currTick;                      // toggle the state to down or pressed                    isDown[i] = true;                 }            }        }    }

    public void run() {       int iKey = 0;       int screenW = getWidth();       int screenH = getHeight();       long lCurrTick = 0; // current system time in milliseconds;              load();

Page 141: netbean

       g = getGraphics();       while(isRunning){                      lCurrTick = System.currentTimeMillis();           iKey = getKeyStates();                      checkKeys(iKey, lCurrTick);                      if (isDown[fireKey]){               isRunning = false;               }                      // get the current position of the van           int cx = Van.getX();           int cy = Van.getY();                      // save the current position in temporary vars           // so we can restore it when we hit a block           int tx = cx;           int ty = cy;                      if ((iKey & GameCanvas.UP_PRESSED) != 0){               // show the van facing up               Van.setFrame(0);                              // move the van upwards               cy -= vanSpeed;           } else if ((iKey & GameCanvas.DOWN_PRESSED) != 0){               // show the van facing down               Van.setFrame(1);                              // move the van downwards               cy += vanSpeed;           } else if ((iKey & GameCanvas.LEFT_PRESSED) != 0){               // show the van facing left               Van.setFrame(2);                              // move the van to the left               cx -= vanSpeed;           } else if ((iKey & GameCanvas.RIGHT_PRESSED) != 0){               // show the van facing right               Van.setFrame(3);                              // move the van to the right               cx += vanSpeed;           }           

Page 142: netbean

           // update the vans position           Van.setPosition(cx, cy);                      //check if the van hits a block           if (Van.collidesWith(blockMap, true)){             //reset the van to the original position               Van.setPosition(tx, ty);           }                      //restore the clipping rectangle to full screen           g.setClip(0, 0, screenW, screenH);                      /* comment out or remove this code                       set drawing color to black           g.setColor(0x000000);                       fill the screen with blackness           g.fillRect(0, 0, screenW, screenH);

            */                      //draw the road           roadMap.paint(g);                      //draw the blocks           blockMap.paint(g);                      // draw the sprite           Van.paint(g);                      flushGraphics();                      try{               Thread.sleep(30);           } catch (Exception ex){                          }       }       g = null;       unload();       fParent.destroyApp(false);       fParent = null;    }}

Page 143: netbean

The way you define the map data depends entirely on you. I chose to use a

binary file because of it's size and extracting the data is pretty straight

forward. No need for custom parsers and such.

You can also find a bunch of tile map editors online with more powerful

features like saving the map data to an XML file and more.

Links to some 2d Map Editors:

← Tiled - Link

← Tile Studio - Link

← Mappy - Link

← Tat Tile Map Editor - Link

Finally, you can also have a look at the new Game Builder that comes with

the latest NetBeans 6 and Mobility bundle. Here's a preview from a tutorial

in the NetBeans Community Docs: Using Game Builder for Java ME

development.

Good luck and have fun!

Handling Text Input or Accepting Character/Player Names

Your game would be more interesting if players can enter their name. It

would make them feel like they really are part of the game. For instance, you

can make certain dialog text address the player's name so it would seem like

the game is "talking" to the player. It would also allow the player to associate

his name with his score as proof of his victory and bragging rights.

What's the fuzz all about? You can always use a TextBox, right? No, my

Page 144: netbean

young padawan. Well, you could probably get away with it. But there's a

couple of reasons why you should avoid using a TextBox in a fullscreen

game. In most phones, you're game is hidden while the TextBox is displayed.

It is usually drawn using the phones U.I. theme and would not look like it's

part of the game. Once again, it's all about aesthetics.

We will be using the code from the tutorial Using Custom Fonts or Bitmap

Fonts to display the characters on the screen. So head over there if you

haven't already.

If you prefer, you can still use the normal Graphics.drawChar() method in

place of the clsFont.drawChar() method. But you at least need to have the

project code which you can download from : Clean MIDP 2.0 Game

Templates.

Lack of Buttons

In this tutorial, we're going to mimic the text input used in arcade consoles

of old. Those coin/token-eating machines only had a 4 way joystick, 6

function buttons, a start button, and a reset button. But these were sufficient

enough to let the player input names or initials for the scoreboard. While a

typical mobile phone has more buttons for us to play with, not all of them

map to the same key codes on different phones.

Fortunately, the GameCanvas class gives us a few standard keys which

more or less works on most phones.

Standard Keys:

← 8 key or UP Button

← 2 key or DOWN Button

← 4 key or LEFT Button

← 6 key or RIGHT Button

← 5 key or FIRE Button

Page 145: netbean

We're going to assign functions to these keys as follows:

← LEFT - select the character to the left of the current selected character

in the string.

← RIGHT - select the character to the right of the current selected

character in the string.

← UP - Scroll through the characters upwards.

← DOWN - Scroll through the characters downwards.

← FIRE - commit changes

The Variables

Let's begin by opening the project from NetBeans and navigate your way to

the clsCanvas code. Add the following global variables before the

clsCanvas constructor:

private clsFont myFont;

// this will hold the resulting textprivate String prText = "AAAAAA";

// the currently selected characterprivate int prSelected = 0;

// width and height of the charactersprivate int prWidth = 9;private int prHeight = 10;

// character spacing - includes width of characterprivate int prSpacing = 12;

// vars for timing the blinking cursorprivate long prStart = 0;private long prDelay = 100;private boolean prShow = true;

    /** Creates a new instance of clsCanvas */    public clsCanvas(midMain m) {

Page 146: netbean

The String prText will contain the text entered by the player and at the

same time provide feed back of the letters the player has chosen. The length

of the string assign to prText will also determine the maximum length of the

text the player can enter and will also be the first characters displayed on

the screen.

The integer prSelected will hold the index of the currently selected

character from the string assigned to prText.

The variables prWidth and prHeight holds the width and height of a

character. These variables will be passed to the clsFont.drawChar()

method for resizing the clipping rectangle.

The integer prSpacing defines the interval or space in pixels between each

characters drawing position (X coordinate). The value assigned to

prSpacing should be the sum of the width of a character and the space

between each character.

Replacing a Single Character

Next, we'll add a new method called replaceCharAt() that will let us change

a single character in a given string. Add the new method above the run()

method:

    public String replaceCharAt(String s, int pos, char c) {       if (pos < (s.length() - 1)){          return s.substring(0, pos) + c + s.substring(pos + 1);       } else {          return s.substring(0, pos) + c;       }    }    

    public void run() {

Page 147: netbean

The replaceCharAt() method requires 3 parameters:

← String s - the original string

← int pos - the position of the character to be replaced

← Char c - the replacement character

User Controls

We also need a new method for detecting/responding to key presses and

toggle the cursors visibility when needed. Add the updatePompt() method

above the run() method:

    public void updatePrompt(long currTick){        // get text length        int len = prText.length();        // get current selected characters ASCII value        int prOrd = (int)prText.charAt(prSelected);                // is it time to change the cursors visibility        if ((currTick - prStart) >= prDelay){            // update starting time            prStart = currTick;            // toggle cursor visibility            prShow = !prShow;        }                if (isDown[leftKey]){            // if not first character            if (prSelected > 0){                // select previous character in string                prSelected--;            }        } else if (isDown[rightKey]){            // if not last character            if (prSelected < (len - 1)){                // select next character in string                prSelected++;            }        } else if (isDown[upKey]){

Page 148: netbean

            if (prOrd == 97){ // small leter a                prOrd = 90; // jump to capital letter Z            } else if (prOrd == 65){ // capital letter A                prOrd = 32; // jump to space character            } else if (prOrd == 32){ // space character                prOrd = 122; // jump to small leter z            } else if ((prOrd > 97) || (prOrd > 65)){                prOrd--; // previous character            }            // replace the selected character with the new character            prText = replaceCharAt(prText, prSelected, (char)prOrd);        } else if (isDown[downKey]){            if (prOrd == 32){  // space character                prOrd = 65; // jump to capilat letter A            } else if (prOrd == 90){ // capital letter Z                prOrd = 97; // jump to small letter a            } else if (prOrd == 122){ // small letter z                prOrd = 32; // jump to space character            } else if ((prOrd < 90) || (prOrd < 122)){                prOrd++; // next character            }            // replace the selected character with the new character            prText = replaceCharAt(prText, prSelected, (char)prOrd);        }    }

    public void run() {

The updatePrompt() method takes a single Long parameter currTick

which is the current time in milliseconds. The value from the parameter is

just used to check if the time that passed between prStart and the currTick

is greater than or equal to the delay defined in prDelay. If so, the boolean

prShow which controls if the cursor is show or hidden is toggled between

true and false.

Notice that the updatePrompt() method allows you to select capital letters,

small letters, and the space character. It wouldn't be too hard to add

Page 149: netbean

numbers there too or even the entire displayable character set. But you

would usually only use capital letters.

You might want to keep the space character though, because the player can

use it as placeholders for unused spaces if the length of your prompt is too

long. This way you can trim the spaces from the value of prText when the

player is finished entering his name.

Displaying the Text

Finally, we'll add a method for drawing the text and the cursor. Again, add

the drawPrompt() method above the run() method:

    public void drawPrompt(Graphics g, int x, int y){        // get length of the string        int len = prText.length();                // loop through the characters        for (int i = 0; i < len; i++){            // get ASCII value            int cIndex = (int)prText.charAt(i);                        // compute next drawing position - X             int cx = x + (i * prSpacing);                        // draw the character            myFont.drawChar(g, cIndex, cx, y, prWidth, prHeight);                        // compute cusor position - Y            int cy = y + 6;

            // if current char is selected            if (i == prSelected){                // if cursor shoud be visible                if (prShow) {                    //draw the cursor using char 95 or underscore character                    myFont.drawChar(g, 95, cx, cy, prWidth, prHeight);                }            } else {                //draw the cursor using char 95 or underscore

Page 150: netbean

character                myFont.drawChar(g, 95, cx, cy, prWidth, prHeight);            }        }    }

    public void run() {

The drawPrompt() method draws each character in the prText string and

cursor underneath each character. The cursor under the selected character

blinks to let the player know which character he is currently editing. The

markers/cursors under the non-selected characters will show the user how

many characters he can enter.

The drawPrompt() method takes 3 parameters:

← Graphics g - the Graphics object used for drawing. It will be passed

to the clsFont.drawChar() method

← int x - the horizontal starting drawing position

← int y - the vertical drawing position

Putting It All Together

Let's modify the code in our run() method to make use of the new methods

we just added. First, add the call to the updatePrompt() method below the

call to checkKeys() like so:

           checkKeys(iKey, lCurrTick);                     updatePrompt(lCurrTick);                     if (isDown[fireKey]){

Page 151: netbean

Then, add the following lines above the call to flushGraphics() like so:

           g.fillRect(0, 0, screenW, screenH);                     myFont.drawString(g, "Enter your name:", 10, 20);

           drawPrompt(g, 10, 40);

           myFont.drawString(g, "LEFT/RIGHT - move cursor", 10, 80);           myFont.drawString(g, "UP/DOWN - change letter", 10, 100);

           myFont.drawString(g, "Hello " + prText.trim() + "!", 10, 140);                     flushGraphics();

You can now run the project and should see something like this:

Page 152: netbean

Here's the completed clsCanvas source code so you can check your work:

package MyGame;

import javax.microedition.lcdui.Graphics;import javax.microedition.lcdui.game.GameCanvas;

public class clsCanvas extends GameCanvas implements Runnable {// key repeat rate in millisecondspublic static final int keyDelay = 250;    

//key constantspublic static final int upKey = 0;public static final int leftKey = 1;public static final int downKey = 2;public static final int rightKey = 3;public static final int fireKey = 4;

//key states for up, left, down, right, and fire keyprivate boolean[] isDown = {    false, false, false, false, false};//last time the key changed stateprivate long[] keyTick = {    0, 0, 0, 0, 0};//lookup table for key constants :Pprivate int[] keyValue = {    GameCanvas.UP_PRESSED, GameCanvas.LEFT_PRESSED,    GameCanvas.DOWN_PRESSED, GameCanvas.RIGHT_PRESSED,     GameCanvas.FIRE_PRESSED};

private boolean isRunning = true;    private Graphics g;private midMain fParent;

private clsFont myFont;

// this will hold the resulting textprivate String prText = "AAAAAA";

// the currently selected character

Page 153: netbean

private int prSelected = 0;

// width and height of the charactersprivate int prWidth = 9;private int prHeight = 10;

// character spacing - includes width of characterprivate int prSpacing = 12;

// vars for timing the blinking cursorprivate long prStart = 0;private long prDelay = 100;private boolean prShow = true;

    /** Creates a new instance of clsCanvas */    public clsCanvas(midMain m) {        super(true);        fParent = m;        setFullScreenMode(true);    }        public void start(){        Thread runner = new Thread(this);        runner.start();    }        public void load(){        try{            // load the images here        }catch(Exception ex){            // exit the app if it fails to load the image            isRunning = false;            return;        }                myFont = new clsFont();        myFont.load("/images/fonts.png");            }      public void unload(){        // make sure the object gets destroyed        myFont.unload();        myFont = null;    }            public void checkKeys(int iKey, long currTick){        long elapsedTick = 0;

Page 154: netbean

        //loop through the keys        for (int i = 0; i < 5; i++){             // by default, key not pressed by user            isDown[i] = false;            // is user pressing the key            if ((iKey & keyValue[i]) != 0){                 elapsedTick = currTick - keyTick[i];                //is it time to toggle key state?                if (elapsedTick >= keyDelay){                     // save the current time                    keyTick[i] = currTick;                      // toggle the state to down or pressed                    isDown[i] = true;                 }            }        }    }       public String replaceCharAt(String s, int pos, char c) {       if (pos < (s.length() - 1)){          return s.substring(0, pos) + c + s.substring(pos + 1);       } else {          return s.substring(0, pos) + c;       }    }               public void updatePrompt(long currTick){        // get text length        int len = prText.length();        // get current selected characters ASCII value        int prOrd = (int)prText.charAt(prSelected);                // is it time to change to cursors visibility        if ((currTick - prStart) >= prDelay){            // update starting time            prStart = currTick;            // toggle cursor visibility            prShow = !prShow;        }                if (isDown[leftKey]){            // if not first character            if (prSelected > 0){                // select previous character in string                prSelected--;            }        } else if (isDown[rightKey]){

Page 155: netbean

            // if not last character            if (prSelected < (len - 1)){                // select next character in string                prSelected++;            }        } else if (isDown[upKey]){            if (prOrd == 97){ // small leter a                prOrd = 90; // jump to capital letter Z            } else if (prOrd == 65){ // capital letter A                prOrd = 32; // jump to space character            } else if (prOrd == 32){ // space character                prOrd = 122; // jump to small leter z            } else if ((prOrd > 97) || (prOrd > 65)){                prOrd--; // previous character            }            // replace the selected character with the new character            prText = replaceCharAt(prText, prSelected, (char)prOrd);        } else if (isDown[downKey]){            if (prOrd == 32){  // space character                prOrd = 65; // jump to capilat letter A            } else if (prOrd == 90){ // capital letter Z                prOrd = 97; // jump to small letter a            } else if (prOrd == 122){ // small letter z                prOrd = 32; // jump to space character            } else if ((prOrd < 90) || (prOrd < 122)){                prOrd++; // next character            }            // replace the selected character with the new character            prText = replaceCharAt(prText, prSelected, (char)prOrd);        }    }        public void drawPrompt(Graphics g, int x, int y){        // get length of the string        int len = prText.length();                // loop through the characters        for (int i = 0; i < len; i++){            // get ASCII value            int cIndex = (int)prText.charAt(i);                        // compute next drawing position - X             int cx = x + (i * prSpacing);

Page 156: netbean

                        // draw the character            myFont.drawChar(g, cIndex, cx, y, prWidth, prHeight);                        // compute cusor position - Y            int cy = y + 6;

            // if current char is selected            if (i == prSelected){                // if cursor shoud be visible                if (prShow) {                    //draw the cursor using char 95 or underscore character                    myFont.drawChar(g, 95, cx, cy, prWidth, prHeight);                }            } else {                //draw the cursor using char 95 or underscore character                myFont.drawChar(g, 95, cx, cy, prWidth, prHeight);            }        }    }

    public void run() {       int iKey = 0;       int screenW = getWidth();       int screenH = getHeight();       long lCurrTick = 0; // current system time in milliseconds;              String sName = "";              load();       g = getGraphics();       while(isRunning){                      lCurrTick = System.currentTimeMillis();           iKey = getKeyStates();

           checkKeys(iKey, lCurrTick);

           updatePrompt(lCurrTick);                     if (isDown[fireKey]){               isRunning = false;    

Page 157: netbean

           }                      //restore the clipping rectangle to full screen           g.setClip(0, 0, screenW, screenH);           //set drawing color to black           g.setColor(0x000000);           //fill the screen with blackness           g.fillRect(0, 0, screenW, screenH);                     myFont.drawString(g, "Enter your name:", 10, 20);

           drawPrompt(g, 10, 40);

           myFont.drawString(g, "LEFT/RIGHT - move cursor", 10, 80);           myFont.drawString(g, "UP/DOWN - change letter", 10, 100);

           myFont.drawString(g, "Hello " + prText.trim() + "!", 10, 140);                     flushGraphics();                      try{               Thread.sleep(30);           } catch (Exception ex){                          }       }       g = null;       unload();       fParent.destroyApp(false);       fParent = null;    }}

The code presented in this tutorial show a very simple way for your game to

accept text input from the user. Using only directional keys/buttons, the

player is able to enter his name which is useful for game customization and

recording scores. One thing to remember is that you can adjust the

maximum length of text the user can enter simply by assigning a string of

the required length to the prText variable. You can even use a bunch of

Page 158: netbean

spaces to initialize it.

If you have ever played games on other hand-held devices with a minimal

set of controls like the GameBoy® or even console systems like the

PlayStation®, you have probably run across other techniques to get text

input from the player. Those techniques have been tried and tested so it

wouldn't hurt to adapt them for you own use. Just remember to keep it

simple