我对在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();
}
}
答案 0 :(得分:1)
<强>动画强>
有几种方法可以做到这一点,但考虑到飞盘的概念转换会很好。这里有一个官方教程:
从那些可用的,PathTransition
将运作良好。通过转换用户“扔”飞盘的方向,您可以生成飞盘Path
可以遵循的Node
。通过修改周期并应用反转,您还可以使飞盘表现得像回旋镖
随着飞盘的旋转,您还可以利用RotationTransition
并将其与沿路径的移动一起应用
<小时/> 应用动画
您可以在飞盘上仅使用mouseReleased
事件应用上述过渡,但正如您特别提到的拖动,我已修改下面的代码以显示这两种方法。一个是已发布的事件,另一个是使用拖放
如果您想了解有关拖放功能的更多信息,请参阅此处:
<小时/> 对原始来源进行微小更改
在下面的实现中,我删除了Entity
类,将其替换为Circle
,因为Entity
没有添加任何内容,其目的似乎只是创建一个Circle
我还删除了static
声明。在这个特定示例中,拥有或删除它们没有任何好处,但static
关键字只应在需要的地方使用。希望这篇热门帖子可以更好地解释原因:
<小时/> 的实现:强>
我已添加评论以澄清某些步骤,但如果有任何不明确的地方,或者您有一些改进,请添加评论
mouseReleased approach:
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
之前预先挂起以下代码段可以应用轮播:
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
(注意拖放实现中的光标更改)