Kxgx.com - 移动开发网

本站快讯:
搜索: 您的位置主页>参考源码>游戏源码>>阅读源码:一个J2ME地牢游戏的源码

一个J2ME地牢游戏的源码

2006-05-29   来源:   作者:未知   【 】 评论:0条
import java.io.*;

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

/**
 * This is the main class of the dungeon game.
 
 @author Carol Hamer
 */

public class Dungeon extends MIDlet implements CommandListener {

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

  /**
   * The canvas that the dungeon is drawn on.
   */
  private DungeonCanvas myCanvas;

  /**
   * the thread that advances the game clock.
   */
  private GameThread myGameThread;

  //-----------------------------------------------------
  //    command fields

  /**
   * The button to exit the game.
   */

  private Command myExitCommand = new Command("Exit", Command.EXIT, 99);

  /**
   * The command to save the game in progress.
   */
  private Command mySaveCommand = new Command("Save Game", Command.SCREEN, 2);

  /**
   * The command to restore a previously saved game.
   */
  private Command myRestoreCommand = new Command("Restore Game",
      Command.SCREEN, 2);

  /**
   * the command to start moving when the game is paused.
   */
  private Command myGoCommand = new Command("Go", Command.SCREEN, 1);

  /**
   * the command to pause the game.
   */
  private Command myPauseCommand = new Command("Pause", Command.SCREEN, 1);



  /**
   * the command to start a new game.
   */
  private Command myNewCommand = new Command("Next Board", Command.SCREEN, 1);

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



