A*寻路游戏系统退出问题
A* pathfinding game system exit issue
我正在制作一个游戏,其中坏人 AI 试图追我,而我使用箭头键离开。在我的主体中,如果玩家试图退出屏幕边界,我有一个 try catch 块。但是出于某种原因,当我单击开始时出现异常,系统打印出 "GAME OVER, you exited the map"。如果 badguy class 中没有我的 reCalcPath 方法,这不会发生,所以它一定是这个方法中的一个问题。
对于这种方法,我有一个地图数组。该数组是一个 40x40 的 20x20 pixels/cells/squares 布尔数组,如果我单击该单元格位置(如果之前为 false)则为 true,绘制一个白色方块,反之亦然。现在我想,检查 badGuy 的单元格位置,然后检查他所有相邻的单元格,如果该单元格的状态为 false,即没有绘制单元格(这意味着在这个意义上没有墙阻挡他),然后检查他和我的球员之间的距离。我使用欧氏距离方法,将 xPlayer-xbadGuy、yPLayer-xBadGuy 视为三角形的对边和相邻边。使用毕达哥拉斯我得到了斜边。对每个相邻单元格执行此操作,斜边最小的单元格意味着最短距离。现在这根本不起作用,因为它称为游戏崩溃。如果 recalcpath 不起作用,请忽略移动方法,因为它无关紧要
主要
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.awt.image.*;
import java.io.*;
public class AStarMaze extends JFrame implements Runnable, MouseListener, MouseMotionListener, KeyListener {
// member data
private boolean isInitialised = false;
private BufferStrategy strategy;
private Graphics offscreenBuffer;
public boolean map[][] = new boolean[40][40];
private boolean isGameRunning = false;
private BadGuy badguy;
private Player player;
private int startI, startJ;
private int endI, endJ;
private String pFilePath, bgFilePath;
// constructor
public AStarMaze () {
//Display the window, centred on the screen
Dimension screensize = java.awt.Toolkit.getDefaultToolkit().getScreenSize();
int x = screensize.width/2 - 400;
int y = screensize.height/2 - 400;
setBounds(x, y, 800, 800);
setVisible(true);
this.setTitle("A* Pathfinding Demo");
bgFilePath = "C:\Users\brads\IdeaProjects\PathfindingAssignment\src\badguy.png";
pFilePath = "C:\Users\brads\IdeaProjects\PathfindingAssignment\src\player.png";
// load raster graphics and instantiate game objects
ImageIcon icon = new ImageIcon(bgFilePath);
Image img = icon.getImage();
badguy = new BadGuy(img);
icon = new ImageIcon(pFilePath);
img = icon.getImage();
player = new Player(img);
// create and start our animation thread
Thread t = new Thread(this);
t.start();
// initialise double-buffering
createBufferStrategy(2);
strategy = getBufferStrategy();
offscreenBuffer = strategy.getDrawGraphics();
// register the Jframe itself to receive mouse and keyboard events
addMouseListener(this);
addMouseMotionListener(this);
addKeyListener(this);
// initialise the map state
for (x=0;x<40;x++) {
for (y=0;y<40;y++) {
map[x][y]=false;
}
}
isInitialised = true;
}
public boolean[][] getMap(){
return map;
}
// thread's entry point
public void run() {
long loops=0;
while ( 1==1 ) {
// 1: sleep for 1/5 sec
try {
Thread.sleep(100);
} catch (InterruptedException e) { }
try {
// 2: animate game objects
if (isGameRunning) {
loops++;
player.move(map); // player moves every frame
if (loops % 3 == 0) // badguy moves once every 3 frames
badguy.reCalcPath(map,player.x,player.y);
// badguy.move(map, player.x, player.y);
}
// 3: force an application repaint
this.repaint();
}
catch(IndexOutOfBoundsException e){
System.out.println("GAME OVER, you exited the map");
System.exit(-1);
}
}
}
private void loadMaze() {
String filename = "C:\Users\brads\IdeaProjects\PathfindingAssignment\maze.txt";
String textinput = null;
try {
BufferedReader reader = new BufferedReader(new FileReader(filename));
textinput = reader.readLine();
reader.close();
}
catch (IOException e) { }
if (textinput!=null) {
for (int x=0;x<40;x++) {
for (int y=0;y<40;y++) {
map[x][y] = (textinput.charAt(x*40+y)=='1');
}
}
}
}
private void saveMaze() {
// pack maze into a string
String outputtext="";
for (int x=0;x<40;x++) {
for (int y=0;y<40;y++) {
if (map[x][y])
outputtext+="1";
else
outputtext+="0";
}
}
try {
String filename = "C:\Users\brads\IdeaProjects\PathfindingAssignment\maze.txt";
BufferedWriter writer = new BufferedWriter(new FileWriter(filename));
writer.write(outputtext);
writer.close();
}
catch (IOException e) { }
}
// mouse events which must be implemented for MouseListener
public void mousePressed(MouseEvent e) {
if (!isGameRunning) {
// was the click on the 'start button'?
int x = e.getX();
int y = e.getY();
if (x>=15 && x<=85 && y>=40 && y<=70) {
isGameRunning=true;
return;
}
// or the 'load' button?
if (x>=315 && x<=385 && y>=40 && y<=70) {
loadMaze();
return;
}
// or the 'save' button?
if (x>=415 && x<=485 && y>=40 && y<=70) {
saveMaze();
return;
}
}
// determine which cell of the gameState array was clicked on
int x = e.getX()/20;
int y = e.getY()/20;
// toggle the state of the cell
map[x][y] = !map[x][y];
// throw an extra repaint, to get immediate visual feedback
this.repaint();
// store mouse position so that each tiny drag doesn't toggle the cell
// (see mouseDragged method below)
prevx=x;
prevy=y;
}
public void mouseReleased(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
public void mouseClicked(MouseEvent e) {}
//
// mouse events which must be implemented for MouseMotionListener
public void mouseMoved(MouseEvent e) {}
// mouse position on previous mouseDragged event
// must be member variables for lifetime reasons
int prevx=-1, prevy=-1;
public void mouseDragged(MouseEvent e) {
// determine which cell of the gameState array was clicked on
// and make sure it has changed since the last mouseDragged event
int x = e.getX()/20;
int y = e.getY()/20;
if (x!=prevx || y!=prevy) {
// toggle the state of the cell
map[x][y] = !map[x][y];
// throw an extra repaint, to get immediate visual feedback
this.repaint();
// store mouse position so that each tiny drag doesn't toggle the cell
prevx=x;
prevy=y;
}
}
//
// Keyboard events
public void keyPressed(KeyEvent e) {
if (e.getKeyCode()==KeyEvent.VK_LEFT)
player.setXSpeed(-1);
else if (e.getKeyCode()==KeyEvent.VK_RIGHT)
player.setXSpeed(1);
else if (e.getKeyCode()==KeyEvent.VK_UP)
player.setYSpeed(-1);
else if (e.getKeyCode()==KeyEvent.VK_DOWN)
player.setYSpeed(1);
}
public void keyReleased(KeyEvent e) {
if (e.getKeyCode()==KeyEvent.VK_LEFT || e.getKeyCode()==KeyEvent.VK_RIGHT)
player.setXSpeed(0);
else if (e.getKeyCode()==KeyEvent.VK_UP || e.getKeyCode()==KeyEvent.VK_DOWN)
player.setYSpeed(0);
}
public void keyTyped(KeyEvent e) { }
//
// application's paint method
public void paint(Graphics g) {
if (!isInitialised)
return;
g = offscreenBuffer; // draw to offscreen buffer
// clear the canvas with a big black rectangle
g.setColor(Color.BLACK);
g.fillRect(0, 0, 800, 800);
// redraw the map
g.setColor(Color.WHITE);
for (int x=0;x<40;x++) {
for (int y=0;y<40;y++) {
if (map[x][y]) {
g.fillRect(x*20, y*20, 20, 20);
}
}
}
// redraw the player and badguy
// paint the game objects
player.paint(g);
badguy.paint(g);
if (!isGameRunning) {
// game is not running..
// draw a 'start button' as a rectangle with text on top
// also draw 'load' and 'save' buttons
g.setColor(Color.GREEN);
g.fillRect(15, 40, 70, 30);
g.fillRect(315, 40, 70, 30);
g.fillRect(415, 40, 70, 30);
g.setFont(new Font("Times", Font.PLAIN, 24));
g.setColor(Color.BLACK);
g.drawString("Start", 22, 62);
g.drawString("Load", 322, 62);
g.drawString("Save", 422, 62);
}
// flip the buffers
strategy.show();
}
// application entry point
public static void main(String[] args) {
AStarMaze w = new AStarMaze();
}
}
播放器Class
import java.awt.Graphics;
import java.awt.Image;
public class Player {
Image myImage;
int x=0,y=0;
int xSpeed=0, ySpeed=0;
public Player( Image i ) {
myImage=i;
x=10;
y=35;
}
public void setXSpeed( int x ) {
xSpeed=x;
}
public void setYSpeed( int y ) {
ySpeed=y;
}
public void move(boolean map[][]) {
int newx=x+xSpeed;
int newy=y+ySpeed;
if (!map[newx][newy]) {
x=newx;
y=newy;
}
}
public void paint(Graphics g) {
g.drawImage(myImage, x*20, y*20, null);
}
}
坏人
import java.awt.Graphics;
import java.awt.Image;
public class BadGuy {
Image myImage;
int x=0,y=0;
int distanceX = 0, distanceY = 0;
boolean hasPath=false;
public BadGuy( Image i ) {
myImage=i;
x = 30;
y = 10;
}
public void reCalcPath(boolean map[][],int targx, int targy) {
// TO DO: calculate A* path to targx,targy, taking account of walls defined in map[][]
int totalDistance = 0;
for (int i = -1; i <= 1; i++) {
for (int j = -1; j <= 1; j++) {
if (!map[(x / 20) + i][(y / 20) + j]) {
if ((((targx - x) ^ 2) + ((targy - y) ^ 2)) <= totalDistance) {
totalDistance = (((targx - x) ^ 2) + ((targy - y) ^ 2));
x = ((x / 20) + i);
y = ((y / 20) + j);
return;
}
}
}
}
System.out.println("Not working");
}
// public void move(boolean map[][],int targx, int targy) {
// if (hasPath) {
// // TO DO: follow A* path, if we have one defined
// }
// else if(map[x / 20][y / 20]) {
// hasPath = false;
// }
// }
public void paint(Graphics g) {
g.drawImage(myImage, x*20, y*20, null);
}
}
您的 catch 块由 IndexOutOfBoundsException 触发。查找有关错误的更多信息的一种简单方法是记录或输出堆栈跟踪。您可以通过将 e.printStackTrace()
添加到您的 catch 块来在您的代码中执行此操作:
catch(IndexOutOfBoundsException e){
System.out.println("GAME OVER, you exited the map");
e.printStackTrace()
System.exit(-1);
}
这应该会显示导致问题的行号。
查看您的 reCalcPath 方法中的代码,我有根据的猜测是导致问题的行是:if (!map[(x / 20) + i][(y / 20) + j]) {
。如果 (x / 20) + i
或 (y / 20) + j
小于 0 或大于您的地图尺寸,它将抛出您看到的 IndexOutOfBoundsException。在您开始且 x=30、y=10、i=-1 和 j=-1 的情况下,计算结果为 map[0][-1].
这就是所谓的边缘情况。您需要检查 x 和 y 是否位于网格边缘的情况。在那种情况下,相邻的网格单元格在地图之外,将引发异常。
我正在制作一个游戏,其中坏人 AI 试图追我,而我使用箭头键离开。在我的主体中,如果玩家试图退出屏幕边界,我有一个 try catch 块。但是出于某种原因,当我单击开始时出现异常,系统打印出 "GAME OVER, you exited the map"。如果 badguy class 中没有我的 reCalcPath 方法,这不会发生,所以它一定是这个方法中的一个问题。
对于这种方法,我有一个地图数组。该数组是一个 40x40 的 20x20 pixels/cells/squares 布尔数组,如果我单击该单元格位置(如果之前为 false)则为 true,绘制一个白色方块,反之亦然。现在我想,检查 badGuy 的单元格位置,然后检查他所有相邻的单元格,如果该单元格的状态为 false,即没有绘制单元格(这意味着在这个意义上没有墙阻挡他),然后检查他和我的球员之间的距离。我使用欧氏距离方法,将 xPlayer-xbadGuy、yPLayer-xBadGuy 视为三角形的对边和相邻边。使用毕达哥拉斯我得到了斜边。对每个相邻单元格执行此操作,斜边最小的单元格意味着最短距离。现在这根本不起作用,因为它称为游戏崩溃。如果 recalcpath 不起作用,请忽略移动方法,因为它无关紧要
主要
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.awt.image.*;
import java.io.*;
public class AStarMaze extends JFrame implements Runnable, MouseListener, MouseMotionListener, KeyListener {
// member data
private boolean isInitialised = false;
private BufferStrategy strategy;
private Graphics offscreenBuffer;
public boolean map[][] = new boolean[40][40];
private boolean isGameRunning = false;
private BadGuy badguy;
private Player player;
private int startI, startJ;
private int endI, endJ;
private String pFilePath, bgFilePath;
// constructor
public AStarMaze () {
//Display the window, centred on the screen
Dimension screensize = java.awt.Toolkit.getDefaultToolkit().getScreenSize();
int x = screensize.width/2 - 400;
int y = screensize.height/2 - 400;
setBounds(x, y, 800, 800);
setVisible(true);
this.setTitle("A* Pathfinding Demo");
bgFilePath = "C:\Users\brads\IdeaProjects\PathfindingAssignment\src\badguy.png";
pFilePath = "C:\Users\brads\IdeaProjects\PathfindingAssignment\src\player.png";
// load raster graphics and instantiate game objects
ImageIcon icon = new ImageIcon(bgFilePath);
Image img = icon.getImage();
badguy = new BadGuy(img);
icon = new ImageIcon(pFilePath);
img = icon.getImage();
player = new Player(img);
// create and start our animation thread
Thread t = new Thread(this);
t.start();
// initialise double-buffering
createBufferStrategy(2);
strategy = getBufferStrategy();
offscreenBuffer = strategy.getDrawGraphics();
// register the Jframe itself to receive mouse and keyboard events
addMouseListener(this);
addMouseMotionListener(this);
addKeyListener(this);
// initialise the map state
for (x=0;x<40;x++) {
for (y=0;y<40;y++) {
map[x][y]=false;
}
}
isInitialised = true;
}
public boolean[][] getMap(){
return map;
}
// thread's entry point
public void run() {
long loops=0;
while ( 1==1 ) {
// 1: sleep for 1/5 sec
try {
Thread.sleep(100);
} catch (InterruptedException e) { }
try {
// 2: animate game objects
if (isGameRunning) {
loops++;
player.move(map); // player moves every frame
if (loops % 3 == 0) // badguy moves once every 3 frames
badguy.reCalcPath(map,player.x,player.y);
// badguy.move(map, player.x, player.y);
}
// 3: force an application repaint
this.repaint();
}
catch(IndexOutOfBoundsException e){
System.out.println("GAME OVER, you exited the map");
System.exit(-1);
}
}
}
private void loadMaze() {
String filename = "C:\Users\brads\IdeaProjects\PathfindingAssignment\maze.txt";
String textinput = null;
try {
BufferedReader reader = new BufferedReader(new FileReader(filename));
textinput = reader.readLine();
reader.close();
}
catch (IOException e) { }
if (textinput!=null) {
for (int x=0;x<40;x++) {
for (int y=0;y<40;y++) {
map[x][y] = (textinput.charAt(x*40+y)=='1');
}
}
}
}
private void saveMaze() {
// pack maze into a string
String outputtext="";
for (int x=0;x<40;x++) {
for (int y=0;y<40;y++) {
if (map[x][y])
outputtext+="1";
else
outputtext+="0";
}
}
try {
String filename = "C:\Users\brads\IdeaProjects\PathfindingAssignment\maze.txt";
BufferedWriter writer = new BufferedWriter(new FileWriter(filename));
writer.write(outputtext);
writer.close();
}
catch (IOException e) { }
}
// mouse events which must be implemented for MouseListener
public void mousePressed(MouseEvent e) {
if (!isGameRunning) {
// was the click on the 'start button'?
int x = e.getX();
int y = e.getY();
if (x>=15 && x<=85 && y>=40 && y<=70) {
isGameRunning=true;
return;
}
// or the 'load' button?
if (x>=315 && x<=385 && y>=40 && y<=70) {
loadMaze();
return;
}
// or the 'save' button?
if (x>=415 && x<=485 && y>=40 && y<=70) {
saveMaze();
return;
}
}
// determine which cell of the gameState array was clicked on
int x = e.getX()/20;
int y = e.getY()/20;
// toggle the state of the cell
map[x][y] = !map[x][y];
// throw an extra repaint, to get immediate visual feedback
this.repaint();
// store mouse position so that each tiny drag doesn't toggle the cell
// (see mouseDragged method below)
prevx=x;
prevy=y;
}
public void mouseReleased(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
public void mouseClicked(MouseEvent e) {}
//
// mouse events which must be implemented for MouseMotionListener
public void mouseMoved(MouseEvent e) {}
// mouse position on previous mouseDragged event
// must be member variables for lifetime reasons
int prevx=-1, prevy=-1;
public void mouseDragged(MouseEvent e) {
// determine which cell of the gameState array was clicked on
// and make sure it has changed since the last mouseDragged event
int x = e.getX()/20;
int y = e.getY()/20;
if (x!=prevx || y!=prevy) {
// toggle the state of the cell
map[x][y] = !map[x][y];
// throw an extra repaint, to get immediate visual feedback
this.repaint();
// store mouse position so that each tiny drag doesn't toggle the cell
prevx=x;
prevy=y;
}
}
//
// Keyboard events
public void keyPressed(KeyEvent e) {
if (e.getKeyCode()==KeyEvent.VK_LEFT)
player.setXSpeed(-1);
else if (e.getKeyCode()==KeyEvent.VK_RIGHT)
player.setXSpeed(1);
else if (e.getKeyCode()==KeyEvent.VK_UP)
player.setYSpeed(-1);
else if (e.getKeyCode()==KeyEvent.VK_DOWN)
player.setYSpeed(1);
}
public void keyReleased(KeyEvent e) {
if (e.getKeyCode()==KeyEvent.VK_LEFT || e.getKeyCode()==KeyEvent.VK_RIGHT)
player.setXSpeed(0);
else if (e.getKeyCode()==KeyEvent.VK_UP || e.getKeyCode()==KeyEvent.VK_DOWN)
player.setYSpeed(0);
}
public void keyTyped(KeyEvent e) { }
//
// application's paint method
public void paint(Graphics g) {
if (!isInitialised)
return;
g = offscreenBuffer; // draw to offscreen buffer
// clear the canvas with a big black rectangle
g.setColor(Color.BLACK);
g.fillRect(0, 0, 800, 800);
// redraw the map
g.setColor(Color.WHITE);
for (int x=0;x<40;x++) {
for (int y=0;y<40;y++) {
if (map[x][y]) {
g.fillRect(x*20, y*20, 20, 20);
}
}
}
// redraw the player and badguy
// paint the game objects
player.paint(g);
badguy.paint(g);
if (!isGameRunning) {
// game is not running..
// draw a 'start button' as a rectangle with text on top
// also draw 'load' and 'save' buttons
g.setColor(Color.GREEN);
g.fillRect(15, 40, 70, 30);
g.fillRect(315, 40, 70, 30);
g.fillRect(415, 40, 70, 30);
g.setFont(new Font("Times", Font.PLAIN, 24));
g.setColor(Color.BLACK);
g.drawString("Start", 22, 62);
g.drawString("Load", 322, 62);
g.drawString("Save", 422, 62);
}
// flip the buffers
strategy.show();
}
// application entry point
public static void main(String[] args) {
AStarMaze w = new AStarMaze();
}
}
播放器Class
import java.awt.Graphics;
import java.awt.Image;
public class Player {
Image myImage;
int x=0,y=0;
int xSpeed=0, ySpeed=0;
public Player( Image i ) {
myImage=i;
x=10;
y=35;
}
public void setXSpeed( int x ) {
xSpeed=x;
}
public void setYSpeed( int y ) {
ySpeed=y;
}
public void move(boolean map[][]) {
int newx=x+xSpeed;
int newy=y+ySpeed;
if (!map[newx][newy]) {
x=newx;
y=newy;
}
}
public void paint(Graphics g) {
g.drawImage(myImage, x*20, y*20, null);
}
}
坏人
import java.awt.Graphics;
import java.awt.Image;
public class BadGuy {
Image myImage;
int x=0,y=0;
int distanceX = 0, distanceY = 0;
boolean hasPath=false;
public BadGuy( Image i ) {
myImage=i;
x = 30;
y = 10;
}
public void reCalcPath(boolean map[][],int targx, int targy) {
// TO DO: calculate A* path to targx,targy, taking account of walls defined in map[][]
int totalDistance = 0;
for (int i = -1; i <= 1; i++) {
for (int j = -1; j <= 1; j++) {
if (!map[(x / 20) + i][(y / 20) + j]) {
if ((((targx - x) ^ 2) + ((targy - y) ^ 2)) <= totalDistance) {
totalDistance = (((targx - x) ^ 2) + ((targy - y) ^ 2));
x = ((x / 20) + i);
y = ((y / 20) + j);
return;
}
}
}
}
System.out.println("Not working");
}
// public void move(boolean map[][],int targx, int targy) {
// if (hasPath) {
// // TO DO: follow A* path, if we have one defined
// }
// else if(map[x / 20][y / 20]) {
// hasPath = false;
// }
// }
public void paint(Graphics g) {
g.drawImage(myImage, x*20, y*20, null);
}
}
您的 catch 块由 IndexOutOfBoundsException 触发。查找有关错误的更多信息的一种简单方法是记录或输出堆栈跟踪。您可以通过将 e.printStackTrace()
添加到您的 catch 块来在您的代码中执行此操作:
catch(IndexOutOfBoundsException e){
System.out.println("GAME OVER, you exited the map");
e.printStackTrace()
System.exit(-1);
}
这应该会显示导致问题的行号。
查看您的 reCalcPath 方法中的代码,我有根据的猜测是导致问题的行是:if (!map[(x / 20) + i][(y / 20) + j]) {
。如果 (x / 20) + i
或 (y / 20) + j
小于 0 或大于您的地图尺寸,它将抛出您看到的 IndexOutOfBoundsException。在您开始且 x=30、y=10、i=-1 和 j=-1 的情况下,计算结果为 map[0][-1].
这就是所谓的边缘情况。您需要检查 x 和 y 是否位于网格边缘的情况。在那种情况下,相邻的网格单元格在地图之外,将引发异常。