在游戏循环的最佳睡眠时间计算的研究 [英] Investigation of optimal sleep time calculation in game loop

查看:290
本文介绍了在游戏循环的最佳睡眠时间计算的研究的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当编程动画,小游戏,我已经认识到令人难以置信的重要性的Thread.sleep(N); 我靠这个方法来告诉操作系统时我的应用程序不需要任何CPU,并以predictable速度用这个让我的计划进度。

When programming animations and little games I've come to know the incredible importance of Thread.sleep(n); I rely on this method to tell the operating system when my application won't need any CPU, and using this making my program progress in a predictable speed.

我的问题是将JRE使用实施不同的操作系统上这一功能的不同的方法。在基于UNIX(或影响)操作系统:作为Ubuntu和OS X上课等,底层JRE实现使用一个运作良好和precise系​​统分配CPU时间到不同的应用程序,所以使我的2D游戏流畅滞后免费的。然而,在Windows 7及以上的微软系统,占用CPU时间的分布似乎不同的方式工作,你平时取回你的CPU时间的睡眠一定量后,与目标睡眠1-2毫秒变化。但是,你得到的睡眠时间额外10-20毫秒阵阵偶然。这将导致我比赛的时候发生这种情况,每隔几秒钟落后一次。我注意到大多数Java游戏我已经在Windows上,我的世界是一个明显的例子。

My problem is that the JRE uses different methods of implementation of this functionality on different operating systems. On UNIX-based (or influenced) OS:es such as Ubuntu and OS X, the underlying JRE implementation uses a well-functioning and precise system for distributing CPU-time to different applications, and so making my 2D game smooth and lag-free. However, on Windows 7 and older Microsoft systems, the CPU-time distribution seems to work differently, and you usually get back your CPU-time after the given amount of sleep, varying with about 1-2 ms from target sleep. However, you get occasional bursts of extra 10-20 ms of sleep time. This causes my game to lag once every few seconds when this happens. I've noticed this problem exists on most Java games I've tried on Windows, Minecraft being a noticeable example.

现在,我一直在寻找在互联网上四处寻找解决这一问题。我见过只用 Thread.yield()的人很多; 而不是视频下载(N); ,这在目前使用的CPU内核获得满负荷,成本完美的作品无论你的游戏其实多少CPU的需求。这是不理想的玩你的游戏笔记本电脑或高能耗的工作站,它是在Mac和Linux系统中不必要的权衡。

Now, I've been looking around on the Internet to find a solution to this problem. I've seen a lot of people using only Thread.yield(); instead of Thread.sleep(n);, which works flawlessly at the cost of the currently used CPU core getting full load, no matter how much CPU your game actually needs. This is not ideal for playing your game on laptops or high energy consumption workstations, and it's an unnecessary trade-off on Macs and Linux systems.

放眼望去进一步,我发现纠正睡眠时间不一致的常用的方法被称为自旋休眠,在这里你只为了睡在一个时间1毫秒,并使用检查一致性的 System.nanoTime (); 方法,这是非常准确的,即使微软的系统。这有助于睡眠不一致的正常1-2毫秒,但它不会帮助睡眠对不一致+ 10-20毫秒的阵阵偶然,因为这通常会导致花费了比我的循环应该采取一切的一个周期有更多的时间在一起。

Looking around further I found a commonly used method of correcting sleep time inconsistencies called "spin-sleep", where you only order sleep for 1 ms at a time and check for consistency using the System.nanoTime(); method, which is very accurate even on Microsoft systems. This helps for the normal 1-2 ms of sleep inconsistency, but it won't help against the occasional bursts of +10-20 ms of sleep inconsistency, since this often results in more time spent than one cycle of my loop should take all together.

在万吨找我发现安迪Malakov,这是提高我的循环非常有帮助的这个神秘的文章:<一href=\"http://andy-malakov.blogspot.com/2010/06/alternative-to-threadsleep.html\">http://andy-malakov.blogspot.com/2010/06/alternative-to-threadsleep.html

