JavaFX时间轴动画效果不佳

时间:2016-01-09 00:10:23

标签: multithreading animation javafx

我目前正在创建一个非常简单的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 initalization
Thread t = new Thread(plane);
t.setDaemon(true);
t.start();

这是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;
        }
    }

}

我意识到代码非常混乱,但是现在我想知道为什么动画会如此剧烈地减速。我不确定我是否正确使用线程。此外,我还尝试了一些解决方案,比如缓存Canvas和Group节点,就像回答this问题一样,但它没有做任何事情。

编辑:似乎它是减缓程序执行速度的线程。

每次在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();
}

4 个答案:

答案 0 :(得分:1)

您应该使用不同的方法。请查看"In JavaFX how do I move a sprite across the screen?"中的答案。

您应该使用AnimationTimer而不是时间轴,并在那里更新画布。

关于你的问题:一个问题可能是你正在将Thread与JavaFX Thread混合,这可能导致问题,如果没有看到完整代码就无法分辨。

答案 1 :(得分:0)

我有similar problem,但您可能需要考虑一些事项,这可以大大提高代码的性能:

  1. 对于需要动画的每个平面,创建一个Path平面动画。看看How to write text along a bezier curve。但是,您的路径可能不是由单个Bèzier曲线构成的,而更可能是Bèzier路径。所以请看一下这些文章:Bezier curves a tutorialBèzier path algorithms以及C#中的示例。然后,您只需为每个平面定义PathTransition
  2. 如果上述因某种原因无法实现,请考虑分担责任:
    • 为正在旅行的飞机创建模型存储(可以像列表或地图一样简单)
    • 创建工作线程(很可能是AnimationTimer)。这个将在UI应用程序线程的每个脉冲上调用,它应该等于您的帧速率。然后,您可以在该线程中更新存储中所有平面的位置,并在UI中更新飞机的位置。看一下这个question and answer
  3. 从我的代码中可以看到,您正在为每个平面创建一个单独的线程。线程是昂贵的对象,特别是对于每次运行线程只需要几个cpu周期的任务。然后你必须考虑线程切换的成本超过实际的计算时间。因此,在使用线程时,请确保将其用于长时间运行的任务。

答案 2 :(得分:0)

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

答案 3 :(得分:-1)

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