2016-04-23 114 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