我编写了下面的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中两个矩形节点的碰撞检测?
答案 0 :(得分:4)
TranslateTransition
并不意味着支持碰撞检测。它简单地将A移动到B而不考虑除其节点之外的任何状态。
您需要一个Transition
机制来了解电路板上的其他对象。
好消息是创建Transition
并不太难。您可以创建一个继承Transition的类,并简单地实现interpolate()
方法。
下面是一个简单的例子。它会创建一个更新的小动画 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
发生碰撞,将其传递给Transition
和interpolate
方法我会对另一个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
}
}
但是,你可以看到它比我的其他方法更冗长,几乎一样。
希望这有帮助。