【问题标题】:When getChildren() returns an observable list with Circle as first element, how do I access the values in Circle?当 getChildren() 返回一个以 Circle 作为第一个元素的可观察列表时,如何访问 Circle 中的值?
【发布时间】:2017-01-29 05:56:01
【问题描述】:

我必须检测两个“球”何时在 javaFX 程序中发生碰撞。每次单击按钮时,都会将一个新球添加到窗格中。我知道 getChildren() 返回一个包含每个球节点的可观察列表,当我用两个圆圈打印列表时,它将打印,例如, 圆[centerX=30.0, centerY=30.0, radius=20.0, fill=0x9ac26780], Circle[centerX=224.0, centerY=92.0, radius=20.0, fill=0x9ac26780]

我的想法是使用嵌套循环将每个球的 (x,y) 坐标与其他球进行比较。如何从每个 Circle 访问 centerX 和 centerY 以便比较它们? 我试过 getChildren().sublist(0,0);以为我会得到第一个元素的 centerX 值,但这不起作用。我也尝试了 getCenterX,因为 Ball 扩展了 Circle,但这也失败了。感谢您的宝贵时间。

public class Exercise20_05 extends Application {
  @Override // Override the start method in the Application class
   public void start(Stage primaryStage) {

MultipleBallPane ballPane = new MultipleBallPane();
ballPane.setStyle("-fx-border-color: yellow");

Button btAdd = new Button("+");
Button btSubtract = new Button("-");
HBox hBox = new HBox(10);
hBox.getChildren().addAll(btAdd, btSubtract);
hBox.setAlignment(Pos.CENTER);

// Add or remove a ball
btAdd.setOnAction(e -> ballPane.add());
btSubtract.setOnAction(e -> ballPane.subtract());

// Pause and resume animation
ballPane.setOnMousePressed(e -> ballPane.pause());
ballPane.setOnMouseReleased(e -> ballPane.play());

// Use a scroll bar to control animation speed
ScrollBar sbSpeed = new ScrollBar();
sbSpeed.setMax(20);
sbSpeed.setValue(10);
ballPane.rateProperty().bind(sbSpeed.valueProperty());

BorderPane pane = new BorderPane();
pane.setCenter(ballPane);
pane.setTop(sbSpeed);
pane.setBottom(hBox);

// Create a scene and place the pane in the stage
Scene scene = new Scene(pane, 250, 150);
primaryStage.setTitle("MultipleBounceBall"); // Set the stage title
primaryStage.setScene(scene); // Place the scene in the stage
primaryStage.show(); // Display the stage
}

private class MultipleBallPane extends Pane {
private Timeline animation;

public MultipleBallPane() {
  // Create an animation for moving the ball
  animation = new Timeline(
    new KeyFrame(Duration.millis(50), e -> moveBall()));
  animation.setCycleCount(Timeline.INDEFINITE); //animation will play indefinitely
  animation.play(); // Start animation
}

public void add() {
  Color color = new Color(Math.random(), 
    Math.random(), Math.random(), 0.5);
  //creates a new Ball at (30, 30) with a radius of 20
  getChildren().add(new Ball(30, 30, 20, color)); 
  ballCollision();
}

public void subtract() {
  if (getChildren().size() > 0) {
    getChildren().remove(getChildren().size() - 1); 
  }
}

public void play() {
  animation.play();
}

public void pause() {
  animation.pause();
}

public void increaseSpeed() {
  animation.setRate(animation.getRate() + 0.1);
}

public void decreaseSpeed() {
  animation.setRate(
    animation.getRate() > 0 ? animation.getRate() - 0.1 : 0);
}

public DoubleProperty rateProperty() {
    return animation.rateProperty();
}

protected void moveBall() {
  for (Node node: this.getChildren()) {
    Ball ball = (Ball)node;
    // Check boundaries
    if (ball.getCenterX() < ball.getRadius() || 
        ball.getCenterX() > getWidth() - ball.getRadius()) {
      ball.dx *= -1; // Change ball move direction
    }
    if (ball.getCenterY() < ball.getRadius() || 
        ball.getCenterY() > getHeight() - ball.getRadius()) {
      ball.dy *= -1; // Change ball move direction
    }

    // Adjust ball position
    ball.setCenterX(ball.dx + ball.getCenterX());
    ball.setCenterY(ball.dy + ball.getCenterY());

    ballCollision();
  }
}

//check for ball collisions
protected void ballCollision() {
    /*System.out.println(getChildren().size());
    getChildren returns an observableList; this observableList is what
    keeps track of the balls (specifically, the nodes added to ballPane)
    added each time the + button is clicked
    */
    ObservableList ballList = getChildren();
    System.out.println(ballList.get(0));
    //if there are 2 or more balls, check for collision
        if (ballList.size() > 1) {
           //compare each (x,y) coordinate value to every other (x,y) value
            for (int i = 0; i < ballList.size(); i++) {
                for (int k = 0; k < ballList.size(); k++) {
//                    if (ballList.sublist(i,i) < 1) {
//                        
//                    }
                }
            }
        }
    }
}

class Ball extends Circle {
 private double dx = 1, dy = 1;

Ball(double x, double y, double radius, Color color) {
  super(x, y, radius);
  setFill(color); // Set ball color
  }
 }

