【问题标题】:Game collision detection between many objects?许多物体之间的游戏碰撞检测?
【发布时间】:2014-06-09 17:14:04
【问题描述】:

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

这是我的主要课程,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");
        }
    }
}

}

然后我有两个类,一个是给玩家的,一个是给敌人的:

玩家:

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() {

}

}

我的 EnemyManager 类通过 List 吸引了多个敌人(在本例中为 3 个):

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() {

}

}

我终于有了 GamePanel 类,它从其他类(PlayerEnemyManager)中提取了 Graphics

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);
    }
}
}

当游戏检测到玩家和敌人之间发生碰撞时,它会在控制台上打印“碰撞”,但之后它会因以下错误而崩溃:

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)

任何人都知道问题是什么以及如何解决它?

提前致谢。

【问题讨论】:

    标签: java multithreading swing collision-detection


    【解决方案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. 主线程调用试图从迭代器中获取一个元素 -> 异常

    你的设计有点缺陷:

    paint 被调用时,你几乎没有控制权,正如方法名称所暗示的那样,它用于绘画。这个方法里的数据不要改,画出来就好了。

    牢记这一点,重新设计您的程序(可以先阅读教程,例如this one)。

    您仍然需要从与修改列表的线程不同的线程中读取Enemy 列表。使用列表的get 方法而不是迭代器并记住,列表大小可以更改,因此您需要稍微同步一下。如果线程“保留”允许它们访问的特定索引范围,这可以有效地完成。

    【讨论】:

      【解决方案2】:

      我尝试了您的程序,但无法使用键 A、W、S、D 移动任何东西。但是,我确实得到了一个并发 mod 异常,就像您说的那样,就在 for 循环的点。

      尝试将所有增强循环更改为正常的 for 循环。例如,在部分

      for (Enemy e : enemies) {
              e.paint(g);
          }
      

      改为:

      for (int i = 0; i < enemies.size(); i++)
          enemies.get(i).paint(g);
      

      执行此操作后,我不再收到错误消息。

      如果您在特定时间对数组执行特定操作,增强的 for 循环会给您带来错误。但我不确定为什么会出现这种情况,因为我不太熟悉图形在 Java 中的幕后工作原理。

      【讨论】:

      • 这不是一个合适的(或正确的修复)。它不抛出 ConcurrentModificationException 的唯一原因是您没有活动的迭代器。相反,他最终会陷入几种糟糕的情况之一,例如 1) 两次绘制同一个敌人 2) 当他超过限制时出现 IndexOutOfBounds 异常 3) 不绘制敌人,因为它们是在他到达最后一个索引后添加的跨度>
      【解决方案3】:

      您收到 ConcurrentModificationException,这意味着您尝试在一个线程中修改集合,同时在另一个线程中对其进行迭代(如果您尝试在迭代时修改集合,这也可能发生在单个线程中,但那是不是这里发生的事情)。

      这一切都源于您没有注意事件发生在哪个线程中。无论您是否意识到,您都有两个活动线程。

      Thread #1 是程序的主线程,应用程序从这里开始

      线程 #2 是 Swing EDT,所有用户操作都从这里开始。

      看起来您正在尝试从主线程更新域模型,同时迭代来自 Swing EDT 的集合。您要么需要同步访问,要么通过稍后将其包装在调用中来使所有更新在 EDT 上执行:

      SwingUtilities.invokeLater(new Runnable(){
        public void run(){
          gamepanel.repaint();
          gamepanel.update();
        }
      });
      

      请注意,在这种情况下,我不知道在循环+Thread.sleep 中简单地使用上述 sn-p 是正确的操作,因为我还没有完成您的所有逻辑。至少你应该阅读tutorial on Swing threading.

      【讨论】:

        猜你喜欢
        • 2020-04-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-03-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多