嗯...当涉及到 Javafx 时,我也对模拟器和游戏感兴趣
几个月前我创建了一个布料模拟器,虽然它并不完美,但它是我的第一个全新模拟器。
你可以期待的问题:
更新UI,所有节点都必须在FXthread上更新
在不影响场景性能的情况下运行更新
AnimationTimer 是最好的解决方案。使用标准动画会创建一堆代码。
我的解决方案是创建一个每 16 毫秒运行一次的 LoopService (ScheduledService)
(大约 60 fps)仅此一项就几乎使我的性能与 AnimationTimer 相比翻了一番。
LoopService 的好处在于您可以让代码在 fxthread 之外运行,并且 onSucceeded 更新 UI 而无需额外的侦听器等。
这是最终结果的 youtube 视频:https://www.youtube.com/watch?v=uRsCcpbsdsg
(出于某种原因,该视频已上传到旧帐户)
您可以在我的实际 youtube 页面上查看进度:
https://www.youtube.com/channel/UCxCaGZeistOC2J_rBF8JbWQ
我的数学远非完美,还需要大量改进,但我涵盖了更新 UI 的困难部分。
在源代码中,我用不同的名称调用服务,但基础是相同的。
布料的源代码在这里:https://github.com/Birdasaur/FXyz
在 Tests 文件夹中,您应该会找到一个可运行的示例。
希望对您有所帮助!
这是该服务的更新版本:
/**
* Simple Looping Service. Useful for games, Simulations, or other items that require a running "Loop".
*
* @author Jason Pollastrini aka jdub1581
*/
public abstract class AbstractLoopService extends ScheduledService<Void>{
private final long ONE_NANO = 1_000_000_000L;
private final double ONE_NANO_INV = 1f / 1_000_000_000L;
private long startTime, previousTime;
private double frameRate, deltaTime;
private final LoopThreadFactory tf = new LoopThreadFactory();
private final ExecutorService cachedExecutor = Executors.newCachedThreadPool(tf);
protected AbstractLoopService() {
this.setPeriod(Duration.millis(16.667)); // eqiv to 60 fps
this.setExecutor(cachedExecutor);
}
protected final double getTimeElapsed() {
return getCurrentTime() * ONE_NANO_INV;
}
protected final long getCurrentTime() {
return System.nanoTime() - startTime;
}
protected final double getFrameRate() {
return frameRate;
}
protected final double getDeltaTime() {
return deltaTime;
}
private void updateTimer() {
deltaTime = (getCurrentTime() - previousTime) * (1.0f / ONE_NANO);
frameRate = 1.0f / deltaTime;
previousTime = getCurrentTime();
}
@Override
public void start() {
super.start();
if (startTime <= 0) {
startTime = System.nanoTime();
}
}
@Override
public void reset() {
super.reset();
startTime = System.nanoTime();
previousTime = getCurrentTime();
}
@Override
protected Task<Void> createTask() {
return new Task<Void>() {
@Override
protected Void call() throws Exception {
updateTimer();
// perform needed background tasks here ..
runInBackground();
return null;
}
};
}
@Override
protected void succeeded() {
super.succeeded();
// Setup to handle Actions for UI here
runOnFXThread();
}
@Override
protected void failed() {
getException().printStackTrace(System.err);
}
@Override
public String toString() {
return "ElapsedTime: " + getCurrentTime() + "\nTime in seconds: " + getTimeElapsed()
+ "\nFrame Rate: " + getFrameRate()
+ "\nDeltaTime: " + getDeltaTime();
}
/*==========================================================================
* Methods for access
*/
protected abstract void runOnFXThread();
protected abstract void runInBackground();
/*==========================================================================
*/
private final class LoopThreadFactory implements ThreadFactory {
public LoopThreadFactory() {
}
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r, "NanoTimerThread");
t.setPriority(Thread.NORM_PRIORITY + 1);
t.setDaemon(true);
return t;
}
}
}