【问题标题】:Java Swing Timer and Animation: how to put it togetherJava Swing 计时器和动画:如何组合在一起
【发布时间】:2015-02-05 19:39:30
【问题描述】:

我将再次重新发布这个问题,试图更准确,并希望我能得到一些帮助,因为这让我发疯了。我正在开发一个最多 6 人的棋盘游戏,每个人都有不同颜色的棋子。我在 BufferedImage 数组中加载了以下图像,将其视为精灵:

这是相对代码,将每个彩色骰子的每个面放在 BufferedImage[] 中的一个位置:

private BufferedImage[] initAnimationBuffer() {
    BufferedImage[] result = new BufferedImage[36];
    for (int i = 0; i < 6; i++) {
        for (int j = i; j < 6 + i; j++)
            result[i + j] = DieSprite.getSprite(j, i, 0);

    }

    return result;
}

然后每个玩家,根据他的颜色,也将有以下矩阵,其中包含根据获得的骰子值/位置的颜色的面。换句话说,这个矩阵包含图像的“一条线”,并按值索引:

private BufferedImage[][] initExactDieFaces() {
    BufferedImage[][] result = new BufferedImage[6][1];
    int row = -1;
    String myColor = this.coreGame.getMyPartecipant().getColor();
    if (myColor.equals(Constants.COLOR[0])) {
        row = 0;
    } else if (myColor.equals(Constants.COLOR[1])) {
        row = 2;
    } else if (myColor.equals(Constants.COLOR[2])) {
        row = 4;
    } else if (myColor.equals(Constants.COLOR[3])) {
        row = 1;
    } else if (myColor.equals(Constants.COLOR[4])) {
        row = 5;
    } else if (myColor.equals(Constants.COLOR[5])) {
        row = 3;
    }
    int offset = 0;
    for (int i = 0; i < 6; i++) {
        result[i][0] = DieSprite.getSprite(row, i, offset);
        offset += 2;
    }
    return result;
}

我想要的是以下内容: - 当按下“翻转骰子”按钮时,我希望在 JPanel 内的特定 JLabel 中显示(例如)20 个随机骰子面(它们应该取自第一个数组 AnimationBuffer) - 上一个动画完成后,我希望显示获得的骰子发射结果(根据颜色 pawn,取自 ExcatDieFaces)。

要做到这一点,我知道我需要 Swing Timer,但我无法将它们放在一起;下面是 startAnimationDie 方法的一些代码,当按下“翻转骰子”按钮时会调用该方法:

private void startAnimationDie(final JPanel dieContainer) {

  final BufferedImage[] animationBuffer = initAnimationBuffer();
  final BufferedImage[][] exactDieFaces = initExactDieFaces();
  final AnimationSprite animation = new AnimationSprite(
                    animationBuffer, Constants.DIE_ANIMATION_SPEED);

  /* getting launch value fromt the core Game */
  int launchResult = coreGame.launchDie();
  coreGame.getMyPartecipant().setLastLaunch(launchResult);

  final Timer timer = new Timer(250, new ActionListener() {

  @Override
  public void actionPerformed(ActionEvent e) {

     dieContainer.removeAll();
     dieContainer.updateUI();
     animation.start();
     JLabel resultDie = new JLabel();
     resultDie.setBounds(60, 265, Constants.DIE_SIZE,Constants.DIE_SIZE);
     resultDie.setIcon(new ImageIcon(animationBuffer[new Random().nextInt(36)]));
     dieContainer.add(resultDie);
     dieContainer.updateUI();
     updateUI();
     repaint();

    }
  });

/* animation begins, rolling faces are shown each time the Timer ends*/
for(int i = 0; i<20; i++) 
  timer.start()

/* showing the final face according to the pawn color and the obtained result from the launch */

dieContainer.removeAll();
dieContainer.updateUI();
AnimationSprite resultAnimation = new AnimationSprite(exactDieFaces[launchResult - 1], 6);
resultAnimation.start(); 
resultAnimation.update();
resultDie.setIcon(new ImageIcon(exactDieFaces[launchResult - 1][0]));
resultDie.setBounds(60, 265, Constants.DIE_SIZE, Constants.DIE_SIZE);
dieContainer.add(resultDie);
dieContainer.updateUI();
dieContainer.repaint();

}

