循环不会等待动画完成,然后再迭代到下一个动画



在玩家移动石头(类似糖果的游戏)后,在逻辑中,我收集有关玩家移动是否会导致结构的信息,然后需要爆炸。当然,一旦一个结构通过移除结构元素并掉落上面的石头而爆炸,就会出现新的结构,这些结构也需要按顺序爆炸。

为此,我有一个 AnimationData 类,它有一个爆炸数据列表,它具有由初始玩家移动引起的找到的结构的大小。

我的代码只适用于一次爆炸,但如果发生多次爆炸,就会搞砸。问题是循环不会等到爆炸动画完成后再继续迭代。

澄清:方法更新Gui,循环内开关动画.setOn完成

视觉: 我录制的单个爆炸和多次爆炸的剪辑

public void updateGui(AnimationData aData) {
final int rowHeight = (int) (boardGPane.getHeight() / boardGPane.getRowConstraints().size());
Coords switchSourceCoords = aData.getSwitchSourceCoords();
Coords switchTargetCoords = aData.getSwitchTargetCoords();
// Apply player move
ParallelTransition switchAnimation = switchStones(switchSourceCoords, switchTargetCoords);
switchAnimation.play();
// Revert switch, if the move was invalid
if (aData.geteData().isEmpty()) {
switchAnimation.setOnFinished(event -> {
ParallelTransition switchBackAnimation = switchStones(switchSourceCoords, switchTargetCoords);
switchBackAnimation.play();
});
} else {
switchAnimation.setOnFinished(event -> {
// Animate explosions for every found Structure
for (ExplosionData eData : aData.geteData()) {
SequentialTransition explosionAnimation = new SequentialTransition();
// Coordinates of where the bonusStone appears
Coords bonusSource = eData.getBonusSourceCoords();
// Coordinates of where the bonusStone need to be repositioned
Coords bonusTarget = eData.getBonusTargetCoords();
// Remove all Structure elements and make Stones above drop to their target
// positions. Also translate them back to the same position for the animation
removeStructureAndReplaceIvs(eData, bonusTarget, bonusSource, rowHeight);
// This shall only proceed if the animation involves handeling a bonusStone
if (bonusSource != null && bonusTarget != null) {
int rowsToMove = bonusTarget.getRow() - bonusSource.getRow();
ImageView bonusIv = (ImageView) JavaFXGUI.getNodeFromGridPane(boardGPane, bonusTarget.getCol(), bonusTarget.getRow());
// BonusStone shall fade in at the source Position
explosionAnimation = bonusStoneFadeIn(explosionAnimation, rowsToMove, bonusIv, rowHeight);
// Translate to targetPosition, if sourcePosition is not equal to targetPosition
explosionAnimation = bonusStoneMoveToTargetCoords(explosionAnimation, rowsToMove, bonusIv, rowHeight);
}
// Make the Stone ImageViews translate from their origin position to their new target positions
explosionAnimation = dropAndFillUpEmptySpace(explosionAnimation, eData, bonusTarget, bonusSource, rowHeight);
explosionAnimation.play();
}
});
}
}

