【JavaFX】当たり判定
本稿はJavaFXで当たり判定に使えそうなメソッドを紹介します。
目当ての判定ができそうにない場合は、swingで紹介したほうを使うのもいいかもしれません。
処理速度を考慮すると、矩形同士の判定か円同士の判定をオススメします。他の判定はどうしても実装したいときに利用しましょう。矩形同士の当たり判定はJavaFXでもメソッドは用意されています。
矩形同士の当たり判定
全てのNodeオブジェクトは矩形同士の当たり判定を実装できます。
Nodeオブジェクトのintersectsメソッドを呼び出します。引数BoundsはNodeオブジェクトのgetBoundsInLocalメソッドで取得できます。
※2018/01/18追記
tranlateX等の変換系の処理を考慮した判定をする場合はgetBoundsInParentを使用してください。
画像だろうが円だろうが、テキストだろうが矩形として判定します。
import javafx.application.Application; import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.paint.Color; import javafx.scene.shape.Rectangle; import javafx.scene.text.Font; import javafx.scene.text.Text; import javafx.stage.Stage; public class Test extends Application{ @Override public void start(Stage primaryStage) throws Exception { View v = new View(); Scene scene = new Scene(v, 400, 300); primaryStage.setScene(scene); primaryStage.show(); } } class View extends Group{ public View() { Rectangle rect1 = new Rectangle(100, 120, 100, 100); Rectangle rect2 = new Rectangle(70, 140, 100, 100); rect1.setStroke(Color.RED); rect1.setFill(null); rect2.setStroke(Color.BLUE); rect2.setFill(null); getChildren().add(rect1); getChildren().add(rect2); Text text = new Text("当たっていません。"); text.setFont(new Font(20)); //矩形同士の当たり判定 if ( rect1.intersects(rect2.getBoundsInLocal()) ) { text.setText("当たっています。"); text.setFill(Color.RED); } text.setY(100); text.setX(200 - text.getBoundsInLocal().getWidth() / 2); getChildren().add(text); } }
点とオブジェクトの不透明部分との当たり判定
NodeオブジェクトのcontainsメソッドはNodeの不透明部分を感知して点と当たり判定できます。
例えば画像の不透明部分をクリック判定として使用できます。
import java.io.File; import javafx.application.Application; import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.image.ImageView; import javafx.scene.input.MouseButton; import javafx.scene.input.MouseEvent; import javafx.scene.paint.Color; import javafx.scene.text.Font; import javafx.scene.text.Text; import javafx.stage.Stage; public class Test extends Application{ @Override public void start(Stage primaryStage) throws Exception { View v = new View(); Scene scene = new Scene(v, 400, 300); primaryStage.setScene(scene); primaryStage.show(); scene.setOnMouseClicked(e -> v.mouseClicked(e)); } } class View extends Group{ Text text = new Text(0, 100, ""); ImageView img = new ImageView(new File("img.png").toURI().toString()); public View() { img.setX(160); img.setY(130); text.setFont(new Font(20)); text.setFill(Color.RED); text.setStroke(Color.RED); getChildren().add(text); getChildren().add(img); } //クリック判定処理 public void mouseClicked(MouseEvent e) { if ( e.getButton() == MouseButton.PRIMARY ) { if ( img.contains(e.getX(), e.getY()) ) { text.setText("画像がクリックされました。"); text.setX(200 - text.getBoundsInLocal().getWidth() / 2); } else { text.setText(""); } } } }
図形と図形の当たり判定
いいメソッドは用意されていなかったのですが、Shapeクラスのintersectメソッドで代用できそうです。
このメソッドであれば、Rectangle、Ellipse、Pathなどとの当たり判定が実装できます。
ただし、処理速度が心配なので、どうしてもというときのみに使用するようにし、基本的には使用しないほうがいいでしょう。
Shapeクラスのintersectメソッドは、二つの図形の共通部分を取得しますが、共通部分が取得できない場合は横幅や縦幅が-1になります。(Java9の時点で)それを判断基準にすればよさそうです。注意点は、不透明部分の共通領域を返すため、fillプロパティの指定は必須となります。デフォルトで黒が指定されているオブジェクトもある為、その場合は指定しなくても良い。
それではサンプルをご覧ください。
import javafx.application.Application; import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.paint.Color; import javafx.scene.shape.ClosePath; import javafx.scene.shape.Ellipse; import javafx.scene.shape.LineTo; import javafx.scene.shape.MoveTo; import javafx.scene.shape.Path; import javafx.scene.shape.QuadCurveTo; import javafx.scene.shape.Shape; import javafx.scene.text.Font; import javafx.scene.text.Text; import javafx.stage.Stage; public class Test extends Application{ @Override public void start(Stage primaryStage) throws Exception { View v = new View(); Scene scene = new Scene(v, 400, 300); primaryStage.setScene(scene); primaryStage.show(); } } class View extends Group{ public View() { Path shape1 = new Path(); Ellipse shape2 = new Ellipse(240, 200, 100, 80); shape1.setStroke(Color.RED); shape2.setStroke(Color.BLUE); shape1.setFill(new Color(0,0,0,0.1)); shape2.setFill(new Color(0,0,0,0.1)); getChildren().add(shape1); getChildren().add(shape2); Text text = new Text("当たっていません。"); text.setFont(new Font(20)); //パスの描画 shape1.getElements().add(new MoveTo(150,120)); shape1.getElements().add(new LineTo(170,130)); shape1.getElements().add(new QuadCurveTo(100, 300, 50, 80)); shape1.getElements().add(new ClosePath()); //図形同士の当たり判定 if ( Shape.intersect(shape1, shape2).getBoundsInLocal().getWidth() != -1 ) { text.setText("当たっています。"); text.setFill(Color.RED); } text.setY(100); text.setX(200 - text.getBoundsInLocal().getWidth() / 2); getChildren().add(text); } }
ディスカッション
コメント一覧
まだ、コメントがありません