循环不等待动画结束再迭代到下一个动画

时间:2018-07-19 09:44:52

标签: java javafx

玩家移动石头(类似于Candycrush的游戏)后,按照逻辑,我收集了有关玩家移动是否导致 Structure 的信息,然后需要将其爆炸 。当然,一旦通过删除结构元素并在上面放下石头将其爆炸,新的结构便会出现,并且也需要顺序爆炸。

为此,我有一个AnimationData类,该类具有ExplosionData列表,该列表具有由最初的玩家移动引起的找到的结构的大小。

我的代码仅一次爆炸就可以正常工作,但是如果有多个爆炸就搞砸了。问题在于循环不会等到爆炸动画完成后才继续迭代。

澄清:方法updateGui,在switchAnimation.setOnFinished内部循环

视觉上: Clips of a single Explosion and multiple Explosion that i recorded

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();
}

1 个答案:

答案 0 :(得分:0)

SequentialTransition可以由其他SequentialTransition组成。对于您的代码,您可以创建一个“母版” SequentialTransition,并在SequentialTransition循环的每次迭代中使用每个创建的for对其进行构建。然后,您将播放主过渡。

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()时,动画将在后台使用一些内部时钟/计时器进行调度,并且该方法将立即返回。

  

从当前位置按以下指示播放Animation   rate。如果Animation正在运行,则无效。

     

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

     

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

     

要从头开始向后播放Animation
  animation.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);
    }

}