Multithreading JavaFX时间轴动画性能差

Multithreading JavaFX时间轴动画性能差,multithreading,animation,javafx,Multithreading,Animation,Javafx,我目前正在创建一个非常简单的JavaFX程序,模拟城市之间运送乘客的飞机和船只。到目前为止,我已经能够使飞机在几个城市进行短途飞行,但问题是,当我添加超过3或4架飞机时,动画速度非常慢 我正在做的是使用Timeline类作为我的主游戏循环,清除并重新绘制画布上每帧60 FPS的平面图像。以下是时间表部分: Timeline gameLoop = new Timeline(); gameLoop.setCycleCount( Timeline.INDEFINITE ); KeyFrame

我目前正在创建一个非常简单的JavaFX程序,模拟城市之间运送乘客的飞机和船只。到目前为止,我已经能够使飞机在几个城市进行短途飞行,但问题是,当我添加超过3或4架飞机时,动画速度非常慢

我正在做的是使用Timeline类作为我的主游戏循环,清除并重新绘制画布上每帧60 FPS的平面图像。以下是时间表部分:

Timeline gameLoop = new Timeline();
gameLoop.setCycleCount( Timeline.INDEFINITE );

    KeyFrame keyFrame = new KeyFrame(
            Duration.seconds(0.017),
            new EventHandler<ActionEvent>()
            {
                public void handle(ActionEvent ae)
                {
                    dynamicGC.clearRect(0, 0, 1280, 800);

                    for (Plane plane : Spawner.planeList)
                    {
                        if(plane.isInFlight()) plane.Draw(dynamicGC);
                    }
                }
            });

gameLoop.getKeyFrames().add(keyFrame);
gameLoop.play();
下面是在Plane类中定义的run()方法:

public void run() {
    int routeCounter = 0;
    int direction = 1;
    boolean active = true;
    double recordedTime = 0.0;
    double aParameter = 0.0;
    while(active) {
        if (inFlight) {
            double tmpTime = System.currentTimeMillis();
            double timeDifference = tmpTime - recordedTime;
            recordedTime = tmpTime;

            double change = planeVelocity * (timeDifference) * direction;
            xCoordinate += change;
            yCoordinate += change * aParameter;
            if ( Math.abs(xCoordinate - Spawner.airportList.get(nextAirport).getXCoordinate()) < Spawner.airportList.get(nextAirport).getDetectionPrecision()
                    && Math.abs(yCoordinate - Spawner.airportList.get(nextAirport).getYCoordinate()) < Spawner.airportList.get(nextAirport).getDetectionPrecision()) {
                inFlight = false;
                routeCounter++;
            }
        } else {
            if(routeCounter < plannedRoute.size() ) {
                nextAirport = plannedRoute.get(routeCounter);
                aParameter = (Spawner.airportList.get(nextAirport).getYCoordinate() - this.yCoordinate)/
                        (Spawner.airportList.get(nextAirport).getXCoordinate() - this.xCoordinate);
                if (Spawner.airportList.get(nextAirport).getXCoordinate() < this.xCoordinate) {
                    direction = -1;
                } else direction = 1;
                recordedTime = System.currentTimeMillis();
                inFlight = true;
            }
            else active = false;
        }
    }

}
public void run(){
int routeCounter=0;
int方向=1;
布尔活动=真;
双记录时间=0.0;
双参数=0.0;
while(活动){
国际单项体育联合会(机上){
double tmpTime=System.currentTimeMillis();
双时差=tmpTime-记录的时间;
记录的时间=tmpTime;
双变=平面速度*(时差)*方向;
xCoordinate+=变化;
Y坐标+=变化*参数;
if(Math.abs(xCoordinate-Spawner.airportList.get(nextAirport.getXCoordinate())
我知道代码非常混乱,但现在我想知道为什么动画速度会如此缓慢。我不确定我是否正确使用了线程。此外,我还尝试了一些解决方案,如缓存画布和组节点,如在问题的答案中,但没有任何效果

编辑:看来是线程减慢了程序的执行速度

每次在JavaFX应用程序线程中使用此处理程序按下某个按钮时,我都会创建一个新平面(以及一个新线程):

@FXML
private void handleNewPassengerPlaneCreated(ActionEvent event) {
    ArrayList<Integer> route = new ArrayList<>();
    int startingAirport = Spawner.randomNumberGenerator.nextInt(Spawner.airportList.size());
    for(int i = 0; i < 3; i++) {
        int addedAirport = Spawner.randomNumberGenerator.nextInt(Spawner.airportList.size());
        if ( addedAirport != startingAirport)
            route.add(addedAirport);
    }

    System.out.println(Spawner.airportList.get(startingAirport).xCoordinate + " " + Spawner.airportList.get(startingAirport).yCoordinate );
    PassengerPlane plane = new PassengerPlane(Spawner.airportList.get(startingAirport).xCoordinate,
            Spawner.airportList.get(startingAirport).yCoordinate, "ID", 10, 1000, route, 300);
    Spawner.planeList.add(plane);
    new Thread(plane).start();
}
@FXML
已创建私有void HandlenewPassengerPlane(ActionEvent事件){
ArrayList路由=新建ArrayList();
int startingAirport=Spawner.randomNumberGenerator.nextInt(Spawner.airportList.size());
对于(int i=0;i<3;i++){
int addDairport=Spawner.randomNumberGenerator.nextInt(Spawner.airportList.size());
if(addedAirport!=启动机场)
路线。添加(AddDairPort);
}
System.out.println(Spawner.airportList.get(startingAirport.xCoordinate+“”+Spawner.airportList.get(startingAirport.yccoordinate);
乘客飞机=新乘客飞机(Spawner.airportList.get(startingairplane).xCoordinate,
产卵器。机场列表。get(startingAirport)。yCoordinate,“ID”,101000,route,300);
产卵器。平面列表。添加(平面);
新螺纹(平面).start();
}

您应该使用不同的方法。看一看表中的答案

您应该使用时间线,而不是时间线,并在其中更新画布

关于您的问题:一个问题可能是您将线程与JavaFX线程混合在一起,这可能会导致问题,如果没有看到完整的代码就无法判断

>我有一个,但是有一些事情您可能需要考虑,这可以极大地提高代码的性能:

  • 对于每个需要动画的平面,创建一个路径,沿该路径对平面进行动画。看一看。然而,您的路径可能不是由一条Bèzier曲线构成的,更可能是一条Bèzier路径。那么看看这些文章:并用C#中的例子。然后,您可以简单地为每个平面定义一个
    路径转换
  • 如果上述原因是不可能的,请考虑拆分责任:
    • 为正在飞行的飞机创建模型存储(可以是简单的列表或地图)
    • 创建一个工作线程(很可能是一个线程)。这个将在UI应用程序线程的每个脉冲上调用,它应该等于您的帧速率。在该线程中,您可以更新存储中所有平面的位置,并更新UI中平面的位置。看看这个

    从代码中我可以看到,您正在为每个平面创建一个单独的线程。线程是昂贵的对象,特别是对于每次运行线程时只需要几个cpu周期的任务。然后,你必须考虑线程切换的成本超过实际计算时间。因此,在使用线程时,请确保将其用于长时间运行的任务。

    我想知道为什么要使用画布来实现此目的。可能只是因为您已经习惯了AWT或类似的框架。场景图更适合这种问题。只需将场景图中的一些对象放置在它们的初始位置,然后在一个AnimationTimer中更新它们的位置和方向以及其他内容,您就完成了。

    问题在于我没有在run()方法中调用Thread.sleep(time),当我在时间=10的情况下添加它时,动画开始顺利运行。

    我将时间线更改为AnimationTimer,不幸的是,它仍然是一样的。看来问题在于线程。我已经做了一个按钮,它产生了一个线程
    @FXML
    private void handleNewPassengerPlaneCreated(ActionEvent event) {
        ArrayList<Integer> route = new ArrayList<>();
        int startingAirport = Spawner.randomNumberGenerator.nextInt(Spawner.airportList.size());
        for(int i = 0; i < 3; i++) {
            int addedAirport = Spawner.randomNumberGenerator.nextInt(Spawner.airportList.size());
            if ( addedAirport != startingAirport)
                route.add(addedAirport);
        }
    
        System.out.println(Spawner.airportList.get(startingAirport).xCoordinate + " " + Spawner.airportList.get(startingAirport).yCoordinate );
        PassengerPlane plane = new PassengerPlane(Spawner.airportList.get(startingAirport).xCoordinate,
                Spawner.airportList.get(startingAirport).yCoordinate, "ID", 10, 1000, route, 300);
        Spawner.planeList.add(plane);
        new Thread(plane).start();
    }