×
Namespaces

Variants
Actions
(Difference between revisions)

Developing a 2D game in Java ME - Part 3

From Nokia Developer Wiki
Jump to: navigation, search
kiran10182 (Talk | contribs)
m (Categories Added)
SergioEstevao (Talk | contribs)

Revision as of 21:08, 29 November 2007

At the end of our last lesson we completed the game menu interface for our Arkanoid game. One of the screens we didn’t create was the Game screen. Our goal is to produce a game screen like the screenshot bellow:

ArkanoidScreenshot.png

For the game screen we cannot use the high level ui elements because we need to have full control the way our game elements are draw and how to react to keypad events. In order to do this we need to use the low level interface classes available in J2ME.

The classes belonging to the low-level groups allow detailed control of the screen elements and events, with them you can specify position, color and size. The tradeoff of more control is lesser portability because you need to adapt to each device capabilities.

The following diagram shows the main classes of this group:

UI-LowLevel-Elements.png

The entry point is the Canvas class wich gives you acess to the system events:

  • keyPressed(), keyRepeated(), keyReleased(), notify the canvas when the keypad is being used
  • pointerPressed(), pointerDragged(), pointerReleased(), notify the canvas when the pointer is being used, available on phones with stylus
  • paint(), notifies the Canvas when it need to paint the screen, this method gives you access to the Graphics object.</li>
  • getWidth(), getHeight(), give access to the current size of the screen available to be draw on.

The Graphics class provides the methods for direct drawing to the screen:

  • drawLine(), draws a line
  • drawRect(), fillRect, draws a Rectangle to the screen
  • fillTriangle(), draws a filled Rectangle
  • drawArc(), fillArc, draws a arc on the screen, can be used to draw circles
  • drawChar(), drawChars, drawString, draws characters to the screen using the current font
  • drawImage(), draws a bitmap image to the screen
  • setFont(), defines the current font being used
  • setColor(), defines the current color being used by the draw methods

To create our game screen we need to extend the Canvas class, and implements our custom paint method.

import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Graphics;
 
public class MyCanvas extends Canvas{
 
int width;
int height;
 
public MyCanvas() {
 
}
 
protected void paint(Graphics g) {
// stores width and height
width = getWidth();
height = getHeight();
// set background color
g.setColor(0,0,0);
// clear screen
g.fillRect(0, 0, width, height);
// draw a red circle that represents a ball
g.setColor(255,0,0);
g.drawArc(100, 100, 5, 5, 0, 360);
// draws a blue rectangle for the pad
g.setColor(0,0,255);
g.fillRect(100, 200, 15, 15);
}
}

To activate our Canvas we need to create it in our Midlet class and then display it on our CommandAction method.

public Displayable initGameCanvas() {
if (gameCanvas == null){
gameCanvas = new MyCanvas();
// add a back Command to return to the menu screen
gameCanvas.addCommand(initBackCommand());
// set the listener to our actions
gameCanvas.setCommandListener(this);
}
return gameCanvas;
}

After you add this code, you can know enter the New Game option in the main menu that will display an amazing black screen with a red ball and a blue pad, not very impressive but it’s a start. Now we need to animate these elements, let's see how to do that. Any ideas?

Game Loop

Before we continue you need to understand the way a tipical game applications works. A game or animation is built according to the principle of repetitively executing a piece of code. This piece of code tracks the value of variables and updates the game state accordingly. Based on the game state, the code then draws/paints/repaints the game screen with the elements that make up the game. The values of the variables may change because of either user interaction or internal game behavior.

The repetitive execution is effected by putting the repetitive code in an continuous loop. Before entering the loop, an variable may be checked to see if the game should still be running, and if not, the loop may be exited. The code in the loop should allow the current thread of execution to sleep every few milliseconds to control the rate at which the update to the game state is done (in effect, how fast the game screen should be refreshed). To put it in coding terms:

public class MyCanvas extends GameCanvas implements Runnable{

public void start() {
run = true;
Thread t = new Thread(this);
t.start();
}
 
public void stop() {
run = false;
}
 
public void run(){
init();
while (run){
// update game elements, positions, colisions, etc..
updateGameState();
// check user input
checkUserInput();
// render screen
updateGameScreen();
// redraws screen
flushGraphics();
// controls at which rate the updates are done
Thread.sleep(10);
}
}

}

As you may have noticed we are using the GameCanvas class, this is a specialization of the Canvas class, optimized for gaming applications. It uses the following techniques:

  • Double buffering, the GameCanvas uses an off-screen image which is used for all painting operations. When all painting is done it can be rendered to the screen using the flushGraphics() method. This eliminates flickering and gives a smoother animation.
  • Stores key state in arrays, trough the method getKeyStates(), you can access a bit array that indicates the state of each key using the constants defined in Canvas: DOWN_PRESSED

Besides the use of GameCanvas we are using a Thread to keep the animation running independent of Midlet events, this way our animation will never wait for system events to repaint itself. Returning to our Arkanoid game we know need to implement the specific game logic for our game. In our game three kinds of entities are present:

  • The pad, our game avatar it’s just a small rectangle that moves left to right in the bottom of the screen.
  • The ball, starts connected to the pad, and when you press fire it goes up vertically, with the horizontal speed of the pad.
  • The bricks, static blocks in top of the screen each time they are hit by the ball they disapear.

