Reputation: 35
EDIT: Java/FX Version: 13.0.2
IDE: Eclipse 4.13.0
Tracker and changePerFrame values are declared as private double variables on the class level.
Goal: Create a timeline that makes a short animation sequence using a Canvas and Image. Image will start with a X/Y location off screen, and an increased Size. The image will then shrink and move into view, creating a sort of zoom-out?/Shrink? effect.
Issues: When creating a new KeyFrame inside the loop, the correct values are not being passed. The values when printed inside the loop using println, are correct, but the values being passed in '(evt -> swordAnimation(xTracker, yTracker, wTracker, hTracker))' are always the same incorrect value. Can anyways explain why this happens?
Timeline timeline2 = new Timeline();
for (int f = 0; f < 17; f++) {
System.out.println("xTracker: "+ xTracker + ", yTracker: " + yTracker + ", wTracker: " + wTracker + ", hTracker: " + hTracker);
timeline2.getKeyFrames().add(
new KeyFrame(Duration.millis(durationTracker),
(evt -> swordAnimation(xTracker, yTracker, wTracker, hTracker)))
);
durationTracker += durationPerFrame;
xTracker += xChangePerFrame;
yTracker += yChangePerFrame;
wTracker += wChangePerFrame;
hTracker += hChangePerFrame;
}
timeline2.setCycleCount(1);
timeline2.play();
swordAnimation Method
private void swordAnimation(double x, double y, double w, double h) {
System.out.println("x: "+ x + ", y: " + y + ", w: " + w + ", h: " + h);
gc.clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
gc.drawImage(swordAnimation[0], x, y, w, h);
}
Here is the println output for the first 5 loops, and the println output when the method is called.
durationTracker: 0.0
xTracker: -75.0, yTracker: -80.95952023988006, wTracker: 1497.6000000000001, hTracker: 864.0
durationTracker: 16.666666666666668
xTracker: -67.1875, yTracker: -72.52623688155923, wTracker: 1427.4, hTracker: 823.5
durationTracker: 33.333333333333336
xTracker: -59.375, yTracker: -64.09295352323839, wTracker: 1357.2, hTracker: 783.0
durationTracker: 50.0
xTracker: -51.5625, yTracker: -55.65967016491756, wTracker: 1287.0, hTracker: 742.5
durationTracker: 66.66666666666667
xTracker: -43.75, yTracker: -47.22638680659672, wTracker: 1216.8, hTracker: 702.0
x: 57.8125, y: 62.40629685157417, w: 304.19999999999953, h: 175.5
x: 57.8125, y: 62.40629685157417, w: 304.19999999999953, h: 175.5
x: 57.8125, y: 62.40629685157417, w: 304.19999999999953, h: 175.5
x: 57.8125, y: 62.40629685157417, w: 304.19999999999953, h: 175.5
x: 57.8125, y: 62.40629685157417, w: 304.19999999999953, h: 175.5
and the output for the println when f=16
durationTracker: 266.66666666666663
xTracker: 50.0, yTracker: 53.97301349325333, wTracker: 374.3999999999995, hTracker: 216.0
Let me know if more information is required, but I believe this is all the information that is needed.
Upvotes: 1
Views: 140
Reputation: 82461
You're storing the values in fields. Those field values are evaluated at the time the lambda expression is invoked. This is after the completion of the loop and therefore every frame sees the final values.
Create final
local variables storing the intermediate values and use them in the lambda instead:
final double xTrackerLocal = xTracker; // you may need to us a different type here
final double yTrackerLocal = yTracker;
final double wTrackerLocal = wTracker;
final double hTrackerLocal = hTracker;
timeline2.getKeyFrames().add(
new KeyFrame(Duration.millis(durationTracker),
(evt -> swordAnimation(xTrackerLocal, yTrackerLocal, wTrackerLocal, hTrackerLocal)))
);
durationTracker += durationPerFrame;
xTracker += xChangePerFrame;
yTracker += yChangePerFrame;
wTracker += wChangePerFrame;
hTracker += hChangePerFrame;
Upvotes: 3