×
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)
(Added in the featured article list)
copyeditor (Talk | contribs)
m
Line 1: Line 1:
 
{{FeaturedArticle}}
 
{{FeaturedArticle}}
 
[[Category:Java]][[Category:Java ME]][[Category:Games]][[Category:How To]][[Category:Code Examples]]
 
[[Category:Java]][[Category:Java ME]][[Category:Games]][[Category:How To]][[Category:Code Examples]]
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 screen shot bellow:
+
At the end of the previous article we completed the game menu interface for the Arkanoid game. The Game screen was, however, not yet created. The goal of this article is to produce a game screen that is similar to the following screenshot.
  
 
<center>[[Image:ArkanoidScreenshot.png]]</center>
 
<center>[[Image:ArkanoidScreenshot.png]]</center>
  
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 Java ME.
+
The high-level UI elements cannot be used for the game screen because we need to have full control of the way the game elements are drawn and how the game reacts to keypad events. In order to do this, we need to use the low-level interface classes available in Java ME.
  
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 trade-off of more control is lesser portability because you need to adapt to each device capabilities.
+
The classes belonging to the low-level groups allow detailed control of the screen elements and events. Using these classes you can specify the position, color, and size. The trade-off of more control is less portability because you need to adapt to the capabilities of each device.
  
 
The following diagram shows the main classes of this group:
 
The following diagram shows the main classes of this group:
Line 13: Line 13:
 
<center>[[Image:UI-LowLevel-Elements.png]]</center>  
 
<center>[[Image:UI-LowLevel-Elements.png]]</center>  
  
The entry point is the Canvas class which gives you access to the system events:
+
The entry point is the Canvas class, which gives you access to the system events:
  
* '''keyPressed(), keyRepeated(), keyReleased()''', notify the canvas when the keypad is being used
+
* '''keyPressed(), keyRepeated(), keyReleased()''' notify the canvas when the keypad is used.
* '''pointerPressed(), pointerDragged(), pointerReleased()''', notify the canvas when the pointer is being used, available on phones with stylus
+
* '''pointerPressed(), pointerDragged(), pointerReleased()''' notify the canvas when the pointer is used, available on phones with a stylus.
* '''paint()''', notifies the Canvas when it need to paint the screen, this method gives you access to the Graphics object.</li>
+
* '''paint()''' notifies the Canvas when it needs to paint the screen. This method gives access to the Graphics object.</li>
* getWidth(), getHeight(), give access to the current size of the screen available to be draw on.
+
* <tt>getWidth()</tt>, <tt>getHeight()</tt> give access to the current size of the screen available to be drawn on.
  
The Graphics class provides the methods for direct drawing to the screen:
+
The Graphics class provides the methods for direct drawing on the screen:
* <strong>drawLine()</strong>, draws a line
+
* <strong>drawLine()</strong> draws a line.
* <strong>drawRect()</strong>, fillRect, draws a Rectangle to the screen
+
* <strong>drawRect()</strong>, fillRect draws a rectangle on the screen.
* <strong>fillTriangle()</strong>, draws a filled Rectangle
+
* <strong>fillTriangle()</strong> draws a filled rectangle.
* <strong>drawArc()</strong>, fillArc, draws a arc on the screen, can be used to draw circles
+
* <strong>drawArc()</strong> fillArc, draws an arc on the screen, can be used to draw circles.
* <strong>drawChar()</strong>, drawChars, drawString, draws characters to the screen using the current font
+
* <strong>drawChar()</strong>, drawChars, drawString draws characters to the screen using the current font.
* <strong>drawImage()</strong>, draws a bitmap image to the screen
+
* <strong>drawImage()</strong>, draws a bitmap image on the screen.
* <strong>setFont()</strong>, defines the current font being used
+
* <strong>setFont()</strong>, defines the current font.
* <strong>setColor()</strong>, defines the current color being used by the draw methods
+
* <strong>setColor()</strong>, 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.
+
To create the game screen we need to extend the Canvas class and implement a custom paint method.
 
<code java>
 
<code java>
 
import javax.microedition.lcdui.Canvas;
 
import javax.microedition.lcdui.Canvas;
Line 61: Line 61:
 
}
 
}
 
</code>
 
</code>
To activate our Canvas we need to create it in our MIDlet class and then display it on our CommandAction method.
+
To activate the Canvas, create it in the MIDlet class and display it on the CommandAction method.
 
<code java>
 
<code java>
 
public Displayable initGameCanvas() {
 
public Displayable initGameCanvas() {
Line 74: Line 74:
 
}
 
}
 
</code>
 
</code>
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?
+
After you add this code, you can enter the New Game option in the main menu that will display a black screen with a red ball and a blue pad. Now these elements need to be animated.
  
