我目前正在创建一个非常简单的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();
}
答案 0 :(得分:1)
您应该使用不同的方法。请查看"In JavaFX how do I move a sprite across the screen?"中的答案。
您应该使用AnimationTimer而不是时间轴,并在那里更新画布。
关于你的问题:一个问题可能是你正在将Thread与JavaFX Thread混合,这可能导致问题,如果没有看到完整代码就无法分辨。
答案 1 :(得分:0)
我有similar problem,但您可能需要考虑一些事项,这可以大大提高代码的性能:
Path
平面动画。看看How to write text along a bezier curve。但是,您的路径可能不是由单个Bèzier曲线构成的,而更可能是Bèzier路径。所以请看一下这些文章:Bezier curves a tutorial和Bèzier path algorithms以及C#中的示例。然后,您只需为每个平面定义PathTransition
。AnimationTimer
)。这个将在UI应用程序线程的每个脉冲上调用,它应该等于您的帧速率。然后,您可以在该线程中更新存储中所有平面的位置,并在UI中更新飞机的位置。看一下这个question and answer 从我的代码中可以看到,您正在为每个平面创建一个单独的线程。线程是昂贵的对象,特别是对于每次运行线程只需要几个cpu周期的任务。然后你必须考虑线程切换的成本超过实际的计算时间。因此,在使用线程时,请确保将其用于长时间运行的任务。
答案 2 :(得分:0)
我想知道你为什么要使用画布来达到这个目的。可能只是因为你已经习惯了AWT或类似的框架。场景图更适合这种问题。只需将一些对象放在场景图中的初始位置,然后更新它们的位置和方向以及ONE AnimationTimer中的其他内容即可。
答案 3 :(得分:-1)
问题是我没有在run()方法中调用Thread.sleep(time),当我在time = 10时添加它时,动画开始顺利运行。