如何更改 JavaFX 中 PathTransition 节点的起始位置?

How can I change the starting position of a node on PathTransition in JavaFX?

我正在尝试构建一个(非常)小型的平台游戏来学习 JavaFX。我认为通过沿 Arc 形状使用 PathTransition 来模拟跳跃可能是个好主意。像这样:

import javafx.util.Duration;
import javafx.animation.PathTransition;
import javafx.scene.shape.Arc;

public void jump() {
    PathTransition jump = new PathTransition();
    Arc path = new Arc(
        figure().getX() + 20, //figure is a custom shape
        figure().getY() + figure().getHeight() / 2, 
        20, 80, 360, 180); 
    jump.setPath(path);
    jump.setNode(figure);
    jump.setAutoReverse(false);
    jump.setDuration(Duration.millis(3000));
    jump.setCycleCount(1);
    jump.play();
}

事件处理器:

figure.setOnKeyPressed((e) -> {
    if (e.getCode() == KeyCode.SPACE) {
        jump();
    }
});

我发现这样做实际上会使我的图形向后跳,因为动画从圆弧的右端开始并在左端结束。我尝试查看 Arc and PathTransition 的文档,但找不到任何可以帮助我解决问题的内容。我在这里错过了什么?

您提供给 Arc 构造函数的最后两个值是弧的起始角度和 angular 长度(以度为单位)。角度是相对于逆时针方向的正 x 轴测量的。所以你从 360 度开始(当然相当于 0 度:即 (centerX + radiusX, centerY))并逆时针(向上和向左)前进半圈。要向右跳转,我认为你希望起始角度为 180 并且 angular 长度为 -180(负向旋转 顺时针 ,a半椭圆)。

注意 PathTransition 通过更改其 translateXtranslateY 属性沿路径移动节点的中心。所以要从负x轴到正x轴顺时针转半圈,需要centerX作为形状的初始水平中心,加上radiusXcenterY作为形状的初始垂直中心。

这是一个 SSCCE,可以按照您想要的方式跳转(我认为...)。我在 Arc 上使用了 set 方法而不是构造函数,只是为了清楚每个值在做什么。如果您愿意,可以使用构造函数实现相同的目的。

import javafx.animation.PathTransition;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Arc;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;

public class ArcTransitionTest extends Application {

    private final double xJumpRadius = 20 ;
    private final double yJumpRadius = 80 ;


    @Override
    public void start(Stage primaryStage) {
        Rectangle rect = new Rectangle(50, 200, 50, 100);
        rect.setFill(Color.CORNFLOWERBLUE);

        Button left = new Button("<");
        left.setOnAction(e -> jumpLeft(rect));

        Button right = new Button(">");
        right.setOnAction(e -> jumpRight(rect));

        HBox controls = new HBox(5, left, right);
        controls.setPadding(new Insets(10));
        controls.setAlignment(Pos.CENTER);

        Pane pane = new Pane(rect);
        BorderPane root = new BorderPane(pane, null, null, controls, null);
        primaryStage.setScene(new Scene(root, 600, 600));
        primaryStage.show();
    }

    private void jumpRight(Rectangle rect) {
        jump(rect, 180, -180, getHorizontalCenter(rect) + xJumpRadius, getVerticalCenter(rect));
    }

    private void jumpLeft(Rectangle rect) {        
        jump(rect, 0, 180, getHorizontalCenter(rect) - xJumpRadius, getVerticalCenter(rect));
    }

    private void jump(Rectangle rect, double startAngle, double angularLength, double centerX, double centerY) {
        Arc arc = new Arc();
        arc.setCenterX(centerX);
        arc.setCenterY(centerY);
        arc.setRadiusX(xJumpRadius);
        arc.setRadiusY(yJumpRadius);
        arc.setStartAngle(startAngle);
        arc.setLength(angularLength);

        PathTransition transition = new PathTransition(Duration.seconds(1), arc, rect);

        transition.playFromStart();
    }

    private double getHorizontalCenter(Rectangle rect) {
        return rect.getX() + rect.getTranslateX() + rect.getWidth() / 2 ;
        // Alternatively:
        // Bounds b = rect.getBoundsInParent();
        // return (b.getMinX() + b.getMaxX()) / 2 ;
    }

    private double getVerticalCenter(Rectangle rect) {
        return rect.getY() + rect.getTranslateY() + rect.getHeight() / 2 ;
        // Alternatively:
        // Bounds b = rect.getBoundsInParent();
        // return (b.getMinY() + b.getMaxY()) / 2 ;
    }

    public static void main(String[] args) {
        launch(args);
    }
}