【问题标题】:JavaFX Frisbee movement on mouse / drag eventJavaFX 飞盘移动鼠标/拖动事件
【发布时间】:2016-04-23 02:58:03
【问题描述】:

我有一个关于在 JavaFX 中实现鼠标拖动事件的正确方法的问题。

我的playGame() 方法目前使用onMouseClicked,但这只是一个占位符

理想情况下,我希望“飞盘”朝鼠标拖动的方向“抛掷”。

什么是这样做的好方法?

package FrisbeeToss;

import javafx.application.Application;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.text.Text;
import javafx.stage.Stage;

public class FrisbeeTossMain extends Application {

private Text info = new Text();
private Entity frisbee, target;

private static final int APP_W = 800;
private static final int APP_H = 600;

private static class Entity extends Parent {
    public Entity(double x, double y, double r, Color c) {
        setTranslateX(x);
        setTranslateY(y);
        Circle circ = new Circle(r, c);
        getChildren().add(circ);
    }
}

private Parent createContent() {
    Pane root = new Pane();
    root.setPrefSize(APP_W, APP_H);

    info.setTranslateX(50);
    info.setTranslateY(50);

    target = new Entity(APP_W /2, APP_H /2, 75, Color.RED);
    frisbee = new Entity(APP_W -20, APP_H -20, 60, Color.GREEN);

    root.getChildren().addAll(info, target, frisbee);

    return root;
}

private void checkCollision(Entity a, Entity b){
    if (a.getBoundsInParent().intersects(b.getBoundsInParent())) {
        info.setText("Target caught frisbee!");
    }
    else {
        info.setText("");
    }
}

private void playGame() {
    frisbee.setOnMouseClicked(event -> {
        System.out.println("Frisbee clicked");

        checkCollision(frisbee, target);
    });
}

@Override
public void start(Stage primaryStage) throws Exception {
    Scene scene = new Scene(createContent());

    primaryStage.setTitle("Frisbee Toss");
    primaryStage.setScene(scene);
    primaryStage.show();

    playGame();

    }
}