private void removeStructureAndReplaceIvs(ExplosionData eData,
Coords bonusTargetCoords,
Coords bonusSourceCoords,
final int rowHeight) {
// Removing the Structure and all stones above by deleting the ImageViews col by col
for (DropInfo info : eData.getExplosionInfo()) {
// Coordinates of the Structure element that is going to be removed in this col
int col = info.getCoords().getCol();
int row = info.getCoords().getRow();
// If a bonusStone will apear, the heightOffset gets reduced by one
int offset = getAppropiateOffset(bonusTargetCoords, info, col);
// Remove the Structure and all ImageViews above
removeImageViewsFromCells(col, row, row + 1);
List<String> stoneToken = info.getFallingStoneToken();
for (int r = row, i = 0; r >= 0; --r, ++i) {
// Fill up removed Cells with new ImageViews values
ImageView newIv = new ImageView(new Image(preImagePath + stoneToken.get(i) + ".png"));
// Place each iv to their target Coords
addImageViewToPane(newIv, col, r);
// Translate all non-bonusStones to the position they were placed before
if (ignoreBonusTargetCoordinates(bonusTargetCoords, bonusSourceCoords, r, col)) {
newIv.setTranslateY(-rowHeight * offset);
}
}
}
}
// If the removed Structure results to generate a bonusStone, make it fade in at source position
private SequentialTransition bonusStoneFadeIn(SequentialTransition explosionAnimation,
int sourceToTargetDiff,
ImageView bonusIv,
final int rowHeight) {
FadeTransition bonusFadeIn = new FadeTransition(Duration.seconds(1), bonusIv);
bonusFadeIn.setFromValue(0f);
bonusFadeIn.setToValue(1f);
// If the target Position is not the same, place it to target and translate to source position
if (sourceToTargetDiff > 0) {
bonusIv.setTranslateY(-rowHeight * sourceToTargetDiff);
}
explosionAnimation.getChildren().add(bonusFadeIn);
return explosionAnimation;
}
// If the bonusStone must be moved from source Coordinates to target Coordinates
private SequentialTransition bonusStoneMoveToTargetCoords(SequentialTransition explosionAnimation,
int sourceToTargetDiff,
ImageView bonusIv,
final int rowHeight) {
// Difference in row from bonusSourceCoordinates to bonusTargetCoordinates
if (sourceToTargetDiff > 0) {
TranslateTransition moveToTargetCoords = new TranslateTransition(Duration.seconds(1), bonusIv);
moveToTargetCoords.fromYProperty().set(-rowHeight * sourceToTargetDiff);
moveToTargetCoords.toYProperty().set(0);
explosionAnimation.getChildren().add(moveToTargetCoords);
}
return explosionAnimation;
}
private SequentialTransition dropAndFillUpEmptySpace(SequentialTransition explosionAnimation,
ExplosionData eData,
Coords bonusTargetCoords,
Coords bonusSourceCoords,
final int rowHeight) {
ParallelTransition animateDrop = new ParallelTransition();
for (int i = 0; i < eData.getExplosionInfo().size(); i++) {
// List of all stoneToken to create respective ImageViews for each col
List<DropInfo> allDropInfo = eData.getExplosionInfo();
int col = allDropInfo.get(i).getCoords().getCol();
int row = allDropInfo.get(i).getCoords().getRow();
// If a bonusStone will apear, the heightOffset gets reduced by one
int offset = getAppropiateOffset(bonusTargetCoords, allDropInfo.get(i), col);
for (int r = row; r >= 0; --r) {
// Drop all Stones above the removed Structure to fill up the empty space
// Ignore possible bonusStones since they are being animated seperately
if (ignoreBonusTargetCoordinates(bonusTargetCoords, bonusSourceCoords, r, col)) {
ImageView iv = (ImageView) JavaFXGUI.getNodeFromGridPane(boardGPane, col, r);
TranslateTransition tt = new TranslateTransition(Duration.millis(1500), iv);
tt.fromYProperty().set(-rowHeight * offset);
tt.toYProperty().set(0);
animateDrop.getChildren().add(tt);
}
}
}
explosionAnimation.getChildren().add(animateDrop);
return explosionAnimation;
}
private int getAppropiateOffset(Coords bonusTargetCoords, DropInfo dropInfo, int col) {
int bonusOffset = (bonusTargetCoords != null && col == bonusTargetCoords.getCol()) ? 1 : 0;
return dropInfo.getHeightOffset() - bonusOffset;
}
private boolean ignoreBonusTargetCoordinates(Coords bonusTargetCoords,
Coords bonusSourceCoords,
int row,
int col) {
return bonusSourceCoords == null
|| bonusTargetCoords != null && col != bonusTargetCoords.getCol()
|| bonusTargetCoords != null && row != bonusTargetCoords.getRow();
}

>SequentialTransition可以由其他SequentialTransition组成。对于您的代码,您可以创建一个"主"SequentialTransition,并使用每次迭代for循环创建的每个SequentialTransition来构建它。然后,您将播放主过渡。