  /**
   * Initialize the canvas and the commands.
   */
  public Dungeon() {
    try {
      // create the canvas and set up the commands:
      myCanvas = new DungeonCanvas(this);
      myCanvas.addCommand(myExitCommand);
      myCanvas.addCommand(mySaveCommand);
      myCanvas.addCommand(myRestoreCommand);
      myCanvas.addCommand(myPauseCommand);
      myCanvas.setCommandListener(this);


    catch (Exception e) {
      // if there's an error during creation, display it as an alert.
      errorMsg(e);
    }
  }

  /**
   * Switch the command to the play again command. (removing other commands

   * that are no longer relevant)
   */
  void setNewCommand() {
    myCanvas.removeCommand(myPauseCommand);
    myCanvas.removeCommand(myGoCommand);
    myCanvas.addCommand(myNewCommand);
  }

  /**
   * Switch the command to the go command. (removing other commands that are
   * no longer relevant)
   */
  void setGoCommand() {
    myCanvas.removeCommand(myPauseCommand);
    myCanvas.removeCommand(myNewCommand);
    myCanvas.addCommand(myGoCommand);
  }

  /**
   * Switch the command to the pause command. (removing other commands that
   * are no longer relevant)
   */
  void setPauseCommand() {
    myCanvas.removeCommand(myNewCommand);
    myCanvas.removeCommand(myGoCommand);
    myCanvas.addCommand(myPauseCommand);
  }

  //----------------------------------------------------------------
  //  implementation of MIDlet
  // these methods may be called by the application management
  // software at any time, so we always check fields for null

  // before calling methods on them.

  /**
   * Start the application.
   */
  public void startApp() throws MIDletStateChangeException {
    if (myCanvas != null) {
      if (myGameThread == null) {


        // create the thread and start the game:
        myGameThread = new GameThread(myCanvas);
        myCanvas.start();
        myGameThread.start();
      else {
        // in case this gets called again after
        // the application has been started once:
        myCanvas.removeCommand(myGoCommand);
        myCanvas.addCommand(myPauseCommand);
        myCanvas.flushKeys();
        myGameThread.resumeGame();
      }
    }
  }

  /**
   * Stop the threads and throw out the garbage.
   */
  public void destroyApp(boolean unconditional)

      throws MIDletStateChangeException {
    myCanvas = null;
    if (myGameThread != null) {
      myGameThread.requestStop();
    }
    myGameThread = null;


    System.gc();
  }

  /**
   * Pause the game.
   */
  public void pauseApp() {
    if (myCanvas != null) {
      setGoCommand();


    }
    if (myGameThread != null) {
      myGameThread.pause();
    }
  }

  //----------------------------------------------------------------
  //  implementation of CommandListener

  /*
   * Respond to a command issued on the Canvas. (reset, exit, or change size
   * prefs).
   */
  public void commandAction(Command c, Displayable s) {
    try {
      if (c == myGoCommand) {
        myCanvas.setNeedsRepaint();
        myCanvas.removeCommand(myGoCommand);
        myCanvas.addCommand(myPauseCommand);
        myCanvas.flushKeys();
        myGameThread.resumeGame();
      else if (c == myPauseCommand) {
        myCanvas.setNeedsRepaint();
        myCanvas.removeCommand(myPauseCommand);
        myCanvas.addCommand(myGoCommand);
        myGameThread.pause();

      else if (c == myNewCommand) {
        myCanvas.setNeedsRepaint();
        // go to the next board and restart the game
        myCanvas.removeCommand(myNewCommand);
        myCanvas.addCommand(myPauseCommand);


        myCanvas.reset();
        myGameThread.resumeGame();
      /*} else if (c == Alert.DISMISS_COMMAND) {
        // if there was a serious enough error to
        // cause an alert, then we end the game
        // when the user is done reading the alert:

        // (Alert.DISMISS_COMMAND is the default
        // command that is placed on an Alert
        // whose timeout is FOREVER)
        destroyApp(false);
        notifyDestroyed();*/
      else if (c == mySaveCommand) {
        myCanvas.setNeedsRepaint();
        myCanvas.saveGame();
      else if (c == myRestoreCommand) {
        myCanvas.setNeedsRepaint();
        myCanvas.removeCommand(myNewCommand);
        myCanvas.removeCommand(myGoCommand);


        myCanvas.addCommand(myPauseCommand);
        myCanvas.revertToSaved();
      else if (c == myExitCommand) {
        destroyApp(false);
        notifyDestroyed();


      }
    catch (Exception e) {
      errorMsg(e);
    }
  }

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

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

  /**
   * Displays an error message alert if something goes wrong.
   */
  void errorMsg(String msg) {
    Alert errorAlert = new Alert("error", msg, null, AlertType.ERROR);
    errorAlert.setCommandListener(this);
    errorAlert.setTimeout(Alert.FOREVER);
    Display.getDisplay(this).setCurrent(errorAlert);
  }

}

/**
 * This class represents doors and keys.
 
 @author Carol Hamer
 */

class DoorKey extends Sprite {



  //---------------------------------------------------------
  //    fields

  /**
   * The image file shared by all doors and keys.
   */
  public static Image myImage;

  /**
   * A code int that indicates the door or key's color.
   */
  private int myColor;

  //---------------------------------------------------------
  //    get/set data

  /**
   @return the door or key's color.
   */
  public int getColor() {
    return (myColor);
  }

  //---------------------------------------------------------
  //    constructor and initializer

  static {
    try {
      myImage = Image.createImage("/images/keys.png");
    catch (Exception e) {
      throw (new RuntimeException(
          "DoorKey.<init>-->failed to load image, caught "
              + e.getClass() ": " + e.getMessage()));
    }
  }

  /**
   * Standard constructor sets the image to the correct frame (according to
   * whether this is a door or a key and what color it should be) and then
   * puts it in the correct location.
   */
  public DoorKey(int color, boolean isKey, int[] gridCoordinates) {


    super(myImage, DungeonManager.SQUARE_WIDTH, DungeonManager.SQUARE_WIDTH);
    myColor = color;
    int imageIndex = color * 2;
    if (isKey) {
      imageIndex++;
    }
    setFrame(imageIndex);
    setPosition(gridCoordinates[0* DungeonManager.SQUARE_WIDTH,
        gridCoordinates[1* DungeonManager.SQUARE_WIDTH);
  }

}

/**
 * This class is a set of simple utility functions that can be used to convert
 * standard data types to bytes and back again. It is used especially for data
 * storage, but also for sending and receiving data.
 
 @author Carol Hamer
 */

class DataConverter {

  //--------------------------------------------------------
  //  utilities to encode small, compactly-stored small ints.

  /**
   * Encodes a coordinate pair into a byte.
   
   @param coordPair
   *            a pair of integers to be compacted into a single byte for
   *            storage. WARNING: each of the two values MUST BE between 0 and
   *            15 (inclusive). This method does not verify the length of the
   *            array (which must be 2!) nor does it verify that the ints are
   *            of the right size.
   */
  public static byte encodeCoords(int[] coordPair) {
    // get the byte value of the first coordinate:

    byte retVal = (new Integer(coordPair[0])).byteValue();
    // move the first coordinate's value up to the top
    // half of the storage byte:
    retVal = (new Integer(retVal << 4)).byteValue();

    // store the second coordinate in the lower half
    // of the byte:
    retVal += (new Integer(coordPair[1])).byteValue();
    return (retVal);
  }

  /**
   * Encodes eight ints into a byte. This could be easily modified to encode
   * eight booleans.
   
   @param eight
   *            an array of at least eight ints. WARNING: all values must be 0
   *            or 1! This method does not verify that the values are in the

   *            correct range nor does it verify that the array is long
   *            enough.
   @param offset
   *            the index in the array eight to start reading data from.
   *            (should usually be 0)
   */

  public static byte encode8(int[] eight, int offset) {
    // get the byte value of the first int:
    byte retVal = (new Integer(eight[offset])).byteValue();

    // progressively move the data up one bit in the
    // storage byte and then record the next int in
    // the lowest spot in the storage byte:
    for (int i = offset + 1; i < + offset; i++) {
      retVal = (new Integer(retVal << 1)).byteValue();
      retVal += (new Integer(eight[i])).byteValue();
    }
    return (retVal);
  }

  //--------------------------------------------------------
  //  utilities to decode small, compactly-stored small ints.

  /**
   * Turns a byte into a pair of coordinates.
   */
  public static int[] decodeCoords(byte coordByte) {
    int[] retArray = new int[2];

    // we perform a bitwise and with the value 15
    // in order to just get the bits of the lower
    // half of the byte:
    retArray[1= coordByte & 15;
    // To get the bits of the upper half of the
    // byte, we perform a shift to move them down:
    retArray[0= coordByte >> 4;
    // bytes in Java are generally assumed to be
    // signed, but in this coding algorithm we
    // would like to treat them as unsigned:
    if (retArray[00) {


      retArray[0+= 16;
    }
    return (retArray);
  }

  /**
   * Turns a byte into eight ints.
   */
  public static int[] decode8(byte data) {

    int[] retArray = new int[8];
    // The flag allows us to look at each bit individually
    // to determine if it is 1 or 0. The number 128
    // corresponds to the highest bit of a byte, so we
    // start with that one.


    int flag = 128;
    // We use a loop that checks
    // the data bit by bit by performing a bitwise
    // and (&) between the data byte and a flag:
    for (int i = 0; i < 8; i++) {

      if ((flag & data!= 0) {
        retArray[i1;
      else {
        retArray[i0;

      }
      // move the flag down one bit so that we can
      // check the next bit of data on the next pass
      // through the loop:
      flag = flag >> 1;
    }
    return (retArray);
  }

  //--------------------------------------------------------
  //  standard integer interpretation

  /**
   * Uses an input stream to convert an array of bytes to an int.
   */
  public static int parseInt(byte[] datathrows IOException {


    DataInputStream stream = new DataInputStream(new ByteArrayInputStream(
        data));
    int retVal = stream.readInt();
    stream.close();
    return (retVal);
  }

  /**
   * Uses an output stream to convert an int to four bytes.
   */
  public static byte[] intToFourBytes(int ithrows IOException {
    ByteArrayOutputStream baos = new ByteArrayOutputStream(4);

    DataOutputStream dos = new DataOutputStream(baos);
    dos.writeInt(i);
    baos.close();
    dos.close();
    byte[] retArray = baos.toByteArray();
    return (retArray);
  }

  //--------------------------------------------------------
  //  integer interpretation illustrated

  /**
   * Java appears to treat a byte as being signed when returning it as an
   * int--this function converts from the signed value to the corresponding

   * unsigned value. This method is used by nostreamParseInt.
   */
  public static int unsign(int signed) {
    int retVal = signed;
    if (retVal < 0) {
      retVal += 256;
    }
    return (retVal);
  }

  /**
   * Takes an array of bytes and returns an int. This version will return the
   * same value as the method parseInt above. This version is included in
   * order to illustrate how Java encodes int values in terms of bytes.


   
   @param data
   *            an array of 1, 2, or 4 bytes.
   */
  public static int nostreamParseInt(byte[] data) {
    // byte 0 is the high byte which is assumed


    // to be signed. As we add the lower bytes
    // one by one, we unsign them because because
    // a single byte alone is interpreted as signed,
    // but in an int only the top byte should be signed.
    // (note that the high byte is the first one in the array)
    int retVal = data[0];

    for (int i = 1; i < data.length; i++) {
      retVal = retVal << 8;
      retVal += unsign(data[i]);
    }
    return (retVal);


  }

  /**
   * Takes an arbitrary int and returns an array of four bytes. This version
   * will return the same byte array as the method intToFourBytes above. This
   * version is included in order to illustrate how Java encodes int values in
   * terms of bytes.
   */
  public static byte[] nostreamIntToFourBytes(int i) {

    byte[] fourBytes = new byte[4];
    // when you take the byte value of an int, it
    // only gives you the lowest byte. So we
    // get all four bytes by taking the lowest
    // byte four times and moving the whole int
    // down by one byte between each one.
    // (note that the high byte is the first one in the array)
    fourBytes[3(new Integer(i)).byteValue();
    i = i >> 8;
    fourBytes[2(new Integer(i)).byteValue();
    i = i >> 8;
    fourBytes[1(new Integer(i)).byteValue();
    i = i >> 8;
    fourBytes[0(new Integer(i)).byteValue();
    return (fourBytes);
  }

  /**
   * Takes an int between -32768 and 32767 and returns an array of two bytes.
   * This does not verify that the argument is of the right size. If the
   * absolute value of i is too high, it will not be encoded correctly.
   */
  public static byte[] nostreamIntToTwoBytes(int i) {

    byte[] twoBytes = new byte[2];
    // when you take the byte value of an int, it
    // only gives you the lowest byte. So we
    // get the lower two bytes by taking the lowest
    // byte twice and moving the whole int


    // down by one byte between each one.
    twoBytes[1(new Integer(i)).byteValue();
    i = i >> 8;
    twoBytes[0(new Integer(i)).byteValue();
    return (twoBytes);
  }

}
/**
 * This class contains the data for the map of the dungeon..
 
 @author Carol Hamer
 */

class BoardDecoder {

  //--------------------------------------------------------
  //  fields

  /**
   * The coordinates of where the player starts on the map in terms of the
   * array indices.
   */
  private int[] myPlayerSquare;

  /**


   * The coordinates of the goal (crown).
   */
  private int[] myGoalSquare;

  /**
   * The coordinates of the doors. the there should be two in a row of each
   * color, following the same sequence as the keys.
   */
  private int[][] myDoors;


  /**
   * The coordinates of the Keys. the there should be of each color, following
   * the same sequence as the doors.
   */
  private int[][] myKeys;

  /**
   * The coordinates of the stone walls of the maze, encoded bit by bit.


   */
  private TiledLayer myLayer;

  /**
   * The data in bytes that gives the various boards. This was created using
   * EncodingUtils... This is a two-dimensional array: Each of the four main
   * sections corresponds to one of the four possible boards.
   */
  private static byte[][] myData = {
      00, -108, -100, -2465215853, -54, -116, -58, -56, -84,
          115, -118, -1, -1, -1281, -103, -15, -12825, -97, -127,
          -12879, -141, -126121, -1221, -113, -49, -1161,
          -100, -3, -1245, -25, -27, -1281, -1, -},


      0112290, -6234, -4372, -59, -2956, -5598126, -79,

          61, -1, -1, -1251, -12817, -2629, -3157, -721,
          -128, -51, -10065, -12457, -21, -12613, -1131,

          -9725, -127, -99, -81, -1, -},
      02108, -2418, -2610230, -5846, -28, -8834, -9897,


          -41, -1, -1, -961, -12657, -997, -12769, -11973,
          -1271, -10959, -1261, -26103, -12765, -103115,


          -12765, -2573, -1281, -1, -},
      03, -11418, -3427, -39, -60, -76, -501189082, -88,
          34, -74, -1, -1, -661, -128121, -26125, -128, -123,
          -10329, -1121, -10949, -1121, -116, -31, -1285,

          -1225, -3213, -127, -51, -1251, -1, -}};

  //--------------------------------------------------------
  //  initialization


  /**
   * Constructor fills data fields by interpreting the data bytes.
   */
  public BoardDecoder(int boardNumthrows Exception {
    // we start by selecting the two dimensional
    // array corresponding to the desired board:


    byte[] data = myData[boardNum];
    // The first two bytes give the version number and
    // the board number, but we ignore them because
    // they are assumed to be correct.
    // The third byte of the first array is the first one
    // we read: it gives the player's starting coordinates:

    myPlayerSquare = DataConverter.decodeCoords(data[2]);
    // the next byte gives the coordinates of the crown:
    myGoalSquare = DataConverter.decodeCoords(data[3]);
    // the next four bytes give the coordinates of the keys:
    myKeys = new int[4][];


    for (int i = 0; i < myKeys.length; i++) {
      myKeys[i= DataConverter.decodeCoords(data[i + 4]);
    }
    // the next eight bytes give the coordinates of the doors:
    myDoors = new int[8][];
    for (int i = 0; i < myDoors.length; i++) {
      myDoors[i= DataConverter.decodeCoords(data[i + 8]);

    }
    // now we create the TiledLayer object that is the
    // background dungeon map:
    myLayer = new TiledLayer(1616,
        Image.createImage("/images/stone.png"),
        DungeonManager.SQUARE_WIDTH, DungeonManager.SQUARE_WIDTH);

    // now we call an internal utility that reads the array
    // of data that gives the positions of the blocks in the
    // walls of this dungeon:
    decodeDungeon(data, myLayer, 16);
  }

  //--------------------------------------------------------
  //  get/set data


  /**
   @return the number of boards currently stored in this class.
   */
  public static int getNumBoards() {
    return (myData.length);
  }

  /**


   * get the coordinates of where the player starts on the map in terms of the
   * array indices.
   */
  public int[] getPlayerSquare() {
    return (myPlayerSquare);
  }

  /**
   * get the coordinates of the goal crown in terms of the array indices.
   */
  public int[] getGoalSquare() {
    return (myGoalSquare);
  }

  /**
   * get the tiled layer that gives the map of the dungeon.
   */
  public TiledLayer getLayer() {
    return (myLayer);
  }

  /**
   * Creates the array of door sprites. (call this only once to avoid creating
   * redundant sprites).
   */
  DoorKey[] createDoors() {
    DoorKey[] retArray = new DoorKey[8];
    for (int i = 0; i < 4; i++) {
      retArray[* inew DoorKey(i, false, myDoors[* i]);

      retArray[* i + 1new DoorKey(i, false, myDoors[* i + 1]);
    }
    return (retArray);
  }

  /**
   * Creates the array of key sprites. (call this only once to avoid creating
   * redundant sprites.)
   */
  DoorKey[] createKeys() {
    DoorKey[] retArray =