Java/Game/Game Swing — различия между версиями

Материал из Java эксперт
Перейти к: навигация, поиск
 
м (1 версия)
 
(нет различий)

Текущая версия на 07:07, 1 июня 2010

Game-Swing:MenuTest

       /*
DEVELOPING GAME IN JAVA 
Caracteristiques
Editeur : NEW RIDERS 
Auteur : BRACKEEN 
Parution : 09 2003 
Pages : 972 
Isbn : 1-59273-005-1 
Reliure : Paperback 
Disponibilite : Disponible a la librairie 
*/

import java.awt.AWTException;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.ruponent;
import java.awt.ruposite;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.DisplayMode;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.Point;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.Transparency;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.RepaintManager;
import javax.swing.SwingUtilities;
/**
 * Extends the InputManagerTest demo and adds Swing buttons for pause, config
 * and quit.
 */
public class MenuTest extends InputManagerTest implements ActionListener {
  public static void main(String[] args) {
    new MenuTest().run();
  }
  protected GameAction configAction;
  private JButton playButton;
  private JButton configButton;
  private JButton quitButton;
  private JButton pauseButton;
  private JPanel playButtonSpace;
  public void init() {
    super.init();
    // make sure Swing components don"t paint themselves
    NullRepaintManager.install();
    // create an addtional GameAction for "config"
    configAction = new GameAction("config");
    // create buttons
    quitButton = createButton("quit", "Quit");
    playButton = createButton("play", "Continue");
    pauseButton = createButton("pause", "Pause");
    configButton = createButton("config", "Change Settings");
    // create the space where the play/pause buttons go.
    playButtonSpace = new JPanel();
    playButtonSpace.setOpaque(false);
    playButtonSpace.add(pauseButton);
    JFrame frame = super.screen.getFullScreenWindow();
    Container contentPane = frame.getContentPane();
    // make sure the content pane is transparent
    if (contentPane instanceof JComponent) {
      ((JComponent) contentPane).setOpaque(false);
    }
    // add components to the screen"s content pane
    contentPane.setLayout(new FlowLayout(FlowLayout.LEFT));
    contentPane.add(playButtonSpace);
    contentPane.add(configButton);
    contentPane.add(quitButton);
    // explicitly layout components (needed on some systems)
    frame.validate();
  }
  /**
   * Extends InputManagerTest"s functionality to draw all Swing components.
   */
  public void draw(Graphics2D g) {
    super.draw(g);
    JFrame frame = super.screen.getFullScreenWindow();
    // the layered pane contains things like popups (tooltips,
    // popup menus) and the content pane.
    frame.getLayeredPane().paintComponents(g);
  }
  /**
   * Changes the pause/play button whenever the pause state changes.
   */
  public void setPaused(boolean p) {
    super.setPaused(p);
    playButtonSpace.removeAll();
    if (isPaused()) {
      playButtonSpace.add(playButton);
    } else {
      playButtonSpace.add(pauseButton);
    }
  }
  /**
   * Called by the AWT event dispatch thread when a button is pressed.
   */
  public void actionPerformed(ActionEvent e) {
    Object src = e.getSource();
    if (src == quitButton) {
      // fire the "exit" gameAction
      super.exit.tap();
    } else if (src == configButton) {
      // doesn"t do anything (for now)
      configAction.tap();
    } else if (src == playButton || src == pauseButton) {
      // fire the "pause" gameAction
      super.pause.tap();
    }
  }
  /**
   * Creates a Swing JButton. The image used for the button is located at
   * "../images/menu/" + name + ".png". The image is modified to create a
   * "default" look (translucent) and a "pressed" look (moved down and to the
   * right).
   * <p>
   * The button doesn"t use Swing"s look-and-feel and instead just uses the
   * image.
   */
  public JButton createButton(String name, String toolTip) {
    // load the image
    String imagePath = "../images/menu/" + name + ".png";
    ImageIcon iconRollover = new ImageIcon(imagePath);
    int w = iconRollover.getIconWidth();
    int h = iconRollover.getIconHeight();
    // get the cursor for this button
    Cursor cursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
    // make translucent default image
    Image image = screen.createCompatibleImage(w, h,
        Transparency.TRANSLUCENT);
    Graphics2D g = (Graphics2D) image.getGraphics();
    Composite alpha = AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
        .5f);
    g.setComposite(alpha);
    g.drawImage(iconRollover.getImage(), 0, 0, null);
    g.dispose();
    ImageIcon iconDefault = new ImageIcon(image);
    // make a pressed iamge
    image = screen.createCompatibleImage(w, h, Transparency.TRANSLUCENT);
    g = (Graphics2D) image.getGraphics();
    g.drawImage(iconRollover.getImage(), 2, 2, null);
    g.dispose();
    ImageIcon iconPressed = new ImageIcon(image);
    // create the button
    JButton button = new JButton();
    button.addActionListener(this);
    button.setIgnoreRepaint(true);
    button.setFocusable(false);
    button.setToolTipText(toolTip);
    button.setBorder(null);
    button.setContentAreaFilled(false);
    button.setCursor(cursor);
    button.setIcon(iconDefault);
    button.setRolloverIcon(iconRollover);
    button.setPressedIcon(iconPressed);
    return button;
  }
}
/**
 * InputManagerTest tests the InputManager with a simple run-and-jump mechanism.
 * The player moves and jumps using the arrow keys and the space bar.
 * <p>
 * Also, InputManagerTest demonstrates pausing a game by not updating the game
 * elements if the game is paused.
 */
