×
Namespaces

Variants
Actions
(Difference between revisions)

Developing a 2D game in Java ME - Part 3

From Nokia Developer Wiki
Jump to: navigation, search
hartti (Talk | contribs)
m (typos, minor formatting)
mtilli (Talk | contribs)
m (Mtilli - - Game loop)
(23 intermediate revisions by 8 users not shown)
Line 1: Line 1:
[[Category:Java]][[Category:Java ME]][[Category:Games]][[Category:How To]][[Category:Code Examples]]
+
[[Category:Java ME]][[Category:Games]][[Category:How To]][[Category:Code Examples]][[Category:Symbian]][[Category:Series 40]][[Category:Series 40 Developer Platform 1.1]][[Category:Series 40 Developer Platform 2.0]][[Category:Nokia Belle]][[Category:Java Runtime 2.3 for Symbian]]
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:
+
{{Abstract|This article shows how to use Java ME's low-level interface classes to create the game screen and access key presses.
 +
This is the third in a series of articles that cover all the basics of developing an application for mobile devices using Java ME, learning the main libraries, classes, and methods available in Java ME.}}
 +
{{ArticleMetaData <!-- v1.1 -->
 +
|sourcecode= [[Media:ArkanoidPart3Source.zip]]
 +
|installfile= [[Media:ArkanoidPart3Binaries.zip]]
 +
|devices= Nokia 701, Nokia Asha 305
 +
|sdk= [http://www.developer.nokia.com/Develop/Java/Tools/ Nokia SDK 1.1 for Java], [http://www.developer.nokia.com/Develop/Java/Tools/ Nokia SDK 2.0 for Java]
 +
|platform= Series 40, Symbian
 +
|devicecompatability= <!-- Compatible devices e.g.: All* (must have internal GPS) -->
 +
|dependencies= <!-- Any other/external dependencies e.g.: Google Maps Api v1.0 -->
 +
|signing= <!-- Signing requirements - empty or one of: Self-Signed, DevCert, Manufacturer -->
 +
|capabilities= <!-- Capabilities required by the article/code example (e.g. Location, NetworkServices. -->
 +
|keywords= <!-- APIs, classes and methods (e.g. QSystemScreenSaver, QList, CBase -->
 +
|id= <!-- Article Id (Knowledge base articles only) -->
 +
|language= <!-- Language category code for non-English topics - e.g. Lang-Chinese -->
 +
|translated-by= <!-- [[User:XXXX]] -->
 +
|translated-from-title= <!-- Title only -->
 +
|translated-from-id= <!-- Id of translated revision -->
 +
|review-by= <!-- After re-review: [[User:username]] -->
 +
|review-timestamp= <!-- After re-review: YYYYMMDD -->
 +
|update-by= <!-- After significant update: [[User:username]]-->
 +
|update-timestamp= <!-- After significant update: YYYYMMDD -->
 +
|creationdate= 20071121
 +
|author= [[User:SergioEstevao]]
 +
}}
 +
{{FeaturedArticle|timestamp=20080824}}
 +
{{SeeAlso|
 +
* [[Developing a 2D game in Java ME - Part 1]]
 +
* [[Developing a 2D game in Java ME - Part 2]]
 +
* [[Developing a 2D game in Java ME - Part 3]]
 +
* [[Developing a 2D game in Java ME - Part 4]]
 +
* [[Developing a 2D game in Java ME - Part 5]]
 +
* [[Developing a 2D game in Java ME - Part 6]]
 +
* [[Developing a 2D game in Java ME - Part 7]]
 +
}}
  
<center>[[Image:ArkanoidScreenshot.png]]</center>
+
== Introduction ==
 +
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.
  
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.
+
<center>[[File:ArkanoidScreenshot.png]]</center>
  
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 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 group 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:
  
<center>[[Image:UI-LowLevel-Elements.png]]</center>  
+
<center>[[File: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.
* getWidth(), getHeight(), give access to the current size of the screen available to be draw on.
+
* '''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 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(), fillRect()</strong> draws a rectangle on the screen.
* <strong>fillTriangle()</strong>, draws a filled Rectangle
+
* <strong>fillTriangle()</strong> draws a filled triangle.
* <strong>drawArc()</strong>, fillArc, draws a arc on the screen, can be used to draw circles
+
* <strong>drawArc(), fillArc()</strong> 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(), drawChars(), drawString()</strong> 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;
 
import javax.microedition.lcdui.Graphics;
 
import javax.microedition.lcdui.Graphics;
  
public class MyCanvas extends Canvas{
+
public class MyCanvas extends Canvas {
  
 
   int width;
 
   int width;
Line 48: Line 85:
 
     height = getHeight();
 
     height = getHeight();
 
     // set background color
 
     // set background color
     g.setColor(0,0,0);
+
     g.setColor(0, 0, 0);
 
     // clear screen
 
     // clear screen
 
     g.fillRect(0, 0, width, height);
 
     g.fillRect(0, 0, width, height);
 
     // draw a red circle that represents a ball
 
     // draw a red circle that represents a ball
     g.setColor(255,0,0);
+
     g.setColor(255, 0, 0);
 
     g.drawArc(100, 100, 5, 5, 0, 360);
 
     g.drawArc(100, 100, 5, 5, 0, 360);
 
     // draws a blue rectangle for the pad
 
     // draws a blue rectangle for the pad
     g.setColor(0,0,255);
+
     g.setColor(0, 0, 255);
 
     g.fillRect(100, 200, 15, 15);     
 
     g.fillRect(100, 200, 15, 15);     
 
   }
 
   }
 
}
 
}
 
</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() {
   if (gameCanvas == null){
+
   if (gameCanvas == null) {
 
     gameCanvas = new MyCanvas();
 
     gameCanvas = new MyCanvas();
 
     // add a back Command to return to the menu screen
 
     // add a back Command to return to the menu screen
Line 73: Line 110:
 
}
 
}
 
</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 work. 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>
public class MyCanvas extends GameCanvas implements Runnable{
+
public class MyCanvas extends GameCanvas implements Runnable {
 
 
public void start() {
+
  public void start() {
  run = true;
+
    run = true;
  Thread t = new Thread(this);
+
    Thread t = new Thread(this);
  t.start();
+
    t.start();
}
+
  }
  
public void stop() {
+
  public void stop() {
  run = false;
+
    run = false;
}
+
  }
  
public void run(){
+
  public void run() {
  init();
+
    init();
  while (run){
+
    while (run) {
    // update game elements, positions, collisions, etc..
+
      // update game elements, positions, collisions, etc.
    updateGameState();
+
      updateGameState();
    // check user input
+
      // check user input
    checkUserInput();
+
      checkUserInput();
    // render screen
+
      // render screen
    updateGameScreen();
+
      updateGameScreen(getGraphics());
    // redraws screen
+
      // redraw screen
    flushGraphics();
+
      this.flushGraphics();
    // controls at which rate the updates are done
+
      // controls at which rate are the updates done
    Thread.sleep(10);
+
      try {
 +
        Thread.sleep(10);
 +
      } catch (InterruptedException e) { 
 +
        e.printStackTrace();
 +
      }
 +
    }
 +
    midlet.endGame(lifes, score, time);
 
   }
 
   }
}
 
 
 
 
}
 
}
 
</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. Through the <tt>getKeyStates()</tt> method you can access a bit array that indicates the state of each key.
  
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, when hit with the pad it bounces to other direction
* 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 through 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>
+
<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>
+
  
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 where 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 {
   public int radium = 2;   
+
   public int radius = 2;   
 
    
 
    
   public Ball(int radium){
+
   public Ball(int radius) {
     this.radium = radium;
+
     this.radius = radius;
     width = radium * 2;
+
     width = radius * 2;
     height = radium * 2;
+
     height = radius * 2;
 
     // red color
 
     // red color
 
     this.color = 0x00FF0000;
 
     this.color = 0x00FF0000;
Line 158: Line 197:
 
    
 
    
 
   /**
 
   /**
   * Paints the ball using a circle
+
   * paints the ball
 
   */
 
   */
 
   public void paint(Graphics g) {
 
   public void paint(Graphics g) {
 
     g.setColor(color);
 
     g.setColor(color);
     g.fillArc(x, y, radium*2, radium*2, 0, 360);
+
     g.fillArc(x, y, radius*2, radius*2, 0, 360);
 
   }
 
   }
 
    
 
    
   /***
+
   /**
   * Updates the ball position.
+
   * updates the ball position
 
   */
 
   */
 
   public void update() {
 
   public void update() {
 
     // update position
 
     // update position
     oldX=x;
+
     oldX = x;
     oldY=y;
+
     oldY = y;
 
     x += speedX;
 
     x += speedX;
 
     y += speedY;     
 
     y += speedY;     
Line 177: Line 216:
 
}
 
}
 
</code>
 
</code>
For the pad we have:
+
For the pad:
 
<code java>
 
<code java>
public class Pad extends Entity{   
+
public class Pad extends Entity {   
 
   int minLimit = 0;
 
   int minLimit = 0;
 
   int maxLimit = 1;
 
   int maxLimit = 1;
Line 189: Line 228:
 
    
 
    
 
   public void paint(Graphics g) {
 
   public void paint(Graphics g) {
     g.setColor(0,0,255);
+
     g.setColor(0, 0, 255);
 
     g.fillRect(x, y, width, height);
 
     g.fillRect(x, y, width, height);
 
   }
 
   }
  
 
   public void update() {
 
   public void update() {
     // change x position according the speed
+
     // change x position according to speed
 
     x += speedX;
 
     x += speedX;
     // check if world bounds are reached
+
   
 +
     // check if the world bounds are reached
 
     if (x < minLimit) {
 
     if (x < minLimit) {
 
       x = minLimit;
 
       x = minLimit;
     }  
+
     }
     if (x+width > maxLimit){
+
   
 +
     if (x + width > maxLimit){
 
       x = maxLimit - width;
 
       x = maxLimit - width;
     }  
+
     }  
 
   }
 
   }
 +
 
}
 
}
 
</code>
 
</code>
Line 213: Line 255:
 
   boolean active = true;   
 
   boolean active = true;   
 
    
 
    
   public Brick(int color){
+
   public Brick(int color) {
 
     this.color = color;
 
     this.color = color;
 
   }
 
   }
 
    
 
    
 
   public void paint(Graphics g) {
 
   public void paint(Graphics g) {
     // only paints if still active
+
     // only paints brick if it's still active
     if (active){
+
     if (active) {
 
       g.setColor(color);
 
       g.setColor(color);
 
       g.fillRect(x, y, width, height);
 
       g.fillRect(x, y, width, height);
 
     }
 
     }
 
 
   }
 
   }
  
Line 232: Line 273:
 
</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 in the canvas class. Create an <tt>init()</tt> method in the Canvas class.
 
<code java>
 
<code java>
public void init(){
+
public void init() {
 
     // resets lifes
 
     // resets lifes
 
     lifes = 3;
 
     lifes = 3;
 +
   
 
     // resets score
 
     // resets score
 
     score = 0;
 
     score = 0;
 +
   
 
     // resets time
 
     // resets time
 
     time = 0;
 
     time = 0;
 +
   
 
     // bricks hit
 
     // bricks hit
 
     bricksHit = 0;
 
     bricksHit = 0;
 +
   
 
     // create a pad
 
     // create a pad
     pad = new Pad(getWidth()/10,getWidth()/10/4);     
+
     pad = new Pad(getWidth()/10, getWidth()/10/4);     
     pad.x = (this.getWidth()-pad.width) / 2;
+
     pad.x = (this.getWidth() - pad.width) / 2;
 
     pad.y = this.getHeight() - (2*pad.height);
 
     pad.y = this.getHeight() - (2*pad.height);
 
     pad.maxLimit = getWidth();
 
     pad.maxLimit = getWidth();
 
     pad.minLimit = 0;
 
     pad.minLimit = 0;
  
     // create ball     
+
     // create a ball     
 
     ball = new Ball(4);
 
     ball = new Ball(4);
 
     ball.x = getWidth() / 2;
 
     ball.x = getWidth() / 2;
Line 256: Line 301:
 
     ball.speedX = 1;
 
     ball.speedX = 1;
 
     ball.speedY = 1;
 
     ball.speedY = 1;
 +
   
 
     // set collision limits
 
     // set collision limits
 
     wallMinX = 0;
 
     wallMinX = 0;
 
     wallMaxX = getWidth();     
 
     wallMaxX = getWidth();     
 
     wallMinY = 0;
 
     wallMinY = 0;
     // to allow to get out of screen
+
   
     wallMaxY = getHeight() + 4 * ball.radium;
+
     // to allow ball get out of screen
 +
     wallMaxY = getHeight() + 4 * ball.radius;
 
      
 
      
 
     // create bricks
 
     // create bricks
 
     Brick brick;
 
     Brick brick;
 
     bricks = new Vector();
 
     bricks = new Vector();
     for (int i=0; (i*(BRICK_WIDTH+2))<getWidth(); i++){
+
   
 +
     for (int i=0; (i*(BRICK_WIDTH+2))<getWidth(); i++) {
 
       brick = new Brick(Util.setColor(255,0,0));
 
       brick = new Brick(Util.setColor(255,0,0));
 
       brick.width  = BRICK_WIDTH;
 
       brick.width  = BRICK_WIDTH;
Line 276: Line 324:
 
   }
 
   }
 
</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
 
 
   protected void updateGameScreen(Graphics g) {
 
   protected void updateGameScreen(Graphics g) {
 
     // stores width and height
 
     // stores width and height
Line 289: Line 336:
 
     // draw score
 
     // draw score
 
     g.setColor(255,255,255);
 
     g.setColor(255,255,255);
     g.drawString("Score:"+score+" Lifes:"+lifes+" Time: "+time, 0, 0, Graphics.TOP|Graphics.LEFT);
+
     g.drawString("Score:" + score + " Lifes:" + lifes + " Time: " + time, 0, 0, Graphics.TOP|Graphics.LEFT);
 
     // draw game elements
 
     // draw game elements
 
     pad.paint(g);
 
     pad.paint(g);
 
     ball.paint(g);
 
     ball.paint(g);
 
     // draw bricks stored in the Vector bricks
 
     // draw bricks stored in the Vector bricks
     for (int i=0; i < bricks.size(); i++){
+
     for (int i=0; i < bricks.size(); i++) {
 
       Brick brick = (Brick)(bricks.elementAt(i));
 
       Brick brick = (Brick)(bricks.elementAt(i));
 
       brick.paint(g);
 
       brick.paint(g);
 
     }
 
     }
 
   }
 
   }
  // updates state of all element in the game
+
</code>
   public void updateGameState(){
+
<code java>
 +
   public void updateGameState() {
 
     pad.update();
 
     pad.update();
 
     ball.update();
 
     ball.update();
Line 309: Line 357:
 
     checkBallOutOfReach();
 
     checkBallOutOfReach();
 
      
 
      
     // check if bricks ended
+
     // check if all bricks are hit
     if (bricksHit == bricks.size()){
+
     if (bricksHit == bricks.size()) {
 
       run = false;
 
       run = false;
 
     }
 
     }
Line 316: Line 364:
 
</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>
  // update game entities according to use presses on keypad
 
 
   public void checkUserInput() {
 
   public void checkUserInput() {
 
     int state = getKeyStates();
 
     int state = getKeyStates();
     if ( (state & GameCanvas.LEFT_PRESSED) > 0) {
+
     if ((state & GameCanvas.LEFT_PRESSED) > 0) {
 
       // move left
 
       // move left
       pad.speedX=-1;    
+
       pad.speedX = -1;
     } else if ( (state & GameCanvas.RIGHT_PRESSED) > 0) {
+
     } else if ((state & GameCanvas.RIGHT_PRESSED) > 0) {
 
       // move right
 
       // move right
       pad.speedX=1;
+
       pad.speedX = 1;
 
     } else {
 
     } else {
 
       // don't move
 
       // don't move
       pad.speedX=0;
+
       pad.speedX = 0;
 
     }
 
     }
 
   }
 
   }
 
</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.
 +
 
 +
The next article explains how to use images to get a better looking game.
  
In the next class we going to see how we can use images to get a better looking game. See you soon.
+
== Downloads ==
 +
* [[Media:ArkanoidPart3Source.zip|Source files]]
 +
* [[Media:ArkanoidPart3Binaries.zip|Binary files]]
  
<strong>Downloads:</strong>
+
Go to [[Developing a 2D game in Java ME - Part 4]]
* [http://sergioestevao.com/midp/wp-content/uploads/2007/11/lesson3-source.rar Full Source Code]
+
* [http://sergioestevao.com/midp/wp-content/uploads/2007/11/formacao3.jad Jad File]
+
* [http://sergioestevao.com/midp/wp-content/uploads/2007/11/formacao3.jar Jar File]
+

Revision as of 10:53, 21 August 2012

This article shows how to use Java ME's low-level interface classes to create the game screen and access key presses. This is the third in a series of articles that cover all the basics of developing an application for mobile devices using Java ME, learning the main libraries, classes, and methods available in Java ME.

Article Metadata
Code ExampleTested with
Devices(s): Nokia 701, Nokia Asha 305
CompatibilityArticle
Created: SergioEstevao (21 Nov 2007)
Last edited: mtilli (21 Aug 2012)
Featured Article
24 Aug
2008

Introduction

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 group 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.
  • 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 triangle.
  • 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 work. 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(getGraphics());
// redraw screen
this.flushGraphics();
// controls at which rate are the updates done
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
midlet.endGame(lifes, score, time);
}

}

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. Through the getKeyStates() method you can access a bit array that indicates the state of each key.

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, when hit with the pad it bounces to other direction
  • 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 through 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.

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 where 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 radius = 2;
 
public Ball(int radius) {
this.radius = radius;
width = radius * 2;
height = radius * 2;
// red color
this.color = 0x00FF0000;
}
 
/**
* paints the ball
*/

public void paint(Graphics g) {
g.setColor(color);
g.fillArc(x, y, radius*2, radius*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 to speed
x += speedX;
 
// check if the 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 brick if it's 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 in the canvas class. Create an init() method in 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 a 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 ball get out of screen
wallMaxY = getHeight() + 4 * ball.radius;
 
// 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:

  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);
}
}
  public void updateGameState() {
pad.update();
ball.update();
 
checkBallCollisionWithWalls();
checkBallCollisionWihPad();
checkBallCollisionWithBricks();
checkBallOutOfReach();
 
// check if all bricks are hit
if (bricksHit == bricks.size()) {
run = false;
}
}

The game needs to react to keypad events to move the player pad:

  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 Developing a 2D game in Java ME - Part 4

570 page views in the last 30 days.
×