使主游戏循环不受控制地运行是否有任何危害?


19

我想知道当我的游戏循环以系统允许的速度运行时是否有任何可能的危害?

我目前有一个循环,通过测量经过的时间(以纳秒为单位),可以以预定义的速度运行游戏逻辑和渲染逻辑,而不会出现问题。实际上,我在循环中执行的任何逻辑都每秒被计时到一定数量的调用。

循环本身的运行速度差不多可以达到我的计算机上每秒大约1170万个循环的速度。

循环(简单的伪代码):

while(!isGameOver){

 if(canPollInputs){
    pollInputs()
 }

 while(canStepLogic){
    stepLogic()
 }

 if(canRender){
    render()
 }
}

我的问题基本上是,如果这个简单的循环没有以受控的速度运行,是否会对系统造成危害?

编辑:这意味着我的逻辑每秒运行30次(30 tps),渲染器以60 fps的速度运行,我轮询输入每秒100次,并且还有一些逻辑可以应付逻辑或渲染花费比预期更长的时间。但是循环本身并未受到限制。

编辑:使用Thread.sleep()例如将主循环节流到每秒250个循环导致减少,但是循环以每秒570个循环而不是所需的250个循环运行(当我在台式机上时会添加代码。)

编辑:在这里,为了弄清楚事情,一个工作的Java gameloop。也可以随意使用它,但不要声称它是您的;)

private void gameLoop() {

    // Time that must elapse before a new run
    double timePerPoll = 1000000000l / targetPPS;
    double timePerTick = 1000000000l / targetTPS;
    double timePerFrame = 1000000000l / targetFPS;
    int maxFrameSkip = (int) ( (1000000000l / MINIMUM_FPS) / timePerTick);

    int achievedPPS = 0;
    int achievedFPS = 0;
    int achievedTPS = 0;

    long timer = TimeUtils.getMillis();

    int loops = 0;

    int achievedLoops = 0;

    long currTime = 0l;
    long loopTime = 0l;

    long accumulatorPPS = 0l;
    long accumulatorTPS = 0l;
    long accumulatorFPS = 0l;

    long lastTime = TimeUtils.getNano();

    while(!isRequestedToStop) {
        currTime = TimeUtils.getNano();
        loopTime = currTime - lastTime;
        lastTime = currTime;

        loops = 0;

        accumulatorPPS += loopTime;
        accumulatorTPS += loopTime;
        accumulatorFPS += loopTime;

        if(accumulatorPPS >= timePerPoll) {
            pollInputs();
            playerLogic();
            achievedPPS++;
            accumulatorPPS -= timePerPoll;
        }

        while(accumulatorTPS >= timePerTick && loops < maxFrameSkip) {
            tick();
            achievedTPS++;
            accumulatorTPS -= timePerTick;
            loops++;
        }

        // Max 1 render per loop so player movement stays fluent
        if(accumulatorFPS >= timePerFrame) {
            render();
            achievedFPS++;
            accumulatorFPS -= timePerFrame;
        }

        if(TimeUtils.getDeltaMillis(timer) > 1000) {
            timer += 1000;
            logger.debug(achievedTPS + " TPS, " + achievedFPS + " FPS, "
                    + achievedPPS + " Polls, " + achievedLoops + " Loops");
            achievedTPS = 0;
            achievedFPS = 0;
            achievedLoops = 0;
        }

        achievedLoops++;
    }
}

如您所见,几乎没有代码在每个循环上运行,而是始终根据经过的实时时间进行一定的选择。问题是指“工人循环”及其如何影响系统。


2
强制性的“固定时间步长”链接:gafferongames.com/game-physics/fix-your-timestep请阅读。还要注意,Thread.Sleep()不能可靠地限制您的时间步长,因为操作系统无法保证在请求的时间后将控制权返回给您的应用程序。(可能会有所不同)。
Roy T.

