Java/Game/Input
A simple keyboard test
/*
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.Color;
import java.awt.DisplayMode;
import java.awt.EventQueue;
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.RenderingHints;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.lang.reflect.InvocationTargetException;
import java.util.LinkedList;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
/**
* A simple keyboard test. Displays keys pressed and released to the screen.
* Useful for debugging key input, too.
*/
public class KeyTest extends GameCore implements KeyListener {
public static void main(String[] args) {
new KeyTest().run();
}
private LinkedList messages = new LinkedList();
public void init() {
super.init();
Window window = screen.getFullScreenWindow();
// allow input of the TAB key and other keys normally
// used for focus traversal
window.setFocusTraversalKeysEnabled(false);
// register this object as a key listener for the window
window.addKeyListener(this);
addMessage("KeyInputTest. Press Escape to exit");
}
// a method from the KeyListner interface
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
// exit the program
if (keyCode == KeyEvent.VK_ESCAPE) {
stop();
} else {
addMessage("Pressed: " + KeyEvent.getKeyText(keyCode));
// make sure the key isn"t processed for anything else
e.consume();
}
}
// a method from the KeyListner interface
public void keyReleased(KeyEvent e) {
int keyCode = e.getKeyCode();
addMessage("Released: " + KeyEvent.getKeyText(keyCode));
// make sure the key isn"t processed for anything else
e.consume();
}
// a method from the KeyListener interface
public void keyTyped(KeyEvent e) {
// this is called after the key is released - ignore it
// make sure the key isn"t processed for anything else
e.consume();
}
/**
* Add a message to the list of messages.
*/
public synchronized void addMessage(String message) {
messages.add(message);
if (messages.size() >= screen.getHeight() / FONT_SIZE) {
messages.remove(0);
}
}
/**
* Draw the list of messages
*/
public synchronized void draw(Graphics2D g) {
Window window = screen.getFullScreenWindow();
g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
// draw background
g.setColor(window.getBackground());
g.fillRect(0, 0, screen.getWidth(), screen.getHeight());
// draw messages
g.setColor(window.getForeground());
int y = FONT_SIZE;
for (int i = 0; i < messages.size(); i++) {
g.drawString((String) messages.get(i), 5, y);
y += FONT_SIZE;
}
}
}
/**
* 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;
}
}
Input Manager Test
/*
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.Color;
import java.awt.ruponent;
import java.awt.Cursor;
import java.awt.DisplayMode;
import java.awt.EventQueue;
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.Window;
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.JFrame;
import javax.swing.SwingUtilities;
/**
* 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.
*/
public 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;
}
}
}
Key Config Test
/*
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.BorderLayout;
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.GridLayout;
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.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.RepaintManager;
import javax.swing.SwingUtilities;
import javax.swing.border.Border;
/**
* Extends the InputManagerTest demo and adds Swing buttons for pause, config
* and quit.
*/
/**
* The KeyConfigTest class extends the MenuTest demo to add a dialog to
* configure the keyboard keys.
*/
public class KeyConfigTest extends MenuTest {
public static void main(String[] args) {
new KeyConfigTest().run();
}
private static final String INSTRUCTIONS = "<html>Click an action"s input box to change it"s keys."
+ "<br>An action can have at most three keys associated "
+ "with it.<br>Press Backspace to clear an action"s keys.";
private JPanel dialog;
private JButton okButton;
private List inputs;
public void init() {
super.init();
inputs = new ArrayList();
// create the list of GameActions and mapped keys
JPanel configPanel = new JPanel(new GridLayout(5, 2, 2, 2));
addActionConfig(configPanel, moveLeft);
addActionConfig(configPanel, moveRight);
addActionConfig(configPanel, jump);
addActionConfig(configPanel, pause);
addActionConfig(configPanel, exit);
// create the panel containing the OK button
JPanel bottomPanel = new JPanel(new FlowLayout());
okButton = new JButton("OK");
okButton.setFocusable(false);
okButton.addActionListener(this);
bottomPanel.add(okButton);
// create the panel containing the instructions.
JPanel topPanel = new JPanel(new FlowLayout());
topPanel.add(new JLabel(INSTRUCTIONS));
// create the dialog border
Border border = BorderFactory.createLineBorder(Color.black);
// create the config dialog.
dialog = new JPanel(new BorderLayout());
dialog.add(topPanel, BorderLayout.NORTH);
dialog.add(configPanel, BorderLayout.CENTER);
dialog.add(bottomPanel, BorderLayout.SOUTH);
dialog.setBorder(border);
dialog.setVisible(false);
dialog.setSize(dialog.getPreferredSize());
// center the dialog
dialog.setLocation((screen.getWidth() - dialog.getWidth()) / 2, (screen
.getHeight() - dialog.getHeight()) / 2);
// add the dialog to the "modal dialog" layer of the
// screen"s layered pane.
screen.getFullScreenWindow().getLayeredPane().add(dialog,
JLayeredPane.MODAL_LAYER);
}
/**
* Adds a label containing the name of the GameAction and an InputComponent
* used for changing the mapped keys.
*/
private void addActionConfig(JPanel configPanel, GameAction action) {
JLabel label = new JLabel(action.getName(), JLabel.RIGHT);
InputComponent input = new InputComponent(action);
configPanel.add(label);
configPanel.add(input);
inputs.add(input);
}
public void actionPerformed(ActionEvent e) {
super.actionPerformed(e);
if (e.getSource() == okButton) {
// hides the config dialog
configAction.tap();
}
}
public void checkSystemInput() {
super.checkSystemInput();
if (configAction.isPressed()) {
// hide or show the config dialog
boolean show = !dialog.isVisible();
dialog.setVisible(show);
setPaused(show);
}
}
/**
* Resets the text displayed in each InputComponent, which is the names of
* the mapped keys.
*/
private void resetInputs() {
for (int i = 0; i < inputs.size(); i++) {
((InputComponent) inputs.get(i)).setText();
}
}
/**
* The InputComponent class displays the keys mapped to a particular action
* and allows the user to change the mapped keys. The user selects an
* InputComponent by clicking it, then can press any key or mouse button
* (including the mouse wheel) to change the mapped value.
*/
class InputComponent extends JTextField {
private GameAction action;
/**
* Creates a new InputComponent for the specified GameAction.
*/
public InputComponent(GameAction action) {
this.action = action;
setText();
enableEvents(KeyEvent.KEY_EVENT_MASK | MouseEvent.MOUSE_EVENT_MASK
| MouseEvent.MOUSE_MOTION_EVENT_MASK
| MouseEvent.MOUSE_WHEEL_EVENT_MASK);
}
/**
* Sets the displayed text of this InputComponent to the names of the
* mapped keys.
*/
private void setText() {
String text = "";
List list = inputManager.getMaps(action);
if (list.size() > 0) {
for (int i = 0; i < list.size(); i++) {
text += (String) list.get(i) + ", ";
}
// remove the last comma
text = text.substring(0, text.length() - 2);
}
// make sure we don"t get deadlock
synchronized (getTreeLock()) {
setText(text);
}
}
/**
* Maps the GameAction for this InputComponent to the specified key or
* mouse action.
*/
private void mapGameAction(int code, boolean isMouseMap) {
if (inputManager.getMaps(action).size() >= 3) {
inputManager.clearMap(action);
}
if (isMouseMap) {
inputManager.mapToMouse(action, code);
} else {
inputManager.mapToKey(action, code);
}
resetInputs();
screen.getFullScreenWindow().requestFocus();
}
// alternative way to intercept key events
protected void processKeyEvent(KeyEvent e) {
if (e.getID() == e.KEY_PRESSED) {
// if backspace is pressed, clear the map
if (e.getKeyCode() == KeyEvent.VK_BACK_SPACE
&& inputManager.getMaps(action).size() > 0) {
inputManager.clearMap(action);
setText("");
screen.getFullScreenWindow().requestFocus();
} else {
mapGameAction(e.getKeyCode(), false);
}
}
e.consume();
}
// alternative way to intercept mouse events
protected void processMouseEvent(MouseEvent e) {
if (e.getID() == e.MOUSE_PRESSED) {
if (hasFocus()) {
int code = InputManager.getMouseButtonCode(e);
mapGameAction(code, true);
} else {
requestFocus();
}
}
e.consume();
}
// alternative way to intercept mouse events
protected void processMouseMotionEvent(MouseEvent e) {
e.consume();
}
// alternative way to intercept mouse events
protected void processMouseWheelEvent(MouseWheelEvent e) {
if (hasFocus()) {
int code = InputManager.MOUSE_WHEEL_DOWN;
if (e.getWheelRotation() < 0) {
code = InputManager.MOUSE_WHEEL_UP;
}
mapGameAction(code, true);
}
e.consume();
}
}
}
/**
* Extends the InputManagerTest demo and adds Swing buttons for pause, config
* and quit.
*/
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
}
}