【问题讨论】:

    标签: java javafx drag-and-drop javafx-8


    【解决方案1】:

    动画

    有几种方法可以做到这一点,但是将飞盘的概念考虑到过渡效果会很好。这里有官方教程:

    从那些可用的,PathTransition 会很好地工作。通过转换用户“扔”飞盘的方向,您可以生成飞盘Node 可以跟随的Path。通过修改循环和应用反转,您还可以使飞盘表现得像回旋镖

    作为飞盘的旋转,您还可以利用 RotationTransition 并将其应用到沿路径的运动中


    应用动画

    您可以在飞盘上仅通过 mouseReleased 事件应用上述转换,但正如您特别提到的拖动,我已经修改了下面的代码以显示这两种方法。一个使用已发布事件,另一个使用拖放操作

    如果您想了解有关拖放功能的更多信息,请参阅此处:


    对原始来源的小改动

    在下面的实现中,我删除了您的 Entity 类,将其替换为 Circle,因为 Entity 没有添加任何内容,其目的似乎只是创建一个 Circle

    我还删除了 static 声明。在这个特定的示例中,拥有或删除它们没有任何好处,但 static 关键字应该只在需要的地方使用。希望这篇受欢迎的帖子能更好地解释原因:


    实现:

    我添加了 cmets 以澄清一些步骤,但如果有任何不清楚的地方,或者您有一些改进,请添加评论

    鼠标释放方法:

    public class FrisbeeTossMain extends Application {
        private Pane root;
        private Text info = new Text();
        private Circle frisbee, target;
        private PathTransition transition;
    
        private final int APP_W = 800;
        private final int APP_H = 600;
        private final double frisbeeX = APP_W -20;
        private final double frisbeeY = APP_H -20;
    
        private Parent createContent() {
            root = new Pane();
            root.setPrefSize(APP_W, APP_H);
    
            info.setTranslateX(50);
            info.setTranslateY(50);
    
            target = new Circle(75, Color.RED);
            target.setLayoutX(APP_W /2);
            target.setLayoutY(APP_H /2);
    
            frisbee = new Circle(60, Color.GREEN);
            frisbee.setLayoutX(frisbeeX);
            frisbee.setLayoutY(frisbeeY);
            frisbee.setFill(new LinearGradient(0, 0, 1, 0, true, CycleMethod.NO_CYCLE,
                    new Stop[] { new Stop(0, Color.BLACK), new Stop(1, Color.GREEN)}));
    
            SimpleBooleanProperty isFrisbeeVisuallyCollidingWithTarget = new SimpleBooleanProperty(false);
            frisbee.boundsInParentProperty().addListener((observable, oldValue, newValue) -> {
                isFrisbeeVisuallyCollidingWithTarget.set(
                        Shape.intersect(frisbee, target).getBoundsInParent().getWidth() >= 0 ? true : false);
            });
    
            isFrisbeeVisuallyCollidingWithTarget.addListener((observable, oldValue, newValue) -> {
                if(newValue && transition != null){
                    //Stop the animation making it appear as though the frisbee was caught
                    transition.stop();
                }
            });
    
            info.textProperty().bind(Bindings.when(isFrisbeeVisuallyCollidingWithTarget)
                    .then("Target caught frisbee!").otherwise(""));
            root.getChildren().addAll(info, target, frisbee);
    
            return root;
        }
    
        private void playGame() {
            frisbee.setOnMouseReleased(event -> {
                //Starting point for the line
                double fromX = frisbeeX - frisbee.getRadius();
                double fromY = frisbeeY - frisbee.getRadius();
    
                //Only "throw" the frisbee if the user has released outside of the frisbee itself
                if(frisbee.getBoundsInParent().contains(event.getSceneX(), event.getSceneY())){
                    return;
                }
    
                //Create a path between the frisbee and released location
                Line line = new Line(fromX, fromY, event.getSceneX(), event.getSceneY());
                transition = new PathTransition(Duration.seconds(1), line, frisbee);
                transition.setAutoReverse(true); //Set the node to reverse along the path
                transition.setCycleCount(2); //2 cycles, first to navigate the path, second to return
                frisbee.relocate(0, 0); //Allow the path to control the location of the frisbee
    
                RotateTransition rotateTransition =
                        new RotateTransition(Duration.seconds(1), frisbee);
                rotateTransition.setByAngle(360f);
                rotateTransition.setCycleCount(2);
                rotateTransition.setAutoReverse(true);
    
                rotateTransition.play();
                transition.play();
            });
        }
    
        @Override
        public void start(Stage primaryStage) throws Exception {
            Scene scene = new Scene(createContent());
    
            primaryStage.setTitle("Frisbee Toss");
            primaryStage.setScene(scene);
            primaryStage.show();
    
            playGame();
        }
    }
    

    拖放实现:

    与上面唯一的区别在于playGame方法内:

    private void playGame() {
        frisbee.setId("frisbee");
    
        frisbee.setOnDragDetected(event -> {
            Dragboard db = frisbee.startDragAndDrop(TransferMode.ANY);
            ClipboardContent content = new ClipboardContent();
            // Store node ID in order to know what is dragged.
            content.putString(frisbee.getId());
            db.setContent(content);
            event.consume();
        });
    
        root.setOnDragOver(event -> {
            event.acceptTransferModes(TransferMode.COPY_OR_MOVE);
            event.consume();
        });
    
        root.setOnDragDropped(event -> {
            //Starting point for the line
            double fromX = frisbeeX - frisbee.getRadius();
            double fromY = frisbeeY - frisbee.getRadius();
    
            //Only "throw" the frisbee if the user has released outside of the frisbee itself
            if(frisbee.getBoundsInParent().contains(event.getSceneX(), event.getSceneY())){
                return;
            }
    
            //Create a path between the frisbee and released location
            Line line = new Line(fromX, fromY, event.getSceneX(), event.getSceneY());
            transition = new PathTransition(Duration.seconds(1), line, frisbee);
            transition.setAutoReverse(true); //Set the node to reverse along the path
            transition.setCycleCount(2); //2 cycles, first to navigate the path, second to return
            frisbee.relocate(0, 0); //Allow the path to control the location of the frisbee
    
            transition.setOnFinished(finishedEvent -> {
                event.setDropCompleted(true);
                event.consume();
            });
            transition.play();
        });
    }
    


    添加轮播:

    可以通过在播放PathTransition之前预先添加以下sn-p来应用旋转:

    RotateTransition rotateTransition = new RotateTransition(Duration.seconds(1), frisbee);
    rotateTransition.setByAngle(360f);
    rotateTransition.setCycleCount(2);
    rotateTransition.setAutoReverse(true);
    rotateTransition.play();
    

    您可以通过将GradientFill 应用于飞盘而不是块颜色来使旋转更加明显

    例如:

    frisbee.setFill(new LinearGradient(0, 0, 1, 0, true, CycleMethod.NO_CYCLE,
        new Stop[] { new Stop(0, Color.BLACK), new Stop(1, Color.GREEN)}));
    


    视觉输出

    订购:mouseReleased | drag-and-drop | mouseReleased with rotation

    (注意拖放实现中的光标变化)

    【讨论】:

      猜你喜欢
      • 2019-06-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-04-22
      • 2023-03-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多