JavaFX Shape.intersect()性能问题
我已经开始编写一个射击游戏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
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();
}