Похоже, у вас есть небольшая логическая ошибка в вашем checkBounds рутина - вы правильно обнаружение столкновений (на основе границ), но перезапись заливки вашего блока при выполнении последующие проверки столкновения в одной и той же процедуре.
Попробуйте что-то вроде этого - это добавляет флаг так, чтобы рутина не «забывает», что столкновение было обнаружено:
private void checkBounds(Shape block) {
boolean collisionDetected = false;
for (Shape static_bloc : nodes) {
if (static_bloc != block) {
static_bloc.setFill(Color.GREEN);
if (block.getBoundsInParent().intersects(static_bloc.getBoundsInParent())) {
collisionDetected = true;
}
}
}
if (collisionDetected) {
block.setFill(Color.BLUE);
} else {
block.setFill(Color.GREEN);
}
}
Обратите внимание, что проверка вы делаете (на основе оценок в родителях) будет сообщать о пересечениях прямоугольника, охватывающего видимые границы узлов внутри одной и той же родительской группы.
Alternate Реализация
В случае, если вам это нужно, я обновил свой первоначальный образец так, чтобы он мог проверить на основе визуальной форме узла, а не ограничительной рамки визуальной форме. Это позволяет точно обнаруживать столкновения для непрямоугольных форм, таких как Круги. Ключом для этого является метод Shape.intersects(shape1, shape2).
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.*;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import java.util.ArrayList;
import javafx.scene.shape.*;
public class CircleCollisionTester extends Application {
private ArrayList<Shape> nodes;
public static void main(String[] args) { launch(args); }
@Override public void start(Stage primaryStage) {
primaryStage.setTitle("Drag circles around to see collisions");
Group root = new Group();
Scene scene = new Scene(root, 400, 400);
nodes = new ArrayList<>();
nodes.add(new Circle(15, 15, 30));
nodes.add(new Circle(90, 60, 30));
nodes.add(new Circle(40, 200, 30));
for (Shape block : nodes) {
setDragListeners(block);
}
root.getChildren().addAll(nodes);
checkShapeIntersection(nodes.get(nodes.size() - 1));
primaryStage.setScene(scene);
primaryStage.show();
}
public void setDragListeners(final Shape block) {
final Delta dragDelta = new Delta();
block.setOnMousePressed(new EventHandler<MouseEvent>() {
@Override public void handle(MouseEvent mouseEvent) {
// record a delta distance for the drag and drop operation.
dragDelta.x = block.getLayoutX() - mouseEvent.getSceneX();
dragDelta.y = block.getLayoutY() - mouseEvent.getSceneY();
block.setCursor(Cursor.NONE);
}
});
block.setOnMouseReleased(new EventHandler<MouseEvent>() {
@Override public void handle(MouseEvent mouseEvent) {
block.setCursor(Cursor.HAND);
}
});
block.setOnMouseDragged(new EventHandler<MouseEvent>() {
@Override public void handle(MouseEvent mouseEvent) {
block.setLayoutX(mouseEvent.getSceneX() + dragDelta.x);
block.setLayoutY(mouseEvent.getSceneY() + dragDelta.y);
checkShapeIntersection(block);
}
});
}
private void checkShapeIntersection(Shape block) {
boolean collisionDetected = false;
for (Shape static_bloc : nodes) {
if (static_bloc != block) {
static_bloc.setFill(Color.GREEN);
Shape intersect = Shape.intersect(block, static_bloc);
if (intersect.getBoundsInLocal().getWidth() != -1) {
collisionDetected = true;
}
}
}
if (collisionDetected) {
block.setFill(Color.BLUE);
} else {
block.setFill(Color.GREEN);
}
}
class Delta { double x, y; }
}
Образец программы вывода. В примере круги перемещались, и пользователь в настоящее время перетаскивает круг, который был помечен как столкновение с другим кругом (путем его рисования синим) - для демонстрационных целей только цвет, который в данный момент перетаскивается, имеет отмеченный цвет столкновения.
Комментариев на основе дополнительных вопросов
Ссылку я отправил к intersection demo application в предыдущем комментарии был иллюстрировать использование различных ограничивающех типов, а не в качестве конкретного типа образца обнаружения столкновений , Для вашего случая использования вам не нужна дополнительная сложность слушателя изменений и проверка на различные типы типов ограничений - достаточно просто установить один тип. Большинство обнаружений столкновений будет интересовать только пересечение визуальных границ, а не других типов границ JavaFX, таких как границы макета или локальные границы узла. Таким образом, вы можете:
- Проверка на пересечение
getBoundsInParent
(как вы делали в вашем первоначальном вопросе), который работает на самой маленькой прямоугольной коробку, которая будет включать в себя визуальные оконечности узла ИЛИ
- Используйте
Shape.intersect(shape1, shape2)
рутину, если вам нужно проверить на основе визуальной формы узла, а не ограничивающей рамки визуальной формы.
Должен ли я использовать setLayoutX или translateX для прямоугольника
В layoutX и layoutY свойства предназначены для позиционирования или прокладки узлов. Свойства translateX и translateY предназначены для временных изменений в визуальном местоположении узла (например, когда узел проходит анимацию). Для вашего примера, хотя любое свойство будет работать, возможно, лучше использовать свойства макета, чем переводные, таким образом, если вы хотите запустить что-то вроде TranslateTransition на узлах, будет более очевидно, что начало и end должны быть такими, чтобы эти значения были относительно текущей позиции макета узла, а не позиции в родительской группе.
Другой способ, которым вы могли бы использовать эти макеты и переводить координаты в тандеме в ваш образец, - это то, что у вас было что-то вроде ESC для отмены во время операции перетаскивания. Вы можете установить layoutX, Y в исходное местоположение вашего узла, запустить операцию перетаскивания, которая устанавливает значения translateX, Y, и если пользователь нажимает ESC, установите для translateX, Y значение 0, чтобы отменить операцию перетаскивания или если пользователь освобождает мышью установите layoutX, Y в layoutX, Y + translateX, Y и установите translateX, Y обратно в 0. Идея состоит в том, что значения перевода используются для временной модификации визуальных координат узла из исходной позиции макета.
будет работать, даже если круги оживлены? Я имею в виду, не перетаскивая круг мышью, что произойдет, если я заставил их передвигаться случайным образом. Будет ли изменение цвета и в этом случае?
Для этого просто измените место, где вызывается функция обнаружения столкновения, и активируется обработчик столкновения. Вместо того, чтобы проверять пересечения на основе события перетаскивания мышью (например, пример выше), вместо этого проверьте наличие конфликтов в прослушивателе изменений на каждом узле boundsInParentProperty()
.
block.boundsInParentProperty().addListener((observable, oldValue, newValue) ->
checkShapeIntersection(block)
);
Примечание: если у вас есть много форм, анимированы, то проверка столкновений один раз в кадре в game loop будет более эффективным, чем при запуске проверки столкновений, когда-либо узел перемещается (как это делается в слушателе изменения boundsInParentProperty выше).
Попробуйте поиграть с этим [демонстрационным приложением для пересечения] (https://gist.github.com/jewelsea/1441960), который я написал, чтобы продемонстрировать взаимосвязи пересечений различных типов границ в JavaFX. – jewelsea
Хорошо, похоже, что все, что меня интересует, прямо сейчас находится на первом классе в этом файле. Одна важная вещь, которую я забираю, - changeListener для проверки на столкновений. Также используйте LayoutBounds для проверок (??). Должен ли я использовать setLayoutX или translateX для прямоугольника? Я вижу, что вы используете setX, но это частное, я думаю, и на doc его непонятно, что является общедоступным методом, который меняет тот же атрибут. – Giannis
Обновленный ответ для ответа на дополнительные вопросы. – jewelsea