我正在寻找一些关于使用JavaFX构建复杂动画的答案。 我正在尝试用活塞移动等构建一个简单的Carnot Engine动画。 与this one类似的东西。
我需要的是开始编写此类模拟的最佳方式。 目前我对FX中的3种模拟类型很熟悉,因此我认为在我的情况下,最佳选择是时间线模拟。
但我仍然不确定如何开始。我应该在窗格上放置不同的砖块,如矩形板,路径,圆圈,甚至没有触及模拟,并尝试将生命放入其中,或者这种方法是完全误解的?这是我期待获得一些答案或至少提示的问题。
答案 0 :(得分:1)
我会从物理引擎类开始,只有文本输出。
获取绘图中每个关键点的坐标,如连接和枢轴,并使用真实物理,计算下一个1/60秒的位置。将这些位置与热变量等一起输出到控制台。确保它是正确的,然后填写真实的形状和动画。 Javafx使用60帧/秒的标准。
如果绘图是2d并且模拟是3d(真实世界)物理,则可能在两者之间进行转换时遇到一些问题。对于奖励积分,您可以进行3D模拟。
答案 1 :(得分:1)
嗯......我对Javafx的模拟器和游戏感兴趣
几个月前我创建了一个布料模拟器,虽然并不完美,但它是我的第一个打磨模拟器。
你可以期待的麻烦:
更新UI,必须在FXthread上更新所有节点 在不妨碍场景性能的情况下运行更新
AnimationTimer是一个好的解决方案。使用标准动画创建一堆代码。
我的解决方案是创建一个每16ms运行一次的LoopService(ScheduledService) (大约60fps)与AnimationTimer相比,这个数字几乎翻了一番。
关于LoopService的好处是你可以让代码在fxthread之外运行,onSucceeded更新UI而不需要额外的监听器等。
以下是youtube上最终结果的视频:https://www.youtube.com/watch?v=uRsCcpbsdsg (由于某些原因,vid已上传到旧帐户)
您可以在我的实际YouTube页面上查看进度:
https://www.youtube.com/channel/UCxCaGZeistOC2J_rBF8JbWQ
我的数学远非完美,它仍然需要进行大量改进,但我介绍了更新UI的难点。 在源代码中,我用不同的名称来调用服务,但基础是相同的。
Cloth的源代码在这里: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;
}
}
}