许多物体之间的游戏碰撞检测?

时间:2014-06-09 17:14:05

标签: java multithreading swing collision-detection

我试图了解如何在多个对象之间实现碰撞检测。 我的项目检测到对象之间的碰撞,但在它之后立即崩溃。

这是我的主要课程JFrameMain 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() {

}

}

最后,我从其他类(GraphicsPlayer)中抽取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)

任何人都知道问题是什么,也许如何解决?

提前致谢。

3 个答案:

答案 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的迭代器失败了,如果你在创建迭代器之后修改了列表,你得到了例外。

由于列表修改和迭代器可以在不同的线程中使用,因此它们很容易干扰,例如:

  1. 主线程创建迭代器
  2. 主线程读取一些元素
  3. 事件调度程序线程调用paint
  4. 事件调度程序线程Enemy添加到列表
  5. 主线程调用尝试从迭代器中获取元素 - &gt;例外

  6. 你的设计有点缺陷:

    当调用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.