switchAnimation.setOnFinished(event -> {
SequentialTransition masterAnimation = new SequentialTransition();
for (ExplosionData eData : aData.geteData()) {
SequentialTransition explosionAnimation = new SequentialTransition();
// ... configure the explosionAnimation ...
masterAnimation.getChildren().add(explosionAnimation); // add to masterAnimation
}
masterAnimation.play(); // play all the explosionAnimations in squential order
});

代码在转到循环的下一次迭代之前不等待动画完成的原因是Animation.play()是"异步调用"。 当您调用play()动画会在后台使用一些内部时钟/计时器进行计划,并且该方法会立即返回。

从当前位置沿 指示的方向播放Animationrate.如果Animation正在运行,则它不起作用。

rate > 0(向前播放)时,如果Animation已定位在 结束,第一个循环不会播放,它被认为是有 已经完成了。这也适用于向后(rate < 0)循环,如果Animation位于开头。但是,如果动画 有cycleCount > 1,以下循环将照常播放。

Animation到达末尾时,动画将停止,并且 播放头保留在最后。

从最后
Animationanimation.setRate(negative rate);
animation.jumpTo(overall duration of animation);
animation.play();

注意:

play()是异步调用,Animation可能不会立即启动。

(我的)


这是一个可行的示例。它需要一个Rectangle并将其转换为场景的每个角落。每个"平移到角落"都是一个单独的动画,它还涉及旋转Rectangle并更改其颜色。然后将所有"平移到角落"动画放入一个SequentialTransition中。单击时禁用顶部的Button,并在主SequentialTransition完成后重新启用。

import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.ParallelTransition;
import javafx.animation.RotateTransition;
import javafx.animation.SequentialTransition;
import javafx.animation.Timeline;
import javafx.animation.TranslateTransition;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Separator;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Main extends Application {
private Button playBtn;
private StackPane groupParent;
private Rectangle rectangle;
@Override
public void start(Stage primaryStage) {
playBtn = new Button("Play Animation");
playBtn.setOnAction(ae -> {
ae.consume();
playBtn.setDisable(true);
playAnimation();
});
HBox btnBox = new HBox(playBtn);
btnBox.setAlignment(Pos.CENTER);
btnBox.setPadding(new Insets(8));
rectangle = new Rectangle(150, 100, Color.BLUE);
groupParent = new StackPane(new Group(rectangle));
groupParent.getChildren().get(0).setManaged(false);
VBox root = new VBox(btnBox, new Separator(), groupParent);
root.setMaxSize(600, 400);
root.setAlignment(Pos.CENTER);
VBox.setVgrow(groupParent, Priority.ALWAYS);
Scene scene = new Scene(root, 600, 400);
primaryStage.setScene(scene);
primaryStage.setTitle("Animation");
primaryStage.setResizable(false);
primaryStage.show();
}
private void playAnimation() {
double maxX = groupParent.getWidth() - rectangle.getWidth();
double maxY = groupParent.getHeight() - rectangle.getHeight();
ParallelTransition pt1 = createAnimation(-25, maxY - 25, 90, Color.FIREBRICK);
ParallelTransition pt2 = createAnimation(maxX, maxY, 180, Color.BLUE);
ParallelTransition pt3 = createAnimation(maxX + 25, 25, 270, Color.FIREBRICK);
ParallelTransition pt4 = createAnimation(0, 0, 360, Color.BLUE);
SequentialTransition st = new SequentialTransition(rectangle, pt1, pt2, pt3, pt4);
st.setOnFinished(ae -> {
ae.consume();
rectangle.setTranslateX(0);
rectangle.setTranslateY(0);
rectangle.setRotate(0);
playBtn.setDisable(false);
});
st.play();
}
private ParallelTransition createAnimation(double x, double y, double r, Color c) {
TranslateTransition tt = new TranslateTransition(Duration.seconds(1.0));
tt.setToX(x);
tt.setToY(y);
RotateTransition rt = new RotateTransition(Duration.seconds(1));
rt.setToAngle(r);
Timeline tl = new Timeline(new KeyFrame(Duration.seconds(1), new KeyValue(rectangle.fillProperty(), c)));
return new ParallelTransition(tt, rt, tl);
}
}

最新更新