如何在Translate Transition(在JavaFX中)中实现冲突检测



我已经编写了以下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();
}
}

如何实现平移转换中两个矩形节点的碰撞检测?

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

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");
}
}
});

TranslateTransition不支持碰撞检测。它只是将A移动到B,而不考虑除其节点之外的任何东西的状态。

您需要一个Transition机制,该机制可以识别板上的其他对象。

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

来自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));
}
};

坏消息是,拥有一个成功的碰撞检测机制有点困难。我真的不是这方面的专家,但我可能会有一个ObservableListNodes发生碰撞,将其传递给Transition,在interpolate方法中,我会对移动的节点与所有其他nodes进行交叉检查,如果他不能移动,则保持它不动。

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

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

好吧,我的方法不同于上面所有的。。。

注意:我使用的是1.8源

我创建了一个可碰撞接口:

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());
}
}    
}

IMHO不是使用Bounds来检查形状碰撞,而是使用布尔值:

(Shape.intersect(s1,s2).getBoundsLocal().getWidth()>-1)

这种方法对形状更准确,因为它将检查形状边界内的非空像素,而不是正常的矩形边界。

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

if(sourceShape.getBoundsInLocal().intersects(otherShape.getBoundsInParent()){
Shape intersect = Shape.intersect(sourceShape, otherShape);
if(intersect.getBoundsInLocal().getWidth > -1){
// handle code here
}
}

不过,正如您所看到的,它更详细,而且实际上与我的另一种方法相同。

希望这能有所帮助。

最新更新