2016-04-23 116 views
1

我有一个关于在JavaFX中实现鼠标拖动事件的正确方法的问题。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(); 

    } 
} 

回答

1

动画

有几种方法可以这样做,而是采取了飞盘的概念,考虑到过渡将工作做好。这里有一个官方教程:

从这些可用,PathTransition将工作做好。通过转换用户“扔”飞盘的方向,您可以生成飞盘Node可以遵循的Path。通过修改周期和运用逆转,你也可以让飞盘像一个回旋镖

由于飞盘的旋转,你也可以采取RotationTransition的优势,并把它旁边的移动路径上


应用动画

你可以在飞盘上应用上面的跳转事件,只是一个mouseReleased事件,但正如你特别提到的拖动,我修改了下面的代码来显示两种方法。一个与释放的事件,并使用拖放和拖放

如果您想了解更多关于拖动和拖放功能,另外,都能在这里找到:


原始源

做在下面的实现中的微小变化,我已经删除您Entity类与CircleEntity没有添加任何,目的似乎取代它只是创造一个Circle

我也去掉了static声明。在这个特定的例子中,没有任何好处,但只有在需要的地方使用static关键字。希望这个热门职位可以更好地解释为什么:


实现:

我已经添加评论,以澄清一些步骤,但如果有什么不明确,或你有一些改进请添加评论

mouseReleased approac H:

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

(请注意,在拖动和拖放实现光标变化)

Frisbee Released Frisbee Drag Frisbee Released with rotation