Reputation: 2549
I want to make an animation using java swing. I have a for loop that changes the location of an image slightly. My question is How can I make the each loop execution takes the specified amount of time?.
Its what I've done but I don't know how to use wait()
and don't know is there a better way.
// 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();
}
}
here my overrided Jpanel PaintComponent()
:
//some drawing
drawChanges(g, getDelta(), animationFrame, FJPanle.FRAMES);
And inside 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;
I want each loop takes, for example, exactly 200 miliseconds. How can I achieve this?
Upvotes: 0
Views: 91
Reputation: 54709
Probably not an acceptable answer, but too long for a comment:
There are several options, depending on the actual intention. The pattern that you described is not uncommon for a "simple game loop". In this case, a code that is similar to the one that you posted is run in an own thread, and regularly triggers a repaint()
in order to paint the updated game state. In your case, it seems that only the animationFrame
variable is increased. For such a simple action, the alternatives that have already been mentioned may be sufficient:
java.util.Timer
with a TimerTask
that only updated the animationFrame
javax.swing.Timer
whose ActionListener
updates the animationFrame
. This might be advantageous here, because you can be sure that the update of the animationFrame
happens on the event dispatch thread. Thus, the update of this variable can not interfere with its usage in the painting method, for examplejava.util.Timer
) you might have to take care of the synchronization on your ownEDIT: Based on the edited question: Similar to what I expected, you are using the animationFrame
in your painting method. The crucial point here are the details about how the animationFrame
variable is used. For example, if your paintComponent
method looks like this
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, ...);
}
...
}
then it may happen that the value of animationFrame
is changed (by another thread, possibly the java.util.Timer
thread) while the paintComponent
method is executed. That means that drawChanges
and drawSomethingElse
may receive different values for animationFrame
. This may cause rendering artefacts (misplaced images, tearing between tiles etc).
This could either be avoided by using a javax.swing.Timer
(because then, the updates of animationFrame
will be done on the same thread as the one that executes paintComponent
), or by making sure that all painting operations use the same value of animationFrame
- roughly like this:
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int animationFrameUsedForAllPainting = animationFrame;
drawChanges(g, getDelta(), animationFrameUsedForAllPainting , FJPanle.FRAMES);
drawSomethingElse(g, animationFrameUsedForAllPainting , ...);
drawEvenMore(g, animationFrameUsedForAllPainting , ...);
}
But apart from that, there is not sooo much difference between the aforementioned approaches in this case. So for simplicity, you could use the javax.swing.Timer
(or the java.util.Timer
when you make sure that the update is "thread safe" regarding the painting operations).
Upvotes: 1
Reputation: 12332
Look into using a Timer
. For example, the scheduleAtFixedRate()
method.
Upvotes: 2