如何在Translate Transition中实现碰撞检测(在JavaFX中)?

时间:2013-12-26 06:01:02

标签: java javafx javafx-2 collision-detection javafx-8

我编写了下面的JavaFX程序,其中两个矩形节点处于转换过渡中:

public class Test extends Application{
public static void main(String[] args) {
    launch(args);
}

@Override
public void start(Stage primaryStage) throws Exception {
    BorderPane borderPane = new BorderPane();
    borderPane.setStyle("-fx-background-color: green;");


    Rectangle rect1 = new Rectangle(20,20,50, 50);
    rect1.setArcHeight(15);
    rect1.setArcWidth(15);
    rect1.setFill(Color.RED);

    Rectangle rect2 = new Rectangle(20,20,30, 30);
    rect2.setArcHeight(15);
    rect2.setArcWidth(15);
    rect2.setFill(Color.RED);

    TranslateTransition translateTransition1 = new TranslateTransition(Duration.millis(2000), rect1);
    translateTransition1.setFromX(0);
    translateTransition1.setToX(300);
    translateTransition1.setToY(300);
    translateTransition1.setCycleCount(Timeline.INDEFINITE);
    translateTransition1.setAutoReverse(true);
    translateTransition1.play();

   TranslateTransition translateTransition2 = new TranslateTransition(Duration.millis(2000), rect2);
   translateTransition2.setFromX(300);
   translateTransition2.setToX(0);
   translateTransition2.setToY(300);
   translateTransition2.setCycleCount(Timeline.INDEFINITE);
   translateTransition2.setAutoReverse(true);
   translateTransition2.play();


    borderPane.getChildren().add(rect1);
    borderPane.getChildren().add(rect2);

    primaryStage.setScene(new Scene(borderPane, 500, 500));
    primaryStage.show();

  }

}

如何实现Translate Transition中两个矩形节点的碰撞检测?

3 个答案:

答案 0 :(得分:4)

TranslateTransition并不意味着支持碰撞检测。它简单地将A移动到B而不考虑除其节点之外的任何状态。

您需要一个Transition机制来了解电路板上的其他对象。

好消息是创建Transition并不太难。您可以创建一个继承Transition的类,并简单地实现interpolate()方法。

From the JavaDoc:

  

下面是一个简单的例子。它会创建一个更新的小动画   Text节点的text属性。它以空字符串开头   逐字逐句添加,直到完成字符串设置为   动画结束。

 final String content = "Lorem ipsum";
 final Text text = new Text(10, 20, "");

 final Animation animation = new Transition() {
     {
         setCycleDuration(Duration.millis(2000));
     }

     protected void interpolate(double frac) {
         final int length = content.length();
         final int n = Math.round(length * (float) frac);
         text.setText(content.substring(0, n));
     }

 };

坏消息是成功的碰撞检测机制有点难度。我真的不是这方面的专家,但我可能会ObservableList Nodes发生碰撞,将其传递给Transitioninterpolate方法我会对另一个nodes移动的节点进行交叉检查,如果他不能移动则保持静止。

如果你想要更好的东西,你可能想要研究像Slick2D这样的2D游戏框架。

答案 1 :(得分:4)

使用矩形很容易;只是得到他们在父母的边界,看看他们是否相交。唯一的缺点是它没有考虑弯曲的角落:如果你想要那种精确度,你可能需要手动计算。对于非矩形形状,您也可以只观察父属性中的边界,但是您需要手动进行计算以查看形状是否相交。

    ObservableBooleanValue colliding = Bindings.createBooleanBinding(new Callable<Boolean>() {

        @Override
        public Boolean call() throws Exception {
            return rect1.getBoundsInParent().intersects(rect2.getBoundsInParent());
        }

    }, rect1.boundsInParentProperty(), rect2.boundsInParentProperty());

    colliding.addListener(new ChangeListener<Boolean>() {
        @Override
        public void changed(ObservableValue<? extends Boolean> obs,
                Boolean oldValue, Boolean newValue) {
            if (newValue) {
                System.out.println("Colliding");
            } else {
                System.out.println("Not colliding");
            }
        }
    });

答案 2 :(得分:1)

编辑:进行了一些简单的修改,并采用了基于状态的方法,代码已更新。

我的方法与上述所有方法不同......

注意:我使用1.8源

我创建了一个Collidable接口:

import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.scene.shape.Shape;

public interface Collidable{
    public enum CollisionState{
        WAITING,
        TOUCHING;        
    }

    ObjectProperty<CollisionState> state = new SimpleObjectProperty<>(CollisionState.WAITING);
    public default ReadOnlyObjectProperty<CollisionState> collisionStateProperty(){return state;}
    public default CollisionState getCollisionState(){return state.get();}