The goal of the game is to make all the bricks disappear as fast as possible, and don’t let ball get away trough the bottom of the screen. We need to keep track of three variables:

  • score of the player, for each brick hit the player earns 10 points
  • number of lifes remaining, each time the ball goes by the bottom the screen the player looses an life
  • time remaining, the player has a limit of time to complete the game

So let's start coding, first we need to define some classes to represent our game entities:

<a href='http://sergioestevao.com/midp/wp-content/uploads/2007/11/arkanoid-entities.png' title='Arkanoid Entities'><img src='http://sergioestevao.com/midp/wp-content/uploads/2007/11/arkanoid-entities.png' alt='Arkanoid Entities' /></a>

As you can see by the diagram we defined a class called Entity, this class is going to be the parent for all our game entities, it will provide them the following functionalities:

  • x,y: defines the current position of our entity
  • speedX, speedY: define the speed of our entity
  • witdth, height: define the size of our entity
  • update(): method where we going to define the behavior of our entity.
  • paint(): method were we use the graphics class to draw our entity
  • collided(): a helper function that allows to check collision between entities

Then we extend the class Entity for each of our game elements and implement the update and paint methods. For the ball we have:

public class Ball extends Entity {
public int radium = 2;
 
public Ball(int radium){
this.radium = radium;
width = radium * 2;
height = radium * 2;
// red color
this.color = 0x00FF0000;
}
 
/**
* Paints the ball using a circle
*/

public void paint(Graphics g) {
g.setColor(color);
g.fillArc(x, y, radium*2, radium*2, 0, 360);
}
 
/***
* Updates the ball position.
*/

public void update() {
// update position
oldX=x;
oldY=y;
x += speedX;
y += speedY;
}
}

For the pad we have:

public class Pad extends Entity{  
int minLimit = 0;
int maxLimit = 1;
 
public Pad(int width, int height) {
this.width = width;
this.height = height;
}
 
public void paint(Graphics g) {
g.setColor(0,0,255);
g.fillRect(x, y, width, height);
}
 
public void update() {
// change x position according the speed
x += speedX;
// check if world bounds are reached
if (x < minLimit) {
x = minLimit;
}
if (x+width > maxLimit){
x = maxLimit - width;
}
}
}

And finally for the bricks:

public class Brick extends Entity {
boolean active = true;
 
public Brick(int color){
this.color = color;
}
 
public void paint(Graphics g) {
// only paints if still active
if (active){
g.setColor(color);
g.fillRect(x, y, width, height);
}
 
}
 
public void update() {
// the bricks don't move
}
}

Now we need create and config all these classes on our canvas class, let's create an init() method on the Canvas class.

public void init(){
// resets lifes
lifes = 3;
// resets score
score = 0;
// resets time
time = 0;
// bricks hit
bricksHit = 0;
// create a pad
pad = new Pad(getWidth()/10,getWidth()/10/4);
pad.x = (this.getWidth()-pad.width) / 2;
pad.y = this.getHeight() - (2*pad.height);
pad.maxLimit = getWidth();
pad.minLimit = 0;
 
// create ball
ball = new Ball(4);
ball.x = getWidth() / 2;
ball.y = getHeight() / 2;
ball.speedX = 1;
ball.speedY = 1;
// set collision limits
wallMinX = 0;
wallMaxX = getWidth();
wallMinY = 0;
// to allow to get out of screen
wallMaxY = getHeight() + 4 * ball.radium;
 
// create bricks
Brick brick;
bricks = new Vector();
for (int i=0; (i*(BRICK_WIDTH+2))<getWidth(); i++){
brick = new Brick(Util.setColor(255,0,0));
brick.width = BRICK_WIDTH;
brick.height = BRICK_HEIGHT;
brick.x = (i*(brick.width+2));
brick.y = 20;
bricks.addElement(brick);
}
}

After we have all the objects created we need to update their state and paint them, so need to add some code to the updateGameState() and updateGameScreen methods():

// draws elements to the screen
protected void updateGameScreen(Graphics g) {
// stores width and height
width = getWidth();
height = getHeight();
// set background color
g.setColor(0,0,0);
// clear screen
g.fillRect(0, 0, width, height);
// draw score
g.setColor(255,255,255);
g.drawString("Score:"+score+" Lifes:"+lifes+" Time: "+time, 0, 0, Graphics.TOP|Graphics.LEFT);
// draw game elements
pad.paint(g);
ball.paint(g);
// draw bricks stored in the Vector bricks
for (int i=0; i < bricks.size(); i++){
Brick brick = (Brick)(bricks.elementAt(i));
brick.paint(g);
}
}
// updates state of all element in the game
public void updateGameState(){
pad.update();
ball.update();
 
checkBallCollisionWithWalls();
checkBallCollisionWihPad();
checkBallCollisionWithBricks();
checkBallOutOfReach();
 
// check if bricks ended
if (bricksHit == bricks.size()){
run = false;
}
}

Just one thing missing is to react for keypad events to move the player pad

  // update game entities according to use presses on keypad
public void checkUserInput() {
int state = getKeyStates();
if ( (state & GameCanvas.LEFT_PRESSED) > 0) {
// move left
pad.speedX=-1;
} else if ( (state & GameCanvas.RIGHT_PRESSED) > 0) {
// move right
pad.speedX=1;
} else {
// don't move
pad.speedX=0;
}
}

Now if you run you emulator you should have a real game screen, where you can play your own game, move the pad hit the ball and erase all the bricks!

In the next class we going to see how we can use images to get a better looking game. See you soon.

Downloads:

416 page views in the last 30 days.
×