After tons of looking I found this cryptic article of Andy Malakov, which was very helpful in improving my loop: 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毫秒, SPIN_YIELD_ preCISION 约10 ns的000对我的Windows 7计算机上的最佳性能。

SLEEP_PRECISION I usually put to about 2 ms, and SPIN_YIELD_PRECISION to about 10 000 ns for best performance on my Windows 7 machine.

在吨多的努力,这是绝对的最好的,我可以想出。所以,因为我还在乎改善这种睡眠方法的准确性,我还是不满意的表现,我想呼吁大家Java游戏黑客和动画师那里寻求建议上了一个更好的解决方案Windows平台。我可以在Windows上使用特定于平台的方法,使其更好?我不在乎有一个小平台特定的$ C $在我的应用程序C,只要广大的code是独立于操作系统的。

After tons of hard work, this is the absolute best I can come up with. So, since I still care about improving the accuracy of this sleep method, and I'm still not satisfied with the performance, I would like to appeal to all of you java game hackers and animators out there for suggestions on a better solution for the Windows platform. Could I use a platform-specific way on Windows to make it better? I don't care about having a little platform specific code in my applications, as long as the majority of the code is OS independent.

我也想知道是否有任何人谁知道微软和甲骨文工作了一个更好的实施视频下载(N); 的方法,或者是什么Oracle的未来计划是改善他们的生活环境的要求高时序精度的应用,如音乐软件和游戏?

I would also like to know if there is anyone who knows about Microsoft and Oracle working out a better implementation of the Thread.sleep(n); method, or what's Oracle's future plans are on improving their environment as the basis of applications requiring high timing accuracy, such as music software and games?

感谢大家阅读我的冗长的问题/条。我希望有些人可能会发现我的研究有帮助!

Thank you all for reading my lengthy question/article. I hope some people might find my research helpful!

推荐答案

您可以使用<一个href=\"http://download.oracle.com/javase/6/docs/api/java/util/Timer.html#scheduleAtFixedRate%28java.util.TimerTask,%20long,%20long%29\"相对=nofollow>与 =htt​​p://download.oracle.com/javase/6/docs/api/java/util/concurrent/Semaphore.html相对= nofollow的>互斥。这是IHMO做你想要什么的最有效的方式。但你应该想想跳帧的情况下,计算机滞后(你可以在计时器code另一个非阻塞互斥做到这一点。)

You could use a cyclic timer associated with a mutex. This is IHMO the most efficient way of doing what you want. But then you should think about skipping frames in case the computer lags (You can do it with another nonblocking mutex in the timer code.)

编辑:一些伪code澄清

Some pseudo-code to clarify

定时器code:

While(true):
  if acquireIfPossible(mutexSkipRender):
    release(mutexSkipRender)
    release(mutexRender)

睡眠code:

acquire(mutexSkipRender)
acquire(mutexRender)
release(mutexSkipRender)

启动值:

mutexSkipRender = 1
mutexRender = 0

修改:更正初始化值

以下code工作$ P $在Windows ptty以及(在循环正好与一个precision到毫秒50FPS)

The following code work pretty well on windows (loops at exactly 50fps with a precision to the millisecond)

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.Semaphore;


public class Main {
    public static void main(String[] args) throws InterruptedException {
        final Semaphore mutexRefresh = new Semaphore(0);
        final Semaphore mutexRefreshing = new Semaphore(1);
        int refresh = 0;

        Timer timRefresh = new Timer();
        timRefresh.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                if(mutexRefreshing.tryAcquire()) {
                    mutexRefreshing.release();
                    mutexRefresh.release();
                }
            }
        }, 0, 1000/50);

        // The timer is started and configured for 50fps
        Date startDate = new Date();
        while(true) { // Refreshing loop
            mutexRefresh.acquire();
            mutexRefreshing.acquire();

            // Refresh 
            refresh += 1;

            if(refresh % 50 == 0) {
                Date endDate = new Date();
                System.out.println(String.valueOf(50.0*1000/(endDate.getTime() - startDate.getTime())) + " fps.");
                startDate = new Date();
            }

            mutexRefreshing.release();
        }
    }
}

这篇关于在游戏循环的最佳睡眠时间计算的研究的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