游戏循环中最佳睡眠时间计算的研究
在编写动画和小游戏时,我开始知道我依靠这种方法来告诉操作系统何时我的应用程序不需要任何CPU,并使我的程序以可预测的速度进行。Thread.sleep(n);
我的问题是JRE在不同的操作系统上使用不同的方法来实现此功能。在基于UNIX(或受影响的)OS:es(如Ubuntu和OS X)上,底层的JRE实现使用功能良好且精确的系统将CPU时间分配给不同的应用程序,从而使我的2D游戏流畅且无延迟。但是,在Windows 7和较旧的Microsoft系统上,CPU时间分布的工作方式似乎不同,并且您通常在给定的睡眠量后恢复CPU时间,与目标睡眠时间大约1-2毫秒不同。但是,您偶尔会获得额外的10-20毫秒睡眠时间。当这种情况发生时,这会导致我的游戏每隔几秒钟滞后一次。我注意到这个问题存在于我在Windows上尝试过的大多数Java游戏中,Minecraft就是一个明显的例子。
现在,我一直在互联网上寻找这个问题的解决方案。我见过很多人只使用 而不是 ,无论您的游戏实际需要多少CPU,它都能以当前使用的CPU内核获得满载为代价完美运行。这对于在笔记本电脑或高能耗工作站上玩游戏并不理想,并且在Mac和Linux系统上是不必要的权衡。Thread.yield();
Thread.sleep(n);
进一步环顾四周,我发现了一种常用的纠正睡眠时间不一致的方法,称为“旋转睡眠”,其中您一次只订购睡眠1毫秒,并使用该方法检查一致性,即使在Microsoft系统上也非常准确。这有助于正常1-2毫秒的睡眠不一致,但它无助于防止偶尔爆发的+10-20毫秒的睡眠不一致,因为这通常会导致花费的时间超过循环的一个周期应该花费的时间。System.nanoTime();
经过大量的研究,我发现了安迪·马拉科夫(Andy Malakov)的这篇神秘文章,这对改善我的循环非常有帮助:http://andy-malakov.blogspot.com/2010/06/alternative-to-threadsleep.html
根据他的文章,我写了这个睡眠方法:
// Variables for calculating optimal sleep time. In nanoseconds (1s = 10^-9ms).
private long timeBefore = 0L;
private long timeSleepEnd, timeLeft;
// The estimated game update rate.
private double timeUpdateRate;
// The time one game loop cycle should take in order to reach the max FPS.
private long timeLoop;
private void sleep() throws InterruptedException {
// Skip first game loop cycle.
if (timeBefore != 0L) {
// Calculate optimal game loop sleep time.
timeLeft = timeLoop - (System.nanoTime() - timeBefore);
// If all necessary calculations took LESS time than given by the sleepTimeBuffer. Max update rate was reached.
if (timeLeft > 0 && isUpdateRateLimited) {
// Determine when to stop sleeping.
timeSleepEnd = System.nanoTime() + timeLeft;
// Sleep, yield or keep the thread busy until there is not time left to sleep.
do {
if (timeLeft > SLEEP_PRECISION) {
Thread.sleep(1); // Sleep for approximately 1 millisecond.
}
else if (timeLeft > SPIN_YIELD_PRECISION) {
Thread.yield(); // Yield the thread.
}
if (Thread.interrupted()) {
throw new InterruptedException();
}
timeLeft = timeSleepEnd - System.nanoTime();
}
while (timeLeft > 0);
}
// Save the calculated update rate.
timeUpdateRate = 1000000000D / (double) (System.nanoTime() - timeBefore);
}
// Starting point for time measurement.
timeBefore = System.nanoTime();
}
SLEEP_PRECISION
我通常将大约2毫秒,大约10 000 ns,以便在我的Windows 7计算机上获得最佳性能。SPIN_YIELD_PRECISION
经过大量的辛勤工作,这是我能想到的绝对最好的。因此,由于我仍然关心提高这种睡眠方法的准确性,并且我仍然对性能不满意,因此我想向所有Java游戏黑客和动画师寻求有关Windows平台更好解决方案的建议。我可以在Windows上使用特定于平台的方式来使其更好吗?我不在乎在我的应用程序中有一些特定于平台的代码,只要大多数代码是独立于操作系统的。
我还想知道是否有人知道微软和甲骨文正在制定更好的方法实现,或者甲骨文未来的计划是什么,以改善他们的环境,作为需要高计时精度的应用程序的基础,如音乐软件和游戏?Thread.sleep(n);
谢谢大家阅读我的冗长问题/文章。我希望有些人会发现我的研究有帮助!