【问题标题】:Collision detection for a circle inside an octagon in JavaFXJavaFX中八角形内圆的碰撞检测
【发布时间】:2015-04-06 00:34:49
【问题描述】:

我是 JavaFX 的新手,在碰撞检测方面遇到了问题。我在八角形内有一个圆圈,我希望通过从墙壁反弹来留在八角形内。目前,如果我在尝试移动圆圈时检查按键事件内部的碰撞,它往往会四处跳动,但是,如果我将碰撞检查放在按键事件之外,则什么也不会发生。目前我的代码只检查左右墙壁的碰撞。这是我的代码:

import java.util.Vector;
import com.sun.javafx.geom.Vec2f;
import javafx.animation.AnimationTimer;
import javafx.animation.Timeline;
import javafx.animation.TranslateTransition;
import javafx.application.Application;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.LongProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleLongProperty;
import javafx.geometry.Bounds;
import javafx.scene.Scene;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Polygon;
import javafx.scene.shape.Shape;
import javafx.scene.transform.Rotate;
import javafx.stage.Stage;
import javafx.util.Duration;

public class Craft extends Application {
double getX; // I later declare this as circ1.getTranslateX()
double getY; // I later declare this as circ1.getTranslateY()

@Override 
public void start(Stage primaryStage) { 

    Pane pane = new Pane();

    Polygon octagon = new Polygon(500,50, 1200,50, 1600,300, 1600,800,        1200,1000, 500,1000, 100,800, 100,300 );
    octagon.setStroke(Color.BLACK);
    octagon.setFill(Color.TRANSPARENT);

    Circle circ1 = new Circle(500.0f, 500.0f, 25.0f);
    circ1.setFill(Color.BLUE);

    Polygon tri1 = new Polygon(530, 495, 530, 505, 540, 500);
    tri1.setFill(Color.BLUE);
    tri1.translateXProperty().bind(circ1.translateXProperty());
    tri1.translateYProperty().bind(circ1.translateYProperty());

    Rotate rotate = new Rotate();
    rotate.pivotXProperty().bind(circ1.centerXProperty());
    rotate.pivotYProperty().bind(circ1.centerYProperty());
    rotate.angleProperty().bind(circ1.rotateProperty());

    tri1.getTransforms().add(rotate);

    pane.getChildren().addAll(octagon, circ1, tri1);

    getX = circ1.getTranslateX();
    getY = circ1.getTranslateY();

    Bounds bounds = octagon.getBoundsInLocal();
    boolean leftWall = circ1.getTranslateX() <= (bounds.getMinX() +          circ1.getRadius());
    boolean topWall = circ1.getLayoutY() <= (bounds.getMinY() + circ1.getRadius());
    boolean rightWall = circ1.getTranslateX() >= (bounds.getMaxX() + circ1.getRadius());
    boolean bottomWall = circ1.getLayoutY() >= (bounds.getMaxY() + circ1.getRadius());

    circ1.setOnKeyPressed((e) -> {
        if(e.getCode() == KeyCode.UP) {
            DoubleProperty circ1VelX = new SimpleDoubleProperty();
            DoubleProperty circ1VelY = new SimpleDoubleProperty();
            LongProperty lastUpdateTime = new SimpleLongProperty();
            AnimationTimer circ1Animation = new AnimationTimer() {
            @Override
              public void handle(long timestamp) {
                if (lastUpdateTime.get() > 0) {
                  double elapsedSeconds = (timestamp - lastUpdateTime.get()) / 1_000_000_000.0 ;
                  double deltaX = elapsedSeconds * circ1VelX.get();
                  double deltaY = elapsedSeconds * circ1VelY.get();
                  double oldX = circ1.getTranslateX();
                  double oldY = circ1.getTranslateY();
                  double newX = oldX + deltaX;
                  double newY = oldY + deltaY;
                  circ1.setTranslateX(newX += 1 * Math.cos(Math.toRadians(circ1.getRotate())) * 0.1);
                  circ1.setTranslateY(newY += 1 * Math.sin(Math.toRadians(circ1.getRotate())) * 0.1);
                }
                lastUpdateTime.set(timestamp);
              }
            };circ1Animation.start();

        }
        else if(e.getCode() == KeyCode.LEFT) {
            circ1.setRotate(circ1.getRotate() - 5);
        }
        else if(e.getCode() == KeyCode.RIGHT) {
            circ1.setRotate(circ1.getRotate() + 5);
        }
        else if(e.getCode() == KeyCode.DOWN) {
            DoubleProperty circ1VelX = new SimpleDoubleProperty();
            DoubleProperty circ1VelY = new SimpleDoubleProperty();
            LongProperty lastUpdateTime = new SimpleLongProperty();
            AnimationTimer circ1Animation = new AnimationTimer() {
            @Override
              public void handle(long timestamp) {
                if (lastUpdateTime.get() > 0) {
                  double elapsedSeconds = (timestamp - lastUpdateTime.get()) / 1_000_000_000.0 ;
                  double deltaX = elapsedSeconds * circ1VelX.get();
                  double deltaY = elapsedSeconds * circ1VelY.get();
                  double oldX = circ1.getTranslateX();
                  double oldY = circ1.getTranslateY();
                  double newX = oldX + deltaX;
                  double newY = oldY + deltaY;
                  circ1.setTranslateX(newX -= 1 * Math.cos(Math.toRadians(circ1.getRotate())) * 0.1);
                  circ1.setTranslateY(newY -= 1 * Math.sin(Math.toRadians(circ1.getRotate())) * 0.1);
                }
                lastUpdateTime.set(timestamp);
              }
            };circ1Animation.start();
        }

    });

    circ1.setOnKeyReleased((e) -> {
        if(e.getCode() == KeyCode.UP) {
            DoubleProperty circ1VelX = new SimpleDoubleProperty();
            DoubleProperty circ1VelY = new SimpleDoubleProperty();
            LongProperty lastUpdateTime = new SimpleLongProperty();
            AnimationTimer circ1Animation = new AnimationTimer() {
            @Override
              public void handle(long timestamp) {
                if (lastUpdateTime.get() > 0) {
                  double elapsedSeconds = (timestamp - lastUpdateTime.get()) / 1_000_000_000.0 ;
                  double deltaX = elapsedSeconds * circ1VelX.get();
                  double deltaY = elapsedSeconds * circ1VelY.get();
                  double oldX = circ1.getTranslateX();
                  double oldY = circ1.getTranslateY();
                  double newX = oldX + deltaX;
                  double newY = oldY + deltaY;
                  circ1.setTranslateX(newX += 0.5 *      Math.cos(Math.toRadians(circ1.getRotate())));
                  circ1.setTranslateY(newY += 0.5 * Math.sin(Math.toRadians(circ1.getRotate())));
                }
                lastUpdateTime.set(timestamp);
              }
            };circ1Animation.start();
        }
        else if(e.getCode() == KeyCode.DOWN) {
            DoubleProperty circ1VelX = new SimpleDoubleProperty();
            DoubleProperty circ1VelY = new SimpleDoubleProperty();
            LongProperty lastUpdateTime = new SimpleLongProperty();
            AnimationTimer circ1Animation = new AnimationTimer() {
            @Override
              public void handle(long timestamp) {
                if (lastUpdateTime.get() > 0) {
                  double elapsedSeconds = (timestamp - lastUpdateTime.get()) / 1_000_000_000.0 ;
                  double deltaX = elapsedSeconds * circ1VelX.get();
                  double deltaY = elapsedSeconds * circ1VelY.get();
                  double oldX = circ1.getTranslateX();
                  double oldY = circ1.getTranslateY();
                  double newX = oldX + deltaX;
                  double newY = oldY + deltaY;
                  circ1.setTranslateX(newX -= 0.5 * Math.cos(Math.toRadians(circ1.getRotate())));
                  circ1.setTranslateY(newY -= 0.5 *       Math.sin(Math.toRadians(circ1.getRotate())));

                }
                lastUpdateTime.set(timestamp);
              }
            };circ1Animation.start();
        }

    });

    if(leftWall || rightWall) {
          circ1.setTranslateX(getX *= -1);
      }



    Scene scene = new Scene(pane, 1700, 1050);
    scene.getStylesheets().add("custom_craft.css");

    primaryStage.setScene(scene);
    primaryStage.setTitle("Craft");
    primaryStage.show();

    circ1.requestFocus();
}

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

}

