• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            白云哥

            身披半件長工衣,懷揣一顆地主心

             

            Structuring the Main Loop

                看到CppBlog上翻譯的一篇游戲主循環(huán),想起之前也看到過一篇類似的文章,因?yàn)楣P記本上第一篇記錄就是這個(gè)主循環(huán)的簡短翻譯,對比了一下,發(fā)現(xiàn)這篇文章對實(shí)現(xiàn)細(xì)節(jié)的描述更多一些,也發(fā)上來與大家共享。

                這篇文章最早是出現(xiàn)在flipcode的論壇上,地址在這里,后來有人整理了一下,并添加了更多的描述,也就是下面的內(nèi)容。原貼地址在這里

                文章中關(guān)于網(wǎng)絡(luò)的描述是指局域網(wǎng)環(huán)境下的情況,另外這個(gè)主循環(huán)也沒有考慮到windows環(huán)境下與Windows Message Loop的結(jié)合,如果是應(yīng)用在windows環(huán)境下,可以再參考下這里,把兩者結(jié)合基本上就差不多了。

                翻譯并未嚴(yán)格遵照原文,為了讀起來流暢,很多句子都是按照我個(gè)人的理解來描述。

             

            This article is about a way of structuring a game's main loop. It includes techniques for handling view drawing with interpolation for smooth animation matched to the frame-rate with fixed-step game logic updating for deterministic game logic. A lot of this is still pretty much a work-in-progress as I muddle my way through, learning better ways of doing things or new tricks to add to my bag, so please bear with me.

            這篇文章描述的是如何組織游戲主循環(huán)的一種方法。內(nèi)容包括了如何處理平滑的動(dòng)畫繪制,并且能夠根據(jù)當(dāng)前幀率做正確的動(dòng)畫插值,另外還要保證固定的游戲邏輯更新幀率,以確保游戲邏輯的計(jì)算結(jié)果是確定的。

            這些內(nèi)容的實(shí)現(xiàn)有很多都還在進(jìn)行中,我也在不斷地學(xué)習(xí)更好的方法,以及將一些新的技巧添加進(jìn)來,所以,希望能夠給我一些耐心與寬容。

            The heart of a game, any game, is the game loop. This is where the action takes place, where the guns fire and the fireball spells fly. In some games, the concept of the game loop may be diffused among different components or game states, which implement their own version of the game loop to be exectued at the proper time, but the idea is still there.

            任何游戲的核心都是游戲主循環(huán)。游戲的動(dòng)作執(zhí)行、子彈的射擊以及火球魔法的飛行等等都是在這里實(shí)現(xiàn)。

            在一些游戲中,你可能會找不到一個(gè)唯一的游戲主循環(huán),取而代之的是,你會在一些組件及狀態(tài)機(jī)中找到各個(gè)不同版本的主循環(huán)。

            其實(shí)這個(gè)原理也是一樣的,只不過是把原來的一個(gè)主循環(huán)拆分成了多個(gè),這樣可以控制游戲在不同的時(shí)間及狀態(tài)下執(zhí)行不同的循環(huán)過程。

             

            The game loop is just that: a loop. It is a repeating sequence of steps or actions which are executed in a timely and (hopefully) efficient manner, parceling out CPU time to all of the myriad tasks the game engine is required to perform: logic, physics, animation, rendering, handling of input. It must be constructed in a deterministic, predictable fashion so as to give expected behavior on a wide array of hardware configurations. Classic failures in this regard include old pre-Pentium DOS-based games that were synchronized to run well on old hardware, but which did not have the controls in place to control the speed on newer hardware, and consequently ran so rapidly that they became unplayable on new hardware. With such a broad diversity of hardware as now exists, there must be tighter controls on the game loop to keep it running at a consistent speed, while still taking advantage of more powerful hardware to render smoother animation at higher framerates.

            簡單來說,游戲主循環(huán)就是一個(gè)循環(huán)過程。

            在這個(gè)不斷重復(fù)執(zhí)行的過程中,我們需要把CPU時(shí)間按照一定的規(guī)則分配到不同的任務(wù)上,這些任務(wù)包括:邏輯、物理、動(dòng)畫、渲染、輸入處理等等。

            游戲的主循環(huán)必須保證是以一種確定的、可預(yù)測的方式來執(zhí)行,這樣才能在大量的不同硬件配置環(huán)境下都得到我們所期望的相同行為。

            以前的DOS游戲曾經(jīng)出現(xiàn)過這樣的問題,它們在舊的硬件上運(yùn)行的很好,但是放到新的硬件上以后就失去控制了,游戲的運(yùn)行速度變的如此之快,以至于根本無法去玩它。

            現(xiàn)在市場上存在這么多的硬件種類,所以就必須要緊緊地控制住游戲的主循環(huán),保證他們以一個(gè)固定的速度運(yùn)行,但同時(shí)又能獲得這些強(qiáng)大的硬件所帶來的好處:以盡可能高的幀率來渲染出更平滑的動(dòng)畫。

             

            Older games frequently tied the rendering of the view very closely to the game loop, drawing the view exactly once per logic update and waiting for a signal from the display system indicating a vertical retrace period, when the electron gun in the CRT monitor was resetting after drawing the screen. This synchronized loops to a predictable rate based on the refresh rate of the monitor, but with the advent of customizable refresh settings this leads again to unpredictable loop behavior. Retrace synchronization is still useful, especially to avoid visual artifacts when rendering the view, but is less useful as a means for synchronizing the game logic updating, which may require finer control.

            以前的游戲經(jīng)常將渲染過程與游戲循環(huán)緊密地綁在一起,首先執(zhí)行一次邏輯更新,然后繪制畫面,接著等待顯示系統(tǒng)觸發(fā)一個(gè)垂直同步信號,之后就是下一輪循環(huán)周期:邏輯更新、渲染、等待……周而復(fù)始。

            這種同步的循環(huán)方法在顯示器的刷新率可預(yù)測的情況下是有效的,但是當(dāng)可以自定義刷新率以后,這種行為又變得不可預(yù)測了。

            垂直同步仍然是有用的,尤其是在避免畫面的渲染出現(xiàn)撕裂的情況下,但是用來同步游戲的邏輯更新就沒多大用了,這時(shí)可能需要更好的控制方法。

             

            The trick, then, is to separate game logic from rendering, and perform them in two separate sub-systems only marginally tied to each other. The game logic updates at it's own pace, and the rendering code draws the screen as fast as it possibly with the most accurate, up-to-date data the logic component can provide.

            這種方法就是將游戲邏輯更新與屏幕渲染過程分離開,將他們放到兩個(gè)分離的子系統(tǒng)中去處理,只是在需要的時(shí)候才與另一個(gè)打交道。

            游戲的邏輯更新嚴(yán)格按照計(jì)劃來執(zhí)行,但是屏幕渲染則以它所能達(dá)到的最大速率來進(jìn)行。

             

            The system I am accustomed to using is based on a Tip of the Day ( http://www.flipcode.com/cgi-bin/msg.cgi?showThread=Tip-MainLoopTimeSteps&forum=totd&id=-1 ) posted to http://www.flipcode.com by Javier Arevalo. It implements a loop wherein the game logic is set to update a fixed number of times per second, while the rendering code is allowed to draw as rapidly as possible, using interpolation to smooth the transition from one visual frame to the next.

            這里描述的方法基于Javier Arevalo發(fā)表在flipcode Tip of the Day 上的代碼來實(shí)現(xiàn)。

            在這個(gè)游戲主循環(huán)里,游戲邏輯更新被設(shè)置為每秒執(zhí)行固定次數(shù),但同時(shí)渲染代碼被允許執(zhí)行盡可能多次,并且還使用了插值來使得兩個(gè)渲染幀之間的動(dòng)畫變化盡可能的平滑。

             

            Briefly, here is the code. I will then attempt in my own crude fashion to explain the workings, though I suggest you check out the original tip at the above link to read Javier's explanation, as well as the forum posts accompanying it which offer up insights and suggestions on how the performance of the loop may be improved.

            閑話少說,下面首先是代碼,然后我會簡單的按我的方式描述一下代碼的工作原理,同時(shí)我建議你閱讀一下上面鏈接地址所給出的內(nèi)容,其中有Javier的解釋,并且論壇上的回貼也有一些不錯(cuò)的內(nèi)容,包括別人的評論和一些關(guān)于如何提高效率的建議。

            (注:flipcode論壇早就已經(jīng)關(guān)閉,上面的鏈接地址已經(jīng)失效,flipcode上只保留有一些優(yōu)秀內(nèi)容的archives在這里能找到這篇原文,其中包括Javier的解釋)

             

            time0 = getTickCount();
            do
            {
              time1 = getTickCount();
              frameTime = 0;
              int numLoops = 0;
            
              while ((time1 - time0) > TICK_TIME && numLoops < MAX_LOOPS)
              {
                GameTickRun();
                time0 += TICK_TIME;
                frameTime += TICK_TIME;
                numLoops++;
              }
              IndependentTickRun(frameTime);
            
              // If playing solo and game logic takes way too long, discard pending time.
              if (!bNetworkGame && (time1 - time0) > TICK_TIME)
                time0 = time1 - TICK_TIME;
            
              if (canRender)
              {
                // Account for numLoops overflow causing percent > 1.
                float percentWithinTick = Min(1.f, float(time1 - time0)/TICK_TIME);
                GameDrawWithInterpolation(percentWithinTick);
              }
            }
            while (!bGameDone);
             

            Structurally, the loop is very simple. The above snippet of code can encapsulate the entire workings of your game.

            從結(jié)構(gòu)上來說,這個(gè)主循環(huán)非常簡單。上面的代碼片段基本上能夠囊括你的游戲的整個(gè)工作過程。

             

            First of all, the main loop portion is embodied as the do{} while(!bGameDone); block. This causes the loop to run endlessly, until some game condition indicates that it is finished and it is time to exit the program, at which point the loop ends and the game can be properly shut down. Each time through the loop, we perform game logic updates, input updating and handling, and rendering. Now, for a breakdown of the sections of the loop.

            首先,主循環(huán)的執(zhí)行過程被包在do…while循環(huán)塊中,這使得游戲主循環(huán)將永不結(jié)束地運(yùn)行,直到游戲明確地被告知需要結(jié)束并且退出程序時(shí)為止。

            在每次進(jìn)入循環(huán)的時(shí)候,我們會執(zhí)行游戲邏輯的更新,輸入更新與處理,還有渲染。接下來,把循環(huán)分為幾個(gè)片段來描述。

             

            time1 = getTickCount();
            frameTime = 0;
            int numLoops = 0;
            
            while ((time1 - time0) > TICK_TIME && numLoops < MAX_LOOPS)
            {
                GameTickRun();
                time0 += TICK_TIME;
                frameTime += TICK_TIME;
                numLoops++;
            }
             

            This portion is the game logic update sequence that forces the game logic (physics updates, object motion, animation cycling, etc...) to update a set number of times per second. This rate is controlled by the TICK_TIME constant, which specifies the number of milliseconds the logic update is supposed to represent in real time. It probably won't take that long to perform, in which case the update won't be performed again until enough time has passed. For example, with TICK_TIME=40, each logic represents 40 milliseconds, thus forcing the code to update game objects at a rate of 25 times per second.

            這部分代碼處理游戲邏輯的更新,并且強(qiáng)制要求游戲邏輯每秒執(zhí)行固定次數(shù)。

            游戲的邏輯處理包括物理更新、對象行為、動(dòng)畫循環(huán)等等。更新速率通過TICK_TIME常量指定,其含義為兩次邏輯更新的時(shí)間間隔,即經(jīng)過TICK_TIME后應(yīng)該再次進(jìn)行更新,而不是說一次邏輯更新要持續(xù)這么長時(shí)間。

            例如,TICK_TIME = 40,其含義為每次游戲邏輯更新后表現(xiàn)40毫秒,這將使得每秒游戲?qū)ο髸桓?5次。

             

            The logic is encapsulated in it's own while loop. It is possible during a given frame that game logic will take too long. If a logic update cycle goes overtime, we can delay rendering for a bit to give ourselves a little extra time to catch up. The while loop will continue to process game logic updates until we are no longer overtime, at which point we can go ahead and continue on with drawing the view. This is good for handling the occasional burp in logic updating, smoothing out the updates and keeping them consistent and deterministic; but in the case that the logic repeatedly goes overtime, it is possible to accumulate more time-debt than the loop can handle. Thus, the MAX_LOOPS constant is in place to dictate a maximum number of times the loop can repeat to try to catch up. It will force the loop to dump out periodically to handle other tasks such as input handling and rendering. A loop that is constantly running at the MAX_LOOPS limit runs like hell and is about as responsive as a tractor with four flat tires, but at least it keeps the loop operating, allowing the user to pass input to terminate the program. Without the MAX_LOOPS failsafe, it would be entirely possible for a slow computer to lock up with no way to exit as it chokes on the loop, trying to catch up but getting further and further behind.

            邏輯更新的代碼被包在他自己的while循環(huán)中。

            可能在某一幀里游戲邏輯的處理時(shí)間會非常長,甚至?xí)瑫r(shí),這時(shí)我們可以稍稍暫停一下屏幕的渲染,使得游戲邏輯更新能夠在這段時(shí)間里趕上來。

            這個(gè)while循環(huán)就是用來讓游戲邏輯繼續(xù)更新,直到我們不再超時(shí),之后我們繼續(xù)游戲的主循環(huán)并且進(jìn)行屏幕渲染。這可以很好地處理突發(fā)的邏輯更新超時(shí),使得我們的更新更加平滑,并保證邏輯的一致性和可預(yù)測性。

            但是如果游戲邏輯處理持續(xù)地超時(shí),甚至使得我們的主循環(huán)無法處理過來,這時(shí)MAX_LOOPS將會起到作用,他將使游戲控制權(quán)從邏輯更新中跳出來,去執(zhí)行如輸入處理和屏幕渲染等其他任務(wù)。

            MAX_LOOP常量限制了這里的while循環(huán)用來追趕邏輯處理時(shí)間時(shí)最多能重復(fù)的次數(shù)。這樣當(dāng)游戲真的出現(xiàn)完全無法處理完邏輯更新的時(shí)候,用戶也有機(jī)會結(jié)束程序。

            如果沒有MAX_LOOP的檢測,很有可能會出現(xiàn)一臺很慢的電腦試圖追上邏輯處理時(shí)間,但是卻越追越遠(yuǎn),而又沒有機(jī)會退出這個(gè)過程,最后陷在了這個(gè)死循環(huán)中……

             

            IndependentTickRun(frameTime);
             

            This section is where input is gathered, events are pumped from the event queue, interface elements such as life bars are updated, and so forth. Javier allows for passing how much time the logic updates took, which can be used for updating on-screen clock or timer displays and the like if necessary. I've never found occasion to use it, but I regularly pass it anyway on the off-chance I'll need it someday. frameTime basically gives the amount of time that was spent in the logic loop performing updates.

            這部分代碼用來處理用戶輸入的捕獲,事件會從事件隊(duì)列中被取出來處理,界面元素,如血條等,會在這里被更新,還有其他類似的處理。

            Javier在這里留了一個(gè)frameTime參數(shù),用來指明邏輯更新所花的時(shí)間。可以用這個(gè)值來更新游戲屏幕上的時(shí)鐘或定時(shí)器等類似信息。

            雖然我目前還沒有找到機(jī)會用它,但我還是習(xí)慣性地把它帶上了,也許某天我會需要。

             

            In order for the game loop to run predictably, you should not modify anything in this step that will affect the game logic. This portion of the loop does not run at a predictable rate, so changes made here can throw off the timing. Only things that are not time-critical should be updated here.

            為了使游戲循環(huán)的運(yùn)行是可預(yù)測的,你不應(yīng)該在這里修改任何可能影響游戲邏輯的東西,因?yàn)榇瞬糠衷谟螒蛑餮h(huán)中的執(zhí)行次數(shù)是不可預(yù)測的,所以在這里只能做一些時(shí)間無關(guān)的更新。

             

            // If playing solo and game logic takes way too long, discard pending time.
            if (!bNetworkGame && (time1 - time0) > TICK_TIME) time0 = time1 - TICK_TIME;
             

            This is where we can shave off our time debt if MAX_LOOPS causes us to dump out of the logic loop, and get things square again--as long as we are not running in a network game. If it is a single-player game, the occasional error in the timing of the game is not that big of a deal, so sometimes it might be simpler when the game logic overruns it's alloted time to just discard the extra time debt and start fresh. Technically, this makes the game "fall behind" where it should be in real time, but in a single player game this has no real effect. In a network game, however, all computers must be kept in synchronization, so we can't just cavalierly discard the pending time. Instead, it sticks around until the next time we enter the logic update loop, where the loop has to repeat itself that many more times to try to catch up. If the time burp is an isolated instance, this is no big deal, as with one or two cycle overruns the loop can catch up. But, again, if the logic is consistently running overtime, the performance of the game will be poor and will lag farther and farther behind. In a networked game, you might want to check for repeated bad performance and logic loop overruns here, to pinpoint slow computers that may be bogging the game down and possibly kick them from the game.

            當(dāng)由于滿足了MAX_LOOP條件而跳出邏輯循環(huán)時(shí),可以在這里減掉多加的上TICK_TIME時(shí)間,并且只在單機(jī)游戲時(shí)才這樣做。

            如果是在單機(jī)游戲中,偶爾出現(xiàn)時(shí)間上的錯(cuò)誤是不會有什么大問題的,所以當(dāng)游戲邏輯執(zhí)行超時(shí)后也沒有多大關(guān)系,我們簡單的把多花的時(shí)間減掉,然后重新開始。從技術(shù)上來說,這會使得游戲有一點(diǎn)點(diǎn)時(shí)間上的落后,但在單機(jī)游戲里這不會有什么實(shí)際的影響。

            但是在網(wǎng)絡(luò)游戲中這樣做卻不行,所有連網(wǎng)的電腦都必須要保持時(shí)間上的同步。

            在網(wǎng)絡(luò)游戲中,如果某臺電腦落后了,他應(yīng)該保持其落后的狀態(tài),然后在下一次進(jìn)入邏輯更新循環(huán)時(shí),自己讓自己多重復(fù)幾次,以便追趕上來。

            如果時(shí)間延遲只是個(gè)孤立事件,這將不會有多大問題,經(jīng)過一兩次超速就會追趕上來,但是如果游戲邏輯更新總是超時(shí),游戲的表現(xiàn)將會非常糟糕,并且最終將會越來越滯后。

            在網(wǎng)絡(luò)游戲中,你可能需要檢查出這些持續(xù)超時(shí),表現(xiàn)總是很差的電腦,并將這些可能拖慢整個(gè)游戲環(huán)境的電腦踢出去。

             

            // Account for numLoops overflow causing percent > 1.
            float percentWithinTick = Min(1.f, float(time1 - time0)/TICK_TIME);
            GameDrawWithInterpolation(percentWithinTick);
             

            This is where the real magic happens, in my opinion. This section performs the rendering of the view. In the case of a loop where TICK_TIME=40, the logic is updating at 25 FPS. However, most video cards today are capable of far greater framerates, so it makes no sense to cripple the visual framerate by locking it to 25 FPS. Instead, we can structure our code so that we can smoothly interpolate from one logic state to the next. percentWithinTick is calculated as a floating point value in the range of [0,1], representing how far conceptually we are into the next game logic tick.

            在這里我們將進(jìn)行屏幕繪制。

            當(dāng)TICK_TIME = 40時(shí),邏輯更新幀率為25FPS,但是現(xiàn)在大多數(shù)顯卡都能支持更高的渲染幀率,所以我們沒必要反而鎖定渲染幀率為25FPS。

            我們可以組織我們的代碼,讓我們能夠平滑的從一個(gè)邏輯狀態(tài)到下一個(gè)狀態(tài)做插值。percentWithinTick為一個(gè)0到1之間的浮點(diǎn)數(shù),表示我們應(yīng)該向下一個(gè)邏輯幀走多遠(yuǎn)。

             

            Every object in the game has certain state regarding LastTick and NextTick. Each object that can move will have a LastPosition and a NextPosition. When the logic section updates the object, then the objects LastPosition is set to it's currentNextPostion and a new NextPosition is calculated based on how far it can move in one logic frame. Then, when the rendering portion executes, percentWithinTick is used to interpolate between these Last and Next positions:

            每個(gè)可移動(dòng)的對象都有LastPosition和NextPosition。邏輯更新部分的代碼在執(zhí)行時(shí)會將對象的LastPosition設(shè)置為當(dāng)前的NextPosition,再根據(jù)它每一幀所能移動(dòng)的距離來計(jì)算出NextPosition。

            然后,在渲染對象的時(shí)候,可以再用percentWithTick來在Last和Next位置之間進(jìn)行插值。

            譯注:

            time0為最后一個(gè)邏輯幀所在的時(shí)間點(diǎn),time1為當(dāng)前實(shí)際時(shí)間,(time1 – time0) / TICK_TIME表示當(dāng)前時(shí)間比最后一個(gè)邏輯幀所在時(shí)間超出了多少百分比,用這個(gè)百分比來向下一邏輯幀做插值。

            如果機(jī)器比較快,在兩個(gè)邏輯更新時(shí)間點(diǎn)之間執(zhí)行了多次屏幕渲染,這樣插值就有效了。此時(shí)time0不變,time1一直在增長,可以根據(jù)增長的百分比來計(jì)算動(dòng)畫應(yīng)該從最后一次執(zhí)行邏輯更新的位置向下一次邏輯更新所在的位置走多遠(yuǎn)。

            也就是上文所說的,在Last和Next位置之間進(jìn)行插值。插值后的實(shí)際位置,即DrawPosition的計(jì)算方法如下:

            動(dòng)畫播放的插值也是類似。

             

            DrawPosition = LastPosition + percentWithinTick * (NextPosition - LastPosition);
             

            The main loop executes over and over as fast as the computer is able to run it, and for a lot of the time we will not be performing logic updates. During these loop cycles when no logic is performed, we can still draw, and as the loop progresses closer to the time of our next logic update, percentWithinTick will increase toward 1. The closer percentWithinTick gets to 1, the closer the a ctual DrawPosition of the object will get to NextPosition. The effect is that the object smoothly moves from Last to Next on the screen, the animation as smooth as the hardware framerate will allow. Without this smooth interpolation, the object would move in a jerk from LastPosition to NextPosition, each time the logic updates at 25FPS. So a lot of drawing cycles would be wasted repeatedly drawing the same exact image over and over, and the animation would be locked to a 25FPS rate that would look bad.

            游戲的主循環(huán)以電腦所能夠達(dá)到的最大速度不停地運(yùn)行,在大多數(shù)時(shí)間里,我們將不需要執(zhí)行邏輯更新。

            但是在這些不執(zhí)行邏輯更新的周期里,我們?nèi)匀豢梢詧?zhí)行屏幕渲染。當(dāng)循環(huán)的進(jìn)程越接近我們的下一次邏輯更新時(shí)間,percentWithinTick就接近1;percentWithinTick越接近1,DrawPosition的位置就越接近NextPosition。

            最后的效果就是游戲?qū)ο笤谄聊簧下摹⑵交貜腖ast位置移動(dòng)到Next位置,動(dòng)作將以硬件幀率所能允許的程度盡可能的平滑。

            如果沒有這個(gè)平滑插值過程,對象位置將會從上一幀所在的LastPosition直接跳到下一幀所在的位置NextPosition。大量的渲染周期都被浪費(fèi)在把對象反復(fù)渲染到相同的位置上,并且動(dòng)畫也只能被鎖定在25FPS,使得看起來效果非常差。

             

            With this technique, logic and drawing are separated. It is possible to perform updates as infrequently as 14 or 15 times per second, far below the threshold necessary for smooth, decent looking visual framerate, yet still maintain the smooth framerate due to interpolation. Low logic update rates such as this are common in games such as real-time strategy games, where logic can eat up a lot of time in pathfinding and AI calculations that would choke a higher rate. Yet, the game will still animate smoothly from logic state to logic state, without the annoying visual hitches of a 15FPS visual framerate. Pretty danged nifty, I must say.

            使用了這項(xiàng)技術(shù)之后,邏輯更新與屏幕渲染被分離開了。

            這將允許我們把邏輯更新幀率降低到14或15FPS,這遠(yuǎn)遠(yuǎn)低于平滑的動(dòng)畫渲染所需要的幀率,但是在使用動(dòng)畫插值之后卻仍然能維持平滑的渲染幀率。

            在實(shí)時(shí)策略類游戲中可能會使用這樣低的邏輯更新幀率,這里邏輯更新會因?qū)ぢ泛虯I計(jì)算而占用大量的時(shí)間,在這種情況下使用高的邏輯更新幀率顯然不行。

            但是,游戲卻仍然能夠在不同的邏輯狀態(tài)之間做平滑的動(dòng)畫過渡,不會因?yàn)橹挥?5FPS的渲染幀率而出現(xiàn)那些令人生厭的動(dòng)畫跳躍現(xiàn)象。

            非常的漂亮,我不得不說。

             

            I've created a simple program in C to demonstrate this loop structure. It requires SDL ( http://www.libsdl.org ) and OpenGL. It's a basic bouncy ball program. The controls are simple: Press q to exit the program or press SPACE to toggle interpolation on and off. With interpolation on, the loop executes exactly as described to smooth out the movement from one logic update to the next. With interpolation off, the interpolation factor percentWithinTick is always set to 1 to simulate drawing without interpolation, in essence locking the visual framerate to the 25FPS of the logic update section. In both cases, the ball moves at exactly the same speed (16 units per update, 25 updates per second), but with interpolation the motion is much smoother and easier on the eyes. Compile and link with SDL and OpenGL to see it in action: http://legion.gibbering.net/golem/files/interp_demo.c

            posted on 2009-09-14 20:29 白云哥 閱讀(2750) 評論(2)  編輯 收藏 引用 所屬分類: GameDev

            評論

            # re: Structuring the Main Loop 2009-09-14 23:32 post

            Practical UML Statecharts in C/C++, Second Edition:Event-Driven Programming for Embedded Systems  回復(fù)  更多評論   

            # re: Structuring the Main Loop 2009-09-16 15:28 喜樂遞

            上島咖啡書店艱苦奮斗是  回復(fù)  更多評論   

            導(dǎo)航

            統(tǒng)計(jì)

            常用鏈接

            留言簿(4)

            隨筆分類

            隨筆檔案

            相冊

            我的鏈接

            搜索

            最新評論

            閱讀排行榜

            評論排行榜

            婷婷综合久久中文字幕蜜桃三电影| 久久精品国产影库免费看| 欧美性大战久久久久久| 狠狠色丁香久久婷婷综合蜜芽五月| 久久婷婷五月综合97色直播| 久久久久久人妻无码| 久久99国产一区二区三区| 人妻少妇久久中文字幕 | 久久天天躁狠狠躁夜夜96流白浆 | 久久亚洲私人国产精品vA| 大伊人青草狠狠久久| 污污内射久久一区二区欧美日韩| 色偷偷偷久久伊人大杳蕉| 国产A级毛片久久久精品毛片| 丁香色欲久久久久久综合网| 亚洲国产精品久久久久婷婷软件| 久久久精品久久久久影院| 国产精品嫩草影院久久| 精品久久一区二区三区| 久久天天躁夜夜躁狠狠躁2022| 精品久久久久中文字幕一区| AAA级久久久精品无码片| 99蜜桃臀久久久欧美精品网站| 国产亚洲精午夜久久久久久| 成人久久久观看免费毛片| 亚洲人成网亚洲欧洲无码久久| 久久涩综合| 久久精品成人免费观看97| 97超级碰碰碰碰久久久久| 国产精品久久影院| 97久久精品无码一区二区| 国产精品无码久久综合| 久久久久人妻精品一区二区三区| 狠狠综合久久综合88亚洲| 久久婷婷国产剧情内射白浆| 久久婷婷色综合一区二区| 综合久久给合久久狠狠狠97色| 欧美成a人片免费看久久| 久久亚洲精品无码播放| 久久久久人妻一区精品| 久久午夜免费视频|