www.pudn.com > resources.zip > JumpCanvas.java


package net.frog_parrot.jump;

import javax.microedition.lcdui.*;
import javax.microedition.lcdui.game.*;

/**
 * This class is the display of the game.
 * 
 * @author Carol Hamer
 */
public class JumpCanvas extends javax.microedition.lcdui.game.GameCanvas {

  //---------------------------------------------------------
  //   dimension fields
  //  (constant after initialization)

  /**
   * the height of the green region below the ground.
   */
  static int GROUND_HEIGHT = 32;

  /**
   * a screen dimension.
   */
  static int CORNER_X;

  /**
   * a screen dimension.
   */
  static int CORNER_Y;

  /**
   * a screen dimension.
   */
  static int DISP_WIDTH;

  /**
   * a screen dimension.
   */
  static int DISP_HEIGHT;

  /**
   * a font dimension.
   */
  static int FONT_HEIGHT;

  /**
   * the default font.
   */
  static Font FONT;

  /**
   * a font dimension.
   */
  static int SCORE_WIDTH;

  /**
   * The width of the string that displays the time,
   * saved for placement of time display.
   */
  static int TIME_WIDTH;

  //---------------------------------------------------------
  //   game object fields

  /**
   * a handle to the display.
   */
  Display myDisplay;

  /**
   * a handle to the MIDlet object (to keep track of buttons).
   */
  Jump myJump;

  /**
   * the LayerManager that handles the game graphics.
   */
  JumpManager myManager;

  /**
   * whether or not the game has ended.
   */
  static boolean myGameOver;

  /**
   * the player's score.
   */
  int myScore = 0;

  /**
   * How many ticks we start with.
   */
  int myInitialGameTicks = 950;

  /**
   * this is saved to determine if the time string needs 
   * to be recomputed.
   */
  int myOldGameTicks = myInitialGameTicks;

  /**
   * the number of game ticks that have passed.
   */
  int myGameTicks = myOldGameTicks;

  /**
   * whether or not this has been painted once.
   */
  boolean myInitialized;

  /**
   * we save the time string to avoid recreating it 
   * unnecessarily.
   */
  static String myInitialString = "1:00";

  /**
   * we save the time string to avoid recreating it 
   * unnecessarily.
   */
  String myTimeString = myInitialString;

  //-----------------------------------------------------
  //    gets/sets

  /**
   * This is called when the game ends.
   */
  static void setGameOver() {
    myGameOver = true;
    GameThread.requestStop();
  }

  /**
   * Find out if the game has ended.
   */
  static boolean getGameOver() {
    return(myGameOver);
  }

  //-----------------------------------------------------
  //    initialization and game state changes

  /**
   * Constructor sets the data, performs dimension calculations, 
   * and creates the graphical objects.
   */
  public JumpCanvas(Jump midlet) {
    super(false);
    myDisplay = Display.getDisplay(midlet);
    myJump = midlet;
  }

  /**
   * This is called as soon as the application begins.
   */
  void start() {
    myGameOver = false;
    myDisplay.setCurrent(this);
    repaint();
  }

  /**
   * sets all variables back to their initial positions.
   */
  void reset() {
    myManager.reset();
    myScore = 0;
    myGameOver = false;
    myGameTicks = myInitialGameTicks;
    myOldGameTicks = myInitialGameTicks;
    repaint();
  }

  /**
   * clears the key states.
   */
  void flushKeys() {
    getKeyStates();
  }

  //-------------------------------------------------------
  //  graphics methods

