Java Swing 计时器和动画:如何将它们组合在一起 [英] Java Swing Timer and Animation: how to put it together

查看:20
本文介绍了Java Swing 计时器和动画:如何将它们组合在一起的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am gonna re-post this questione again trying to be more precise and hoping I will get some help because this is driving me crazy. I am developing a board game with up to 6 player, each one with a different colored pawn. I have the following image that is loaded in BufferedImage arrays treating it as a sprite:

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

and this is the relative code, putting each face of each colored die in a position in the 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;
}

然后每个玩家,根据他的颜色,也将根据获得的骰子值/位置包含以下包含他的颜色的面孔的矩阵.换句话说,这个矩阵包含图像的一行",并按值索引:

Then each player, according to his color, wil have also the following matrix containing the faces of his color according to the obtained die value/position. In other words this matrix contains "a line" of the image and it is indexed by value:

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).

What I want is the following: -when the "flip die" button is pressed, I want that (for example) 20 random die faces are shown (they should be taken from the first array, AnimationBuffer) in a specific JLabel inside a JPanel -as soon as the previous animation has finished, I want that the obtained result of the launch of the die is shown (according to the color pawn, taken from ExcatDieFaces).

为了得到这个,我知道我需要 Swing Timer,但我无法把它放在一起;这是按下翻转模具"按钮时调用的 startAnimationDie 方法的一些代码:

To get this I know that I need Swing Timer but I am not able to put it all together; here's some code of the startAnimationDie method which is called when the "flip die" button is pressed:

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 但我不能把所有的部分放在一起......你能帮忙吗?

How can I make it work? I think I am supposed to use Swing.invokeAndWait but I cannot put together all the pieces...Can you help please?

推荐答案

  1. 不要调用 updateUI,除非您正在处理安装外观和感觉,否则它并没有按照您的想法去做(而且效率非常低)
  2. 不要每次都重新构建 UI,这是一项耗时的工作,会使动画看起来静止和交错,并且可能会闪烁很多.相反,只需更新标签的 icon 属性
  3. 使用单个 Timer,让它增加一个计数器,这样你就知道它被调用了多少次,并在每个刻度上更新骰子和计数器.
  1. Don't call updateUI, unless you're dealing with installing a look and feel, it's not doing what you think it is (and it's very inefficient)
  2. Don't rebuild the UI each time, this is time consuming work, which is going to make the animation look stilled and staggered and probably flash a lot. Instead, simply update the icon property of the label
  3. Use a single Timer, allow it to increment a counter, so you know how many times it's been called and update the die roll and counter on each tick.

Timer 想象成一种循环,在每次迭代(滴答)时,你需要做一些事情(比如增加计数器)

Think of the Timer as a kind of loop, where on each iteration (tick), you need to do something (like increment the counter)

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

(Note- When it looks like the die has "stalled", it's because the image is been displayed more then once in sequence. You could over come this by placing all the images into a List and using Collections.shuffle. Do this three times, adding the result to another List should give you 24, no-repeating sequence (ok, it "might" repeat on the boundaries, but it's better then using 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);
        }

    }

}

这篇关于Java Swing 计时器和动画:如何将它们组合在一起的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