    BooleanProperty collided = new SimpleBooleanProperty(false){{
            addListener((ObservableValue<? extends Boolean> observable1, Boolean oldValue, Boolean touching) -> {
                if(touching){
                    state.set(CollisionState.TOUCHING);
                }else{
                    state.set(CollisionState.WAITING);
                }
            });
    }};
    public default boolean hasCollided(){return collided.get();}
    public default BooleanProperty collidedProperty(){return collided;}

    public default void checkCollision(Shape src, Shape other){
        if(Shape.intersect(src, other).getBoundsInLocal().getWidth() > -1 && !getCollisionState().equals(CollisionState.TOUCHING)){
            collided.set(true); 
            handleCollision(other);
        }else if(Shape.intersect(src, other).getBoundsInLocal().getWidth() <= 0){
            collided.set(false);            
        }
    }

    public void handleCollision(Shape other);

}

一个简单的实现:

import javafx.animation.Animation;
import javafx.animation.ParallelTransition;
import javafx.animation.TranslateTransition;
import javafx.application.Application;
import javafx.beans.Observable;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.Shape;
import javafx.stage.Stage;
import javafx.util.Duration;

/**
 *
 * @author Dub-Laptop
 */
public class CollisionTesting extends Application {

    private TranslateTransition cAnim;

    @Override
    public void start(Stage primaryStage) {        

        Group root = new Group();

        Scene scene = new Scene(root);

        primaryStage.setTitle("Collision Testing");
        primaryStage.setScene(scene);
        primaryStage.show();

        Rectangle r = new Rectangle(100,50, Color.AQUA);
        r.setLayoutX(10);
        r.setLayoutY(200);

        CollidableCircle c = new CollidableCircle(50, Color.GREEN);
        c.setLayoutX(800);
        c.setLayoutY(200);

        /* can change this to anything you like 
           I used translateXProperty for simplicity
        */                                  
        c.translateXProperty().addListener((Observable observable) -> {
            c.checkCollision(c, r);
        });

        root.getChildren().addAll(r, c);

        TranslateTransition rAnim = new TranslateTransition();

        rAnim.setToX(600);
        rAnim.setAutoReverse(true);
        rAnim.setCycleCount(Animation.INDEFINITE);
        rAnim.setDuration(Duration.seconds(5));
        rAnim.setNode(r);

        cAnim = new TranslateTransition();

        cAnim.setToX(-590);
        cAnim.setAutoReverse(true);
        cAnim.setCycleCount(Animation.INDEFINITE);
        cAnim.setDuration(Duration.seconds(5));
        cAnim.setNode(c);

        rAnim.play();
        cAnim.play();
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        launch(args);
    }

    private class CollidableCircle extends Circle implements Collidable{

        public CollidableCircle(double radius, Paint fill) {
            super(radius, fill);
            new AnimationTimer(){
                @Override
                public void handle(long now) {
                    root.getChildren().filtered((Node n)->{
                        return !n.equals(CollidableCircle.this) && n instanceof Shape;
                    }).forEach(other ->{
                        checkCollision(CollidableCircle.this, (Shape)other);
                    });
                }
            }.start();
            // I added this for local property changes to this node
            collisionStateProperty().addListener((ObservableValue<? extends CollisionState> observable, CollisionState oldValue, CollisionState newValue) -> {
                if(newValue.equals(CollisionState.TOUCHING)){  
                    setScaleX(1.25);
                    setScaleY(1.25);
                    setFill(Color.GREENYELLOW);
                    cAnim.pause();
                }else if(newValue.equals(CollisionState.WAITING)){      
                    setScaleX(1.0);
                    setScaleY(1.0);
                    setFill(Color.GREEN);
                    cAnim.play();
                }
            });
        }

        @Override
        public void handleCollision(Shape other) {            
           // handle updates that affect other node here
           System.out.println("Collided with : " + other.getClass().getSimpleName());
        }

    }    

}

恕我直言,而不是使用Bounds来检查Shape碰撞,请使用布尔值:

(Shape.intersect(s1,s2).getBoundsInLocal()。getWidth()&gt; -1)

这种方法对于Shapes更准确,因为它将检查Shape Bounds中的非null像素,而不是正常的矩形边界。

虽然如果你真的想使用Bounds,这也应该有效:

if(sourceShape.getBoundsInLocal().intersects(otherShape.getBoundsInParent()){
    Shape intersect = Shape.intersect(sourceShape, otherShape);
    if(intersect.getBoundsInLocal().getWidth > -1){
        // handle code here
    }
}
但是,你可以看到它比我的其他方法更冗长,几乎一样。

希望这有帮助。