日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

游戏循环

發(fā)布時(shí)間:2024/4/11 编程问答 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 游戏循环 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

實(shí)現(xiàn)一個(gè)游戲的一種非常流行的方式看起來(lái)像這樣:

while (playing) {advance state by one framerender the new framesleep until it’s time to do the next frame }

這種方式有幾個(gè)問(wèn)題,最基本的是游戲可以定義什么是 “幀” 的想法。不同的顯示器將以不同的頻率刷新,且頻率可能隨時(shí)間而變。如果你產(chǎn)生幀的速度比顯示器能夠展示它們的快,你將不得不偶爾丟棄一個(gè)。如果你生成它們的速度太慢,SurfaceFlinger 將周期性地?zé)o法獲得新緩沖區(qū)并重新展示之前的幀。這兩種情況都會(huì)導(dǎo)致可見的毛刺。

你需要做的就是匹配顯示器的幀率,并根據(jù)自上一幀開始經(jīng)過(guò)了多長(zhǎng)時(shí)間來(lái)推進(jìn)游戲狀態(tài)。有兩種方式做到這一點(diǎn):(1) 填充BufferQueue,并依賴“交換緩沖區(qū)”的背壓;(2) 使用 Choreographer (API 16+)。

隊(duì)列填充

這實(shí)現(xiàn)起來(lái)很簡(jiǎn)單:僅僅盡快交換緩沖區(qū)。在早期的 Android 版本中,這實(shí)際可能付出的代價(jià)是 SurfaceView#lockCanvas() 將使你休眠 100ms。現(xiàn)在,現(xiàn)在它被 BufferQueue 加速了,BufferQueue 清空的速度可以和 SurfaceFlinger 一樣快。

Android Breakout 中可以看到一個(gè)這種方法的例子。它使用了 GLSurfaceView,其運(yùn)行于一個(gè)調(diào)用應(yīng)用程序的 onDrawFrame() 回調(diào)并交換緩沖區(qū)的循環(huán)中。如果 BufferQueue 滿了,eglSwapBuffers() 將等待直到有緩沖區(qū)可用。緩沖區(qū)在 SurfaceFlinger 釋放它們時(shí)可用,在為顯示器獲取一個(gè)新的之后,緩沖區(qū)就可以使用。由于這發(fā)生在 VSYNC 時(shí),你的繪制循環(huán)時(shí)序?qū)⑴c刷新頻率匹配。大多是。

這種方法有兩個(gè)問(wèn)題。首先,應(yīng)用程序被綁定到了 SurfaceFlinger 活動(dòng),根據(jù)需要做多少工作以及是否與其他進(jìn)程競(jìng)爭(zhēng) CPU 時(shí)間,將需要花費(fèi)不同的時(shí)間。由于你的游戲狀態(tài)根據(jù)緩沖區(qū)交換的時(shí)間推進(jìn),你的動(dòng)畫將不會(huì)以固定頻率更新。當(dāng)以 60fps 運(yùn)行時(shí),隨著時(shí)間的推移,平均值不一致,盡管你可能不會(huì)注意到顛簸。

其次,第一對(duì)緩沖區(qū)交換將發(fā)生的非常快,由于 BufferQueue 還沒(méi)有滿。幀之間計(jì)算的時(shí)間將接近于零,因此游戲?qū)a(chǎn)生一些什么也沒(méi)發(fā)生的幀。在一個(gè)像 Breakout 這樣的游戲中,其在每一次刷新時(shí)更新屏幕,除了游戲首次啟動(dòng)(或取消暫停)時(shí)隊(duì)列總是滿的,所以效果不明顯。偶爾暫停動(dòng)畫,然后返回盡可能快的模式的游戲可能會(huì)看到奇怪的打嗝。

Choreographer

Choreographer 允許你設(shè)置一個(gè)在下次 VSYNC 時(shí)被調(diào)用的回調(diào)。實(shí)際的 VSYNC 時(shí)間作為一個(gè)參數(shù)傳入。因此即使你的應(yīng)用沒(méi)有立即喚醒,對(duì)于顯示器何時(shí)開始刷新你依然有一個(gè)精確的圖景。使用這個(gè)值,而不是當(dāng)前時(shí)間,將為你的游戲狀態(tài)更新邏輯產(chǎn)生一個(gè)一致的時(shí)間源。

