2013-02-21 14 views
12

私はいくつかの衝突検出を試みています。このテストでは、私は単純な矩形Shapeを使用しており、それらが衝突しているかどうかを調べるためにBoundをチェックしています。検出は期待通りには機能しませんが。私はオブジェクトを移動するさまざまな方法(再配置、setLayoutX、Y)と異なるバインドチェック(boundsInLocal、boundsInParrentなど)を試みたが、私はまだこれを動作させることができません。表示されているように、検出は1つのオブジェクトに対してのみ機能します.3つのオブジェクトがある場合でも、1つだけが衝突を検出します。これは、問題を実証するいくつかの作業コードです:あなたはルーチンあなたのcheckBoundsにわずかな論理エラーを持っているようJavaFXとの形状の衝突を確認する

import javafx.application.Application; 
import javafx.event.EventHandler; 
import javafx.scene.Cursor; 
import javafx.scene.Group; 
import javafx.scene.Scene; 
import javafx.scene.input.MouseEvent; 
import javafx.scene.paint.Color; 
import javafx.scene.shape.Rectangle; 
import javafx.stage.Stage; 

import java.util.ArrayList; 


public class CollisionTester extends Application { 


    private ArrayList<Rectangle> rectangleArrayList; 

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

    public void start(Stage primaryStage) { 
     primaryStage.setTitle("The test"); 
     Group root = new Group(); 
     Scene scene = new Scene(root, 400, 400); 

     rectangleArrayList = new ArrayList<Rectangle>(); 
     rectangleArrayList.add(new Rectangle(30.0, 30.0, Color.GREEN)); 
     rectangleArrayList.add(new Rectangle(30.0, 30.0, Color.RED)); 
     rectangleArrayList.add(new Rectangle(30.0, 30.0, Color.CYAN)); 
     for(Rectangle block : rectangleArrayList){ 
      setDragListeners(block); 
     } 
     root.getChildren().addAll(rectangleArrayList); 
     primaryStage.setScene(scene); 
     primaryStage.show(); 
    } 

    public void setDragListeners(final Rectangle block) { 
     final Delta dragDelta = new Delta(); 

     block.setOnMousePressed(new EventHandler<MouseEvent>() { 
      @Override 
      public void handle(MouseEvent mouseEvent) { 
       // record a delta distance for the drag and drop operation. 
       dragDelta.x = block.getTranslateX() - mouseEvent.getSceneX(); 
       dragDelta.y = block.getTranslateY() - mouseEvent.getSceneY(); 
       block.setCursor(Cursor.NONE); 
      } 
     }); 
     block.setOnMouseReleased(new EventHandler<MouseEvent>() { 
      @Override 
      public void handle(MouseEvent mouseEvent) { 
       block.setCursor(Cursor.HAND); 
      } 
     }); 
     block.setOnMouseDragged(new EventHandler<MouseEvent>() { 
      @Override 
      public void handle(MouseEvent mouseEvent) { 

       block.setTranslateX(mouseEvent.getSceneX() + dragDelta.x); 
       block.setTranslateY(mouseEvent.getSceneY() + dragDelta.y); 
       checkBounds(block); 

      } 
     }); 
    } 

    private void checkBounds(Rectangle block) { 
     for (Rectangle static_bloc : rectangleArrayList) 
      if (static_bloc != block) { 
       if (block.getBoundsInParent().intersects(static_bloc.getBoundsInParent())) { 
        block.setFill(Color.BLUE);  //collision 
       } else { 
        block.setFill(Color.GREEN); //no collision 
       } 
      } else { 
       block.setFill(Color.GREEN); //no collision -same block 
      } 
    } 

    class Delta { 
     double x, y; 
    } 
} 
+2

