Basic MIDP 2.0 Game Template Part 2

Jump to part: 1 | 2 | 3

Creating the GameCanvas

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.

New Java Class


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 */
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;

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() 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){
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){



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;
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.


Finally we can go to the next step: Displaying the GameCanvas.


5 comments   |   post a comment
said...
Why do you set 'g = null;' in run ?
said...
To remove the reference when the game ends. The VM is supposed to do this automatically when the application ends but it's good practice to make sure you free all the objects you created in the application and set them to null when you don't need those objects anymore.
said...
I keep getting a red line under "Graphics" in "private Graphics g;". What do I do?
said...
You have to add this in the import statements:

import javax.microedition.lcdui.Graphics;

...or if you are using NetBeans 5.5, just press ALT+SHIFT+F which is the shortcut for Fix Imports, and CTRL+SHIFT+I for NetBeans 6.0.

Also try checking the code before that line.

Good luck!!!
in the code

instead of doing this
/****************************/
int iKey = 0;
while(isRunning){
iKey = getKeyStates();
/****************************/

can we declare iKey variable inside while as in starting of while loop we are replacing this every time so to lessen the scope of this variable???
like this..
/****************************/
while(isRunning){
int iKey = getKeyStates();
/****************************/

just a suggestion...
otherwise i really liked your blog, you have done a really really good job!!!!