我试图了解如何在多个对象之间实现碰撞检测。 我的项目检测到对象之间的碰撞,但在它之后立即崩溃。
这是我的主要课程JFrame
和Main Loop
:
public class Window {
public static void main(String[] args){
GamePanel gamepanel = new GamePanel();
JFrame f = new JFrame("Multiple Collision Detection");
f.setSize(400, 400);
f.add(gamepanel);
f.setVisible(true);
f.setResizable(false);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setLocationRelativeTo(null);
while(true){
gamepanel.repaint();
gamepanel.update();
try{
Thread.sleep(10);
}catch(Exception e){
System.out.println("Main Loop Error");
}
}
}
}
然后我有两个班,一个是玩家,另一个是敌人:
PLAYER:
public class Player {
int x = 175, y = 175, w = 50, h = 50, dx = 0, dy = 0;
Rectangle rect;
public void paint(Graphics g) {
rect = new Rectangle(x, y, w, h);
g.setColor(Color.black);
g.fillRect(rect.x, rect.y, rect.width, rect.height);
g.setColor(Color.CYAN);
g.drawRect(x, y, w, h);
}
public void setDx(int dx) {
this.dx = dx;
}
public void setDy(int dy) {
this.dy = dy;
}
public void move() {
x += dx;
y += dy;
}
public void update() {
move();
}
}
的敌人:
public class Enemy {
int x, y, w = 35, h = 35;
Rectangle rect;
public Enemy(int x, int y) {
this.x = x;
this.y = y;
}
public void paint(Graphics g) {
rect = new Rectangle(x, y, w, h);
g.setColor(Color.red);
g.fillRect(rect.x, rect.y, rect.width, rect.height);
}
public void update() {
}
}
我通过List
吸引了多个敌人(在本案例中为3)的EnemyManager类:
public class EnemyManager {
Player player = new Player();
Rectangle playerrect;
Rectangle enemyrect;
List<Enemy> enemies = new ArrayList<Enemy>();
public void paint(Graphics g) {
enemies.add(new Enemy(20, 20));
enemies.add(new Enemy(320, 20));
enemies.add(new Enemy(20, 320));
for (Enemy e : enemies) {
e.paint(g);
}
}
public void update() {
}
}
最后,我从其他类(Graphics
和Player
)中抽取EnemyManager
的GamePanel类:
public class GamePanel extends JPanel implements KeyListener{
Player player = new Player();
EnemyManager enemymanager = new EnemyManager();
public void paint(Graphics g){
g.setColor(Color.LIGHT_GRAY);
g.fillRect(0, 0, 400, 400);
player.paint(g);
enemymanager.paint(g);
}
public void checkPlayerEnemyCollision(){
for(Enemy e : enemymanager.enemies){
if(e.rect.intersects(player.rect)){
System.out.println("Collision");
}
}
}
public void update(){
addKeyListener(this);
setFocusable(true);
player.update();
enemymanager.update();
checkPlayerEnemyCollision();
}
@Override
public void keyPressed(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_W){
player.setDy(-2);
}
if(e.getKeyCode() == KeyEvent.VK_S){
player.setDy(2);
}
if(e.getKeyCode() == KeyEvent.VK_A){
player.setDx(-2);
}
if(e.getKeyCode() == KeyEvent.VK_D){
player.setDx(2);
}
if(e.getKeyCode() == KeyEvent.VK_ESCAPE){
System.exit(0);
}
}
@Override
public void keyReleased(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_W){
player.setDy(0);
}
if(e.getKeyCode() == KeyEvent.VK_S){
player.setDy(0);
}
if(e.getKeyCode() == KeyEvent.VK_A){
player.setDx(0);
}
if(e.getKeyCode() == KeyEvent.VK_D){
player.setDx(0);
}
}
}
当游戏检测到玩家与敌人之间发生碰撞时,会打印出“碰撞”#34;到控制台但之后它崩溃了这个错误:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
at java.util.ArrayList$Itr.next(Unknown Source)
at GamePanel.checkPlayerEnemyCollision(GamePanel.java:22)
at GamePanel.update(GamePanel.java:34)
at Window.main(Window.java:20)
任何人都知道问题是什么,也许如何解决?
提前致谢。
答案 0 :(得分:1)
我尝试了你的程序,但是使用键A,W,S,D无法移动任何东西。但是,我确实得到了一个像你说的那样的并发mod异常,就在for循环的时候。
尝试将所有增强型循环更改为正常循环。例如,在部分
中for (Enemy e : enemies) {
e.paint(g);
}
更改为:
for (int i = 0; i < enemies.size(); i++)
enemies.get(i).paint(g);
完成此操作后,我再也没有收到错误。
如果在特定时间对数组执行某些操作,增强的for循环会给出错误。但是我不确定为什么在这种情况下,因为我不太熟悉Java中幕后的图形工作方式。
答案 1 :(得分:1)
paint
。您可以修改此方法中的列表
event dispatcher thread
-> GamePanel.paint
-> EnemyManager.paint
-> enemies.add
但是,您通过GamePanel.checkPlayerEnemyCollision
中的“foreach”循环使用列表的迭代器:
for(Enemy e : enemymanager.enemies)
但ArrayList
的迭代器失败了,如果你在创建迭代器之后修改了列表,你得到了例外。
由于列表修改和迭代器可以在不同的线程中使用,因此它们很容易干扰,例如:
paint
Enemy
添加到列表你的设计有点缺陷:
当调用paint
并且方法名称暗示它用于绘画时,你几乎无法控制。不要更改此方法中的数据,只需绘制它。
重新设计您的计划时请记住这一点(可能先阅读一下教程,例如this one)。
您仍需要从与修改列表的线程不同的线程中读取Enemy
列表。使用列表的get
方法而不是迭代器,请记住,列表大小可以更改,因此您需要稍微同步一些事情。如果线程“保留”允许它们访问的特定索引范围,则可以有效地完成此操作。
答案 2 :(得分:0)
您正在获取ConcurrentModificationException,这意味着您在一个线程中尝试修改集合,而在另一个线程中迭代它(如果您尝试修改集合,这也可能发生在单个线程中)迭代,但这不是在这里发生的事情。)
这一切都源于你不注意你的事件发生在哪个线程的事实。无论你是否意识到,你都有两个活跃的线程。
线程#1是程序主线程,它是应用程序从开始的地方
线程#2是Swing EDT,您的所有用户操作均来自。
看起来您正在尝试从主线程更新域模型,同时迭代Swing EDT中的集合。您需要同步访问权限,或者在EDT上执行所有更新,方法是将其包装在以后的调用中:
SwingUtilities.invokeLater(new Runnable(){
public void run(){
gamepanel.repaint();
gamepanel.update();
}
});
请注意,我不知道在循环+ Thread.sleep中使用上面的代码段是正确的操作,因为我还没有通过你的所有逻辑。至少你应该阅读tutorial on Swing threading.