Java Swing Timer 和 Animation:如何组合在一起
Java Swing Timer and Animation: how to put it together
我将再次 post 这个问题,试图更加精确,希望我能得到一些帮助,因为这让我发疯。我正在开发一款最多有 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;
}
然后每个玩家,根据他的颜色,还会有以下矩阵,其中包含根据获得的骰子 value/position 他颜色的面孔。换句话说,该矩阵包含图像的 "a line" 并且按值索引:
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;
}
我想要的是以下内容:
- 当按下 "flip die" 按钮时,我希望(例如)在 JPanel 内的特定 JLabel 中显示 20 个随机模面(它们应该从第一个数组 AnimationBuffer 中获取)
- 一旦前面的动画完成,我希望显示骰子发射的结果(根据颜色 pawn,取自 ExcatDieFaces)。
为了得到这个,我知道我需要 Swing Timer,但我无法将它们放在一起;这是按下 "flip die" 按钮时调用的 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
,除非您正在处理安装外观,否则它不会像您想象的那样工作(而且效率非常低)
- 不要每次都重建 UI,这是一项耗时的工作,这会使动画看起来静止和交错,并且可能会经常闪烁。相反,只需更新标签
的 icon
属性
- 使用单个
Timer
,让它增加一个计数器,这样您就知道它被调用了多少次,并在每次更新时更新掷骰和计数器。
将 Timer
视为一种循环,在每次迭代(滴答)中,您需要做一些事情(例如增加计数器)
(注意 - 当骰子看起来有 "stalled" 时,这是因为图像按顺序显示了不止一次。您可以通过将所有图像放入 List
并使用 Collections.shuffle
。这样做三次,将结果添加到另一个 List
应该给你 24,无重复序列(好的,它 "might" 在边界上重复,但更好然后使用 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);
}
}
}
我将再次 post 这个问题,试图更加精确,希望我能得到一些帮助,因为这让我发疯。我正在开发一款最多有 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;
}
然后每个玩家,根据他的颜色,还会有以下矩阵,其中包含根据获得的骰子 value/position 他颜色的面孔。换句话说,该矩阵包含图像的 "a line" 并且按值索引:
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;
}
我想要的是以下内容: - 当按下 "flip die" 按钮时,我希望(例如)在 JPanel 内的特定 JLabel 中显示 20 个随机模面(它们应该从第一个数组 AnimationBuffer 中获取) - 一旦前面的动画完成,我希望显示骰子发射的结果(根据颜色 pawn,取自 ExcatDieFaces)。
为了得到这个,我知道我需要 Swing Timer,但我无法将它们放在一起;这是按下 "flip die" 按钮时调用的 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
,除非您正在处理安装外观,否则它不会像您想象的那样工作(而且效率非常低) - 不要每次都重建 UI,这是一项耗时的工作,这会使动画看起来静止和交错,并且可能会经常闪烁。相反,只需更新标签 的
- 使用单个
Timer
,让它增加一个计数器,这样您就知道它被调用了多少次,并在每次更新时更新掷骰和计数器。
icon
属性
将 Timer
视为一种循环,在每次迭代(滴答)中,您需要做一些事情(例如增加计数器)
(注意 - 当骰子看起来有 "stalled" 时,这是因为图像按顺序显示了不止一次。您可以通过将所有图像放入 List
并使用 Collections.shuffle
。这样做三次,将结果添加到另一个 List
应该给你 24,无重复序列(好的,它 "might" 在边界上重复,但更好然后使用 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);
}
}
}