class InputManagerTest extends GameCore {
  public static void main(String[] args) {
    new InputManagerTest().run();
  }
  protected GameAction jump;
  protected GameAction exit;
  protected GameAction moveLeft;
  protected GameAction moveRight;
  protected GameAction pause;
  protected InputManager inputManager;
  private Player player;
  private Image bgImage;
  private boolean paused;
  public void init() {
    super.init();
    Window window = screen.getFullScreenWindow();
    inputManager = new InputManager(window);
    // use these lines for relative mouse mode
    //inputManager.setRelativeMouseMode(true);
    //inputManager.setCursor(InputManager.INVISIBLE_CURSOR);
    createGameActions();
    createSprite();
    paused = false;
  }
  /**
   * Tests whether the game is paused or not.
   */
  public boolean isPaused() {
    return paused;
  }
  /**
   * Sets the paused state.
   */
  public void setPaused(boolean p) {
    if (paused != p) {
      this.paused = p;
      inputManager.resetAllGameActions();
    }
  }
  public void update(long elapsedTime) {
    // check input that can happen whether paused or not
    checkSystemInput();
    if (!isPaused()) {
      // check game input
      checkGameInput();
      // update sprite
      player.update(elapsedTime);
    }
  }
  /**
   * Checks input from GameActions that can be pressed regardless of whether
   * the game is paused or not.
   */
  public void checkSystemInput() {
    if (pause.isPressed()) {
      setPaused(!isPaused());
    }
    if (exit.isPressed()) {
      stop();
    }
  }
  /**
   * Checks input from GameActions that can be pressed only when the game is
   * not paused.
   */
  public void checkGameInput() {
    float velocityX = 0;
    if (moveLeft.isPressed()) {
      velocityX -= Player.SPEED;
    }
    if (moveRight.isPressed()) {
      velocityX += Player.SPEED;
    }
    player.setVelocityX(velocityX);
    if (jump.isPressed() && player.getState() != Player.STATE_JUMPING) {
      player.jump();
    }
  }
  public void draw(Graphics2D g) {
    // draw background
    g.drawImage(bgImage, 0, 0, null);
    // draw sprite
    g.drawImage(player.getImage(), Math.round(player.getX()), Math
        .round(player.getY()), null);
  }
  /**
   * Creates GameActions and maps them to keys.
   */
  public void createGameActions() {
    jump = new GameAction("jump", GameAction.DETECT_INITAL_PRESS_ONLY);
    exit = new GameAction("exit", GameAction.DETECT_INITAL_PRESS_ONLY);
    moveLeft = new GameAction("moveLeft");
    moveRight = new GameAction("moveRight");
    pause = new GameAction("pause", GameAction.DETECT_INITAL_PRESS_ONLY);
    inputManager.mapToKey(exit, KeyEvent.VK_ESCAPE);
    inputManager.mapToKey(pause, KeyEvent.VK_P);
    // jump with spacebar or mouse button
    inputManager.mapToKey(jump, KeyEvent.VK_SPACE);
    inputManager.mapToMouse(jump, InputManager.MOUSE_BUTTON_1);
    // move with the arrow keys...
    inputManager.mapToKey(moveLeft, KeyEvent.VK_LEFT);
    inputManager.mapToKey(moveRight, KeyEvent.VK_RIGHT);
    // ... or with A and D.
    inputManager.mapToKey(moveLeft, KeyEvent.VK_A);
    inputManager.mapToKey(moveRight, KeyEvent.VK_D);
    // use these lines to map player movement to the mouse
    //inputManager.mapToMouse(moveLeft,
    //  InputManager.MOUSE_MOVE_LEFT);
    //inputManager.mapToMouse(moveRight,
    //  InputManager.MOUSE_MOVE_RIGHT);
  }
  /**
   * Load images and creates the Player sprite.
   */
  private void createSprite() {
    // load images
    bgImage = loadImage("../images/background.jpg");
    Image player1 = loadImage("../images/player1.png");
    Image player2 = loadImage("../images/player2.png");
    Image player3 = loadImage("../images/player3.png");
    // create animation
    Animation anim = new Animation();
    anim.addFrame(player1, 250);
    anim.addFrame(player2, 150);
    anim.addFrame(player1, 150);
    anim.addFrame(player2, 150);
    anim.addFrame(player3, 200);
    anim.addFrame(player2, 150);
    player = new Player(anim);
    player.setFloorY(screen.getHeight() - player.getHeight());
  }
}
/**
 * The Player extends the Sprite class to add states (STATE_NORMAL or
 * STATE_JUMPING) and gravity.
 */
