|
/*
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.Component;
import java.awt.Cursor;
import java.awt.DisplayMode;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Graphics;
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.Rectangle;
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;
public class Simple3DTest2 extends GameCore {
public static void main(String[] args) {
new Simple3DTest2().run();
}
protected PolygonRenderer polygonRenderer;
protected ViewWindow viewWindow;
protected List polygons;
private boolean drawFrameRate = false;
private boolean drawInstructions = true;
// for calculating frame rate
private int numFrames;
private long startTime;
private float frameRate;
protected InputManager inputManager;
private GameAction exit = new GameAction("exit");
private GameAction smallerView = new GameAction("smallerView",
GameAction.DETECT_INITAL_PRESS_ONLY);
private GameAction largerView = new GameAction("largerView",
GameAction.DETECT_INITAL_PRESS_ONLY);
private GameAction frameRateToggle = new GameAction("frameRateToggle",
GameAction.DETECT_INITAL_PRESS_ONLY);
private GameAction goForward = new GameAction("goForward");
private GameAction goBackward = new GameAction("goBackward");
private GameAction goUp = new GameAction("goUp");
private GameAction goDown = new GameAction("goDown");
private GameAction goLeft = new GameAction("goLeft");
private GameAction goRight = new GameAction("goRight");
private GameAction turnLeft = new GameAction("turnLeft");
private GameAction turnRight = new GameAction("turnRight");
private GameAction tiltUp = new GameAction("tiltUp");
private GameAction tiltDown = new GameAction("tiltDown");
private GameAction tiltLeft = new GameAction("tiltLeft");
private GameAction tiltRight = new GameAction("tiltRight");
public void init(DisplayMode[] modes) {
super.init(modes);
inputManager = new InputManager(screen.getFullScreenWindow());
inputManager.setRelativeMouseMode(true);
inputManager.setCursor(InputManager.INVISIBLE_CURSOR);
inputManager.mapToKey(exit, KeyEvent.VK_ESCAPE);
inputManager.mapToKey(goForward, KeyEvent.VK_W);
inputManager.mapToKey(goForward, KeyEvent.VK_UP);
inputManager.mapToKey(goBackward, KeyEvent.VK_S);
inputManager.mapToKey(goBackward, KeyEvent.VK_DOWN);
inputManager.mapToKey(goLeft, KeyEvent.VK_A);
inputManager.mapToKey(goLeft, KeyEvent.VK_LEFT);
inputManager.mapToKey(goRight, KeyEvent.VK_D);
inputManager.mapToKey(goRight, KeyEvent.VK_RIGHT);
inputManager.mapToKey(goUp, KeyEvent.VK_PAGE_UP);
inputManager.mapToKey(goDown, KeyEvent.VK_PAGE_DOWN);
inputManager.mapToMouse(turnLeft, InputManager.MOUSE_MOVE_LEFT);
inputManager.mapToMouse(turnRight, InputManager.MOUSE_MOVE_RIGHT);
inputManager.mapToMouse(tiltUp, InputManager.MOUSE_MOVE_UP);
inputManager.mapToMouse(tiltDown, InputManager.MOUSE_MOVE_DOWN);
inputManager.mapToKey(tiltLeft, KeyEvent.VK_INSERT);
inputManager.mapToKey(tiltRight, KeyEvent.VK_DELETE);
inputManager.mapToKey(smallerView, KeyEvent.VK_SUBTRACT);
inputManager.mapToKey(smallerView, KeyEvent.VK_MINUS);
inputManager.mapToKey(largerView, KeyEvent.VK_ADD);
inputManager.mapToKey(largerView, KeyEvent.VK_PLUS);
inputManager.mapToKey(largerView, KeyEvent.VK_EQUALS);
inputManager.mapToKey(frameRateToggle, KeyEvent.VK_R);
// create the polygon renderer
createPolygonRenderer();
// create polygons
polygons = new ArrayList();
createPolygons();
}
// create a house (convex polyhedra)
public void createPolygons() {
SolidPolygon3D poly;
// walls
poly = new SolidPolygon3D(new Vector3D(-200, 0, -1000), new Vector3D(
200, 0, -1000), new Vector3D(200, 250, -1000), new Vector3D(
-200, 250, -1000));
poly.setColor(Color.WHITE);
polygons.add(poly);
poly = new SolidPolygon3D(new Vector3D(-200, 0, -1400), new Vector3D(
-200, 250, -1400), new Vector3D(200, 250, -1400), new Vector3D(
200, 0, -1400));
poly.setColor(Color.WHITE);
polygons.add(poly);
poly = new SolidPolygon3D(new Vector3D(-200, 0, -1400), new Vector3D(
-200, 0, -1000), new Vector3D(-200, 250, -1000), new Vector3D(
-200, 250, -1400));
poly.setColor(Color.GRAY);
polygons.add(poly);
poly = new SolidPolygon3D(new Vector3D(200, 0, -1000), new Vector3D(
200, 0, -1400), new Vector3D(200, 250, -1400), new Vector3D(
200, 250, -1000));
poly.setColor(Color.GRAY);
polygons.add(poly);
// door and windows
poly = new SolidPolygon3D(new Vector3D(0, 0, -1000), new Vector3D(75,
0, -1000), new Vector3D(75, 125, -1000), new Vector3D(0, 125,
-1000));
poly.setColor(new Color(0x660000));
polygons.add(poly);
poly = new SolidPolygon3D(new Vector3D(-150, 150, -1000), new Vector3D(
-100, 150, -1000), new Vector3D(-100, 200, -1000),
new Vector3D(-150, 200, -1000));
poly.setColor(new Color(0x660000));
polygons.add(poly);
// roof
poly = new SolidPolygon3D(new Vector3D(-200, 250, -1000), new Vector3D(
200, 250, -1000), new Vector3D(75, 400, -1200), new Vector3D(
-75, 400, -1200));
poly.setColor(new Color(0x660000));
polygons.add(poly);
poly = new SolidPolygon3D(new Vector3D(-200, 250, -1400), new Vector3D(
-200, 250, -1000), new Vector3D(-75, 400, -1200));
poly.setColor(new Color(0x330000));
polygons.add(poly);
poly = new SolidPolygon3D(new Vector3D(200, 250, -1400), new Vector3D(
-200, 250, -1400), new Vector3D(-75, 400, -1200), new Vector3D(
75, 400, -1200));
poly.setColor(new Color(0x660000));
polygons.add(poly);
poly = new SolidPolygon3D(new Vector3D(200, 250, -1000), new Vector3D(
200, 250, -1400), new Vector3D(75, 400, -1200));
poly.setColor(new Color(0x330000));
polygons.add(poly);
}
public void createPolygonRenderer() {
// make the view window the entire screen
viewWindow = new ViewWindow(0, 0, screen.getWidth(),
screen.getHeight(), (float) Math.toRadians(75));
Transform3D camera = new Transform3D(0, 100, 0);
polygonRenderer = new SolidPolygonRenderer(camera, viewWindow);
}
/**
* Sets the view bounds, centering the view on the screen.
*/
public void setViewBounds(int width, int height) {
width = Math.min(width, screen.getWidth());
height = Math.min(height, screen.getHeight());
width = Math.max(64, width);
height = Math.max(48, height);
viewWindow.setBounds((screen.getWidth() - width) / 2, (screen
.getHeight() - height) / 2, width, height);
}
public void update(long elapsedTime) {
if (exit.isPressed()) {
stop();
return;
}
// check options
if (largerView.isPressed()) {
setViewBounds(viewWindow.getWidth() + 64,
viewWindow.getHeight() + 48);
} else if (smallerView.isPressed()) {
setViewBounds(viewWindow.getWidth() - 64,
viewWindow.getHeight() - 48);
}
if (frameRateToggle.isPressed()) {
drawFrameRate = !drawFrameRate;
}
// cap elapsedTime
elapsedTime = Math.min(elapsedTime, 100);
float angleChange = 0.0002f * elapsedTime;
float distanceChange = .5f * elapsedTime;
Transform3D camera = polygonRenderer.getCamera();
Vector3D cameraLoc = camera.getLocation();
// apply movement
if (goForward.isPressed()) {
cameraLoc.x -= distanceChange * camera.getSinAngleY();
cameraLoc.z -= distanceChange * camera.getCosAngleY();
}
if (goBackward.isPressed()) {
cameraLoc.x += distanceChange * camera.getSinAngleY();
cameraLoc.z += distanceChange * camera.getCosAngleY();
}
if (goLeft.isPressed()) {
cameraLoc.x -= distanceChange * camera.getCosAngleY();
cameraLoc.z += distanceChange * camera.getSinAngleY();
}
if (goRight.isPressed()) {
cameraLoc.x += distanceChange * camera.getCosAngleY();
cameraLoc.z -= distanceChange * camera.getSinAngleY();
}
if (goUp.isPressed()) {
cameraLoc.y += distanceChange;
}
if (goDown.isPressed()) {
cameraLoc.y -= distanceChange;
}
// look up/down (rotate around x)
int tilt = tiltUp.getAmount() - tiltDown.getAmount();
tilt = Math.min(tilt, 200);
tilt = Math.max(tilt, -200);
// limit how far you can look up/down
float newAngleX = camera.getAngleX() + tilt * angleChange;
newAngleX = Math.max(newAngleX, (float) -Math.PI / 2);
newAngleX = Math.min(newAngleX, (float) Math.PI / 2);
camera.setAngleX(newAngleX);
// turn (rotate around y)
int turn = turnLeft.getAmount() - turnRight.getAmount();
turn = Math.min(turn, 200);
turn = Math.max(turn, -200);
camera.rotateAngleY(turn * angleChange);
// tilet head left/right (rotate around z)
if (tiltLeft.isPressed()) {
camera.rotateAngleZ(10 * angleChange);
}
if (tiltRight.isPressed()) {
camera.rotateAngleZ(-10 * angleChange);
}
}
public void draw(Graphics2D g) {
// draw polygons
polygonRenderer.startFrame(g);
for (int i = 0; i < polygons.size(); i++) {
polygonRenderer.draw(g, (Polygon3D) polygons.get(i));
}
polygonRenderer.endFrame(g);
drawText(g);
}
public void drawText(Graphics g) {
// draw text
g.setColor(Color.WHITE);
if (drawInstructions) {
g.drawString("Use the mouse/arrow keys to move. "
+ "Press Esc to exit.", 5, fontSize);
}
// (you may have to turn off the BufferStrategy in
// ScreenManager for more accurate tests)
if (drawFrameRate) {
calcFrameRate();
g.drawString(frameRate + " frames/sec", 5, screen.getHeight() - 5);
}
}
public void calcFrameRate() {
numFrames++;
long currTime = System.currentTimeMillis();
// calculate the frame rate every 500 milliseconds
if (currTime > startTime + 500) {
frameRate = (float) numFrames * 1000 / (currTime - startTime);
startTime = currTime;
numFrames = 0;
}
}
}
/**
* Simple abstract class used for testing. Subclasses should implement the
* draw() method.
*/
abstract class GameCore {
protected static final int DEFAULT_FONT_SIZE = 24;
// various lists of modes, ordered by preference
protected static final DisplayMode[] MID_RES_MODES = {
new DisplayMode(800, 600, 16, 0), new DisplayMode(800, 600, 32, 0),
new DisplayMode(800, 600, 24, 0), new DisplayMode(640, 480, 16, 0),
new DisplayMode(640, 480, 32, 0), new DisplayMode(640, 480, 24, 0),
new DisplayMode(1024, 768, 16, 0),
new DisplayMode(1024, 768, 32, 0),
new DisplayMode(1024, 768, 24, 0), };
protected static final DisplayMode[] LOW_RES_MODES = {
new DisplayMode(640, 480, 16, 0), new DisplayMode(640, 480, 32, 0),
new DisplayMode(640, 480, 24, 0), new DisplayMode(800, 600, 16, 0),
new DisplayMode(800, 600, 32, 0), new DisplayMode(800, 600, 24, 0),
new DisplayMode(1024, 768, 16, 0),
new DisplayMode(1024, 768, 32, 0),
new DisplayMode(1024, 768, 24, 0), };
protected static final DisplayMode[] VERY_LOW_RES_MODES = {
new DisplayMode(320, 240, 16, 0), new DisplayMode(400, 300, 16, 0),
new DisplayMode(512, 384, 16, 0), new DisplayMode(640, 480, 16, 0),
new DisplayMode(800, 600, 16, 0), };
private boolean isRunning;
protected ScreenManager screen;
protected int fontSize = DEFAULT_FONT_SIZE;
/**
* 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 {
if (screen != null) {
screen.restoreScreen();
}
lazilyExit();
}
}
/**
* Exits the VM from a daemon thread. The daemon thread waits 2 seconds then
* calls System.exit(0). Since the VM should exit when only daemon threads
* are running, this makes sure System.exit(0) is only called if neccesary.
* It's neccesary if the Java Sound system is running.
*/
public void lazilyExit() {
Thread thread = new Thread() {
public void run() {
// first, wait for the VM exit on its own.
try {
Thread.sleep(2000);
} catch (InterruptedException ex) {
}
// system is still running, so force an exit
System.exit(0);
}
};
thread.setDaemon(true);
thread.start();
}
/**
* Sets full screen mode and initiates and objects.
*/
public void init() {
init(MID_RES_MODES);
}
/**
* Sets full screen mode and initiates and objects.
*/
public void init(DisplayMode[] possibleModes) {
screen = new ScreenManager();
DisplayMode displayMode = screen.findFirstCompatibleMode(possibleModes);
screen.setFullScreen(displayMode);
Window window = screen.getFullScreenWindow();
window.setFont(new Font("Dialog", Font.PLAIN, fontSize));
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();
// don't take a nap! run as fast as possible
/*
* 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 PolygonRenderer class is an abstract class that transforms and draws
* polygons onto the screen.
*/
abstract class PolygonRenderer {
protected ScanConverter scanConverter;
protected Transform3D camera;
protected ViewWindow viewWindow;
protected boolean clearViewEveryFrame;
protected Polygon3D sourcePolygon;
protected Polygon3D destPolygon;
/**
* Creates a new PolygonRenderer with the specified Transform3D (camera) and
* ViewWindow. The view is cleared when startFrame() is called.
*/
public PolygonRenderer(Transform3D camera, ViewWindow viewWindow) {
this(camera, viewWindow, true);
}
/**
* Creates a new PolygonRenderer with the specified Transform3D (camera) and
* ViewWindow. If clearViewEveryFrame is true, the view is cleared when
* startFrame() is called.
*/
public PolygonRenderer(Transform3D camera, ViewWindow viewWindow,
boolean clearViewEveryFrame) {
this.camera = camera;
this.viewWindow = viewWindow;
this.clearViewEveryFrame = clearViewEveryFrame;
init();
}
/**
* Create the scan converter and dest polygon.
*/
protected void init() {
destPolygon = new Polygon3D();
scanConverter = new ScanConverter(viewWindow);
}
/**
* Gets the camera used for this PolygonRenderer.
*/
public Transform3D getCamera() {
return camera;
}
/**
* Indicates the start of rendering of a frame. This method should be called
* every frame before any polygons are drawn.
*/
public void startFrame(Graphics2D g) {
if (clearViewEveryFrame) {
g.setColor(Color.black);
g.fillRect(viewWindow.getLeftOffset(), viewWindow.getTopOffset(),
viewWindow.getWidth(), viewWindow.getHeight());
}
}
/**
* Indicates the end of rendering of a frame. This method should be called
* every frame after all polygons are drawn.
*/
public void endFrame(Graphics2D g) {
// do nothing, for now.
}
/**
* Transforms and draws a polygon.
*/
public boolean draw(Graphics2D g, Polygon3D poly) {
if (poly.isFacing(camera.getLocation())) {
sourcePolygon = poly;
destPolygon.setTo(poly);
destPolygon.subtract(camera);
boolean visible = destPolygon.clip(-1);
if (visible) {
|