对于那些讨厌阅读冗长问题的人,请阅读下面的完整代码,运行它,再按几次SPACE,您将得到一个ConcurrentModificationException。一个简单的问题:如何解决?问题是尝试退出屏幕时从列表中删除Fireball。问题所在是Timer代码。

如果您需要更多信息,请继续阅读。

在OP询问如何拍摄火球图像的this question中,我用this answer回答,指示应使用数据结构保存火球。 IMO这是@@ $$的一半答案。我认为这是因为我提供的代码不完整,因为它没有考虑何时需要从数据结构中删除火球,例如,当火球移出屏幕或是否发生碰撞时与对方球员。因此,最终它变成了无穷无尽的List火球,我认为这不是有效的方法,也不是正确的方法。

这是我的方法。有一个Fireball类,其中包含火球以及x和y位置的图像。我所做的就是继续通过键绑定向Fireball添加List实例,并使用计时器移动xFireball位置使其具有动画效果

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






因此,出于这个原因,我说这是一个不完整的答案-“因为当火球需要从数据结构中移除时(例如,当火球移出屏幕或与火球碰撞时,它并没有考虑在内)对手球员”

我确实尝试通过执行此操作将其考虑在内,如果Fireball的位置超出了屏幕宽度,则将其从列表中删除

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


但是,与此有关的问题是,一旦x到达屏幕末端并要从Fireball中删除​​,我就会得到一个List。我搜索了解决方法,有人建议使用ConcurrentModificationException,但是当我尝试使用此方法时,当Iterator中存在许多Fireballs时,我仍然会遇到异常。

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




所以我的问题是,为这种情况设置动画的正确方法是什么(避免在退出屏幕时从列表中删除球),以避免List?问题所在是ConcurrentModificationException代码。

这是您可以运行的代码

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

最佳答案

您不能从List循环内的for-each中删除​​项目。我不知道细节,但是我知道通常是行不通的。

相反,获取IteratorList并使用它的remove方法代替...

Iterator<Fireball> it = fireBalls.iterator();
while (it.hasNext()) {
    Fireball ball = it.next();
    if (ball.x > D_W) {
        // You can't call this.  The Iterator is backed by the ArrayList
        //fireBalls.remove(ball);
        it.remove();
    } else {
        ball.x += X_INC;
        repaint();
    }
}


开心的火球垃圾邮件!

关于java - 如何正确为数据结构中的图像设置动画而不获取ConcurrentModificationException,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/21541991/

10-10 07:43