class Player extends Sprite {
  public static final int STATE_NORMAL = 0;
  public static final int STATE_JUMPING = 1;
  public static final float SPEED = .3f;
  public static final float GRAVITY = .002f;
  private int floorY;
  private int state;
  public Player(Animation anim) {
    super(anim);
    state = STATE_NORMAL;
  }
  /**
   * Gets the state of the Player (either STATE_NORMAL or STATE_JUMPING);
   */
  public int getState() {
    return state;
  }
  /**
   * Sets the state of the Player (either STATE_NORMAL or STATE_JUMPING);
   */
  public void setState(int state) {
    this.state = state;
  }
  /**
   * Sets the location of "floor", where the Player starts and lands after
   * jumping.
   */
  public void setFloorY(int floorY) {
    this.floorY = floorY;
    setY(floorY);
  }
  /**
   * Causes the Player to jump
   */
  public void jump() {
    setVelocityY(-1);
    state = STATE_JUMPING;
  }
  /**
   * Updates the player"s positon and animation. Also, sets the Player"s state
   * to NORMAL if a jumping Player landed on the floor.
   */
  public void update(long elapsedTime) {
    // set vertical velocity (gravity effect)
    if (getState() == STATE_JUMPING) {
      setVelocityY(getVelocityY() + GRAVITY * elapsedTime);
    }
    // move player
    super.update(elapsedTime);
    // check if player landed on floor
    if (getState() == STATE_JUMPING && getY() >= floorY) {
      setVelocityY(0);
      setY(floorY);
      setState(STATE_NORMAL);
    }
  }
}
/**
 * The GameAction class is an abstract to a user-initiated action, like jumping
 * or moving. GameActions can be mapped to keys or the mouse with the
 * InputManager.
 */
class GameAction {
  /**
   * Normal behavior. The isPressed() method returns true as long as the key
   * is held down.
   */
  public static final int NORMAL = 0;
  /**
   * Initial press behavior. The isPressed() method returns true only after
   * the key is first pressed, and not again until the key is released and
   * pressed again.
   */
  public static final int DETECT_INITAL_PRESS_ONLY = 1;
  private static final int STATE_RELEASED = 0;
  private static final int STATE_PRESSED = 1;
  private static final int STATE_WAITING_FOR_RELEASE = 2;
  private String name;
  private int behavior;
  private int amount;
  private int state;
  /**
   * Create a new GameAction with the NORMAL behavior.
   */
  public GameAction(String name) {
    this(name, NORMAL);
  }
  /**
   * Create a new GameAction with the specified behavior.
   */
  public GameAction(String name, int behavior) {
    this.name = name;
    this.behavior = behavior;
    reset();
  }
  /**
   * Gets the name of this GameAction.
   */
  public String getName() {
    return name;
  }
  /**
   * Resets this GameAction so that it appears like it hasn"t been pressed.
   */
  public void reset() {
    state = STATE_RELEASED;
    amount = 0;
  }
  /**
   * Taps this GameAction. Same as calling press() followed by release().
   */
  public synchronized void tap() {
    press();
    release();
  }
  /**
   * Signals that the key was pressed.
   */
  public synchronized void press() {
    press(1);
  }
  /**
   * Signals that the key was pressed a specified number of times, or that the
   * mouse move a spcified distance.
   */
  public synchronized void press(int amount) {
    if (state != STATE_WAITING_FOR_RELEASE) {
      this.amount += amount;
      state = STATE_PRESSED;
    }
  }
  /**
   * Signals that the key was released
   */
  public synchronized void release() {
    state = STATE_RELEASED;
  }
  /**
   * Returns whether the key was pressed or not since last checked.
   */
  public synchronized boolean isPressed() {
    return (getAmount() != 0);
  }
  /**
   * For keys, this is the number of times the key was pressed since it was
   * last checked. For mouse movement, this is the distance moved.
   */
  public synchronized int getAmount() {
    int retVal = amount;
    if (retVal != 0) {
      if (state == STATE_RELEASED) {
        amount = 0;
      } else if (behavior == DETECT_INITAL_PRESS_ONLY) {
        state = STATE_WAITING_FOR_RELEASE;
        amount = 0;
      }
    }
    return retVal;
  }
}
/**
 * Simple abstract class used for testing. Subclasses should implement the
 * draw() method.
 */