是的,这就是伪代码的问题。我做了gaffer编写的大部分内容(目前正在研究与我的循环结构互补的增量实现)。如果我不能使用Thread.sleep(),那么标准Java中还有什么可用?
dot_Sp0T 2014年

1
您通常对Thread.Sleep(0)使用繁忙循环,并希望达到最佳效果。Gaffer在几篇文章中谈到了很多。
罗伊(Roy T.)

好吧,基本上这就是我一直在做的,只是没有thread.sleep(0),以及最初引起这个问题的原因。对系统而言,繁忙的循环(即使很少的重复逻辑都停止工作,也不繁忙)有什么危害吗?
dot_Sp0T 2014年

不久前,一个错误导致Star​​craft II在某些条件下执行此操作。最终,它实际上融化了一些GPU。是的,不受控制的游戏循环很可能造成伤害。
梅森惠勒

Answers:


35

这将导致一个CPU内核始终以100%运行。这通常不会对系统造成任何损害。CPU设计为可以100%运行数小时。但是在移动设备上,它将迅速耗尽电池电量并加热设备,这可能会使您在商店评价中花费大约星星。在台式计算机上,这不是什么大问题,但是它将消耗更多的用户电力,导致CPU风扇旋转得更快,这可能会引起一些噪音并浪费CPU周期,而这些周期可能会被其他进程使用。尽管这些不是关键缺陷,但它们仍然是不良样式,因此,应尽可能避免使用它们。

您没有说出游戏逻辑循环在内部如何工作,但是当您使用增量时间方法时(您所做的每次计算都花费了自上次调用以来的时间),因此您要处理的增量非常小。时间值。这意味着您可能会遇到浮点错误的问题,这可能会导致各种奇怪的行为。同样,系统计时器的分辨率通常受到限制,因此,当逻辑循环太快时,您可以获得的delta-t值为零,这可能导致被零除,从而导致崩溃。

为了减轻这个问题,您应该将帧速率(图形和逻辑)限制在人眼可以感知的最大范围内。有多少争议,取决于动画的类型和显示的类型,但估计范围为40 FPS至120 FPS。这意味着您需要在20ms到8ms之间设置每个循环的最短执行时间。如果循环迭代完成得更快,请让cpu线程sleep保留剩余时间。


感谢您的解释Philipp。实际上,我确实提到了这样一个事实,我以一定的fps和tps运行,并且每秒仅进行一定次数的轮询。会尽力使它更清晰。
dot_Sp0T

@ dot_Sp0T编辑完问题后,只有第一段和最后一句话与您的情况有关。但是,我想保留我回答的第二和第三段,因为它们可能对其他人有帮助。
菲利普2014年

我想也将接受您的回答,因为第一段会很好地给出答案。您介意以视觉方式将其与其余答案区分开吗?
dot_Sp0T

在台式机上播放未打补丁的《文明2》,您会发现这是一个问题。至少我知道。耸耸肩
corsiKa 2014年

1
除了对电池和风扇的考虑之外,增加的CPU使用率还可能导致过多的热量,这可能令人不舒服(例如,笔记本电脑,尤其是我的rMBP),甚至导致PC关闭(特别是带有尘土飞扬的散热器的老式PC)。如果所有内核都运行100%,这些因素会激怒。
NPSF3000,2014年

2

您正在浪费CPU周期。这意味着笔记本电脑,平板电脑和手机上的电池时间更少,电费更高,机器产生的热量更多,风扇噪音更大。另外,您可能正在吞噬其他重要系统进程的周期(例如,窗口服务器可能会变得生涩),这可能会影响游戏玩法。当今的抢先式多任务系统上的某些系统调度程序也会对使用过多周期的应用程序造成不利影响,因此您可能会很快变慢,然后在受到系统限制时突然发现奇怪的混蛋。