JavaFXのさまざまな境界型の交差関係を示すために書いたこの[交差デモアプリケーション](https://gist.github.com/jewelsea/1441960)を試してみてください。 – jewelsea

+0

それは私が今興味を持っているすべてのものがそのファイルのファーストクラスにあるように見えます。私が拾う重要なことの1つは、衝突をチェックするためのchangeListenerです。チェック(?)でLayoutBoundsも使用してください。私はsetLayoutXまたはtranslateXを長方形に使うべきですか?私はsetXを使っていますが、私は推測してプライベートだと思います。 – Giannis

+0

追加の質問に答えるように更新されました。 – jewelsea

答えて

23

が見える - あなたは正しく(境界に基づいて)の衝突を検出しているが、あなたが実行するとき、あなたのブロックの塗りつぶしを上書きしますその後の同じルーチンでの衝突チェック。

このような何かを試してみてください - ルーチンは衝突が検出されたことを「忘れる」しないように、それはフラグを追加します。

private void checkBounds(Shape block) { 
    boolean collisionDetected = false; 
    for (Shape static_bloc : nodes) { 
    if (static_bloc != block) { 
     static_bloc.setFill(Color.GREEN); 

     if (block.getBoundsInParent().intersects(static_bloc.getBoundsInParent())) { 
     collisionDetected = true; 
     } 
    } 
    } 

    if (collisionDetected) { 
    block.setFill(Color.BLUE); 
    } else { 
    block.setFill(Color.GREEN); 
    } 
} 

注意をその(親で境界に基づいて)あなたがやっているチェックします同じ親グループ内のノードの可視境界を囲む矩形の交差を報告します。ノードの視覚的な形状ではなく、視覚的な形状のバウンディングボックスに基づいてチェックすることができるように

別の実装は、あなたがそれを必要とする場合には

、私はあなたの元のサンプルを更新しました。サークルなどの非長方形の形状の衝突を正確に検出することができます。これの鍵は、Shape.intersects(shape1, shape2)メソッドです。

import javafx.application.Application; 
import javafx.event.EventHandler; 
import javafx.scene.*; 
import javafx.scene.input.MouseEvent; 
import javafx.scene.paint.Color; 
import javafx.stage.Stage; 

import java.util.ArrayList; 
import javafx.scene.shape.*; 

public class CircleCollisionTester extends Application { 

    private ArrayList<Shape> nodes; 

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

    @Override public void start(Stage primaryStage) { 
    primaryStage.setTitle("Drag circles around to see collisions"); 
    Group root = new Group(); 
    Scene scene = new Scene(root, 400, 400); 

    nodes = new ArrayList<>(); 
    nodes.add(new Circle(15, 15, 30)); 
    nodes.add(new Circle(90, 60, 30)); 
    nodes.add(new Circle(40, 200, 30)); 
    for (Shape block : nodes) { 
     setDragListeners(block); 
    } 
    root.getChildren().addAll(nodes); 
    checkShapeIntersection(nodes.get(nodes.size() - 1)); 

    primaryStage.setScene(scene); 
    primaryStage.show(); 
    } 

    public void setDragListeners(final Shape block) { 
    final Delta dragDelta = new Delta(); 

    block.setOnMousePressed(new EventHandler<MouseEvent>() { 
     @Override public void handle(MouseEvent mouseEvent) { 
     // record a delta distance for the drag and drop operation. 
     dragDelta.x = block.getLayoutX() - mouseEvent.getSceneX(); 
     dragDelta.y = block.getLayoutY() - mouseEvent.getSceneY(); 
     block.setCursor(Cursor.NONE); 
     } 
    }); 
    block.setOnMouseReleased(new EventHandler<MouseEvent>() { 
     @Override public void handle(MouseEvent mouseEvent) { 
     block.setCursor(Cursor.HAND); 
     } 
    }); 
    block.setOnMouseDragged(new EventHandler<MouseEvent>() { 
     @Override public void handle(MouseEvent mouseEvent) { 
     block.setLayoutX(mouseEvent.getSceneX() + dragDelta.x); 
     block.setLayoutY(mouseEvent.getSceneY() + dragDelta.y); 
     checkShapeIntersection(block); 
     } 
    }); 
    } 

    private void checkShapeIntersection(Shape block) { 
    boolean collisionDetected = false; 
    for (Shape static_bloc : nodes) { 
     if (static_bloc != block) { 
     static_bloc.setFill(Color.GREEN); 

     Shape intersect = Shape.intersect(block, static_bloc); 
     if (intersect.getBoundsInLocal().getWidth() != -1) { 
      collisionDetected = true; 
     } 
     } 
    } 

    if (collisionDetected) { 
     block.setFill(Color.BLUE); 
    } else { 
     block.setFill(Color.GREEN); 
    } 
    } 

    class Delta { double x, y; } 
} 

サンプルプログラム出力。サンプルでは円がドラッグされていて、ユーザーは現在、別の円と衝突してマークされたサークルをドラッグしています(青でペイントしています)。デモンストレーションの目的で、現在ドラッグされている円に衝突マークが付きます。

collisions

私は以前のコメントでintersection demo applicationに投稿

リンクが衝突検出サンプルの特定のタイプとして、様々な境界型の使用を説明するのではなくした追加の質問に基づくコメント。ユースケースでは、変更リスナーの複雑さとさまざまな種類の境界タイプのチェックが不要になります。ただ1つのタイプで解決すれば十分です。ほとんどの衝突検出は、ノードのレイアウト境界やローカル境界などの他のJavaFX境界タイプではなく、視覚的境界の交差にのみ関心があります。

  1. ノードの視覚的な四肢を網羅します最小の長方形のボックス上で動作getBoundsInParentの交差点のためのチェック(あなたがあなたの元の質問で行ったように)OR
  2. 場合Shape.intersect(shape1, shape2)ルーチンを使用します。だから、どちらかのことができビジュアルシェイプのバウンディングボックスではなく、ノードのビジュアルシェイプに基づいてチェックする必要があります。

IはlayoutXとlayoutY特性を位置決めまたはノードをレイアウトするために意図されている矩形

ためsetLayoutX又は移動Xを使用すべきです。 translateXおよびtranslateYプロパティは、ノードの視覚的な位置を一時的に変更するためのものです(ノードがアニメーションを実行しているときなど)。あなたの例ではどちらのプロパティも動作しますが、レイアウトプロパティを変換するよりもレイアウトプロパティを使うほうが良いでしょう。つまり、ノード上でTranslateTransitionのようなものを実行したければ、最終変換値は、親グループ内の位置ではなく、ノードの現在のレイアウト位置に関連する値である必要があります。

ドラッグ操作の途中でキャンセルするESCのようなものがあった場合、これらのレイアウトを使用してサンプルを並べて座標を変換する別の方法があります。 layoutX、Yをノードの初期位置に設定し、translateX、Y値を設定するドラッグ操作を開始し、ユーザーがESCを押した場合は、translateX、Yを0に戻してドラッグ操作をキャンセルするか、ユーザーがマウスを離した場合layoutX、YをlayoutX、Y + translateX、Yに設定し、translateX、Yを0に戻します。変換は、元のレイアウト位置からノードのビジュアル座標を一時的に変更するために使用されます。

円がアニメーション化されていても交差しますか?私は、マウスで円をドラッグすることなく、無作為に動かすとどうなりますか?この場合でも色が変わるのでしょうか?

これを行うには、衝突検出機能が呼び出される場所と衝突ハンドラが呼び出された場所を変更します。マウスのドラッグイベントに基づいて交差点をチェックするのではなく、代わりに各ノードのboundsInParentProperty()の変更リスナ内の衝突をチェックします。

block.boundsInParentProperty().addListener((observable, oldValue, newValue) -> 
     checkShapeIntersection(block) 
); 

注:図形をたくさん持っているならば、衝突チェックを実行するよりも効率的になりますgame loop以内に1フレームに1回の衝突をチェックし、アニメ化された時はいつでもboundsInParentProperty変更リスナーに行われているように任意のノードに移動します(上記)。

+0

本当にありがとう、いくつかの概念をクリアしました。 – Giannis

+0

まだこのプロジェクトに取り組んでおり、別の問題に直面しています。 ScratchブロックやGoogle Blockyに似たブロックを作成するために、シーンビルダでいくつかの図形を組み合わせるよりも優れた方法を提案できますか?問題は、ブロックを他人の内部に合わせるように変換することです。 – Giannis

+0

シェイプの組み合わせはオリジナルとは異なる質問ですが、シェイプの組み合わせの問題はそれほど明白ではありません。必要なシェイプとシェイプの組み合わせを正確に示すサンプルやイメージを追加する新しい質問をお願いします。 – jewelsea