I'm building a game in Java and I need to synchronize the animation executions.
Currently they will occur simultaneously, while accessing the same data.
I'd prefer that the animations stack up, and execute one at a time.
在这个游戏中,当你 Select 一个积木时,它会随机向左或向右旋转90度.
我正在使用以下代码.
static class GUI extends JFrame {
JPanel panel = new JPanel(new GridLayout(10, 10));
static class Box extends JLabel {...}
GUI() {
for (int i = 0; i < 100; i++) panel.add(new Box());
setDefaultCloseOperation(EXIT_ON_CLOSE);
setResizable(false);
add(panel);
pack();
setVisible(true);
}
}
And, here is the Box class.
Variables bg and c are background and color, a is angle, and d is direction where true is clockwise.
static class Box extends JLabel {
static Random r = new Random();
int bg, c = r.nextInt(bg = 0xffffff);
int a0, a = 90 * r.nextInt(1, 4);
boolean d;
Box() {
setPreferredSize(new Dimension(100, 100));
setBackground(bg());
setBorder(BorderFactory.createLineBorder(c(), 2, true));
setOpaque(true);
addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
super.mouseClicked(e);
EventQueue.invokeLater(() -> {
d = r.nextInt(2) == 0;
bg -= 0x0f0f0f;
animate();
setBackground(bg());
});
}
});
}
void animate() {
a0 = a;
new Timer(10, e -> {
a += d ? -5 : 5;
if (Math.abs(a - a0) == 90) {
((Timer) e.getSource()).stop();
if (a == 360) a = 0;
}
repaint();
}).start();
}
Color c() { return new Color(c); }
Color bg() { return new Color(bg); }
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(c());
g2.setStroke(new BasicStroke(10));
g2.rotate(Math.toRadians(a), 50, 50);
g2.draw(new Line2D.Double(50, 50, 50, 0));
g2.setBackground(bg());
}
}
When I double-click a JLabel it rotates indefinitely. How do I synchronize this?
A single click works correctly.
基本上,我希望每次点击鼠标都会产生一个新的100,直到前一个完成后才会开始.
On a final note, should I be using the volatile keyword for a0 and a?
And, if anyone has any recommendations on my code, feel free.
编辑
我相信我找到了解决办法.
I've created a Box inner-class, Animation, which implements Runnable.
In the run method I make the adjustments to Box, and start the Timer object.
The enclosing Box class, now has a List queue, which is appended to for each mouse-click.
When the Animation object finishes, it will remove itself from queue, and start the next, if any.
For a0 and a I am using the AtomicInteger class.
This has significantly improved the performance of the rotation.
AtomicInteger a0 = new AtomicInteger();
AtomicInteger a = new AtomicInteger(90 * r.nextInt(1, 4));
class Animation implements Runnable {
@Override
public void run() {
d = r.nextInt(2) == 0;
bg -= 0x0f0f0f;
setBackground(bg());
setText(String.valueOf(s));
a0.set(a.get());
new Timer(10, l -> {
a.set(a.get() + (d ? -5 : 5));
repaint();
if (Math.abs(a.get() - a0.get()) == 90) {
((Timer) l.getSource()).stop();
if (a.get() == 360) a.set(0);
queue.remove(0);
if (queue.size() != 0) queue.get(0).run();
}
}).start();
}
}
此外,我创建了一个方法rotate,以从mouseClicked方法调用.
void rotate() {
queue.add(new Animation());
if (queue.size() == 1) queue.get(0).run();
}
Here is the complete code for Box, re-factored.
I've additionally added text to the box, to count the clicks.
static class Box extends JLabel {
static Random r = new Random();
int bg, c = r.nextInt(bg = 0xffffff), s = 0;
AtomicInteger a0 = new AtomicInteger();
AtomicInteger a = new AtomicInteger(90 * r.nextInt(1, 4));
boolean d;
List<Animation> queue = new ArrayList<>();
Box() {
setPreferredSize(new Dimension(100, 100));
setBackground(bg());
setBorder(BorderFactory.createLineBorder(c(), 2, true));
setOpaque(true);
setFont(new Font("Mono", Font.PLAIN, 100));
setForeground(c());
setText(String.valueOf(s));
setHorizontalAlignment(SwingConstants.CENTER);
setVerticalTextPosition(SwingConstants.CENTER);
addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
super.mouseClicked(e);
s++;
rotate();
}
});
}
void rotate() {
queue.add(new Animation());
if (queue.size() == 1) queue.get(0).run();
}
Color c() { return new Color(c); }
Color bg() { return new Color(bg); }
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g.create();
g2.setColor(c());
g2.setStroke(new BasicStroke(10));
g2.rotate(Math.toRadians(a.get()), 50, 50);
g2.draw(new Line2D.Double(50, 50, 50, 0));
g2.setBackground(bg());
g2.dispose();
}
class Animation implements Runnable {...}
}