abstract class GameCore {
  protected static final int FONT_SIZE = 24;
  private static final DisplayMode POSSIBLE_MODES[] = {
      new DisplayMode(800, 600, 32, 0), new DisplayMode(800, 600, 24, 0),
      new DisplayMode(800, 600, 16, 0), new DisplayMode(640, 480, 32, 0),
      new DisplayMode(640, 480, 24, 0), new DisplayMode(640, 480, 16, 0) };
  private boolean isRunning;
  protected ScreenManager screen;
  /**
   * Signals the game loop that it"s time to quit
   */
  public void stop() {
    isRunning = false;
  }
  /**
   * Calls init() and gameLoop()
   */
  public void run() {
    try {
      init();
      gameLoop();
    } finally {
      screen.restoreScreen();
    }
  }
  /**
   * Sets full screen mode and initiates and objects.
   */
  public void init() {
    screen = new ScreenManager();
    DisplayMode displayMode = screen
        .findFirstCompatibleMode(POSSIBLE_MODES);
    screen.setFullScreen(displayMode);
    Window window = screen.getFullScreenWindow();
    window.setFont(new Font("Dialog", Font.PLAIN, FONT_SIZE));
    window.setBackground(Color.blue);
    window.setForeground(Color.white);
    isRunning = true;
  }
  public Image loadImage(String fileName) {
    return new ImageIcon(fileName).getImage();
  }
  /**
   * Runs through the game loop until stop() is called.
   */
  public void gameLoop() {
    long startTime = System.currentTimeMillis();
    long currTime = startTime;
    while (isRunning) {
      long elapsedTime = System.currentTimeMillis() - currTime;
      currTime += elapsedTime;
      // update
      update(elapsedTime);
      // draw the screen
      Graphics2D g = screen.getGraphics();
      draw(g);
      g.dispose();
      screen.update();
      // take a nap
      try {
        Thread.sleep(20);
      } catch (InterruptedException ex) {
      }
    }
  }
  /**
   * Updates the state of the game/animation based on the amount of elapsed
   * time that has passed.
   */
  public void update(long elapsedTime) {
    // do nothing
  }
  /**
   * Draws to the screen. Subclasses must override this method.
   */
  public abstract void draw(Graphics2D g);
}
/**
 * The ScreenManager class manages initializing and displaying full screen
 * graphics modes.
 */
