游戏编程设计模式——Game Loop
意圖
將游戲時(shí)間的進(jìn)度從玩家輸入和處理器速度中分離出來。
動(dòng)機(jī)
如果讓我選一個(gè)本書最不能少的模式,那就是這個(gè)。游戲循環(huán)是游戲編程模式中最精髓的一個(gè)例子。幾乎所有的游戲都會(huì)有它,再也沒有第二個(gè)應(yīng)用如此廣泛的。但是在游戲之外有很少用到。
要看它是如何起作用的,讓我們把記憶拉回到過去。在哪個(gè)編程跟洗碗工一樣的青蔥歲月。你需要把一堆代碼塞進(jìn)機(jī)器,按下按鈕,等待,然后出結(jié)果。這就是批次模式的編程——一旦工作完成,程序就停止運(yùn)行了。
你今天仍然能夠看到,雖然謝天謝地我們不要在卡片上寫代碼了。Shell腳本,命令行程序,甚至是那些本書中的簡化小Python腳本,都是批次模式的編程。
采訪CPU
最后,當(dāng)程序員們發(fā)現(xiàn)必須先把一堆代碼放到計(jì)算辦公司,然后回來等幾個(gè)小時(shí),才能拿到結(jié)果。這改起bug來就會(huì)非常慢。他們希望能夠得到及時(shí)的反饋。交互式編程誕生了,一些最早的交互式程序,就是游戲:
你就可以跟程序?qū)崟r(shí)進(jìn)行交流。它會(huì)等待你的輸入,然后響應(yīng)你。你可以再回復(fù),輪流進(jìn)行就像你在幼兒園學(xué)到的那樣。輪到你的時(shí)候,它就靜靜地坐在那里,一動(dòng)不動(dòng)。就像這樣:
事件循環(huán)
當(dāng)今的圖形化UI應(yīng)用很像以前的冒險(xiǎn)游戲,當(dāng)你取下他們的皮膚就會(huì)發(fā)現(xiàn)這一點(diǎn)。你的word處理器通常也是直到你按鍵或者點(diǎn)擊時(shí)才開始做事。
不同的地方只有它不用文本命令行,而是用用戶的輸入事件——鼠標(biāo)點(diǎn)擊或者按鍵。它仍然像文字冒險(xiǎn)游戲那樣等待用戶輸入。
不像這些通用軟件,游戲在用戶不提供輸入的時(shí)候也要進(jìn)行下去。如果你就坐在屏幕前發(fā)呆,游戲也不會(huì)停止。動(dòng)畫依然會(huì)播放,特效依然會(huì)舞動(dòng)。如果你不幸運(yùn),怪物有可能就開始咬你的英雄了。
這就是游戲循環(huán)的第一個(gè)關(guān)鍵點(diǎn):它處理用戶輸入,但是并不等待輸入。循環(huán)照樣運(yùn)行。
我們后面會(huì)繼續(xù)完善,但是基本的元素都有了。processInput()處理了從上一次調(diào)用以來的所有用戶輸入。然后,update()進(jìn)行一步游戲模擬。運(yùn)行AI和物理(通常按照這個(gè)順序)。最后,render()繪制一遍游戲,讓玩家能看到發(fā)生了什么。
延遲的世界
如果輸入操作不打斷游戲循環(huán),那一個(gè)明顯的問題就會(huì)冒出來:循環(huán)運(yùn)行的有多快?每一次循環(huán)都要處理游戲中大量的狀態(tài)。我們可以看到游戲世界中的居民,也可以看到他們鐘表的指針滴答向前。
與此同時(shí),玩家的時(shí)鐘也在在跑。如果要衡量游戲循環(huán)的快慢,我們可以用“每秒幀數(shù)”這個(gè)概念。如果游戲循環(huán)快,FPS就會(huì)很高,游戲也會(huì)更流暢,更快速。如果游戲循環(huán)慢,游戲就會(huì)卡得像幻燈片。
在我們現(xiàn)有的簡陋循環(huán)中,我們讓它有多快,跑多快,兩個(gè)方面決定幀率。第一個(gè)是每幀它需要干多少工作。復(fù)雜的物理運(yùn)算,大量的游戲?qū)ο?#xff0c;大量的圖形細(xì)節(jié)會(huì)讓你的CPU和GPU異常忙碌,因此,它會(huì)讓完成一幀的時(shí)間更長。
第二個(gè)因素是底層平臺(tái)的速度。更快的芯片可以在相同時(shí)間內(nèi)運(yùn)行更多的代碼。多核心,多GPU,專業(yè)音頻硬件,和操作系統(tǒng)的調(diào)度都決定了以在一個(gè)時(shí)間片中能做多少工作。
時(shí)間縮漲
在早期的視頻游戲中,游戲循環(huán)的時(shí)間間隔是固定的。如果你要寫一個(gè)NES或者Apple IIe的游戲,你會(huì)很清楚地知道你的游戲會(huì)運(yùn)行在什么樣的CPU上。所有你所擔(dān)心的就只剩下每一個(gè)時(shí)間片要處理多少工作。
老游戲都很小心地編寫代碼,好讓計(jì)算機(jī)在每一幀處理正好足夠的運(yùn)算,好讓游戲能夠以開發(fā)者希望的速度運(yùn)行。但是如果這個(gè)游戲在更快或者更慢的機(jī)器上跑,那游戲就會(huì)加速或者減速。
現(xiàn)在,很少有開發(fā)者能夠清楚地知道自己的游戲?qū)?huì)運(yùn)行在什么樣的硬件上。相反,我們的游戲必須適應(yīng)千變?nèi)f化的設(shè)備。
這就是游戲循環(huán)的另外一個(gè)關(guān)鍵點(diǎn):它讓游戲在不同的底層硬件上也能以相同的速度運(yùn)行。
模式
游戲循環(huán)在整個(gè)游戲過程中在不停地運(yùn)行著,每一次循環(huán),它都不停頓地處理用戶輸入,更新游戲狀態(tài),渲染游戲。它通過切分一小段時(shí)間,來控制游戲速度。
何時(shí)用
設(shè)計(jì)模式要是用錯(cuò)了,還不如不用,所以這個(gè)部分有必要提一個(gè)醒。設(shè)計(jì)模式的目的不是讓你在代碼中強(qiáng)行插入。
但是這個(gè)模式有一點(diǎn)不同。我們?cè)谑褂玫臅r(shí)候可以非常自信。如果你在使用游戲引擎,即使不自己寫,它也會(huì)被用到。
你可能認(rèn)為在回合制游戲中可以不用游戲循環(huán)。但是即使玩家不輸入的時(shí)候游戲不進(jìn)行,但視覺和聽覺是不斷變化的。動(dòng)畫和音樂在你等待回合的時(shí)候也是在運(yùn)行的。
記住
我們?cè)谶@里討論的是游戲中最重要的代碼。有人說程序90%的時(shí)間在跑10%的代碼。游戲循環(huán)一定在這10%里面。小心處理這些代碼,并時(shí)刻注意它們的效率。
你可能需要協(xié)調(diào)與平臺(tái)事件循環(huán)的關(guān)系
如果你要在一個(gè)具有內(nèi)置圖形UI和事件循環(huán)的操作系統(tǒng)上開發(fā)游戲,就會(huì)有兩天循環(huán)運(yùn)行在游戲中,你需要把它們用好。
有時(shí),你可以只用你自己的循環(huán)。例如,如果你在用Windows API編寫游戲,你的main()函數(shù)可以創(chuàng)建一個(gè)游戲循環(huán)。在里面你,你可以調(diào)用PeekMessage()去處理和分發(fā)操作系統(tǒng)的事件。不像GetMessage(),PeekMessage()不能中會(huì)中斷等待用戶輸入,所以你的游戲循環(huán)可以持續(xù)運(yùn)行下去。
有的平臺(tái),你不能這么容易的操作事件循環(huán)。如果你的目標(biāo)平臺(tái)是瀏覽器,瀏覽器事件循環(huán)的實(shí)現(xiàn)被深埋在瀏覽器的內(nèi)核中。因此,事件循環(huán)就會(huì)顯式的運(yùn)行,你就應(yīng)該把它當(dāng)做你自己的游戲循環(huán)使用了。你可以調(diào)用像requestAnimationFrame()這樣的函數(shù),去驅(qū)動(dòng)你的游戲運(yùn)行。
簡單的代碼
經(jīng)過這么長的介紹,游戲循環(huán)的代碼就已經(jīng)很清楚了。我們來看幾種變化,并分析他們的優(yōu)缺點(diǎn)。
游戲循環(huán)驅(qū)動(dòng)了AI,渲染,和其他游戲系統(tǒng),但是這些并不屬于這個(gè)模式,所以我們這里只寫一個(gè)簡單的函數(shù)調(diào)用。像render(),update()以及其他函數(shù)的實(shí)現(xiàn),就當(dāng)做讀者的自己的練習(xí)吧。
能跑多快跑多快
我們已經(jīng)看到一個(gè)最簡單的游戲循環(huán):
這樣做的問題在于,你不能控制游戲能跑多快。在快機(jī)器上,循環(huán)運(yùn)行的飛快,以至于玩家都反應(yīng)不過來發(fā)生了什么。在滿機(jī)器上,游戲就會(huì)卡頓。如果你有一些很重的內(nèi)容,或者有AI和物理,游戲就會(huì)運(yùn)行的很慢。
打一個(gè)小盹
第一個(gè)變化我們來看一個(gè)簡單的解決辦法。如果你希望游戲運(yùn)行在60FPS。也就是給每一幀大約16毫秒的時(shí)間。只要你的游戲處理和渲染在小于這個(gè)時(shí)間的時(shí)間內(nèi)完成,你可以得到一個(gè)穩(wěn)定的幀率。你只需要處理這一幀,然后等待,直到開始下一幀,就像這樣:
?
代碼如下:
這里的sleep()保證了即使計(jì)算機(jī)處理的很快,也不會(huì)導(dǎo)致游戲運(yùn)行的更快。但是如果游戲運(yùn)行的太慢,就沒啥用了。如果你更新和渲染一幀用時(shí)超過16毫秒,你的sleep()函數(shù)就會(huì)幫倒忙。如果我們用的是穿越回來的電腦,一切問題都解決了,可惜事與愿違。
相反,如果游戲太慢了,我們需要減少每一幀要做的工作——砍掉一些圖形效果,減弱AI。但是這回影響所有玩家的游戲體驗(yàn),包括那些擁有高端機(jī)器的人。
一小步,一大步
讓我們?cè)囈幌赂鼜?fù)雜的辦法。這個(gè)問題我們可以簡化成:
1、每一次更新把游戲時(shí)間前進(jìn)了一個(gè)固定的時(shí)間段。
2、處理游戲運(yùn)算的時(shí)候,實(shí)際上使用了一段固定的真實(shí)時(shí)間。
當(dāng)后者用時(shí)超過前者時(shí),游戲就被放慢了。如果我們用多于16ms的時(shí)間去處理應(yīng)該在16ms內(nèi)完成的事情,那就不可能保持應(yīng)有的游戲速度。但是如果我們讓游戲前進(jìn)多于16ms的進(jìn)度,那就可以保持游戲速度,只不過刷新頻率變慢了而已。
這個(gè)想法就要去確定一個(gè)時(shí)間段,它代表了從上一幀到現(xiàn)在經(jīng)過了多少真實(shí)時(shí)間。這段時(shí)間越長,游戲進(jìn)度的步子就越大。它總會(huì)與真實(shí)時(shí)間相匹配,因?yàn)樗鼤?huì)一點(diǎn)點(diǎn)趨近于真實(shí)時(shí)間。我們把這種方法叫做可變的或者動(dòng)態(tài)時(shí)間段。就像這樣:
每一幀,我們都會(huì)計(jì)算從上一幀開始我們耗費(fèi)了多少真實(shí)時(shí)間。當(dāng)我們更新游戲狀態(tài)的時(shí)候,我們把這個(gè)時(shí)間穿進(jìn)去。游戲引擎會(huì)根據(jù)這個(gè)時(shí)間去調(diào)整游戲世界的進(jìn)度。
比如,你有一顆穿過屏幕的子彈。在固定的時(shí)間段內(nèi),每一幀,你可以根據(jù)他的速度移動(dòng)它。在一個(gè)變化的時(shí)間段內(nèi),你可以根據(jù)這個(gè)時(shí)間段去縮放他的速度。隨著時(shí)間段的變大,每一幀它走過的路程也就越長。這顆子彈將會(huì)在相同的真實(shí)時(shí)間內(nèi)穿越屏幕。無論它在一個(gè)兩倍快的機(jī)器上,還是在四倍慢的機(jī)器上。這樣看起來已經(jīng)勝利了:
在不同的機(jī)器上,游戲運(yùn)行的進(jìn)度相同。
機(jī)器更快的玩家,可以得到更流暢的游戲體驗(yàn)。
但是,這里有一個(gè)潛在的問題:我們給游戲增加了不確定性和不穩(wěn)定性,我這里有一個(gè)例子:
比如,我又一個(gè)雙人聯(lián)網(wǎng)對(duì)戰(zhàn)的游戲,弗雷德有一臺(tái)牛逼游戲計(jì)算機(jī),但喬治用了一臺(tái)古老的PC。前面說的那顆子彈,在他們的屏幕上穿越。在弗雷德的機(jī)器上,游戲運(yùn)行的很快,所以每一幀時(shí)間都很短。比如,子彈穿越屏幕用了50幀。但在可憐的喬治的機(jī)器上,可能只有5幀。
這就意味著,在弗雷德的機(jī)器上,物理引擎更新了子彈的位置50次,但在喬治的機(jī)器上只進(jìn)行了5次。大多數(shù)游戲使用了浮點(diǎn)數(shù),他們會(huì)出現(xiàn)化整誤差。每次你把兩個(gè)浮點(diǎn)數(shù)相加,得到的結(jié)果可能不同。弗雷德的機(jī)器多進(jìn)行了十倍的運(yùn)算,游戲賬號(hào)買賣所以他可能會(huì)有比喬治更大的誤差。所以同樣一顆子彈可能停在不同的地方。
這只是其中一個(gè)比較嚴(yán)重的問題,還有更多。為了能夠?qū)崟r(shí)運(yùn)行,物理引擎往往用逼近法去模擬力學(xué)原理。為了不讓逼近法失控,會(huì)加入一些阻尼。這些阻尼是按步起作用的。所以,這也會(huì)讓物理引擎變得不穩(wěn)定。
這些例子很嚴(yán)重,鞭策我們繼續(xù)改進(jìn)…
加把勁
引擎中的一個(gè)部分不會(huì)收幀率變化的影響,那就是渲染。因?yàn)殇秩疽嬷皇抢L制一瞬間的內(nèi)容,它不關(guān)心與上一次繪制之間的時(shí)間有多長。它渲染的是當(dāng)前的一切。
我們可以利用這一點(diǎn)進(jìn)行改進(jìn)。我們可以用固定的時(shí)間間隔去更新游戲,這樣會(huì)讓一切都更簡單更穩(wěn)定,像物理和AI。而我們可以去調(diào)整渲染的速度,以節(jié)省一些處理時(shí)間。
就像這樣:從上一次更新開始,到現(xiàn)在所用的一個(gè)固定的時(shí)間段,我們把這段時(shí)間當(dāng)成真實(shí)世界比我們“領(lǐng)先”的時(shí)間。我們用一連串的固定時(shí)間來追趕它,代碼如下:
在每一幀的開始,我們根據(jù)消耗的真實(shí)時(shí)間,更新lag。這代表了游戲時(shí)間比真實(shí)世界時(shí)間慢了多少。然后我們?cè)趦?nèi)部起一個(gè)循環(huán)去更新游戲,每一步消耗一個(gè)固定時(shí)間段,直到它追上現(xiàn)實(shí)時(shí)間。一旦我們干上它,我們?cè)匍_啟渲染。就像這樣:
?
注意這里的時(shí)間間隔不再是可見的幀率了。MS_PER_UPDATE只是我們用來更新游戲的時(shí)間粒度。間隔越短,追趕真實(shí)時(shí)間的處理次數(shù)就越多。間隔越長,游戲變化就月劇烈。你會(huì)希望這個(gè)時(shí)間足夠短,最好比60FPS還要快,這樣游戲就會(huì)在快機(jī)器上模擬的更準(zhǔn)確。
但是要小心不能搞的太短,你需要確保這個(gè)時(shí)間段要比update()的時(shí)間長,即使是在最慢的機(jī)器上。否則,你的游戲就會(huì)跟不上真實(shí)時(shí)間。
幸運(yùn)的是,我們?yōu)樽约毫袅艘恍┯嗟亍N覀儼唁秩緩?qiáng)行從更新循環(huán)中分拆出來。這也節(jié)省了很大一部分CPU時(shí)間。渲染的結(jié)果就是游戲可以用固定的時(shí)間間隔和恒定的速度運(yùn)行,不管在什么樣的機(jī)器上。只不過在慢機(jī)器上,玩家看到的變化稍微劇烈一些。
卡在中間
我們還遺留了一個(gè)問題。我們用固定的時(shí)間間隔更新游戲,但是我們?cè)诓淮_定的時(shí)間點(diǎn)渲染。這意味著在玩家看來,游戲經(jīng)常在兩次更新之間進(jìn)行顯示。
這是時(shí)間線:
?
你看到了,我們的更新操作很緊湊,間隔固定。然而我們的渲染卻不一樣,比更新頻率要低,并且不穩(wěn)定。這樣也還好。令人不爽的是渲染并不一定跟更新在一起。看一下第三次渲染。它正好在兩次更新之間:
?
想像一下,一顆子彈要穿越屏幕。在第一次更新的時(shí)候,它在屏幕左邊。第二次更新的時(shí)候它移動(dòng)到了屏幕右邊。游戲在兩次更新之間做了渲染,所以玩家期待的應(yīng)該是子彈出現(xiàn)在屏幕中央。在我們現(xiàn)有的實(shí)現(xiàn)中,它仍然會(huì)出現(xiàn)在屏幕左側(cè)。這就意味著游戲看起來會(huì)磕磕絆絆。
不過,我們知道渲染的時(shí)間離兩次更新的時(shí)間有多久:它儲(chǔ)存在lag里面。當(dāng)它小于更新間隔時(shí),我們跳出更新循環(huán),而不是為0的時(shí)候。這就是我們距離下一次更新的時(shí)間。
當(dāng)我們渲染的時(shí)候,我們把它傳進(jìn)去。
渲染器知道每一個(gè)游戲?qū)ο蠛退?dāng)前的速度。發(fā)現(xiàn)子彈離屏幕的左邊緣20像素,并且每幀向又移動(dòng)400像素。如果我們?cè)趦蓭恼虚g,我們會(huì)傳入0.5給render()。因此,它就會(huì)把子彈華仔中間,在220像素上。噠噠!平滑的運(yùn)動(dòng)。
當(dāng)然,這也許會(huì)造成一個(gè)錯(cuò)誤結(jié)果。當(dāng)我們計(jì)算下一幀的時(shí)候,可能會(huì)發(fā)現(xiàn)子彈遇到了障礙,或者減速了,或者有別的變化。我們渲染的位置有可能跟它下一幀的位置沖突。但是如果不完全更新完物理和AI,我們也不知道。
所以,這種推演或多或少的是在猜測并且有時(shí)會(huì)出錯(cuò)。幸運(yùn)的是這種錯(cuò)誤不容易被發(fā)現(xiàn)。至少它不會(huì)比卡頓更容易被感受到。
設(shè)計(jì)決策
限于篇幅,有更多的內(nèi)容我們并沒有講。一旦攙和進(jìn)了像垂直同步,多線程,多GPU這些東西,一個(gè)真正的游戲循環(huán)就會(huì)變得很恐怖。在更高的層次,有幾個(gè)問題你需要考慮:
是你控制游戲循環(huán),還是平臺(tái)?
更多情況下,這不需要你選擇。如果你的游戲運(yùn)行在瀏覽器上,你就不能寫你自己的傳統(tǒng)意義上的游戲循環(huán)。瀏覽器的基于事件的機(jī)制天然支持。與之相似,如果你用現(xiàn)成的游戲引擎,你就直接用它提供的游戲循環(huán),而不需要自己造輪子。
使用平臺(tái)的消息循環(huán):
1> 簡單,你不需要擔(dān)心寫和優(yōu)化游戲的核心循環(huán)。
2> 它可以在平臺(tái)上很好的運(yùn)行。你不需要關(guān)注是不是要給平臺(tái)時(shí)間去處理它自己的消息,緩存消息,或者處理輸入沖突。
3> 你會(huì)喪失對(duì)時(shí)間的控制。平臺(tái)會(huì)在他覺得合適的時(shí)間去調(diào)用你的代碼。如果調(diào)用頻率和流暢度你不滿意,那也沒招,更可惡的是,大多數(shù)應(yīng)用消息循環(huán)在設(shè)計(jì)的時(shí)候壓根就沒考慮游戲,所以經(jīng)常很慢,切不穩(wěn)定。
使用游戲引擎的循環(huán):
1>你真的沒必要自己寫。寫一個(gè)游戲循環(huán)是很蛋疼的事情。因?yàn)楹诵拇a每一幀都會(huì)調(diào)用,所以一些很小的bug和效率問題都會(huì)大大的影響你的游戲。一個(gè)可靠的游戲循環(huán)是選擇已有引擎的一個(gè)很重要的原因。
2> 你不去寫它,當(dāng)然,帶來的問題就是,即使引擎不能完美的滿足你,你也束手無策。
自己寫:
1> 完全控制。你可以為所欲為。你可以設(shè)計(jì)成最適合你游戲的方式。
2> 你必須要面對(duì)平臺(tái)。應(yīng)用框架或者操作系統(tǒng)通常都需要處理一些自己的消息。如果你接管了循環(huán),它就得不到這些消息了。你需要手動(dòng)控制這些消息,好讓框架不被卡死。
如何管理能量消耗?
這不像5年前那樣,游戲是運(yùn)行在街機(jī)或者一些專用的手持設(shè)備上。現(xiàn)在出現(xiàn)在了智能手機(jī),筆記本,和其他移動(dòng)設(shè)備上。就出現(xiàn)了你需要關(guān)注的問題了。一個(gè)游戲運(yùn)行的很漂亮,但是不到半小時(shí)就把玩家的手機(jī)搞的沒電了,這不會(huì)讓玩家開心。
現(xiàn)在,你不僅需要考慮你的游戲看起來很漂亮,還需要考慮盡可能的少用CPU。也就是當(dāng)你在一幀中做完了該做的時(shí)間,就讓CPU去sleep。
盡可能的快跑:
這可能是PC游戲的做法(也可能運(yùn)行在筆記本上)。你的游戲循環(huán)可能從不讓操作系統(tǒng)調(diào)用sleep,相反,空余的時(shí)鐘,會(huì)被用來提高FPS,或者圖形表現(xiàn)力。
這回給你帶來最好的游戲體驗(yàn),但是也會(huì)耗費(fèi)更多的電。如果玩家用筆記本,他們就會(huì)得到一個(gè)很好的暖腿寶。
限制幀率:
移動(dòng)端游戲通常更注重游戲性而不是圖形表現(xiàn)。很多游戲都會(huì)這只幀率上限(通常是30或者60FPS)。如果游戲循環(huán)在時(shí)間限制前完成工作,它會(huì)調(diào)用sleep歇一歇。
這給了玩家“足夠的”體驗(yàn),并且讓電池更抗用一些。
怎樣控制游戲速度?
一個(gè)游戲循環(huán)有兩個(gè)要點(diǎn):不阻斷的用戶輸入和適應(yīng)時(shí)間分割。輸入很直觀,關(guān)鍵在于你如何處理時(shí)間。有無數(shù)的平臺(tái)可以跑游戲,但一款游戲只能在少數(shù)幾個(gè)平臺(tái)上跑,如何適應(yīng)變化是關(guān)鍵。
固定時(shí)間間隔并且不同步
這就是我們的第一個(gè)示例代碼。你的游戲循環(huán)有多快,跑多快。
1> 簡單,這是主要(呃,也是唯一的)好處。
2> 游戲速度完全依賴硬件和游戲復(fù)雜度。主要缺點(diǎn)就是只要有一點(diǎn)變化就會(huì)影響游戲速度。這正是游戲循環(huán)要解決的。
帶同步的固定時(shí)間間隔
進(jìn)一步復(fù)雜一點(diǎn)的就是使用固定時(shí)間間隔但是加入驗(yàn)車或者同步點(diǎn),避免游戲運(yùn)行的太快。
1> 也很簡單。它只比前面的多了一行代碼。在多數(shù)游戲循環(huán)中,不管怎樣,都要進(jìn)行同步。至少你需要雙緩存你的圖形,并且同步翻轉(zhuǎn)緩沖區(qū),去刷新顯示。
2> 對(duì)能量很友好。這對(duì)移動(dòng)游戲顯得更重要。你不希望干掉玩家的電池。你只需要讓它sleep幾毫秒,而不是把更多的處理塞進(jìn)去,就會(huì)節(jié)省能量。
3> 游戲不會(huì)跑的太快,這就消除了固定間隔的一半憂慮。
4> 游戲跑太慢。如果它用太長的時(shí)間去更新和渲染,游戲也會(huì)變慢。因?yàn)檫@種做飯并沒有把更新和渲染分離。它不僅會(huì)降低幀率,也會(huì)降低游戲速度。
可變時(shí)間間隔
我在這里把它當(dāng)成一種可選方案,因?yàn)槲艺J(rèn)識(shí)的開發(fā)者,大多數(shù)都不贊成這種做法。最好記住為什么它是一個(gè)壞想法:
1> 它適應(yīng)過快或者過慢。如果游戲不能追上真實(shí)時(shí)間,它就會(huì)增加時(shí)間間隔,直到追上。
2> 它會(huì)讓游戲變得不可控,不穩(wěn)定。這才是真正的問題。在可變的時(shí)間間隔下,物理和網(wǎng)絡(luò)就會(huì)變得更難控制。
固定的更新時(shí)間,可變的渲染
我們最后展示的代碼是最復(fù)雜的,但也是適應(yīng)性最強(qiáng)的。它用固定時(shí)間間隔更新,但是如果需要,可以丟掉渲染幀去追趕真實(shí)時(shí)間。
1> 它適應(yīng)過快或過慢。只要游戲能夠及時(shí)更新,游戲時(shí)間就不會(huì)落后。如果玩家的機(jī)器夠好,那就會(huì)得到更流暢的游戲體驗(yàn)。
2> 它更復(fù)雜。主要的缺點(diǎn)就是實(shí)現(xiàn)起來更復(fù)雜,你必須確定一個(gè)更新時(shí)間間隔,足夠小已適應(yīng)高端機(jī)器,足夠大以適應(yīng)低端機(jī)器。
總結(jié)
以上是生活随笔為你收集整理的游戏编程设计模式——Game Loop的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 那些年,我在游戏开发中改过的bug:靠不
- 下一篇: 如何解决游戏延迟,增强用户体验? 几种可