== Game Loop ==
+
== Game loop ==
Before we continue you need to understand the way a typical 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.
+
Before you continue, you need to understand the way typical game applications works. A game or animation is built by repetitiously executing a piece of code. This piece of code tracks the value of the variables and updates the game state accordingly. Based on the game state, the code draws/paints/repaints the game screen with the elements that build the game. The values of the variables may change because of 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).
+
This is created by putting the code that is repeated in a continuous loop. Before entering the loop, a variable can be checked to see if the game should still be running. If not, the loop can  be exited. The code in the loop should allow the current execution thread to sleep every few milliseconds to control the rate at which the game state is updated (that is, how fast the game screen should be refreshed).
 
To put it in coding terms:
 
To put it in coding terms:
 
<code java>
 
<code java>
Line 112: Line 112:
 
}
 
}
 
</code>
 
</code>
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:
+
As you may have noticed, the GameCanvas class is used. The GameCanvas is a special case of the Canvas class, optimized for games. 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.
+
* Double buffering. The GameCanvas uses an off-screen image which is used for all painting operations. When painting is completed, it can be rendered to the screen using the <tt>flushGraphics()</tt> 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
+
* Storing the key state in arrays. Trough the <tt>getKeyStates()</tt> method 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.
+
Besides using GameCanvas, use a Thread to keep the animation running independently of MIDlet events. This way the 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:
+
In the Arkanoid game we now need to implement the specific game logic for the game. The game has three kinds of entities:
  
* The pad, our game avatar it’s just a small rectangle that moves left to right in the bottom of the screen.
+
* The pad, our game avatar, is just a small rectangle that moves from 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 ball is first 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 disappear.
+
* The bricks are static blocks in the upper part of the screen. When they are hit by the ball they disappear.
  
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.
+
The goal of the game is to make the bricks disappear as fast as possible, and not let the ball get away trough the bottom of the screen.
We need to keep track of three variables:
+
The game needs to keep track of three variables:
  
* score of the player, for each brick hit the player earns 10 points
+
* Score of the player. The player earns 10 points each time a brick is hit.
* number of lifes remaining, each time the ball goes by the bottom the screen the player looses an life
+
* Number of lifes remaining. Each time the ball goes through the bottom of the screen the player looses a life.
* time remaining, the player has a limit of time to complete the game
+
* Time remaining. The player must complete the game in a limited time.
  
So let's start coding, first we need to define some classes to represent our game entities:
+
First define some classes to represent the game entities:
 
<center>
 
<center>
 
<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>
 
<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>
 
</center>
 
</center>
  
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:
+
The class Entity will be the parent for all the game entities. It provides the following functionalities:
  
* <strong>x,y</strong>: defines the current position of our entity
+
* <strong>x,y</strong>: Defines the current position of the entity.
* <strong>speedX, speedY</strong>: define the speed of our entity
+
* <strong>speedX, speedY</strong>: Defines the speed of the entity.
* <strong>witdth, height</strong>: define the size of our entity
+
* <strong>width, height</strong>: Defines the size of the entity.
* <strong>update()</strong>: method where we going to define the behavior of our entity.
+
* <strong>update()</strong>: Defines the the behavior of the entity.
* <strong>paint()</strong>: method were we use the graphics class to draw our entity
+
* <strong>paint()</strong>: Method were the graphics class is used to draw the entity.
* <strong>collided()</strong>: a helper function that allows to check collision between entities
+
* <strong>collided()</strong>: 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.  
+
Next, extend the class Entity for each game element and implement the update and paint methods.  
For the ball we have:
+
For the ball:
 
<code java>
 
<code java>
 
public class Ball extends Entity {
 
public class Ball extends Entity {
Line 178: Line 178:
 
}
 
}
 
</code>
 
</code>
For the pad we have:
+
For the pad:
 
<code java>
 
<code java>
 
public class Pad extends Entity{   
 
public class Pad extends Entity{   
Line 233: Line 233:
 
</code>
 
</code>
  
Now we need create and configure all these classes on our canvas class, let's create an init() method on the Canvas class.
+
Now create and configure all these classes on the canvas class. Create an <tt>init()</tt> method on the Canvas class.
 
<code java>
 
<code java>
 
public void init(){
 
public void init(){
Line 277: Line 277:
 
   }
 
   }
 
</code>
 
</code>
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():
+
After all objects have been created, update their state and paint them. Add some code to the <tt>updateGameState()</tt> and <tt>updateGameScreen()</tt> methods:
 
<code java>
 
<code java>
 
// draws elements to the screen
 
// draws elements to the screen
Line 300: Line 300:
 
     }
 
     }
 
   }
 
   }
   // updates state of all element in the game