许多铁杆游戏玩家还从头开始构建定制PC,这是其风扇规格的边缘。在这种情况下,炎热的天气和游戏产生的热量可能会(至少)使机器触发安全性甚至导致零件过热,死(最坏的情况)。因此,如果这是您的目标受众,则可能需要确保始终在“顶部”留一点空间。

最后,存在游戏性问题,在这些问题中,与速度较慢的计算机相比,您可能会更喜欢速度更快的计算机。让游戏循环以某个特定的频率设置上限,并仅将与非游戏相关的方面(例如,渲染更高保真度的图形,环绕声效果等)用于其他循环,将使您的游戏更加公平。


相反,我会热情地建造自己的机器的人使用好的风扇,甚至是水冷风扇。仅HTPC或mini ITX机箱可能在该部分上有限制。否则,贫穷但热情的人们将使用盒子ventirad。即使在100%的温度下也足够了,但还是可以
v.oddou 2014年

我只是从经验中重复。如果您查看《星际迷航》在线版,他们实际上在GUI中有一个单独的设置,该设置将在其主循环中插入sleep()来防止某些机器过热,因此,如果Neverwinter和STO认为有必要添加它。请记住,激情!=技能。统计数据显示,有很多技术娴熟且充满激情的修补匠,但也有初学者和其他仍在学习中的人,辍学率很高,据统计,他们是大多数人。
uliwitness 2014年

由于这些评论,我来重新阅读您的答案。您列出了有效的分数,但可惜的是,对于速度更快的机器还是速度较慢的机器,争议并没有真正解决。该逻辑循环(称为gameloop在第三段)封顶。问题在于限制主干循环的上限,该主干循环简单地重新评估了是否应该拉动输入,计算逻辑或应渲染帧的每次运行-因此,除非您的机器非常
la脚,

1

你不应该有紧张的动作/游戏

您可以通过两种方式实现游戏逻辑-与实时相关,或与“回合” /处理步骤的数量相关。如果您的游戏中有些东西向左移动,那么如果您的stepLogic()调用了100次而不是50次,它的动作是否会有所不同?

如果您的代码在所有地方都明确地处理了经过的时间并且正确地进行了处理,那么可能就可以了;但是如果您的代码中有任何内容取决于“步骤”的数量,那么您将得到不想要的副作用。

首先,应该不断移动的事物的速度将发生不可预测的变化(取决于处理器的使用情况),这使得控件变得非常烦人-如果游戏突然加速或减速,您将无法精确打孔或跳跃。即使对于完全没有“抽搐”的游戏,如果事情进展不顺利,它也会看起来很烦人。

其次,取决于计算机速度,您可能会遇到角色能力问题-一个典型的例子是旧的Quake问题,其中最大跳跃范围无意中取决于fps。

即使您尝试使代码与fps无关,也可能会出现这些问题,四舍五入错误的累积通常会导致此类错误。


1
就我喜欢您的答案而言,它与问题无关,因为我不是在询问紧张的动作/游戏,而是关于不受控制的循环(不控制任何逻辑或渲染或任何其他操作)对系统的危害
dot_Sp0T

1

唯一的潜在危害是功耗,因此不要在移动设备上这样做。相反,相当多的嵌入式系统将整个生命周期循环地等待着事情发生。

尽可能简单地渲染游戏帧通常是完全正常的,有时甚至没有计时器来补偿不同CPU速度的游戏玩法。Bullfrog的赛车游戏Hi-Octane对此尤为糟糕,我怀疑这就是提到Civ2的海报所指的内容。

如果您使用的是Windows系统或类似系统,我建议您至少在每次通过时都对输入进行轮询,以确保快速响应输入。


它不是计时器的问题,不是我想说的计时器的问题,还是性能计数器。一个人需要在某个时间点获得实时信息,并且自由循环会完美地工作。但是,计时器(从常规中断的意义上讲)正试图调节一般使用中的循环速度。我认为这不好。最好是使用swap/ present/ flip功能做伺候垂直同步信号VSYNC,为规范游戏循环。
v.oddou 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.