 /**
  * The main method is only needed for the IDE with limited
  * JavaFX support. Not needed for running from the command line.
  */
 public static void main(String[] args) {
   launch(args);
 }
}

编辑:多亏了几个人,我才能够进行碰撞检查。一个球将被移除,但我得到 ConcurrentModificationException。这是更新的方法:

protected void ballCollision() {
    ObservableList ballList = getChildren();
    //if there are 2 or more balls, check for collision
        if (ballList.size() > 1) {
           //compare each (x,y) coordinate value to every other (x,y) value
            for (int i = 0; i < ballList.size(); i++) {
                for (int k = i + 1; k < ballList.size(); k++) {
                    Circle c1 = (Circle) ballList.get(i);
                    Circle c2 = (Circle) ballList.get(k);

                    if ((c1.getCenterX() <= c2.getCenterX() * 1.10 &&
                        (c1.getCenterX() >= c2.getCenterX()*.90)) &&
                       ((c1.getCenterY() <= c2.getCenterY() * 1.10) && 
                         c1.getCenterY() >= c2.getCenterY() * .90)){

                            ballList.remove(c2);

                    }
                }
            }
        }
    }

最终编辑:感谢 David Wallace 抽出宝贵时间帮助我。问题是我在 moveBall 方法的 for-each 循环中调用了 ballCollision。一旦我把它移到循环之外,它就完美地工作了。

【问题讨论】:

  • 如果您知道所有子节点都是Circles,只需执行ballList.get(i)、转换为Circle 并调用getCenterX() 等。
  • @James_D 谢谢,这行得通。现在我必须在发生碰撞时移除一个球。当我尝试这样做时,我得到了 ConcurrentModificationException。我认为这是因为我在迭代列表时试图从列表中删除一个元素。

标签: java javafx


【解决方案1】:

您可以像对待任何其他List 一样对待ObservableList。您可能希望将元素转换为正确的类,如此处所示。使用Math 类的hypot 方法计算中心之间的距离。

for (int first = 0; first < ballList.size(); first++) {
    Ball firstBall = (Ball) ballList.get(first);
    for (int second = first + 1; second < ballList.size(); second++) {
        Ball secondBall = (Ball) ballList.get(second);

        double distanceBetweenCentres = Math.hypot(
            firstBall.getCenterX() - secondBall.getCenterX(), 
            firstBall.getCenterY() - secondBall.getCenterY());

        if (distanceBetweenCentres <= firstBall.getRadius() + secondBall.getRadius()) {
            System.out.println("Collision between ball " + first + " and ball " + second);
        }

    }
}

【讨论】:

  • 效果很好,谢谢。新问题是,当我尝试移除其中一个碰撞球时,我得到了 ConcurrentModificationException。我认为这是因为我在迭代列表以检查冲突时试图从列表中删除一个元素。
  • 这不应该发生,因为如果您使用get 从列表中获取内容,您就不会对其进行迭代。您没有尝试使用 for-each 循环吗?
  • 您可以做的一件事是建立一个您想要移除的球的索引列表,并在完成碰撞检查后将它们全部移除。
  • 我肯定在使用 get,而不是 for-each。我发布了更好的方法作为对我原始帖子的编辑。
  • 您是否在同一球列表的另一个 for-each 循环中间调用它,例如 moveBall 方法中的那个?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-14
  • 1970-01-01
  • 2021-10-31
相关资源
最近更新 更多