【问题讨论】:

    标签: javafx collision-detection


    【解决方案1】:

    您要搜索的是几个问题的答案,例如。 G。我想你的球应该连续移动,而不是在移动时结结巴巴。此外,球应该一直在移动,而不仅仅是在按键上。而且你对 AnimationTimer 的使用是错误的。

    关于交叉点:如果您只有一个矩形,那么答案很简单。但你没有。因此,您需要检查八边形或单独的行。这本身就是非常数学的。关于线 圆交点,这里已经有几个问题和答案。然后你有关于从对角墙反弹的角度等问题。

    这是一个快速而肮脏的例子:

    • 我将您的多边形转换为单独的线
    • 我在圆形和每条线之外创建了一个新形状,这样可以检查它们是否相交

    您可以拖动圆圈并通过改变颜色查看交叉点。

    import java.util.ArrayList;
    import java.util.List;
    
    import javafx.application.Application;
    import javafx.event.EventHandler;
    import javafx.scene.Group;
    import javafx.scene.Node;
    import javafx.scene.Scene;
    import javafx.scene.input.MouseEvent;
    import javafx.scene.paint.Color;
    import javafx.scene.shape.Circle;
    import javafx.scene.shape.Line;
    import javafx.scene.shape.Polygon;
    import javafx.scene.shape.Shape;
    import javafx.stage.Stage;
    
    public class CircleLineIntersection extends Application {
    
        Circle circle;
        List<Line> lines = new ArrayList<>();
    
        Color defaultStroke = Color.GREEN;
        Color defaultFill = defaultStroke.deriveColor(1, 1, 1, 0.3);
    
        Color hitStroke = Color.RED;
        Color hitFill = hitStroke.deriveColor(1, 1, 1, 0.3);
    
        public static void main(String[] args) {
            launch(args);
        }
    
        @Override
        public void start(Stage primaryStage) {
    
            Group root = new Group();
    
            circle = new Circle(100, 100, 50);
            circle.setStroke( defaultStroke);
            circle.setFill( defaultFill);
    
            Polygon octagon = new Polygon(500, 50, 1200, 50, 1600, 300, 1600, 800, 1200, 1000, 500, 1000, 100, 800, 100, 300);
    
            // create lines out of the octagon
            int size = octagon.getPoints().size();
            for (int i = 0; i < size; i += 2) {
    
                double startX = octagon.getPoints().get(i);
                double startY = octagon.getPoints().get(i + 1);
                double endX = octagon.getPoints().get((i + 2) % size);
                double endY = octagon.getPoints().get((i + 3) % size);
    
                Line line = new Line(startX, startY, endX, endY);
    
                lines.add(line);
            }
    
            MouseGestures mg = new MouseGestures();
            mg.makeDraggable(circle);
    
            root.getChildren().addAll(lines);
            root.getChildren().addAll(circle);
    
            primaryStage.setScene(new Scene(root, 1920, 1080));
            primaryStage.show();
        }
    
    
        public class MouseGestures {
    
            class DragContext {
                double x;
                double y;
            }
    
            DragContext dragContext = new DragContext();
    
            public void makeDraggable(Node node) {
                node.setOnMousePressed(onMousePressedEventHandler);
                node.setOnMouseDragged(onMouseDraggedEventHandler);
                node.setOnMouseReleased(onMouseReleasedEventHandler);
            }
    
            EventHandler<MouseEvent> onMousePressedEventHandler = new EventHandler<MouseEvent>() {
    
                @Override
                public void handle(MouseEvent event) {
    
                    Circle circle = ((Circle) (event.getSource()));
    
                    dragContext.x = circle.getCenterX() - event.getSceneX();
                    dragContext.y = circle.getCenterY() - event.getSceneY();
    
                }
            };
    
            EventHandler<MouseEvent> onMouseDraggedEventHandler = new EventHandler<MouseEvent>() {
    
                @Override
                public void handle(MouseEvent event) {
    
                    Circle circle = ((Circle) (event.getSource()));
    
                    circle.setCenterX(dragContext.x + event.getSceneX());
                    circle.setCenterY(dragContext.y + event.getSceneY());
    
                    // check intersection of lines vs circle
                    boolean intersects = false;
                    for (Line line : lines) {
    
                        // TODO: this is heavy on performance, better implement your own line <-> circle intersection algorithm
                        Shape shape = Shape.intersect( line, circle);
                        intersects = shape.getBoundsInLocal().getWidth() >= 0 || shape.getBoundsInLocal().getHeight() >= 0;
    
                        if( intersects) {
                            break;
                        }
                    }
    
                    // set color depending on intersection
                    if( intersects) {
                        circle.setStroke( hitStroke);
                        circle.setFill( hitFill);
                    }  else {
                        circle.setStroke( defaultStroke);
                        circle.setFill( defaultFill);
                    }
    
                }
            };
    
            EventHandler<MouseEvent> onMouseReleasedEventHandler = new EventHandler<MouseEvent>() {
    
                @Override
                public void handle(MouseEvent event) {
    
                }
            };
    
        }
    
    }
    

    【讨论】:

    • 这太棒了,它绝对让我走上正轨。你是什​​么意思我对动画计时器的使用是错误的?我是 JavaFX 新手。
    • 您在每次按键时都使用 AnimationTimer。那是错误的。 AnimationTimer 应该是 1 个计时器,而不是连续运行,在该计时器中,您检查是否按下了某个键,处理移动、碰撞等。基本上这是您的游戏循环。
    【解决方案2】:

    我当然知道 awt 的答案...将多边形投射到区域并使用 intersects 方法。

    http://docs.oracle.com/javase/7/docs/api/java/awt/geom/Area.html

    您可能必须在八边形周围定义一个厚度不为零的新多边形和(区域)新多边形框架(...)

    frame.intersect((Area)circ1).isEmpty()!=true
    

    圆圈和框架发生碰撞。

    编辑: Collision detector in javafx (2d maze) 提供了一些关于使用 javafxCanvas 处理对象冲突的说明

    【讨论】:

      猜你喜欢
      • 2018-05-07
      • 1970-01-01
      • 2013-01-28
      • 1970-01-01
      • 2014-09-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-28
      相关资源
      最近更新 更多