原文地址 http://blog.163.com/wufei_spring_c/blog/static/39922794200827102518402/

【翻译】深入理解XNA的GameTime 

原文地址:http://blogs.msdn.com/shawnhar/archive/2007/07/25/understanding-gametime.aspx

这篇文章是针对XNA1.0来说的,在XNA2.0中会有一些区别,Shawn对XNA2.0的GameTime也有一篇文章http://blogs.msdn.com/shawnhar/archive/2007/11/23/game-timing-in-xna-game-studio-2-0.aspx,不过是以这篇文章为基础的。Understanding GameTime    深入理解 GameTime

 

Time can be a surprisingly slippery concept to get to grips with. Back when I was working on Allegro it caused the most common question from new programmers, and even though XNA does more than Allegro to handle time for you, it appears some people are still confused. Hopefully this post will clarify how time works in the XNA Framework.

时间(循环)能够成为一个巧妙而难以理解的概念,之前,我在研究Allegro的时候,关于时间循环的问题是新手最常见的问题,虽然XNA相对于Allegro在时间循环为使用者提供了更多的帮助,但一些人依然在此问题上迷惑不清。希望此贴能够阐明XNAFramework中的时钟循环是如何工作的。

 

Game.IsFixedTimeStep = true

 

By default XNA runs in fixed timestep mode, with a TargetElapsedTime of 60 frames per second. This provides a simple guarantee:

默认的情况下,XNA运行在固定时间间隔模式下,该模式下的目标帧频是60每秒。这满足了一般游戏的需要:

 

1.        We will call your Update method exactly 60 times per second

2.        We will call your Draw method whenever we feel like it

 

1.        Update函数会在一秒钟内被调用60次。

2.        Draw函数会在线程空闲时被调用。

 

Digging into exactly what that means, you will realize there are several possible scenarios depending on how long your Update and Draw methods take to execute.

让我们看得更深入一些,时间循环的运行会有几种不同的情况。

 

The simplest situation is that the total time you spend in Update + Draw is exactly 1/60 of a second. In this case we will call Update, then call Draw, then look at the clock and notice it is time for another Update, then Draw, and so on. Simple!

最简单的情况是游戏调用一次Update和Draw函数的时间刚好是1/60秒,在这种情况下,程序调用了Update,然后调用Draw,然后看一看时间发现应该调用下一次Update函数了,然后调用Draw,就这样重复。

 

What if your Update + Draw takes less than 1/60 of a second? Also simple. Here we call Update, then call Draw, then look at the clock, notice we have some time left over, so wait around twiddling our thumbs until it is time to call Update again.

那么当你的Update和Draw函数需要少于1/60秒的时间会怎样呢?也很简单,调用Update,然后调用Draw,然后查看时间,等到时间到达该进行下一次Update的时候再进行一次调用。

 

What if Update + Draw takes longer than 1/60 of a second? This is where things get complicated. There are many reasons why this could happen:

那么如果Update和Draw函数需要多于1/60秒的时间呢?这种情况下事情变得有一些复杂了。而该情况发生的原因有很多:

 

1.       The computer might be slightly too slow to run the game at the desired speed.

2.       Or the computer might be way too slow to run the game at the desired speed!

3.       The computer might be basically fast enough, but this particular frame might have taken an unusually long time for some reason. Perhaps there were too many explosions on screen, or the game had to load a new texture, or there was a garbage collection.

4.       You could have paused the program in the debugger.

 

1.         你的计算机的性能不足以用需求的帧频来运行这个程序,然而差得并不远。

2.         你的计算机的性能相差需求很远。

3.         计算机的性能能够满足运行游戏的基本需求,然而对于特定的帧而言,计算机需要用更长的时间来更新和绘制,可能这些帧中有大量的爆炸需要计算或绘制。

4.         你可能在debugger过程中暂停了程序。

 

We do the same thing in response to all four causes of slowness:

对于以上情况引发的缓慢,XNAFramework会采取以下的手段来应对。

 

1.        Set GameTime.IsRunningSlowly to true.

2.        Call Update extra times (without calling Draw) until we catch up.

3.        If things are getting ridiculous and we are too far behind, we just give up.

 

1.        设置GameTime.IsRunningSlowly为true。

2.        多次调用Update函数(而不调用Draw)直到赶上了正常的进度。

3.        如果时间很紧急而我们被落下了很多,我们便放弃追赶。

 

If you think about how this algorithm deals with the four possible causes of slowdown I listed above, you'll see it handles them all rather well:

如果你考虑了这个方法具体是怎样解决以上列出的四个运行缓慢的原因的,你就会发现该方法对每一种情况都解决地很好:

 

1.       Even if the computer is too slow to run Update + Draw inside a single frame, chances are that Update + Update + Draw will fit into two frames. The game may look a little jerky, but it will still play correctly. If the programmer is particularly smart they might even notice that we set IsRunningSlowly, and automatically reduce detail or turn off special effects to speed things up.