class ScreenManager {
  private GraphicsDevice device;
  /**
   * Creates a new ScreenManager object.
   */
  public ScreenManager() {
    GraphicsEnvironment environment = GraphicsEnvironment
        .getLocalGraphicsEnvironment();
    device = environment.getDefaultScreenDevice();
  }
  /**
   * Returns a list of compatible display modes for the default device on the
   * system.
   */
  public DisplayMode[] getCompatibleDisplayModes() {
    return device.getDisplayModes();
  }
  /**
   * Returns the first compatible mode in a list of modes. Returns null if no
   * modes are compatible.
   */
  public DisplayMode findFirstCompatibleMode(DisplayMode modes[]) {
    DisplayMode goodModes[] = device.getDisplayModes();
    for (int i = 0; i < modes.length; i++) {
      for (int j = 0; j < goodModes.length; j++) {
        if (displayModesMatch(modes[i], goodModes[j])) {
          return modes[i];
        }
      }
    }
    return null;
  }
  /**
   * Returns the current display mode.
   */
  public DisplayMode getCurrentDisplayMode() {
    return device.getDisplayMode();
  }
  /**
   * Determines if two display modes "match". Two display modes match if they
   * have the same resolution, bit depth, and refresh rate. The bit depth is
   * ignored if one of the modes has a bit depth of
   * DisplayMode.BIT_DEPTH_MULTI. Likewise, the refresh rate is ignored if one
   * of the modes has a refresh rate of DisplayMode.REFRESH_RATE_UNKNOWN.
   */
  public boolean displayModesMatch(DisplayMode mode1, DisplayMode mode2)
  {
    if (mode1.getWidth() != mode2.getWidth()
        || mode1.getHeight() != mode2.getHeight()) {
      return false;
    }
    if (mode1.getBitDepth() != DisplayMode.BIT_DEPTH_MULTI
        && mode2.getBitDepth() != DisplayMode.BIT_DEPTH_MULTI
        && mode1.getBitDepth() != mode2.getBitDepth()) {
      return false;
    }
    if (mode1.getRefreshRate() != DisplayMode.REFRESH_RATE_UNKNOWN
        && mode2.getRefreshRate() != DisplayMode.REFRESH_RATE_UNKNOWN
        && mode1.getRefreshRate() != mode2.getRefreshRate()) {
      return false;
    }
    return true;
  }
  /**
   * Enters full screen mode and changes the display mode. If the specified
   * display mode is null or not compatible with this device, or if the
   * display mode cannot be changed on this system, the current display mode
   * is used.
   * <p>
   * The display uses a BufferStrategy with 2 buffers.
   */
  public void setFullScreen(DisplayMode displayMode) {
    final JFrame frame = new JFrame();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setUndecorated(true);
    frame.setIgnoreRepaint(true);
    frame.setResizable(false);
    device.setFullScreenWindow(frame);
    if (displayMode != null && device.isDisplayChangeSupported()) {
      try {
        device.setDisplayMode(displayMode);
      } catch (IllegalArgumentException ex) {
      }
      // fix for mac os x
      frame.setSize(displayMode.getWidth(), displayMode.getHeight());
    }
    // avoid potential deadlock in 1.4.1_02
    try {
      EventQueue.invokeAndWait(new Runnable() {
        public void run() {
          frame.createBufferStrategy(2);
        }
      });
    } catch (InterruptedException ex) {
      // ignore
    } catch (InvocationTargetException ex) {
      // ignore
    }
  }
  /**
   * Gets the graphics context for the display. The ScreenManager uses double
   * buffering, so applications must call update() to show any graphics drawn.
   * <p>
   * The application must dispose of the graphics object.
   */
  public Graphics2D getGraphics() {
    Window window = device.getFullScreenWindow();
    if (window != null) {
      BufferStrategy strategy = window.getBufferStrategy();
      return (Graphics2D) strategy.getDrawGraphics();
    } else {
      return null;
    }
  }
  /**
   * Updates the display.
   */
  public void update() {
    Window window = device.getFullScreenWindow();
    if (window != null) {
      BufferStrategy strategy = window.getBufferStrategy();
      if (!strategy.contentsLost()) {
        strategy.show();
      }
    }
    // Sync the display on some systems.
    // (on Linux, this fixes event queue problems)
    Toolkit.getDefaultToolkit().sync();
  }
  /**
   * Returns the window currently used in full screen mode. Returns null if
   * the device is not in full screen mode.
   */
  public JFrame getFullScreenWindow() {
    return (JFrame) device.getFullScreenWindow();
  }
  /**
   * Returns the width of the window currently used in full screen mode.
   * Returns 0 if the device is not in full screen mode.
   */
  public int getWidth() {
    Window window = device.getFullScreenWindow();
    if (window != null) {
      return window.getWidth();
    } else {
      return 0;
    }
  }
  /**
   * Returns the height of the window currently used in full screen mode.
   * Returns 0 if the device is not in full screen mode.
   */
  public int getHeight() {
    Window window = device.getFullScreenWindow();
    if (window != null) {
      return window.getHeight();
    } else {
      return 0;
    }
  }
  /**
   * Restores the screen"s display mode.
   */
  public void restoreScreen() {
    Window window = device.getFullScreenWindow();
    if (window != null) {
      window.dispose();
    }
    device.setFullScreenWindow(null);
  }
  /**
   * Creates an image compatible with the current display.
   */
  public BufferedImage createCompatibleImage(int w, int h, int transparancy) {
    Window window = device.getFullScreenWindow();
    if (window != null) {
      GraphicsConfiguration gc = window.getGraphicsConfiguration();
      return gc.createCompatibleImage(w, h, transparancy);
    }
    return null;
  }
}
/**
 * The InputManager manages input of key and mouse events. Events are mapped to
 * GameActions.
 */