不幸的是,在每個(gè) VSYNC 之后你得到回調(diào)的事實(shí)并不能保證你的回調(diào)將及時(shí)執(zhí)行,或者你將能夠迅速地執(zhí)行回調(diào)。你的應(yīng)用程序?qū)⑿枰獧z測(cè)它落后的情況,并手動(dòng)丟棄幀。

Grafika 中的 "Record GL app" activity 提供了一個(gè)這種方法的例子。在一些設(shè)備上 (比如 Nexus 4 和 Nexus 5),如果你只是坐著觀看,activity 將開始下丟幀。GL 渲染是微不足道的,但偶爾地 View 元素會(huì)被重繪,如果設(shè)備已經(jīng)掉入了節(jié)電模式的話測(cè)量/布局過(guò)程可能消耗非常長(zhǎng)的時(shí)間。(根據(jù)systrace,在Android 4.4上的時(shí)鐘緩慢之后,需要28ms而不是6ms。如果在屏幕上拖動(dòng)你的手指,它認(rèn)為你正在與 activity 交互,因此時(shí)鐘速度將保持高速,且你將從不會(huì)丟棄幀。)

簡(jiǎn)單的修復(fù)辦法是在 Choreographer 回調(diào)中,如果當(dāng)前時(shí)間晚于
VSYNC 之后 N 毫秒就丟棄幀。理想的 N 值根據(jù)之前觀察到的 VSYNC 間隔決定。比如,如果刷新周期是 16.7ms (60fps),你可以在你運(yùn)行多于 15 ms 之后丟棄幀。

如果你觀看 "Record GL app 運(yùn)行,你將看到丟棄的幀的計(jì)數(shù)增加,甚至能夠在丟棄幀時(shí)在邊緣看到紅色的閃光。除非你的視力非常好,盡管,你將看不到動(dòng)畫波動(dòng)。在 60fps 的情況下,只要?jiǎng)赢嬕院愣ǖ乃俣壤^續(xù)前進(jìn),應(yīng)用程序可以丟棄偶爾的幀,而沒(méi)有任何人能注意到。你能逃脫多少次取決于你在繪制什么,顯示器的特性,以及使用該應(yīng)用程序的人員是否在檢測(cè)閃避。

線程管理

一般來(lái)說(shuō),如果你正在向 SurfaceView,GLSurfaceView,或 TextureView 渲染,你想要在一個(gè)專門的線程中執(zhí)行該渲染。不要在 UI 線程中做任何 “重活” 或任何需要不確定時(shí)間的事情。

Breakout 和 "Record GL app" 使用專門的渲染線程, 且它們還在該線程中更新動(dòng)畫狀態(tài)。只要游戲狀態(tài)能夠快速更新這就是合理的方法。

其它的游戲?qū)⒂螒蜻壿嫼弯秩就耆珠_。如果你有一個(gè)簡(jiǎn)單的游戲,它什么也不做,只是每 100ms 移動(dòng)一個(gè)塊,你可以讓專門的線程只做這些:

run() {Thread.sleep(100);synchronized (mLock) {moveBlock();}}

(您可能希望使睡眠時(shí)間是基于一個(gè)固定的時(shí)鐘的偏移計(jì)算的,以防止漂移 - sleep() 不是完美的一致的,moveBlock() 接收非零的時(shí)間值 - 但你可以根據(jù)你的想法來(lái)。)

當(dāng)繪制代碼喚醒時(shí),它只是獲得鎖,獲得時(shí)鐘的當(dāng)前位置,釋放鎖,并繪制。而不是基于幀間增量時(shí)間進(jìn)行分?jǐn)?shù)移動(dòng),你只需要一個(gè)線程來(lái)移動(dòng)事物,而另一個(gè)線程可以在繪圖開始時(shí)隨時(shí)繪制事物。

對(duì)于任何復(fù)雜的場(chǎng)景,您都希望創(chuàng)建一個(gè)按照喚醒時(shí)間排序的即將到來(lái)的事件列表,并且在下一個(gè)事件到期之前睡休眠,但這是一樣的。

原文

總結(jié)

以上是生活随笔為你收集整理的游戏循环的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。