JavaFX Shape.intersect()性能问题

JavaFX Shape.intersect()性能问题,java,javafx,javafx-2,collision-detection,javafx-8,Java,Javafx,Javafx 2,Collision Detection,Javafx 8,我已经开始编写一个射击游戏JavaFX应用程序。我正在使用Shape.intersect()检查子弹与目标的碰撞。下面是我的代码,我简化了它,以便在这里发布 public class TestShapeIntersect extends Application{ AnchorPane anchorPane; ArrayList<Rectangle> targetObjects; public static void main(String[] arg){ la

我已经开始编写一个射击游戏JavaFX应用程序。我正在使用Shape.intersect()检查子弹与目标的碰撞。下面是我的代码,我简化了它,以便在这里发布

public class TestShapeIntersect extends Application{
 AnchorPane anchorPane;      
 ArrayList<Rectangle> targetObjects;
public static void main(String[] arg){
    launch(arg);
}

@Override
public void start(Stage stage) throws Exception {
    final Rectangle gun = new Rectangle(50, 50, Color.RED);
    anchorPane = new AnchorPane();
    anchorPane.getChildren().add(gun);  
    generateTargetObjects(50); // Number of target objects
    anchorPane.getChildren().addAll(targetObjects);
    gun.setX(50);
    gun.setY(200);
    Scene scene = new Scene(anchorPane,300,300,Color.GREEN);
    stage.setScene(scene);
    stage.show();

    scene.setOnKeyPressed(new EventHandler<KeyEvent>() {
        @Override
        public void handle(KeyEvent event) {
            Rectangle bullet = new Rectangle(5,10,Color.ORANGE);
            bullet.setX(75);
            bullet.setY(200);
            anchorPane.getChildren().add(bullet);
            animateBullet(bullet);

        }
    });             
}

private void generateTargetObjects(int noOfTargetObj) {
    targetObjects = new ArrayList<Rectangle>();
    for(int i=1; i<=noOfTargetObj;i++){
        Rectangle rect = new Rectangle(30, 30, Color.YELLOW);
        targetObjects.add(rect);        
    }       
}

 void animateBullet(final Rectangle bullet){
        Timeline timeline = new Timeline();
        timeline.setCycleCount(500);
        final KeyFrame kf = new KeyFrame(Duration.millis(2), new EventHandler<ActionEvent>() {

            @Override
            public void handle(ActionEvent event) {
                bullet.setY(bullet.getY()-1);               
                checkCollision(bullet);
            }       
        });    
        timeline.getKeyFrames().add(kf);        
        timeline.play();
}

 //This method will check if there is any collision happened between the bullets and the targets.
 //If collision happens then both bullet and target object will be disappeared.
 void checkCollision(Rectangle bullet){
     int noOfTargetObjs = targetObjects.size();

        for(int i=0; i<noOfTargetObjs;i++)
        {
            if(targetObjects.get(i).isVisible()==true && bullet.isVisible()==true){
                Shape intersectShape= Shape.intersect(bullet, targetObjects.get(i));
                if(intersectShape.getBoundsInLocal().getWidth() != -1){                 
                    targetObjects.get(i).setVisible(false);
                    bullet.setVisible(false);
                }
            }
        }    
 }
}

有人能告诉我为什么会发生这种情况,解决这个问题的办法是什么吗?还是因为我的实现方式?

在执行应用程序时,我经历了不同的行为。我的第一个镜头在翻译过程中没有任何中断。但几次拍摄后,应用程序开始减速。我尝试通过执行以下步骤来提高代码的性能:

void animateBullet(final Rectangle bullet){
       final Timeline timeline = new Timeline();
       timeline.setCycleCount(125); //changed
       final KeyFrame kf = new KeyFrame(Duration.millis(16), new EventHandler<ActionEvent>() {

           @Override
           public void handle(ActionEvent event) {
               bullet.setY(bullet.getY()-8);     //changed          
               checkCollisionThreaded(bullet); //changed
               //added
               if(bullet.getX() < 0 || bullet.getX() > bullet.getParent().getBoundsInParent().getWidth()
               || bullet.getY() < 0 || bullet.getY() > bullet.getParent().getBoundsInParent().getHeight())
               {
                   bullet.setVisible(false);
                   timeline.stop();
                   AnchorPane ap = (AnchorPane) bullet.getParent();
                   ap.getChildren().remove(bullet);

               }
           }       
       });    
       timeline.getKeyFrames().add(kf); 
       timeline.play();
}
有一些违反规则“不要使用与JavaFX应用程序线程不同的线程接触场景图上的任何对象”,但据我所见,只有读取方法才能在
call()
方法中访问场景图(及其对象)。此方法在新的
线程
上运行,从而提高了性能。方法
succeed()
在JavaFX应用程序线程上运行,因此我们可以安全地从场景图中删除内容。我假设你想在目标被击中后将其从场景中移除


应该说,可能存在与多线程代码相关的问题。获取
final int nootTargetObjs=targetObjects.size()时可能会出错时。为了降低代码的复杂性,我省略了任何同步。

在执行应用程序时,我经历了一种不同的行为。我的第一个镜头在翻译过程中没有任何中断。但几次拍摄后,应用程序开始减速。我尝试通过执行以下步骤来提高代码的性能:

void animateBullet(final Rectangle bullet){
       final Timeline timeline = new Timeline();
       timeline.setCycleCount(125); //changed
       final KeyFrame kf = new KeyFrame(Duration.millis(16), new EventHandler<ActionEvent>() {

           @Override
           public void handle(ActionEvent event) {
               bullet.setY(bullet.getY()-8);     //changed          
               checkCollisionThreaded(bullet); //changed
               //added
               if(bullet.getX() < 0 || bullet.getX() > bullet.getParent().getBoundsInParent().getWidth()
               || bullet.getY() < 0 || bullet.getY() > bullet.getParent().getBoundsInParent().getHeight())
               {
                   bullet.setVisible(false);
                   timeline.stop();
                   AnchorPane ap = (AnchorPane) bullet.getParent();
                   ap.getChildren().remove(bullet);

               }
           }       
       });    
       timeline.getKeyFrames().add(kf); 
       timeline.play();
}
有一些违反规则“不要使用与JavaFX应用程序线程不同的线程接触场景图上的任何对象”,但据我所见,只有读取方法才能在
call()
方法中访问场景图(及其对象)。此方法在新的
线程
上运行,从而提高了性能。方法
succeed()
在JavaFX应用程序线程上运行,因此我们可以安全地从场景图中删除内容。我假设你想在目标被击中后将其从场景中移除


应该说,可能存在与多线程代码相关的问题。获取
final int nootTargetObjs=targetObjects.size()时可能会出错时。为了降低代码的复杂性,我省略了任何同步操作。

我的猜测是,您发送的请求太多,无法形成.intersect(…),这可能是一种相当昂贵的执行方法。最初,这会导致性能问题,但当对该方法的调用数量达到某个阈值时,JVM的JIT编译器会启动并编译该方法,从而缓解一些问题。(同样,这都是猜测。)

对项目符号使用TranslateTransition并侦听其boundsInParent属性来检查碰撞似乎效果更好。我认为原因是使用这种技术只会在JavaFX机器实际移动子弹时检查碰撞。在代码中,执行这些检查的频率要高得多


我猜您发送的请求太多,无法塑造.intersect(…),这可能是一种相当昂贵的执行方法。最初,这会导致性能问题,但当对该方法的调用数量达到某个阈值时,JVM的JIT编译器会启动并编译该方法,从而缓解一些问题。(同样,这都是猜测。)

对项目符号使用TranslateTransition并侦听其boundsInParent属性来检查碰撞似乎效果更好。我认为原因是使用这种技术只会在JavaFX机器实际移动子弹时检查碰撞。在代码中,执行这些检查的频率要高得多


监听器在boundsInParent属性上的想法很好。因此,您的意思是,使用时间线转换很难实现这样的应用程序。但我认为大多数动画游戏似乎只使用时间线。翻译只是时间线的一个特例。您可以在这里使用时间线,但代码会稍微复杂一些,因为TranslateTransitions是专门为此类动画设计的。但是在任何一种情况下,我都会观察boundsInParent属性来检测冲突。监听器在boundsInParent属性上的想法很好。所以你的意思是说使用时间线转换很难实现这样的应用程序。但我认为大多数动画游戏似乎只使用时间线。翻译只是时间线的一个特例。您可以在这里使用时间线,但代码会稍微复杂一些,因为TranslateTransitions是专门为此类动画设计的。但是在任何一种情况下,我都会观察boundsInParent属性来检测冲突。只有在JavaFX应用程序线程上才允许读取作为实时场景图一部分的节点的属性。不能保证您的代码会给出正确的结果。我不认为你可以通过同步来解决这个问题,因为场景图本身假设单线程访问。是的,你当然是对的。我应该在我的回答中更好地提到这一点。同步部分用于
ArrayList
TargetObject
及其大小。@denhackl,我是javafx新手,不知道javafx线程。到目前为止,您的代码工作正常,但是在阅读了描述的最后一部分和注释之后,我觉得这个解决方案可能没有效率。您能否提供一些链接,让我可以找到上面提到的JavaFX线程概念:“do?”
 public void checkCollisionThreaded(final Rectangle bullet)
{
    final int noOfTargetObjs = targetObjects.size();       
    Task<Integer> t = new Task<Integer>()
    {

        @Override
        protected Integer call() throws Exception
        {
            for(int i=0; i<noOfTargetObjs;i++)
            {
                if(targetObjects.get(i).isVisible()==true && bullet.isVisible()==true){
                    Shape intersectShape= Shape.intersect(bullet, targetObjects.get(i));
                    if(intersectShape.getBoundsInLocal().getWidth() != -1){
                        return i;
                    }
                }
            }    
            return -1;
        }

        @Override
        protected void succeeded()
        {
            super.succeeded();
            if(this.getValue().intValue() != -1)
            {
              Node obj = targetObjects.get(this.getValue().intValue());
              obj.setVisible(false);
              AnchorPane ap = (AnchorPane) obj.getParent();
              ap.getChildren().remove(obj);              
              targetObjects.remove(this.getValue().intValue());

              bullet.setVisible(false);
            }
        }
    };

    Thread thread = new Thread(t);
    thread.start();
}