+
   // updates state of all elements in the game
 
   public void updateGameState(){
 
   public void updateGameState(){
 
     pad.update();
 
     pad.update();
Line 317: Line 317:
 
</code>
 
</code>
  
Just one thing missing is to react for keypad events to move the player pad
+
The game needs to react to keypad events to move the player pad:
  
 
<code java>
 
<code java>
Line 336: Line 336:
 
</code>
 
</code>
  
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!
+
Now if you run the game in the 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.
+
The next article explains how to use images to get a better looking game.
  
 
<strong>Downloads:</strong>
 
<strong>Downloads:</strong>

Revision as of 10:44, 26 August 2008

Featured Article

At the end of the previous article we completed the game menu interface for the Arkanoid game. The Game screen was, however, not yet created. The goal of this article is to produce a game screen that is similar to the following screenshot.

ArkanoidScreenshot.png

The high-level UI elements cannot be used for the game screen because we need to have full control of the way the game elements are drawn and how the game reacts to keypad events. In order to do this, we need to use the low-level interface classes available in Java ME.

The classes belonging to the low-level groups allow detailed control of the screen elements and events. Using these classes you can specify the position, color, and size. The trade-off of more control is less portability because you need to adapt to the capabilities of each device.

The following diagram shows the main classes of this group:

UI-LowLevel-Elements.png

The entry point is the Canvas class, which gives you access to the system events:

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

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

  • drawLine() draws a line.
  • drawRect(), fillRect draws a rectangle on the screen.
  • fillTriangle() draws a filled rectangle.
  • drawArc() fillArc, draws an 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 on the screen.
  • setFont(), defines the current font.
  • setColor(), defines the current color being used by the draw methods.

To create the game screen we need to extend the Canvas class and implement a 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 the Canvas, create it in the MIDlet class and display it on the 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 enter the New Game option in the main menu that will display a black screen with a red ball and a blue pad. Now these elements need to be animated.

Game loop

Before you continue, you need to understand the way typical game applications works. A game or animation is built by repetitiously executing a piece of code. This piece of code tracks the value of the variables and updates the game state accordingly. Based on the game state, the code draws/paints/repaints the game screen with the elements that build the game. The values of the variables may change because of user interaction or internal game behavior.

This is created by putting the code that is repeated in a continuous loop. Before entering the loop, a variable can be checked to see if the game should still be running. If not, the loop can be exited. The code in the loop should allow the current execution thread to sleep every few milliseconds to control the rate at which the game state is updated (that is, 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, collisions, 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, the GameCanvas class is used. The GameCanvas is a special case of the Canvas class, optimized for games. It uses the following techniques:

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

Besides using GameCanvas, use a Thread to keep the animation running independently of MIDlet events. This way the animation will never wait for system events to repaint itself. In the Arkanoid game we now need to implement the specific game logic for the game. The game has three kinds of entities:

  • The pad, our game avatar, is just a small rectangle that moves from left to right in the bottom of the screen.
  • The ball is first to the pad, and when you press fire it goes up vertically with the horizontal speed of the pad.
  • The bricks are static blocks in the upper part of the screen. When they are hit by the ball they disappear.

The goal of the game is to make the bricks disappear as fast as possible, and not let the ball get away trough the bottom of the screen. The game needs to keep track of three variables:

  • Score of the player. The player earns 10 points each time a brick is hit.
  • Number of lifes remaining. Each time the ball goes through the bottom of the screen the player looses a life.
  • Time remaining. The player must complete the game in a limited time.

First define some classes to represent the 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>

The class Entity will be the parent for all the game entities. It provides the following functionalities:

  • x,y: Defines the current position of the entity.
  • speedX, speedY: Defines the speed of the entity.
  • width, height: Defines the size of the entity.
  • update(): Defines the the behavior of the entity.
  • paint(): Method were the graphics class is used to draw the entity.
  • collided(): Helper function that allows to check collision between entities.

Next, extend the class Entity for each game element and implement the update and paint methods. For the ball:

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:

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 create and configure all these classes on the canvas class. 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 all objects have been created, update their state and paint them. 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 elements in the game
public void updateGameState(){
pad.update();
ball.update();
 
checkBallCollisionWithWalls();
checkBallCollisionWihPad();
checkBallCollisionWithBricks();
checkBallOutOfReach();
 
// check if bricks ended
if (bricksHit == bricks.size()){
run = false;
}
}

The game needs to react to 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 the game in the 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.

The next article explains how to use images to get a better looking game.

Downloads:


Go To Part 4

431 page views in the last 30 days.
×