对于那些讨厌阅读长问题的人,获取下面的完整代码,运行它,点击SPACE
几次,你会得到一个ConcurrentModificationException
。简单的问题:你如何解决它?问题是试图删除一个Fireball
当它退出屏幕时从列表中删除。这Timer
代码就是问题所在。
如果您想了解更多信息,请继续阅读。
In 这个问题当OP问如何拍摄火球图像时,我回答道这个答案表明应该使用数据结构来保存火球。在我看来,这是半个@$$ 答案。我认为这是因为我给出的代码不完整,因为它没有考虑何时需要从数据结构中删除火球,例如火球何时移出屏幕或是否发生碰撞与对方球员。所以最终它就变成了无尽的List
火球,我认为这既不高效也不正确。
我是这样做的。有一个Fireball
包含火球以及 x 和 y 位置图像的类。我所做的只是不断添加Fireball
实例到List
通过按键绑定并通过计时器移动动画x
的位置Fireball
Timer timer = new Timer(40, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
for (Fireball ball : fireBalls) {
ball.x += X_INC;
repaint();
}
}
});
...
getActionMap().put("hadouken", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
fireBalls.add(new Fireball(fireball));
}
});
所以我说这是一个不完整的答案,因为这个原因 -“因为它没有考虑何时需要从数据结构中删除火球,例如火球何时移出屏幕或是否与对方玩家发生碰撞”
I did尝试通过这样做来考虑这一点,删除Fireball
从列表中如果是x
位置超出屏幕宽度
Timer timer = new Timer(40, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
for (Fireball ball : fireBalls) {
if (ball.x > D_W) {
fireBalls.remove(ball);
} else {
ball.x += X_INC;
repaint();
}
}
}
});
但这样做的问题是,一旦Fireball
到达屏幕末尾并且将从中删除List
,我得到一个ConcurrentModificationException
。我搜索了如何解决这个问题,有些人建议使用Iterator
,但是当我尝试这个时,我仍然遇到很多异常Fireballs
存在于List
public void actionPerformed(ActionEvent e) {
Iterator<Fireball> it = fireBalls.iterator();
while (it.hasNext()) {
Fireball ball = it.next();
if (ball.x > D_W) {
fireBalls.remove(ball);
} else {
ball.x += X_INC;
repaint();
}
}
}
所以我的问题是,为这种情况设置动画的正确方法是什么(当球退出屏幕时从列表中删除球),以避免ConcurrentModificationException
? The Timer
代码就是问题所在。
这是您可以运行的代码
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.util.*;
import java.util.List;
import java.util.logging.*;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.Timer;
public class WannaBeStreetFighter extends JPanel {
private static final int D_W = 700;
private static final int D_H = 250;
private static final int X_INC = 10;
List<Fireball> fireBalls;
BufferedImage ryu;
BufferedImage fireball;
BufferedImage background;
public WannaBeStreetFighter() {
try {
ryu = ImageIO.read(new URL("http://www.sirlin.net/storage/street_fighter/ryu_hadoken_pose.png?__SQUARESPACE_CACHEVERSION=1226531909576"));
background = ImageIO.read(new URL("http://fightingstreet.com/folders/variousinfofolder/ehondasbath/hondasfz3stage.gif"));
fireball = ImageIO.read(new URL("http://farm6.staticflickr.com/5480/12297371495_ec19ded155_o.png"));
} catch (IOException ex) {
Logger.getLogger(WannaBeStreetFighter.class.getName()).log(Level.SEVERE, null, ex);
}
fireBalls = new LinkedList<>();
Timer timer = new Timer(40, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
for (Fireball ball : fireBalls) {
if (ball.x > D_W) {
fireBalls.remove(ball);
} else {
ball.x += X_INC;
repaint();
}
}
}
});
timer.start();
InputMap inputMap = getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
inputMap.put(KeyStroke.getKeyStroke("SPACE"), "hadouken");
getActionMap().put("hadouken", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
fireBalls.add(new Fireball(fireball));
}
});
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(background, 0, 0, D_W, D_H, this);
g.drawImage(ryu, 50, 125, 150, 115, this);
for (Fireball ball : fireBalls) {
ball.drawFireball(g);
}
}
@Override
public Dimension getPreferredSize() {
return new Dimension(D_W, D_H);
}
private class Fireball {
Image fireball;
int x = 150;
int y = 125;
public Fireball(Image image) {
fireball = image;
}
public void drawFireball(Graphics g) {
g.drawImage(fireball, x, y, 50, 50, null);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame("Best Street Fighter ever");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new WannaBeStreetFighter());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}