2014-06-09 4 views
-1

여러 객체간에 충돌 감지를 구현하는 방법을 이해하려고합니다. 프로젝트가 개체 간의 충돌을 감지했지만 즉시 충돌합니다.많은 개체 간의 게임 충돌 감지?

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

} 

} 

I했습니다 통해 (이 경우 3) 하나 이상의 적을 그리는 EnemyManager의 클래스 A List :

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을 그리는의 GamePanel 클래스 (PlayerEnemyManager) :

게임이 플레이어와 적 사이의 충돌을 감지
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); 
    } 
} 
} 

, 그것은 콘솔에 "충돌"을 인쇄하지만 그 후이 오류와 충돌 :

누구든지 문제가 무엇인지, 어떻게 해결할 수 있는지 알고 있습니까?

미리 감사드립니다.

답변

1

paint을 읽어야합니다. 이 방법의 목록을 수정

당신이 GamePanel.checkPlayerEnemyCollision에서 "foreach는"루프를 통해 목록의 반복자를 사용하지만
event dispatcher thread 
-> GamePanel.paint 
-> EnemyManager.paint 
-> enemies.add 

:

for(Enemy e : enemymanager.enemies) 

하지만 당신은 목록을 수정하면 ArrayList의 반복자는 실패 당신이 가진 예외를 가지고 iterator가 생성 된 후에. 목록 수정 및 반복자 이후

는 예를 들면, 그들은 easyly 방해 할 수있는 다른 스레드에서 사용 할 수 있습니다

  1. 메인 스레드는 메인 쓰레드 몇 가지 요소를 읽어
  2. 반복자를 생성
  3. 이벤트 디스패처 스레드 전화 paint
  4. ev 엔트 디스패처 스레드는 목록에 Enemy을 추가
  5. 반복자에서 요소를 가져 오려고 시도메인 스레드 호출 -> 예외

귀하의 디자인 결함이 조금 있습니다 :

당신은 조금있다가 컨트롤, paint이 호출 될 때 메서드 이름에서 알 수 있듯이 페인팅에 사용됩니다. 이 메서드에서 데이터를 변경하지 말고 그냥 그립니다.

프로그램을 다시 디자인하십시오 (튜토리얼을 먼저 읽어보십시오 (예 : this one).

목록을 수정하는 스레드와 다른 스레드에서 Enemy 목록을 읽어야합니다. 반복자 대신 목록의 get 메서드를 사용하고 목록 크기가으로 변경 될 수 있으므로 약간의 동기화가 필요합니다. 스레드가 액세스 할 수있는 특정 인덱스 범위를 "예약"하는 경우 효과적으로 수행 할 수 있습니다.

1

나는 당신의 프로그램을 시도했으나 A, W, S, D 키를 사용하여 움직일 수는 없었다. 그러나 for 루프의 시점에서 말한 것처럼 동시적인 mod 예외를 얻는다.

모든 향상된 루프를 일반 for 루프로 변경해보십시오. 예를 들어, 일부

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

변화에 :

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

이 일 후에, 나는 더 이상 오류가 발생하지 않았다.

루프 용으로 향상되면 특정 시간에 배열에 특정 작업을 수행하면 오류가 발생합니다. 그러나 왜 이런 경우인지 모르겠다. 자바에서 그래픽이 어떻게 작동하는지 익숙하지 않기 때문이다.

+0

그건 적절하지 않습니다 (또는 올바른 수정). ConcurrentModificationException을 발생시키지 않는 유일한 이유는 활성 Iterator가 없기 때문입니다. 대신 그는 1) 같은 적을 두 번 그린다. 2) 한계를 넘을 때 IndexOutOfBounds를 예외로한다. 3) 마지막 인덱스를 얻은 후 추가되기 때문에 적을 페인팅하지 않는다. –

0

ConcurrentModificationException이 발생합니다. 이는 다른 스레드에서 컬렉션을 수정하면서 하나의 스레드에서 컬렉션을 수정하려고한다는 것을 의미합니다 (반복 중에 컬렉션을 수정하려고하면 단일 스레드 내에서 발생할 수도 있습니다. 그게 여기서 일어나는 것이 아닙니다).

이 모든 것은 이벤트가 발생하는 스레드에주의를 기울이지 않는다는 사실에서 기인합니다. 실감 여부와 관계없이 두 개의 활성 스레드가 있습니다.

스레드 # 1 프로그램 앱이 사용자의 작업 모두에서 시작 스윙 EDT입니다

스레드 # 2에서 시작되는 메인 스레드이다.

Swing EDT의 컬렉션을 반복하면서 메인 스레드에서 도메인 모델을 업데이트하려는 것 같습니다.

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

참고 단순히 루프 + Thread.sleep를에 위의 코드를 사용하는 것을 모르는 : 당신은 액세스를 동기화하거나 나중에 호출에 포장하여 모든 업데이트가 EDT에서 수행해야 하나 나는이 모든 경우를 거치지 않았으므로 올바른 행동입니다. 적어도 이벤트 디스패처 스레드에서 호출되는 tutorial on Swing threading.