2.       If the computer is way too slow (ie. if the Update method alone is too slow to fit into a single frame) there isn't really much we can do, other than set IsRunningSlowly, cross our fingers, and hope the game might do something clever in response to that. Most of the time, though, if you find yourself in this situation you just need a faster computer!

3.       If a particular frame happens to take unusually long, we automatically call Update some extra times to catch up, after which everything carries on as normal. The player may notice a slight glitch, but we automatically correct for this to minimize the impact.

4.       Pausing in the debugger will leave the game a long way behind where the clock says it should be, so our algorithm will give up, accept that it has lost some time, and continue running smoothly from the time execution was resumed.

 

1.         即使是计算机有点慢以致不能在一帧中运行完Update和Draw函数,而以两次Update函数和一次Draw函数的调用作为两帧,游戏会显得有些迟缓,但依然会正确地运行。如果程序员能够聪明地检测到IsRunningSlowly的值为真,并降低游戏的细节的话,帧频又能提高。

2.         如果计算机太慢,(例如Update函数都无法在一帧中运行完成)XNAFramework除了设置IsRunningSlowly并祈祷程序员注意到这一点之外,做不了别的事情。对于用户而言,但这种情况发生的时候,可能说明你需要一个更快的机器。

3.         如果特殊的帧在偶然情况下花掉了过多的时间,XNAFramework自动地多次额外调用Update函数来追赶时间,追赶上之后游戏就恢复正常运行的状态了。用户可能会注意到一段短时的停滞,但我们已经将影响讲到了最低。

4.         在debugger中中断了程序会让游戏比预期的循环时间落下很多,于是我们的方法会放弃追赶,接受遗失了不少时间的事实,然后在继续运行后重新平滑的运行。

 

In summary, you don't need to do anything special to take advantage of our our fixed timestep logic. Just make sure you put all your game logic inside the Update method (not in Draw!) and everything will run at a nice constant speed.

总结来说,你几乎不需要做额外的工作就可以享受固定时间间隔方式的好处。你只需要确定游戏所有的逻辑都在Update函数中,游戏就会以一个好的恒定的速率运行。

 

For bonus points you can automatically adjust your game detail in response to the IsRunningSlowly flag, but most games are fine not bothering with this.

作为程序的编写者,你可以在检测到IsRunningSlowly真的时侯自动地调节游戏的细节程度来是游戏更优秀。但对于绝大多数的游戏并不需要担心这一点。

 

If you put breakpoints on your Update and Draw methods you may notice us calling Update more often than Draw, but this is just because the breakpoint has made us late and we are trying to catch up. Timing is a great example of the Heisenberg Uncertainty Principle: by examining the way the timing system is behaving, you change the timings, and get different results than when you let the game run normally.

如果你在Update或者Draw函数中设置了断点,你可能会注意到Update函数比Draw函数调用的更多,这是因为断点使游戏尝试去追赶落下的时间。时间的调整过程反映了海森堡不确定法则:当实时系统的计时方法被改变时,结果也会较正常的情况发生改变。

 

You can change the TargetElapsedTime property if you want to run a fixed timestep game at something other than 60 frames per second. We chose that default because it matches the standard television refresh rate, but some games might want to slow this down to 30 frames per second.

如果你希望将你的游戏运行在另一个帧频下,你可以改变TargetElapsedTime属性的值。我们选择60帧每秒作为默认值是因为它遵从标准的电视刷新率,但一些游戏可能希望减慢到30帧每秒。

 

Game.IsFixedTimeStep = false

 

If you disable fixed timesteps, XNA does nothing clever for you. Our algorithm in this mode is extremely simple:

如果你禁用了固定时间间隔模式,XNA并不会帮你做什么聪明的事,在这种模式下,我们的算法相当简单。

 

1.        Update

2.        Draw

3.        Rinse, lather, repeat

 

1.        调用Update

2.        调用Draw

3.        等待,然后重复

 

(that is actually a slight simplification, but the details are unimportant)

(实际上说得稍微有一点简化,但其中的细节并不重要)

 

There are some significant advantages to running in this mode:

在这种模式下运行也有一些好处:

 

1.        Because the game is not tied to any fixed update frequency, it can do a better job of exactly matching your monitor refresh rate. This is important for twitch FPS games that want to render at exactly 70, 90, 120, or however else the user has configured their monitor.

2.        This mode can be more efficient when running on slow computers. Instead of having to call Update several times to catch up, we can just call it once with a large ElapsedTime value. If the game is programmed well, it may be able to handle that more efficiently than if Update was called several times with a smaller ElapsedTime.

 

1.        因为游戏并没有固定到任何更新频率,游戏就能够更好的匹配你的显示器的刷新率。对于FPS游戏来说,提供改变固定帧频或是匹配显示器的选项很重要。

