太空入侵者生成初始的小行星 Java 失败
太空入侵者生成初始的小行星 Java 失败
我写了一个类似于太空侵略者的游戏,其中有一个火箭飞船和一些陨石对象会下落,游戏的目标是存活下来并射击尽可能多的陨石。\n我在游戏开始时生成陨石遇到了一些问题。\n我的思路是,在 GamePanel
构造函数中,在启动 ActionListener
Timer
之前,我生成了一个固定数量为 Globals.NUM_OF_ASTEROIDS
的陨石,并且每当一个陨石在 updateGameState
方法中被销毁时,一个新的陨石会被添加到 asteroids
的 ArrayList
中。\n我尝试了一个简单的 for
循环来生成所有陨石,然后启动一个计时器。我还尝试在计时器中生成陨石。这两种解决方案都会产生一堆错误:\n
Exception in thread "AWT-EventQueue-0" java.util.ConcurrentModificationException at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1013) at java.base/java.util.ArrayList$Itr.next(ArrayList.java:967) at GamePanel.updateGameState(GamePanel.java:65) at GamePanel$1.actionPerformed(GamePanel.java:42) at java.desktop/javax.swing.Timer.fireActionPerformed(Timer.java:311) at java.desktop/javax.swing.Timer$DoPostEvent.run(Timer.java:243) at java.desktop/java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:318) at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:771) at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:722) at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:716) at java.base/java.security.AccessController.doPrivileged(AccessController.java:399) at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86) at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:741) at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203) at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124) at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113) at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109) at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101) at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)
\n我还将我的 GamePanel
类的源代码附上。请注意,这还是一个未完成的项目。\n我在构造函数中标记了有问题的部分。\n
import java.awt.Color; import java.awt.Graphics; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.util.ArrayList; import java.util.List; import javax.swing.JPanel; import javax.swing.Timer; public class GamePanel extends JPanel implements KeyListener { private static final long serialVersionUID = -1036125108881682872L; private Listasteroids; private List projectiles; private RocketShip rocketShip; private Timer timer; private int end; //0 - the game is still in progress //1 - the game has ended public GamePanel() { setBackground(Color.BLACK); asteroids = new ArrayList<>(); projectiles = new ArrayList<>(); rocketShip = new RocketShip(Globals.WINDOW_WIDTH / 2, Globals.SHIP_HEIGHT); this.end = 0; addKeyListener(this); setFocusable(true); ///////////////////////////////////////////////////////// //这个循环引起了错误。 ///// for(int i = 0; i < Globals.NUM_OF_ASTEROIDS; i++) { ///// asteroids.add(new Asteroid()); ///// } ///// ///////////////////////////////////////////////////////// timer = new Timer(10, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { updateGameState(""); repaint(); } }); timer.start(); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); rocketShip.paintComponent(g); for (Asteroid asteroid : asteroids) { asteroid.paintComponent(g); } for (Projectile projectile : projectiles) { projectile.paintComponent(g); } } public void updateGameState(String direction) { rocketShip.move(direction); for (Asteroid asteroid : asteroids) { asteroid.move(); if (asteroid.isOutOfBounds()) { asteroids.remove(asteroid); asteroids.add(new Asteroid()); } } for (Projectile projectile : projectiles) { projectile.move(); // projectile is out of bounds if (projectile.getX() - (projectile.getHeight() / 2) > Globals.WINDOW_HEIGHT) { projectiles.remove(projectile); } } // check for collisions //asteoids with projectiles for (Asteroid asteroid : asteroids) { for (Projectile projectile : projectiles) { if (projectile.getX() + projectile.getWidth() > asteroid.getX() && projectile.getX() < asteroid.getX() + asteroid.getWidth() && projectile.getY() + projectile.getHeight() > asteroid.getY() && projectile.getY() < asteroid.getY() + asteroid.getHeight()) { asteroids.remove(asteroid); projectiles.remove(projectile); //add a new asteroid asteroids.add(new Asteroid()); //score increase } } } // asteroids with rocketship for (Asteroid asteroid : asteroids) { if (rocketShip.getX() + rocketShip.getWidth() > asteroid.getX() && rocketShip.getX() < asteroid.getX() + asteroid.getWidth() && rocketShip.getY() + rocketShip.getHeight() > asteroid.getY() && rocketShip.getY() < asteroid.getY() + asteroid.getHeight()) { // rocketShip and asteroid have collided asteroids.remove(asteroid); // game over logic this.end = 1; timer.stop(); } } } @Override public void keyTyped(KeyEvent e) { if (e.getKeyChar() == ' ') { Projectile projectile = new Projectile(rocketShip.getX() + rocketShip.getWidth() / 2, rocketShip.getY()); projectiles.add(projectile); } } @Override public void keyPressed(KeyEvent e) { // TODO Auto-generated method stub int keyCode = e.getKeyCode(); if(keyCode == KeyEvent.VK_RIGHT) { updateGameState("right"); } if(keyCode == KeyEvent.VK_LEFT) { updateGameState("left"); } } @Override public void keyReleased(KeyEvent e) { } }
\n编辑:如果我删除上述部分,游戏将正常运行,移动和射击机制正常工作,但是没有生成陨石。\n非常感谢您的帮助!感谢您花时间阅读我的问题。 <3
Space Invaders生成初始的Asteroids时出现了Java失败。问题的原因是在Asteroid检查的for循环中,将其更改为默认的for(int i = 0; i < asteroids.size(); i++)。解决方法是将新Asteroids的生成移到updateGameState方法中。
以下是整理的文章:
Space Invaders生成初始的Asteroids时出现了Java失败。问题的原因是在Asteroid检查的for循环中,将其更改为默认的for(int i = 0; i < asteroids.size(); i++)。解决方法是将新Asteroids的生成移到updateGameState方法中。
import java.awt.Color; import java.io.*; import java.awt.Graphics; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.Clip; import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.UnsupportedAudioFileException; import javax.swing.JPanel; import javax.swing.Timer; public class GamePanel extends JPanel implements KeyListener { private static final long serialVersionUID = -1036125108881682872L; private List<Asteroid> asteroids; private List<Projectile> projectiles; private RocketShip rocketShip; private Timer timer; private int end; // 0 - the game is still in progress // 1 - the game has ended public GamePanel() { setBackground(Color.BLACK); asteroids = new ArrayList<>(); projectiles = new ArrayList<>(); rocketShip = new RocketShip(Globals.WINDOW_WIDTH / 2, Globals.SHIP_HEIGHT); this.end = 0; addKeyListener(this); setFocusable(true); timer = new Timer(10, new ActionListener() { public void actionPerformed(ActionEvent e) { updateGameState(""); repaint(); } }); timer.start(); } public void paintComponent(Graphics g) { super.paintComponent(g); rocketShip.paintComponent(g); for (Asteroid asteroid : asteroids) { asteroid.paintComponent(g); } for (Projectile projectile : projectiles) { projectile.paintComponent(g); } // start and end screen, also score } public void updateGameState(String direction) { rocketShip.move(direction); rocketShip.move(direction); while (asteroids.size() < Globals.NUM_OF_ASTEROIDS) { asteroids.add(new Asteroid()); //System.out.println("asteroid added\n"); } for (int i = 0; i < asteroids.size(); i++) { Asteroid asteroid = asteroids.get(i); asteroid.move(); if (asteroid.isOutOfBounds()) { asteroids.remove(i); asteroids.add(new Asteroid()); i--; } } for (Projectile projectile : projectiles) { projectile.move(); // projectile is out of bounds if (projectile.getX() - (projectile.getHeight() / 2) > Globals.WINDOW_HEIGHT) { projectiles.remove(projectile); } } // check for collisions // asteoids with projectiles for (Asteroid asteroid : asteroids) { for (Projectile projectile : projectiles) { if (projectile.getX() + projectile.getWidth() > asteroid.getX() && projectile.getX() < asteroid.getX() + asteroid.getWidth() && projectile.getY() + projectile.getHeight() > asteroid.getY() && projectile.getY() < asteroid.getY() + asteroid.getHeight()) { playAsteroidDestroyedSound(); asteroids.remove(asteroid); projectiles.remove(projectile); // add a new asteroid asteroids.add(new Asteroid()); // score increase } } } // asteroids with rocketship for (Asteroid asteroid : asteroids) { if (rocketShip.getX() + rocketShip.getWidth() > asteroid.getX() && rocketShip.getX() < asteroid.getX() + asteroid.getWidth() && rocketShip.getY() + rocketShip.getHeight() > asteroid.getY() && rocketShip.getY() < asteroid.getY() + asteroid.getHeight()) { // rocketShip and asteroid have collided asteroids.remove(asteroid); // game over logic this.end = 1; timer.stop(); } } } public void playShootingSound() { try { AudioInputStream in = AudioSystem.getAudioInputStream(new File("audio/shooting.wav")); Clip clip = AudioSystem.getClip(); clip.open(in); clip.start(); } catch (UnsupportedAudioFileException | LineUnavailableException | IOException e) { System.out.println("ERROR: playShootingSound"); } } public void playAsteroidDestroyedSound() { try { AudioInputStream in = AudioSystem.getAudioInputStream(new File("audio/asterDestroid.wav")); Clip clip = AudioSystem.getClip(); clip.open(in); clip.start(); } catch (UnsupportedAudioFileException | LineUnavailableException | IOException e) { System.out.println("ERROR: playAsterDestroSound"); } } public void keyTyped(KeyEvent e) { if (e.getKeyChar() == ' ') { playShootingSound(); Projectile projectile = new Projectile(rocketShip.getX() + rocketShip.getWidth() / 2, rocketShip.getY()); projectiles.add(projectile); } } public void keyPressed(KeyEvent e) { int keyCode = e.getKeyCode(); if (keyCode == KeyEvent.VK_RIGHT) { updateGameState("right"); } if (keyCode == KeyEvent.VK_LEFT) { updateGameState("left"); } } public void keyReleased(KeyEvent e) { } }
注意:你的paintComponent实现和使用方式基本上是错误的 - 永远不要在另一个组件上调用它,而是添加组件并让默认的绘制工作完成!
问题的出现原因是在遍历列表时,同时对其进行了修改。解决方法是使用迭代器来遍历列表,并且使用迭代器的remove()方法来删除元素。
以下是解决问题的代码示例:
Iteratoriterator = asteroids.iterator(); while (iterator.hasNext()) { Asteroid asteroid = iterator.next(); asteroid.move(); if (asteroid.isOutOfBounds()) { iterator.remove(); } }
使用迭代器遍历列表可以避免在遍历过程中对列表进行修改造成的问题。通过调用迭代器的remove()方法来删除元素可以确保不会出现并发修改异常。