【JavaFX】当たり判定

2018年1月18日

本稿はJavaFXで当たり判定に使えそうなメソッドを紹介します。

目当ての判定ができそうにない場合は、swingで紹介したほうを使うのもいいかもしれません。

処理速度を考慮すると、矩形同士の判定円同士の判定をオススメします。他の判定はどうしても実装したいときに利用しましょう。矩形同士の当たり判定はJavaFXでもメソッドは用意されています。

矩形同士の当たり判定

全てのNodeオブジェクトは矩形同士の当たり判定を実装できます。

Nodeオブジェクトのintersectsメソッドを呼び出します。引数BoundsはNodeオブジェクトのgetBoundsInLocalメソッドで取得できます。

※2018/01/18追記
tranlateX等の変換系の処理を考慮した判定をする場合はgetBoundsInParentを使用してください。

画像だろうが円だろうが、テキストだろうが矩形として判定します。

  1.  
  2. import javafx.application.Application;
  3. import javafx.scene.Group;
  4. import javafx.scene.Scene;
  5. import javafx.scene.paint.Color;
  6. import javafx.scene.shape.Rectangle;
  7. import javafx.scene.text.Font;
  8. import javafx.scene.text.Text;
  9. import javafx.stage.Stage;
  10.  
  11. public class Test extends Application{
  12. @Override
  13. public void start(Stage primaryStage) throws Exception {
  14. View v = new View();
  15. Scene scene = new Scene(v, 400, 300);
  16. primaryStage.setScene(scene);
  17. primaryStage.show();
  18. }
  19. }
  20.  
  21. class View extends Group{
  22. public View() {
  23. Rectangle rect1 = new Rectangle(100, 120, 100, 100);
  24. Rectangle rect2 = new Rectangle(70, 140, 100, 100);
  25. rect1.setStroke(Color.RED);
  26. rect1.setFill(null);
  27. rect2.setStroke(Color.BLUE);
  28. rect2.setFill(null);
  29. getChildren().add(rect1);
  30. getChildren().add(rect2);
  31. Text text = new Text("当たっていません。");
  32. text.setFont(new Font(20));
  33.  
  34. //矩形同士の当たり判定
  35. if ( rect1.intersects(rect2.getBoundsInLocal()) ) {
  36. text.setText("当たっています。");
  37. text.setFill(Color.RED);
  38. }
  39. text.setY(100);
  40. text.setX(200 - text.getBoundsInLocal().getWidth() / 2);
  41. getChildren().add(text);
  42. }
  43. }
実行結果

点とオブジェクトの不透明部分との当たり判定

NodeオブジェクトのcontainsメソッドはNodeの不透明部分を感知して点と当たり判定できます。

例えば画像の不透明部分をクリック判定として使用できます。

  1.  
  2. import java.io.File;
  3.  
  4. import javafx.application.Application;
  5. import javafx.scene.Group;
  6. import javafx.scene.Scene;
  7. import javafx.scene.image.ImageView;
  8. import javafx.scene.input.MouseButton;
  9. import javafx.scene.input.MouseEvent;
  10. import javafx.scene.paint.Color;
  11. import javafx.scene.text.Font;
  12. import javafx.scene.text.Text;
  13. import javafx.stage.Stage;
  14.  
  15. public class Test extends Application{
  16. @Override
  17. public void start(Stage primaryStage) throws Exception {
  18. View v = new View();
  19. Scene scene = new Scene(v, 400, 300);
  20. primaryStage.setScene(scene);
  21. primaryStage.show();
  22. scene.setOnMouseClicked(e -> v.mouseClicked(e));
  23. }
  24. }
  25.  
  26. class View extends Group{
  27. Text text = new Text(0, 100, "");
  28. ImageView img = new ImageView(new File("img.png").toURI().toString());
  29. public View() {
  30. img.setX(160);
  31. img.setY(130);
  32. text.setFont(new Font(20));
  33. text.setFill(Color.RED);
  34. text.setStroke(Color.RED);
  35. getChildren().add(text);
  36. getChildren().add(img);
  37. }
  38.  
  39.  
  40. //クリック判定処理
  41. public void mouseClicked(MouseEvent e) {
  42. if ( e.getButton() == MouseButton.PRIMARY ) {
  43. if ( img.contains(e.getX(), e.getY()) ) {
  44. text.setText("画像がクリックされました。");
  45. text.setX(200 - text.getBoundsInLocal().getWidth() / 2);
  46. } else {
  47. text.setText("");
  48. }
  49. }
  50. }
  51. }
実行結果

図形と図形の当たり判定

いいメソッドは用意されていなかったのですが、Shapeクラスのintersectメソッドで代用できそうです。

このメソッドであれば、Rectangle、Ellipse、Pathなどとの当たり判定が実装できます。

ただし、処理速度が心配なので、どうしてもというときのみに使用するようにし、基本的には使用しないほうがいいでしょう。

Shapeクラスのintersectメソッドは、二つの図形の共通部分を取得しますが、共通部分が取得できない場合は横幅や縦幅が-1になります。(Java9の時点で)それを判断基準にすればよさそうです。注意点は、不透明部分の共通領域を返すため、fillプロパティの指定は必須となります。デフォルトで黒が指定されているオブジェクトもある為、その場合は指定しなくても良い。

それではサンプルをご覧ください。

  1.  
  2. import javafx.application.Application;
  3. import javafx.scene.Group;
  4. import javafx.scene.Scene;
  5. import javafx.scene.paint.Color;
  6. import javafx.scene.shape.ClosePath;
  7. import javafx.scene.shape.Ellipse;
  8. import javafx.scene.shape.LineTo;
  9. import javafx.scene.shape.MoveTo;
  10. import javafx.scene.shape.Path;
  11. import javafx.scene.shape.QuadCurveTo;
  12. import javafx.scene.shape.Shape;
  13. import javafx.scene.text.Font;
  14. import javafx.scene.text.Text;
  15. import javafx.stage.Stage;
  16.  
  17. public class Test extends Application{
  18. @Override
  19. public void start(Stage primaryStage) throws Exception {
  20. View v = new View();
  21. Scene scene = new Scene(v, 400, 300);
  22. primaryStage.setScene(scene);
  23. primaryStage.show();
  24. }
  25. }
  26.  
  27. class View extends Group{
  28. public View() {
  29. Path shape1 = new Path();
  30. Ellipse shape2 = new Ellipse(240, 200, 100, 80);
  31. shape1.setStroke(Color.RED);
  32. shape2.setStroke(Color.BLUE);
  33. shape1.setFill(new Color(0,0,0,0.1));
  34. shape2.setFill(new Color(0,0,0,0.1));
  35. getChildren().add(shape1);
  36. getChildren().add(shape2);
  37. Text text = new Text("当たっていません。");
  38. text.setFont(new Font(20));
  39.  
  40. //パスの描画
  41. shape1.getElements().add(new MoveTo(150,120));
  42. shape1.getElements().add(new LineTo(170,130));
  43. shape1.getElements().add(new QuadCurveTo(100, 300, 50, 80));
  44. shape1.getElements().add(new ClosePath());
  45.  
  46. //図形同士の当たり判定
  47. if ( Shape.intersect(shape1, shape2).getBoundsInLocal().getWidth() != -1 ) {
  48. text.setText("当たっています。");
  49. text.setFill(Color.RED);
  50. }
  51. text.setY(100);
  52. text.setX(200 - text.getBoundsInLocal().getWidth() / 2);
  53. getChildren().add(text);
  54. }
  55. }
実行結果

JavaJavaFX

Posted by nompor