我怎样才能让它工作?我想我应该使用 Swing.invokeAndWait 但我不能把所有的部分放在一起......你能帮忙吗?

【问题讨论】:

  • 首先不要调用updateUI,除非您安装自定义外观,否则您永远不需要调用它。其次,不要删除所有组件,只需更新标签的 die 图像 (setIcon)。第三,使用单个Timer,它可以重复给定的次数(使用计数器)并且会更新模具面......
  • 好的,对于前两个建议我没问题。关于计时器,我使用的是计数器,不是吗?它是在 for 循环中激活的……如果可以的话,请给我更多帮助……
  • 你启动Timer 20 次,没有任何效果...除了启动一次...您必须将Timer 视为一种循环...
  • 好的,所以计时器每次结束时都会重新开始......那么我应该如何以及在哪里停止它?
  • ((Timer)evt).stop() 一旦counter 达到20(或它的限制)...

标签: java swing animation sprite


【解决方案1】:
  1. 请勿致电updateUI,除非您正在处理安装外观和感觉,否则它不会按照您的想法进行(而且效率非常低)
  2. 不要每次都重新构建 UI,这是一项耗时的工作,这会使动画看起来静止和交错,并且可能会闪烁很多。相反,只需更新标签的 icon 属性
  3. 使用单个 Timer,允许它增加一个计数器,这样您就知道它被调用了多少次,并在每个滴答时更新掷骰子和计数器。

Timer 视为一种循环,在每次迭代(滴答声)中,您都需要做一些事情(比如增加计数器)

(注意-当看起来骰子“停止”时,这是因为图像按顺序显示不止一次。您可以通过将所有图像放入 List 并使用 Collections.shuffle 来解决这个问题. 重复此操作 3 次,将结果添加到另一个 List 应该会给你 24 个不重复的序列(好吧,它“可能”在边界上重复,但最好使用 Math.random ;))

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private BufferedImage[] dice = new BufferedImage[6];
        private JLabel die;

        public TestPane() {
            try {
                BufferedImage img = ImageIO.read(new File("/Users/swhitehead/Documents/Die.png"));
                int width = 377 / 6;
                for (int index = 0; index < 6; index++) {
                    dice[index] = img.getSubimage(width * index, 0, width, width);
                }
            } catch (IOException ex) {
                ex.printStackTrace();
            }
            setLayout(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridwidth = GridBagConstraints.REMAINDER;
            die = new JLabel(new ImageIcon(dice[0]));
            add(die, gbc);

            JButton roll = new JButton("Roll");
            add(roll, gbc);

            roll.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    roll.setEnabled(false);
                    Timer timer = new Timer(250, new ActionListener() {
                        private int counter;
                        private int lastRoll;
                        @Override
                        public void actionPerformed(ActionEvent e) {
                            if (counter < 20) {
                                counter++;
                                lastRoll = (int)(Math.random() * 6);
                                System.out.println(counter + "/" + lastRoll);
                                die.setIcon(new ImageIcon(dice[lastRoll]));
                            } else {
                                lastDieRollWas(lastRoll);
                                ((Timer)e.getSource()).stop();
                                roll.setEnabled(true);
                            }
                        }
                    });
                    timer.start();
                }
            });
        }

        protected void lastDieRollWas(int roll) {
            System.out.println("You rolled " + (roll + 1));
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200);
        }

    }

}

【讨论】:

  • 这是您的好心。发自内心的感谢!看来我现在取得了不错的成绩:)
  • 另一个问题:如果我想等待滚动完成后再让玩家在棋盘上移动,我应该使用 invokeAndWait() 对吗?
  • 不!它可能会引发您尝试的异常。如果你注意到,当我启动计时器时,我禁用了按钮,当我停止计时器时,我重新启用它,但我也调用“lastDieRollWas”,这就是你应该做的。当你停止 To we 时,调用她的方法让你的程序知道滚动已经停止
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-03-14
  • 2017-05-18
  • 2014-02-10
  • 2015-04-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多