class InputManager implements KeyListener, MouseListener, MouseMotionListener,
    MouseWheelListener {
  /**
   * An invisible cursor.
   */
  public static final Cursor INVISIBLE_CURSOR = Toolkit.getDefaultToolkit()
      .createCustomCursor(Toolkit.getDefaultToolkit().getImage(""),
          new Point(0, 0), "invisible");
  // mouse codes
  public static final int MOUSE_MOVE_LEFT = 0;
  public static final int MOUSE_MOVE_RIGHT = 1;
  public static final int MOUSE_MOVE_UP = 2;
  public static final int MOUSE_MOVE_DOWN = 3;
  public static final int MOUSE_WHEEL_UP = 4;
  public static final int MOUSE_WHEEL_DOWN = 5;
  public static final int MOUSE_BUTTON_1 = 6;
  public static final int MOUSE_BUTTON_2 = 7;
  public static final int MOUSE_BUTTON_3 = 8;
  private static final int NUM_MOUSE_CODES = 9;
  // key codes are defined in java.awt.KeyEvent.
  // most of the codes (except for some rare ones like
  // "alt graph") are less than 600.
  private static final int NUM_KEY_CODES = 600;
  private GameAction[] keyActions = new GameAction[NUM_KEY_CODES];
  private GameAction[] mouseActions = new GameAction[NUM_MOUSE_CODES];
  private Point mouseLocation;
  private Point centerLocation;
  private Component comp;
  private Robot robot;
  private boolean isRecentering;
  /**
   * Creates a new InputManager that listens to input from the specified
   * component.
   */
  public InputManager(Component comp) {
    this.rup = comp;
    mouseLocation = new Point();
    centerLocation = new Point();
    // register key and mouse listeners
    comp.addKeyListener(this);
    comp.addMouseListener(this);
    comp.addMouseMotionListener(this);
    comp.addMouseWheelListener(this);
    // allow input of the TAB key and other keys normally
    // used for focus traversal
    comp.setFocusTraversalKeysEnabled(false);
  }
  /**
   * Sets the cursor on this InputManager"s input component.
   */
  public void setCursor(Cursor cursor) {
    comp.setCursor(cursor);
  }
  /**
   * Sets whether realtive mouse mode is on or not. For relative mouse mode,
   * the mouse is "locked" in the center of the screen, and only the changed
   * in mouse movement is measured. In normal mode, the mouse is free to move
   * about the screen.
   */
  public void setRelativeMouseMode(boolean mode) {
    if (mode == isRelativeMouseMode()) {
      return;
    }
    if (mode) {
      try {
        robot = new Robot();
        recenterMouse();
      } catch (AWTException ex) {
        // couldn"t create robot!
        robot = null;
      }
    } else {
      robot = null;
    }
  }
  /**
   * Returns whether or not relative mouse mode is on.
   */
  public boolean isRelativeMouseMode() {
    return (robot != null);
  }
  /**
   * Maps a GameAction to a specific key. The key codes are defined in
   * java.awt.KeyEvent. If the key already has a GameAction mapped to it, the
   * new GameAction overwrites it.
   */
  public void mapToKey(GameAction gameAction, int keyCode) {
    keyActions[keyCode] = gameAction;
  }
  /**
   * Maps a GameAction to a specific mouse action. The mouse codes are defined
   * herer in InputManager (MOUSE_MOVE_LEFT, MOUSE_BUTTON_1, etc). If the
   * mouse action already has a GameAction mapped to it, the new GameAction
   * overwrites it.
   */
  public void mapToMouse(GameAction gameAction, int mouseCode) {
    mouseActions[mouseCode] = gameAction;
  }
  /**
   * Clears all mapped keys and mouse actions to this GameAction.
   */
  public void clearMap(GameAction gameAction) {
    for (int i = 0; i < keyActions.length; i++) {
      if (keyActions[i] == gameAction) {
        keyActions[i] = null;
      }
    }
    for (int i = 0; i < mouseActions.length; i++) {
      if (mouseActions[i] == gameAction) {
        mouseActions[i] = null;
      }
    }
    gameAction.reset();
  }
  /**
   * Gets a List of names of the keys and mouse actions mapped to this
   * GameAction. Each entry in the List is a String.
   */
  public List getMaps(GameAction gameCode) {
    ArrayList list = new ArrayList();
    for (int i = 0; i < keyActions.length; i++) {
      if (keyActions[i] == gameCode) {
        list.add(getKeyName(i));
      }
    }
    for (int i = 0; i < mouseActions.length; i++) {
      if (mouseActions[i] == gameCode) {
        list.add(getMouseName(i));
      }
    }
    return list;
  }
  /**
   * Resets all GameActions so they appear like they haven"t been pressed.
   */
  public void resetAllGameActions() {
    for (int i = 0; i < keyActions.length; i++) {
      if (keyActions[i] != null) {
        keyActions[i].reset();
      }
    }
    for (int i = 0; i < mouseActions.length; i++) {
      if (mouseActions[i] != null) {
        mouseActions[i].reset();
      }
    }
  }
  /**
   * Gets the name of a key code.
   */
  public static String getKeyName(int keyCode) {
    return KeyEvent.getKeyText(keyCode);
  }
  /**
   * Gets the name of a mouse code.
   */
  public static String getMouseName(int mouseCode) {
    switch (mouseCode) {
    case MOUSE_MOVE_LEFT:
      return "Mouse Left";
    case MOUSE_MOVE_RIGHT:
      return "Mouse Right";
    case MOUSE_MOVE_UP:
      return "Mouse Up";
    case MOUSE_MOVE_DOWN:
      return "Mouse Down";
    case MOUSE_WHEEL_UP:
      return "Mouse Wheel Up";
    case MOUSE_WHEEL_DOWN:
      return "Mouse Wheel Down";
    case MOUSE_BUTTON_1:
      return "Mouse Button 1";
    case MOUSE_BUTTON_2:
      return "Mouse Button 2";
    case MOUSE_BUTTON_3:
      return "Mouse Button 3";
    default:
      return "Unknown mouse code " + mouseCode;
    }
  }
  /**
   * Gets the x position of the mouse.
   */
  public int getMouseX() {
    return mouseLocation.x;
  }
  /**
   * Gets the y position of the mouse.
   */
  public int getMouseY() {
    return mouseLocation.y;
  }
  /**
   * Uses the Robot class to try to postion the mouse in the center of the
   * screen.
   * <p>
   * Note that use of the Robot class may not be available on all platforms.
   */
  private synchronized void recenterMouse() {
    if (robot != null && comp.isShowing()) {
      centerLocation.x = comp.getWidth() / 2;
      centerLocation.y = comp.getHeight() / 2;
      SwingUtilities.convertPointToScreen(centerLocation, comp);
      isRecentering = true;
      robot.mouseMove(centerLocation.x, centerLocation.y);
    }
  }
  private GameAction getKeyAction(KeyEvent e) {
    int keyCode = e.getKeyCode();
    if (keyCode < keyActions.length) {
      return keyActions[keyCode];
    } else {
      return null;
    }
  }
  /**
   * Gets the mouse code for the button specified in this MouseEvent.
   */
  public static int getMouseButtonCode(MouseEvent e) {
    switch (e.getButton()) {
    case MouseEvent.BUTTON1:
      return MOUSE_BUTTON_1;
    case MouseEvent.BUTTON2:
      return MOUSE_BUTTON_2;
    case MouseEvent.BUTTON3:
      return MOUSE_BUTTON_3;
    default:
      return -1;
    }
  }
  private GameAction getMouseButtonAction(MouseEvent e) {
    int mouseCode = getMouseButtonCode(e);
    if (mouseCode != -1) {
      return mouseActions[mouseCode];
    } else {
      return null;
    }
  }
  // from the KeyListener interface
  public void keyPressed(KeyEvent e) {
    GameAction gameAction = getKeyAction(e);
    if (gameAction != null) {
      gameAction.press();
    }
    // make sure the key isn"t processed for anything else
    e.consume();
  }
  // from the KeyListener interface
  public void keyReleased(KeyEvent e) {
    GameAction gameAction = getKeyAction(e);
    if (gameAction != null) {
      gameAction.release();
    }
    // make sure the key isn"t processed for anything else
    e.consume();
  }
  // from the KeyListener interface
  public void keyTyped(KeyEvent e) {
    // make sure the key isn"t processed for anything else
    e.consume();
  }
  // from the MouseListener interface
  public void mousePressed(MouseEvent e) {
    GameAction gameAction = getMouseButtonAction(e);
    if (gameAction != null) {
      gameAction.press();
    }
  }
  // from the MouseListener interface
  public void mouseReleased(MouseEvent e) {
    GameAction gameAction = getMouseButtonAction(e);
    if (gameAction != null) {
      gameAction.release();
    }
  }
  // from the MouseListener interface
  public void mouseClicked(MouseEvent e) {
    // do nothing
  }
  // from the MouseListener interface
  public void mouseEntered(MouseEvent e) {
    mouseMoved(e);
  }
  // from the MouseListener interface
  public void mouseExited(MouseEvent e) {
    mouseMoved(e);
  }
  // from the MouseMotionListener interface
  public void mouseDragged(MouseEvent e) {
    mouseMoved(e);
  }
  // from the MouseMotionListener interface
  public synchronized void mouseMoved(MouseEvent e) {
    // this event is from re-centering the mouse - ignore it
    if (isRecentering && centerLocation.x == e.getX()
        && centerLocation.y == e.getY()) {
      isRecentering = false;
    } else {
      int dx = e.getX() - mouseLocation.x;
      int dy = e.getY() - mouseLocation.y;
      mouseHelper(MOUSE_MOVE_LEFT, MOUSE_MOVE_RIGHT, dx);
      mouseHelper(MOUSE_MOVE_UP, MOUSE_MOVE_DOWN, dy);
      if (isRelativeMouseMode()) {
        recenterMouse();
      }
    }
    mouseLocation.x = e.getX();
    mouseLocation.y = e.getY();
  }
  // from the MouseWheelListener interface
  public void mouseWheelMoved(MouseWheelEvent e) {
    mouseHelper(MOUSE_WHEEL_UP, MOUSE_WHEEL_DOWN, e.getWheelRotation());
  }
  private void mouseHelper(int codeNeg, int codePos, int amount) {
    GameAction gameAction;
    if (amount < 0) {
      gameAction = mouseActions[codeNeg];
    } else {
      gameAction = mouseActions[codePos];
    }
    if (gameAction != null) {
      gameAction.press(Math.abs(amount));
      gameAction.release();
    }
  }
}
class Sprite {
  private Animation anim;
  // position (pixels)
  private float x;
  private float y;
  // velocity (pixels per millisecond)
  private float dx;
  private float dy;
  /**
   * Creates a new Sprite object with the specified Animation.
   */
  public Sprite(Animation anim) {
    this.anim = anim;
  }
  /**
   * Updates this Sprite"s Animation and its position based on the velocity.
   */
  public void update(long elapsedTime) {
    x += dx * elapsedTime;
    y += dy * elapsedTime;
    anim.update(elapsedTime);
  }
  /**
   * Gets this Sprite"s current x position.
   */
  public float getX() {
    return x;
  }
  /**
   * Gets this Sprite"s current y position.
   */
  public float getY() {
    return y;
  }
  /**
   * Sets this Sprite"s current x position.
   */
  public void setX(float x) {
    this.x = x;
  }
  /**
   * Sets this Sprite"s current y position.
   */
  public void setY(float y) {
    this.y = y;
  }
  /**
   * Gets this Sprite"s width, based on the size of the current image.
   */
  public int getWidth() {
    return anim.getImage().getWidth(null);
  }
  /**
   * Gets this Sprite"s height, based on the size of the current image.
   */
  public int getHeight() {
    return anim.getImage().getHeight(null);
  }
  /**
   * Gets the horizontal velocity of this Sprite in pixels per millisecond.
   */
  public float getVelocityX() {
    return dx;
  }
  /**
   * Gets the vertical velocity of this Sprite in pixels per millisecond.
   */
  public float getVelocityY() {
    return dy;
  }
  /**
   * Sets the horizontal velocity of this Sprite in pixels per millisecond.
   */
  public void setVelocityX(float dx) {
    this.dx = dx;
  }
  /**
   * Sets the vertical velocity of this Sprite in pixels per millisecond.
   */
  public void setVelocityY(float dy) {
    this.dy = dy;
  }
  /**
   * Gets this Sprite"s current image.
   */
  public Image getImage() {
    return anim.getImage();
  }
}
/**
 * The Animation class manages a series of images (frames) and the amount of
 * time to display each frame.
 */
