我想用java swing制作动画。我有一个for循环,可以稍微改变图像的位置。我的问题是如何让每个循环执行花费指定的时间?。
它是我所做的但我不知道如何使用wait()
并且不知道是否有更好的方法。
// inside of a MyJPanel class extending JPanel
for (int i = 0; i < FJframe.FRAMES; i++){
long start = System.currentTimeMillis();
animationFrame = i;
this.repaint();
long end = System.currentTimeMillis();
long remainder = (end - start);
System.out.println(remainder);
try {
this.wait(200 - remainder);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
这里我的重写Jpanel PaintComponent()
:
//一些绘画
drawChanges(g,getDelta(),animationFrame,FJPanle.FRAMES);
在drawChanges(Graphics g,ArrayList deltas,int frame,int frames)中:
// a switch_case with cases similar to this one.
case AGENT_ATTACK:
// tu phase badi animation e kill
//drawImage(g2d, map[delta.getCell().x][delta.getCell().y], attacker);
source = map[delta.getSource().x][delta.getSource().y];
dest = map[delta.getDestination().x][delta.getDestination().y];
distanceX = dest.getCenter().x -
source.getCenter().x;
distanceY = dest.getCenter().y -
source.getCenter().y;
if (counter < frames / 2){
g2d.drawImage(ImageHolder.attacker, source.getBounds().x + (int)(((float)counter/frames) * distanceX),
source.getBounds().y + (int)(((float)counter/frames) * distanceY),
null);
}
else{
g2d.drawImage(ImageHolder.attacker, dest.getBounds().x - (int)(((float)counter/frames) * distanceX),
dest.getBounds().y - (int)(((float)counter/frames) * distanceY),
null);
}
break;
我希望每个循环都需要,例如,恰好200毫秒。我怎样才能做到这一点?
答案 0 :(得分:2)
考虑使用Timer
。例如,scheduleAtFixedRate()
方法。
答案 1 :(得分:1)
可能不是一个可接受的答案,但评论的时间太长了:
根据实际意图,有几种选择。您描述的模式对于“简单的游戏循环”并不罕见。在这种情况下,与您发布的代码类似的代码在自己的线程中运行,并定期触发repaint()
以绘制更新的游戏状态。在您的情况下,似乎仅 animationFrame
变量增加了。对于这样一个简单的行动,已经提到的替代方案可能就足够了:
java.util.Timer
的{{1}}来更新TimerTask
animationFrame
更新javax.swing.Timer
的{{1}}。这个可能在这里是有利的,因为您可以确定ActionListener
的更新发生在事件派发线程上。因此,此变量的更新不会干扰绘制方法中的用法,例如animationFrame
)您可能需要自己处理同步 编辑:根据编辑过的问题:与我的预期相似,您在绘画方法中使用animationFrame
。这里的关键点是有关如何使用java.util.Timer
变量的详细信息。例如,如果您的animationFrame
方法看起来像这样
animationFrame
然后可能会发生paintComponent
的值被更改(由另一个线程,可能是class MyJPanel extends JPanel {
private int animationFrame = 0;
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
drawChanges(g, getDelta(), animationFrame, FJPanle.FRAMES);
drawSomethingElse(g, animationFrame, ...);
drawEvenMore(g, animationFrame, ...);
}
...
}
线程),而执行animationFrame
方法。这意味着java.util.Timer
和paintComponent
可能会收到drawChanges
的不同的值。这可能导致渲染伪像(错位的图像,在瓷砖之间撕裂等)。
这可以通过使用drawSomethingElse
来避免(因为那时,animationFrame
的更新将在与执行javax.swing.Timer
的线程相同的线程上完成),或者通过制作确保所有绘画操作使用相同的animationFrame
值 - 大致如下:
paintComponent
但除此之外,在这种情况下,上述方法之间并没有太大的区别。因此,为简单起见,您可以使用animationFrame
(或 @Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int animationFrameUsedForAllPainting = animationFrame;
drawChanges(g, getDelta(), animationFrameUsedForAllPainting , FJPanle.FRAMES);
drawSomethingElse(g, animationFrameUsedForAllPainting , ...);
drawEvenMore(g, animationFrameUsedForAllPainting , ...);
}
来确保更新对于绘制操作是“线程安全的”。)