2.        这个模式在较慢的机器上会运行更高效。不再需要调用Update函数多次来追赶时间,而是在更大的时间间隔中进行循环。如果这个游戏被良好的编程,它也许能够比不停得追赶运行得更高效。

 

The downside is that variable timestep games are much harder to program. Even simple computations such as "add velocity to position" must be adapted to take the elapsed time into account. For anything more than the most trivial calculations this requires you to integrate the equation, so you can evaluate how it changes over a period of time. Some people are good at calculus, but I'm not, and I don't much enjoy having to deal with such things every time I want to move a game object!

非固定间隔模式不好的一面是使游戏变得更难编写。即使是像“加速移动到某地点”这样的简单运算都需要使用时间间隔作为参数, 不固定的时间间隔可能会要求你进行积分等计算。一些人可能长于计算,但我并不,而且我并不喜欢在仅仅只需要移动游戏中的一个物体的时间处理这样的事情。

 

Holy Wars (圣战!?)

 

So, which mode is better? Game programmers have argued about this since the dawn of time, and will probably still be arguing when the universe implodes around us.

那么,那种方式更好呢?游戏程序员在时间诞生之初就在争论这个问题,而且可能到到宇宙大爆炸的时候他们还在争论。

 

As a rule of thumb, console programmers prefer fixed timesteps, while PC programmers usually go for variable timing mode. There are two main reasons for this disparity:

一条准则是这样的,控制台(指XBOX之类)程序员偏向固定时间间隔,而PC程序员通常考虑多种时间模式。有两个原因导致了这样的差异。

 

1.        Consoles are usually connected to a 60 hz television, while PC monitors can be set to many different refresh rates.

2.        The speed of a console is known ahead of time, so while games must handle single frames that take longer than expected, it is unlikely they will be consistently too slow for the machine they are running on. PC games must support a wider range of hardware, so it is more important for them to cope well with way-too-slow machines.

 

1.        控制台通常连接到60hz的电视,而PC的显示器能够被设置为多种不同的刷新率。

2.        控制台的速度通常是超前的。所以即使有时单帧花的时间比预期的长,但这样的情况在当前运行的控制台上通常并不会持续。相比而言,PC游戏需要支持更大范围的硬件,所以适配慢的的机器对PC游戏更加重要。

 

But there are no absolute rules. I've seen console games that worked well using variable timesteps, and I personally shipped a PC title that ran just fine with a 60 fps fixed timestep.

但在这一点上并没有绝对的规则,我见过控制台上的游戏在不同的时间间隔模式下运行得很好,而我个人组装的一台PC机也能够很好的以60fps在固定时间间隔模式下运行。

 

XNA defaults to fixed timestep mode because that is easier to program, but you are welcome to change this if you disagree with our choice.

XNA默认使用固定时间间隔模式因为这样更容易编写程序,但若你不同意我们的观点,欢迎你改变它。

 

 

Multiple Update Frequencies

多重更新频率

 

There is no law saying all parts of a game must update at the same frequency. For instance MotoGP combined three different update rates:

没有什么法则规定了游戏的所有部分都必须以同样的频率进行更新。做为一个例子,MotoGP结合了3中不同的更新频率:

 

1.        The main game logic ran at a fixed timestep 60 fps. This included input, sound, user interface logic, camera movement, rider animations, AI, and graphical effects.

2.        The physics update ran at a fixed timestep 120 fps (we just called it twice in a row from our main Update method). This provided a more accurate simulation, which was important for simulating vehicles moving at extremely high speeds and right on the edge of loosing traction.

3.        The network update ran at a fixed timestep anywhere between 4 and 30 fps, depending on how many players were in the game. The more players there were, the more data we had to send, so we adjusted by sending it less often to conserve bandwidth.

 

1.        主要的游戏逻辑在60fps的固定频率下运行。它包括了输入、声音、用户界面的逻辑、摄像头的移动、动画、AI,以及图形效果。

2.        物理更新运行在120fps的固定频率下运行(我们只是在主要的Update方法中两次调用它)。这样就提供了更精确的物理模拟。这对模拟高速运行的车辆有重要的意义。

3.        网络部分的更新则运行在4到30fps之间的固定频率中。具体的频率由游戏中的玩家数量决定。存在越多的玩家,就有更多需要发送的数据,所以我们降低更新频率以限制对带宽的需求。

 

Running some parts of your update logic less often than others is a great way to make games more efficient. For instance pathfinding and AI often only need to be updated a couple of times a second. Once you have found a good path or made the decision to attack, you can follow that decision without having to repeat the entire original calculation on each update.

 

以较低的频率更新逻辑的特定部分而以更高频率更新其他部分能够使游戏的效率更高。比如,路径选择和AI通常只需要在1秒中运行几次。一但你找到了一个好的路径或是决定了攻击的方式,就可以遵照决定来执行,而不需要在更新中重复全部的计算。

posted on 2013-04-19 18:55  竟飞工作室  阅读(411)  评论(0编辑  收藏  举报