class Animation {
  private ArrayList frames;
  private int currFrameIndex;
  private long animTime;
  private long totalDuration;
  /**
   * Creates a new, empty Animation.
   */
  public Animation() {
    frames = new ArrayList();
    totalDuration = 0;
    start();
  }
  /**
   * Adds an image to the animation with the specified duration (time to
   * display the image).
   */
  public synchronized void addFrame(Image image, long duration) {
    totalDuration += duration;
    frames.add(new AnimFrame(image, totalDuration));
  }
  /**
   * Starts this animation over from the beginning.
   */
  public synchronized void start() {
    animTime = 0;
    currFrameIndex = 0;
  }
  /**
   * Updates this animation"s current image (frame), if neccesary.
   */
  public synchronized void update(long elapsedTime) {
    if (frames.size() > 1) {
      animTime += elapsedTime;
      if (animTime >= totalDuration) {
        animTime = animTime % totalDuration;
        currFrameIndex = 0;
      }
      while (animTime > getFrame(currFrameIndex).endTime) {
        currFrameIndex++;
      }
    }
  }
  /**
   * Gets this Animation"s current image. Returns null if this animation has
   * no images.
   */
  public synchronized Image getImage() {
    if (frames.size() == 0) {
      return null;
    } else {
      return getFrame(currFrameIndex).image;
    }
  }
  private AnimFrame getFrame(int i) {
    return (AnimFrame) frames.get(i);
  }
  private class AnimFrame {
    Image image;
    long endTime;
    public AnimFrame(Image image, long endTime) {
      this.image = image;
      this.endTime = endTime;
    }
  }
}
/**
 * The NullRepaintManager is a RepaintManager that doesn"t do any repainting.
 * Useful when all the rendering is done manually by the application.
 */
class NullRepaintManager extends RepaintManager {
  /**
   * Installs the NullRepaintManager.
   */
  public static void install() {
    RepaintManager repaintManager = new NullRepaintManager();
    repaintManager.setDoubleBufferingEnabled(false);
    RepaintManager.setCurrentManager(repaintManager);
  }
  public void addInvalidComponent(JComponent c) {
    // do nothing
  }
  public void addDirtyRegion(JComponent c, int x, int y, int w, int h) {
    // do nothing
  }
  public void markCompletelyDirty(JComponent c) {
    // do nothing
  }
  public void paintDirtyRegions() {
    // do nothing
  }
}