我正在使用Java AWT类来制作由两侧矩形组成的墙。 wall类扩展了矩形,并使用相同的变量绘制到屏幕上。
Walls Class:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
public class Walls extends Rectangle {
public Walls(int startX, int startY, int width, int height ){
//super(startX, startY, width, height);
//
//Checking with this way for the collision detection
this.x = startX;
this.y = startY;
this.width = width;
this.height = height;
//this.wallStartX = startX;
//this.wallStartY = startY;
//this.wallWidth = width;
//this.wallHeight = height;
}
public void draw(Graphics g){
g.setColor(Color.GRAY);
//g.fillRect( this.wallStartX, this.wallStartY, this.wallWidth, this.wallHeight);
//Trying with the rectangle methods
g.fillRect( this.x, this.y, this.width, this.height);
}
我使用rectangle.intersects方法检查碰撞预测。它适用于左墙,但它似乎始终与右墙碰撞,即使玩家方块没有碰到。我在左右墙上使用了相同的代码,并且我确保墙的参数对于上面类中的矩形是相同的。
基于来自Java中杀手代码编程的蠕虫游戏
RacerPanel:
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.text.DecimalFormat;
import java.util.Random;
import java.awt.event.KeyEvent; //Should be for Key Usage
import java.awt.event.KeyListener;
public class RacingPanel extends GamePanel implements KeyListener {
///KeyListener from https://www.youtube.com/watch?v=5UaEUrbpDPE
//Q: Serial ID? http://stackoverflow.com/questions/285793/what-is-a- serialversionuid-and-why-should-i-use-it
private static final long serialVersionUID = 1825086766957972644L;
private DecimalFormat df = new DecimalFormat("0.##"); // 2 dp
private Main_Racer mrTop;
private Racer bob;
private Walls josephine;
private Walls jeremy;
private static Random randomGenerator = new Random();
boolean wallBuilder;
int leftCreateorDestroy = 100;
int rightCreateorDestroy = 100;
//private ArrayList()[] Walls kinderland;
//Q
private Font font;
private FontMetrics metrics;
//Q: definitely find out what this method is doing
public RacingPanel( Main_Racer rc, long period) {
super(period);
mrTop = rc;
this.addKeyListener(this);
}
//In case we want to integrate mice
//Q: Why void? Why protected?
protected void mousePress( int x, int y){
}
//Q: in the worm game, it calls
//fred.move(), presumably to move the worm
//wcTop.setTimeSpent(timeSpentInGame);
//Probably a counter
protected void simpleUpdate(){
}
//Q: Assuming that this should be things the
//game starts off with, like the block,
//the course. Also, font
protected void simpleInitialize(){
//T: Maybe I need to initialize the racer
bob = new Racer(PWIDTH, PHEIGHT);
josephine = new Walls(0, 35, 100, 1);
jeremy = new Walls(PWIDTH-100, 35, 100,1);
//That did it!
// so the arguments to the racer update change
//where the square was rendered
//TJ: I need to loop in such a way that will draw random variables
// set up message font
font = new Font("SansSerif", Font.BOLD, 24);
metrics = this.getFontMetrics(font);
}
protected void simpleRender(Graphics dbg) {
if(!gameOver){
//These two seem pretty straight forward
dbg.setColor(Color.blue);
dbg.setFont(font);
// report frame count & average FPS and UPS at top left
// dbg.drawString("Frame Count " + frameCount, 10, 25);
dbg.drawString("Average FPS/UPS: " + df.format(averageFPS) + ", "
+ df.format(averageUPS), 20, 25); // was (10,55)
dbg.setColor(Color.black);
// draw game elements: the obstacles and the worm
//This'll probably be the block and the
//course instead
//obs.draw(dbg);
//fred.draw(dbg);
bob.draw(dbg);
//jeremy.draw(dbg);
//See if we can draw this thing over and over
for(int i = 35; i < PHEIGHT; i++){
wallBuilder = randomGenerator.nextBoolean();
if(wallBuilder == false){
if ( leftCreateorDestroy < PWIDTH/2 - 50)
leftCreateorDestroy++;
}
else{
if(leftCreateorDestroy > 0){
leftCreateorDestroy--;
}
}
josephine = new Walls(0, i, leftCreateorDestroy, 1);
josephine.draw(dbg);
wallBuilder = randomGenerator.nextBoolean();
//This wall keeps colliding with the other wall
//Also, it isn't set to the other side of the screen due to conventions
if(wallBuilder == false){
//if ( (PWIDTH - rightCreateorDestroy) > (PWIDTH/2-150))
if ( rightCreateorDestroy > 0)
rightCreateorDestroy--;
}
else{
if((PWIDTH/2 + 50) < PWIDTH - rightCreateorDestroy){
rightCreateorDestroy++;
}
}
//PWIDTH - rightCreateorDestroy is the length of the
jeremy = new Walls((PWIDTH - rightCreateorDestroy), i, rightCreateorDestroy, 1);
//jeremy = new Walls((PWIDTH - rightCreateorDestroy), i, 100, 1);
//Let's see if it will draw negative; will not;
jeremy.draw(dbg);
//Now to put up collision detection
if(bob.intersects(josephine) ){
System.out.println("Ouch!");
gameOver = true;
}
//System.out.println(bob.);
else if( bob.intersects(jeremy) ){
Rectangle Sektor = bob.intersection(jeremy);
System.out.println(Sektor);
System.out.println(jeremy);
}
//System.out.println("Yow!");
//Problems seem to be with the right side; let's take out the right and check.
//For some reason, intersecting all of the time.
}
}
//1msecond is too long. Starts clipping;
/* try{
Thread.sleep(1000);
}catch ( InterruptedException exc ){
System.out.println("No work");
} */
}
//So this does draw over and over again
//FPS/UPS Still good
//Pretty straight forward as well. Just need
//to see when it is called
protected void gameOverMessage(Graphics g)
// center the game-over message in the panel
{
//Keeps updating after death...
String msg = "Game Over. Your Score: " + timeSpentInGame;
int x = (PWIDTH - metrics.stringWidth(msg)) / 2;
int y = (PHEIGHT - metrics.getHeight()) / 2;
g.setColor(Color.red);
g.setFont(font);
g.drawString(msg, x, y);
} // end of gameOverMessage()
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_RIGHT){
//System.out.println("Right");
//Should set x position to current position plus 1
bob.setRacerPositionX(bob.getRacerPositionX() + 1);
}
if (e.getKeyCode() == KeyEvent.VK_LEFT){
// System.out.println("Left");
bob.setRacerPositionX(bob.getRacerPositionX() - 1);
}
if (e.getKeyCode() == KeyEvent.VK_UP){
//System.out.println("Up");
bob.setRacerPositionY(bob.getRacerPositionY() - 1);
}
if (e.getKeyCode() == KeyEvent.VK_DOWN){
//System.out.println("Down");
bob.setRacerPositionY(bob.getRacerPositionY() + 1);
}
}
游戏小组类:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.text.DecimalFormat;
import javax.swing.JPanel;
public abstract class GamePanel extends JPanel implements Runnable {
private static final long serialVersionUID = 1863596360846514344L;
private static long MAX_STATS_INTERVAL = 1000000000L;
// private static long MAX_STATS_INTERVAL = 1000L;
// record stats every 1 second (roughly)
private static int NUM_FPS = 10;
// number of FPS values stored to get an average
protected static final int PWIDTH = 500; // size of panel
protected static final int PHEIGHT = 400;
protected static final int NO_DELAYS_PER_YIELD = 16;
/*
* Number of frames with a delay of 0 ms before the animation thread yields
* to other running threads.
*/
protected static int MAX_FRAME_SKIPS = 5; // was 2;
// no. of frames that can be skipped in any one animation loop
// i.e the games state is updated but not rendered
private Thread animator; // the thread that performs the animation
protected boolean running = false; // used to stop the animation thread
protected boolean isPaused = false;
protected long period; // period between drawing in _nanosecs_
// used for gathering statistics
private long statsInterval = 0L; // in ns
private long prevStatsTime;
private long totalElapsedTime = 0L;
private long gameStartTime;
protected int timeSpentInGame = 0; // in seconds
private long frameCount = 0;
private double fpsStore[];
private long statsCount = 0;
protected double averageFPS = 0.0;
private long framesSkipped = 0L;
private long totalFramesSkipped = 0L;
private double upsStore[];
protected double averageUPS = 0.0;
private DecimalFormat df = new DecimalFormat("0.##"); // 2 dp
// used at game termination
protected boolean gameOver = false;
// off screen rendering
private Graphics dbg;
private Image dbImage = null;
public GamePanel(long period) {
this.period = period;
setBackground(Color.white);
setPreferredSize(new Dimension(PWIDTH, PHEIGHT));
setFocusable(true);
requestFocus(); // the JPanel now has focus, so receives key events
readyForTermination();
simpleInitialize();
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
mousePress(e.getX(), e.getY());
}
});
// initialise timing elements
fpsStore = new double[NUM_FPS];
upsStore = new double[NUM_FPS];
for (int i = 0; i < NUM_FPS; i++) {
fpsStore[i] = 0.0;
upsStore[i] = 0.0;
}
} // end of GamePanel()
/**
* Sets up a listener to kill the game loop if the user attempts to quit
*/
private void readyForTermination() {
addKeyListener(new KeyAdapter() {
// listen for esc, q, end, ctrl-c on the canvas to
// allow a convenient exit from the full screen configuration
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if ((keyCode == KeyEvent.VK_ESCAPE)
|| (keyCode == KeyEvent.VK_Q)
|| (keyCode == KeyEvent.VK_END)
|| ((keyCode == KeyEvent.VK_C) && e.isControlDown())) {
running = false;
}
}
});
} // end of readyForTermination()
/**
* Triggers the animation loop to start
*/
public void addNotify()
// wait for the JPanel to be added to the JFrame before starting
{
super.addNotify(); // creates the peer
startGame(); // start the thread
}
/**
* Starts the animation loop
*/
private void startGame()
// initialise and start the thread
{
if (animator == null || !running) {
animator = new Thread(this);
animator.start();
}
} // end of startGame()
// ------------- game life cycle methods ------------
// called by the JFrame's window listener methods
public void resumeGame()
// called when the JFrame is activated / deiconified
{
isPaused = false;
}
public void pauseGame()
// called when the JFrame is deactivated / iconified
{
isPaused = true;
}
public void stopGame()
// called when the JFrame is closing
{
running = false;
}
// ----------------------------------------------
public void run()
/* The frames of the animation are drawn inside the while loop. */
{
long beforeTime, afterTime, timeDiff, sleepTime;
long overSleepTime = 0L;
int noDelays = 0;
long excess = 0L;
gameStartTime = System.nanoTime();
prevStatsTime = gameStartTime;
beforeTime = gameStartTime;
running = true;
while (running) {
gameUpdate();
//T Entering Game Render
System.out.println("Entering Game Render");
gameRender();
paintScreen();
afterTime = System.nanoTime();
timeDiff = afterTime - beforeTime;
sleepTime = (period - timeDiff) - overSleepTime;
if (sleepTime > 0) { // some time left in this cycle
try {
Thread.sleep(sleepTime / 1000000L); // nano -> ms
} catch (InterruptedException ex) {
}
overSleepTime = (System.nanoTime() - afterTime) - sleepTime;
} else { // sleepTime <= 0; the frame took longer than the period
excess -= sleepTime; // store excess time value
overSleepTime = 0L;
if (++noDelays >= NO_DELAYS_PER_YIELD) {
Thread.yield(); // give another thread a chance to run
noDelays = 0;
}
}
beforeTime = System.nanoTime();
/*
* If frame animation is taking too long, update the game state
* without rendering it, to get the updates/sec nearer to the
* required FPS.
*/
int skips = 0;
while ((excess > period) && (skips < MAX_FRAME_SKIPS)) {
excess -= period;
gameUpdate(); // update state but don't render
skips++;
}
framesSkipped += skips;
storeStats();
}
printStats();
System.exit(0); // so window disappears
} // end of run()
/**
* Renders to the backbuffer
*/
private void gameRender() {
if (dbImage == null) {
dbImage = createImage(PWIDTH, PHEIGHT);
if (dbImage == null) {
System.out.println("dbImage is null");
return;
} else
dbg = dbImage.getGraphics();
//T
System.out.println("dbg set");
}
// clear the background
dbg.setColor(Color.white);
dbg.fillRect(0, 0, PWIDTH, PHEIGHT);
//T
simpleRender(dbg);
if (gameOver)
gameOverMessage(dbg);
} // end of gameRender()
/**
* Paints the back buffer
*/
private void paintScreen()
// use active rendering to put the buffered image on-screen
{
Graphics g;
try {
g = this.getGraphics();
if ((g != null) && (dbImage != null))
g.drawImage(dbImage, 0, 0, null);
g.dispose();
} catch (Exception e) {
System.out.println("Graphics context error: " + e);
}
} // end of paintScreen()
/**
* Should be update the game state
*/
private void gameUpdate() {
if (!isPaused && !gameOver)
simpleUpdate();
} // end of gameUpdate()
private void storeStats() {
frameCount++;
statsInterval += period;
if (statsInterval >= MAX_STATS_INTERVAL) { // record stats every
// MAX_STATS_INTERVAL
long timeNow = System.nanoTime();
timeSpentInGame = (int) ((timeNow - gameStartTime) / 1000000000L);
long realElapsedTime = timeNow - prevStatsTime; // time since last
// stats collection
totalElapsedTime += realElapsedTime;
totalFramesSkipped += framesSkipped;
double actualFPS = 0; // calculate the latest FPS and UPS
double actualUPS = 0;
if (totalElapsedTime > 0) {
actualFPS = (((double) frameCount / totalElapsedTime) * 1000000000L);
actualUPS = (((double) (frameCount + totalFramesSkipped) / totalElapsedTime) * 1000000000L);
}
// store the latest FPS and UPS
fpsStore[(int) statsCount % NUM_FPS] = actualFPS;
upsStore[(int) statsCount % NUM_FPS] = actualUPS;
statsCount = statsCount + 1;
double totalFPS = 0.0; // total the stored FPSs and UPSs
double totalUPS = 0.0;
for (int i = 0; i < NUM_FPS; i++) {
totalFPS += fpsStore[i];
totalUPS += upsStore[i];
}
if (statsCount < NUM_FPS) { // obtain the average FPS and UPS
averageFPS = totalFPS / statsCount;
averageUPS = totalUPS / statsCount;
} else {
averageFPS = totalFPS / NUM_FPS;
averageUPS = totalUPS / NUM_FPS;
}
framesSkipped = 0;
prevStatsTime = timeNow;
statsInterval = 0L; // reset
}
} // end of storeStats()
private void printStats() {
System.out.println("Frame Count/Loss: " + frameCount + " / "
+ totalFramesSkipped);
System.out.println("Average FPS: " + df.format(averageFPS));
System.out.println("Average UPS: " + df.format(averageUPS));
System.out.println("Time Spent: " + timeSpentInGame + " secs");
} // end of printStats()
/**
* Should implement game specific rendering
*
* @param g
*/
protected abstract void simpleRender(Graphics g);
/**
* Should display a game specific game over message
*
* @param g
*/
protected abstract void gameOverMessage(Graphics g);
protected abstract void simpleUpdate();
/**
* This just gets called when a click occurs, no default behavior
*/
protected abstract void mousePress(int x, int y);
/**
* Should be overridden to initialize the game specific components
*/
protected abstract void simpleInitialize();
} // GamePanel类的结束
赛车类:
import java.awt.*;
import java.awt.geom.*;
public class Racer extends Rectangle {
//Only method that will likely be common between
//the two areas. This presumably draws to screen
//Size of Car
private static final int carSize = 20;
//Changing racer position from array to x and y coordinates
private int pWidth, pHeight; // panel dimensions
private long startTime; // in ms
public Racer (int pW, int pH){
this.width = pW;
this.height = pH;
//Meant to start racer in the center of the screen
//But it does not
this.x = pW/2;
this.y = pW/2;
}
public void draw(Graphics g){
g.setColor(Color.blue);
g.fillRect( x, y, carSize, carSize);
}
public int getRacerPositionX() {
return x;
}
public void setRacerPositionX(int x) {
this.x = x;
}
public int getRacerPositionY() {
return y;
}
public void setRacerPositionY(int y) {
this.y = y;
}
}
主赛车手:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Main_Racer extends JFrame implements WindowListener
{
private static int DEFAULT_FPS = 80;
private RacingPanel rp;
//Let's leave now and make the panel
//Q: Just a value; why JTextField instead of int?
private JTextField jtfBox;
//Q: Just a value; why JTextField instead of int?
private JTextField jtfTime;
//Main_Racer Constructor
public Main_Racer(long period)
{
//Q: Gotta find this constructor
super("The Main Racder");
//Q:What does the argument do?
makeGUI(period);
addWindowListener( this);
pack();
setResizable(false);
setVisible(true);
}
//Q: Warrants an in depth look
private void makeGUI(long period)
{
Container c = getContentPane(); // default BorderLayout used
rp = new RacingPanel(this, period);
c.add(rp, "Center");
JPanel ctrls = new JPanel(); // a row of textfields
ctrls.setLayout( new BoxLayout(ctrls, BoxLayout.X_AXIS));
//Keeps track of time. Might still be useful
jtfTime = new JTextField("Time Spent: 0 secs");
jtfTime.setEditable(false);
ctrls.add(jtfTime);
c.add(ctrls, "South");
} // end of makeGUI()
//Pretty straing forward
public void setTimeSpent(long t)
{ jtfTime.setText("Time Spent: " + t + " secs"); }
// QQQ -----------------Going to copy the window listening methods, as probably n
//need em all
public void windowActivated(WindowEvent e)
{ rp.resumeGame(); }
public void windowDeactivated(WindowEvent e)
{ rp.pauseGame(); }
public void windowDeiconified(WindowEvent e)
{ rp.resumeGame(); }
public void windowIconified(WindowEvent e)
{ rp.pauseGame(); }
public void windowClosing(WindowEvent e)
{ rp.stopGame(); }
public void windowClosed(WindowEvent e) {}
public void windowOpened(WindowEvent e) {}
//=================================================================================
public static void main(String args[])
{
int fps = DEFAULT_FPS;
if (args.length != 0)
fps = Integer.parseInt(args[0]);
long period = (long) 1000.0/fps;
System.out.println("fps: " + fps + "; period: " + period + " ms");
new Main_Racer(period*1000000L); // ms --> nanosecs
}
} // end of WormChase class
碰撞检测只能在一侧工作的原因是什么?