2013-02-21 3 views
12

Я пытаюсь обнаружить некоторые столкновения. Для этого теста я использую простой прямоугольник Shape и проверяю их Bound, чтобы понять, если они сталкиваются. Хотя обнаружение не работает должным образом. Я попытался использовать разные способы перемещения объекта (переместить, setLayoutX, Y), а также различные связанные проверки (boundsInLocal, boundsInParrent и т. Д.), Но я все еще не могу заставить это работать. Как видите, обнаружение работает только для одного объекта, даже если у вас есть три объекта, только один обнаруживает столкновение. Это какой-то рабочий код, демонстрирующий проблема:Проверка столкновения фигур с JavaFX

import javafx.application.Application; 
import javafx.event.EventHandler; 
import javafx.scene.Cursor; 
import javafx.scene.Group; 
import javafx.scene.Scene; 
import javafx.scene.input.MouseEvent; 
import javafx.scene.paint.Color; 
import javafx.scene.shape.Rectangle; 
import javafx.stage.Stage; 

import java.util.ArrayList; 


public class CollisionTester extends Application { 


    private ArrayList<Rectangle> rectangleArrayList; 

    public static void main(String[] args) { 
     launch(args); 
    } 

    public void start(Stage primaryStage) { 
     primaryStage.setTitle("The test"); 
     Group root = new Group(); 
     Scene scene = new Scene(root, 400, 400); 

     rectangleArrayList = new ArrayList<Rectangle>(); 
     rectangleArrayList.add(new Rectangle(30.0, 30.0, Color.GREEN)); 
     rectangleArrayList.add(new Rectangle(30.0, 30.0, Color.RED)); 
     rectangleArrayList.add(new Rectangle(30.0, 30.0, Color.CYAN)); 
     for(Rectangle block : rectangleArrayList){ 
      setDragListeners(block); 
     } 
     root.getChildren().addAll(rectangleArrayList); 
     primaryStage.setScene(scene); 
     primaryStage.show(); 
    } 

    public void setDragListeners(final Rectangle 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.getTranslateX() - mouseEvent.getSceneX(); 
       dragDelta.y = block.getTranslateY() - 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.setTranslateX(mouseEvent.getSceneX() + dragDelta.x); 
       block.setTranslateY(mouseEvent.getSceneY() + dragDelta.y); 
       checkBounds(block); 

      } 
     }); 
    } 

    private void checkBounds(Rectangle block) { 
     for (Rectangle static_bloc : rectangleArrayList) 
      if (static_bloc != block) { 
       if (block.getBoundsInParent().intersects(static_bloc.getBoundsInParent())) { 
        block.setFill(Color.BLUE);  //collision 
       } else { 
        block.setFill(Color.GREEN); //no collision 
       } 
      } else { 
       block.setFill(Color.GREEN); //no collision -same block 
      } 
    } 

    class Delta { 
     double x, y; 
    } 
} 
+2

Попробуйте поиграть с этим [демонстрационным приложением для пересечения] (https://gist.github.com/jewelsea/1441960), который я написал, чтобы продемонстрировать взаимосвязи пересечений различных типов границ в JavaFX. – jewelsea

+0

Хорошо, похоже, что все, что меня интересует, прямо сейчас находится на первом классе в этом файле. Одна важная вещь, которую я забираю, - changeListener для проверки на столкновений. Также используйте LayoutBounds для проверок (??). Должен ли я использовать setLayoutX или translateX для прямоугольника? Я вижу, что вы используете setX, но это частное, я думаю, и на doc его непонятно, что является общедоступным методом, который меняет тот же атрибут. – Giannis

+0

Обновленный ответ для ответа на дополнительные вопросы. – jewelsea

ответ

23

Похоже, у вас есть небольшая логическая ошибка в вашем 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; } 
} 

Образец программы вывода. В примере круги перемещались, и пользователь в настоящее время перетаскивает круг, который был помечен как столкновение с другим кругом (путем его рисования синим) - для демонстрационных целей только цвет, который в данный момент перетаскивается, имеет отмеченный цвет столкновения.

collisions

Комментариев на основе дополнительных вопросов

Ссылку я отправил к intersection demo application в предыдущем комментарии был иллюстрировать использование различных ограничивающех типов, а не в качестве конкретного типа образца обнаружения столкновений , Для вашего случая использования вам не нужна дополнительная сложность слушателя изменений и проверка на различные типы типов ограничений - достаточно просто установить один тип. Большинство обнаружений столкновений будет интересовать только пересечение визуальных границ, а не других типов границ JavaFX, таких как границы макета или локальные границы узла. Таким образом, вы можете:

  1. Проверка на пересечение getBoundsInParent (как вы делали в вашем первоначальном вопросе), который работает на самой маленькой прямоугольной коробку, которая будет включать в себя визуальные оконечности узла ИЛИ
  2. Используйте 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 выше).

+0

Большое спасибо за вашу помощь, он действительно прояснил некоторые концепции. – Giannis

+0

Все еще работаю над этим проектом, и я столкнулся с другой проблемой. Не могли бы вы предложить лучший способ, чем комбинировать несколько фигур в сценаристе, чтобы создавать блоки, которые будут использоваться подобно блокам Scratch или Google Blocky? Проблема состоит в том, чтобы преобразовать блоки в другие. – Giannis

+0

Комбинация формы - это другой вопрос из оригинала, и вопрос о комбинации фигур не так уж и ясен. Я бы посоветовал задать новый вопрос, предоставляя более подробное описание и четкие образцы изображений, которые демонстрируют именно некоторые формы и комбинации форм, которые вам нужны. – jewelsea

Смежные вопросы