  /**
   * paint the game graphic on the screen.
   */
  public void paint(Graphics g) {
    // perform the calculations if necessary:
    if(!myInitialized) {
      CORNER_X = g.getClipX();
      CORNER_Y = g.getClipY();
      DISP_WIDTH = g.getClipWidth();
      DISP_HEIGHT = g.getClipHeight();
      FONT = g.getFont();
      FONT_HEIGHT = FONT.getHeight();
      SCORE_WIDTH = FONT.stringWidth("Score: 000");
      TIME_WIDTH = FONT.stringWidth("Time: " + myInitialString);
      myInitialized = true;
    }
    // clear the screen:
    g.setColor(0xffffff);
    g.fillRect(CORNER_X, CORNER_Y, DISP_WIDTH, DISP_HEIGHT);
    g.setColor(0x0000ff00);
    g.fillRect(CORNER_X, CORNER_Y + DISP_HEIGHT - GROUND_HEIGHT, 
	       DISP_WIDTH, DISP_HEIGHT);
    // create (if necessary) then paint the layer manager:
    try {
      if(myManager == null) {
	myManager = new JumpManager(CORNER_X, CORNER_Y + FONT_HEIGHT*2, 
	     DISP_WIDTH, DISP_HEIGHT - FONT_HEIGHT*2 - GROUND_HEIGHT);
      } 
      myManager.paint(g);
    } catch(Exception e) {
      errorMsg(g, e);
    }
    // draw the time and score
    g.setColor(0);
    g.setFont(FONT);
    g.drawString("Score: " + myScore, 
		 (DISP_WIDTH - SCORE_WIDTH)/2, 
		 DISP_HEIGHT + 5 - GROUND_HEIGHT, g.TOP|g.LEFT);
    g.drawString("Time: " + formatTime(), 
		   (DISP_WIDTH - TIME_WIDTH)/2, 
		   CORNER_Y + FONT_HEIGHT, g.TOP|g.LEFT);
    // write game over if the game is over
    if(myGameOver) {
      myJump.setNewCommand();
      // clear the top region:
      g.setColor(0xffffff);
      g.fillRect(CORNER_X, CORNER_Y, DISP_WIDTH, FONT_HEIGHT*2 + 1);
      int goWidth = FONT.stringWidth("Game Over");
      g.setColor(0);
      g.setFont(FONT);
      g.drawString("Game Over", (DISP_WIDTH - goWidth)/2, 
      		   CORNER_Y + FONT_HEIGHT, g.TOP|g.LEFT);
    }
  }

  /**
   * a simple utility to make the number of ticks look like a time...
   */
  public String formatTime() {
    if((myGameTicks / 16) + 1 != myOldGameTicks) {
      myTimeString = "";
      myOldGameTicks = (myGameTicks / 16) + 1;
      int smallPart = myOldGameTicks % 60;
      int bigPart = myOldGameTicks / 60;
      myTimeString += bigPart + ":";
      if(smallPart / 10 < 1) {
	myTimeString += "0";
      }
      myTimeString += smallPart;
    }
    return(myTimeString);
  }

  //-------------------------------------------------------
  //  game movements

  /**
   * Tell the layer manager to advance the layers and then 
   * update the display.
   */
  void advance() {
    myGameTicks--;
    myScore += myManager.advance(myGameTicks);
    if(myGameTicks == 0) {
      setGameOver();
    }
    // paint the display
    try {
      paint(getGraphics());
      flushGraphics();
    } catch(Exception e) {
      errorMsg(e);
    }
    // we do a very short pause to allow the other thread 
    // to update the information about which keys are pressed:
    synchronized(this) {
      try {
	wait(1);
      } catch(Exception e) {}
    }
  }

  /**
   * Respond to keystrokes.
   */
  public void checkKeys() { 
    if(! myGameOver) {
      int keyState = getKeyStates();
      if((keyState & LEFT_PRESSED) != 0) {
	myManager.setLeft(true);
      } 
      if((keyState & RIGHT_PRESSED) != 0) {
	myManager.setLeft(false);
      }
      if((keyState & UP_PRESSED) != 0) {
	myManager.jump();
      } 
    }
  }

  //-------------------------------------------------------
  //  error methods

  /**
   * Converts an exception to a message and displays 
   * the message..
   */
  void errorMsg(Exception e) {
    errorMsg(getGraphics(), e);
    flushGraphics();
  }

  /**
   * Converts an exception to a message and displays 
   * the message..
   */
  void errorMsg(Graphics g, Exception e) {
    if(e.getMessage() == null) {
      errorMsg(g, e.getClass().getName());
    } else {
      errorMsg(g, e.getClass().getName() + ":" + e.getMessage());
    }
  }

  /**
   * Displays an error message if something goes wrong.
   */
  void errorMsg(Graphics g, String msg) {
    // clear the screen
    g.setColor(0xffffff);
    g.fillRect(CORNER_X, CORNER_Y, DISP_WIDTH, DISP_HEIGHT);
    int msgWidth = FONT.stringWidth(msg);
    // write the message in red
    g.setColor(0x00ff0000);
    g.setFont(FONT);
    g.drawString(msg, (DISP_WIDTH - msgWidth)/2, 
		 (DISP_HEIGHT - FONT_HEIGHT)/2, g.TOP|g.LEFT);
    myGameOver = true;
  }

}