玩家移动石头(类似于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();
}
答案 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);
}
}