• <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>

            3d Game Walkman

            3d圖形渲染,網(wǎng)絡(luò)引擎 — tonykee's Blog
            隨筆 - 45, 文章 - 0, 評論 - 309, 引用 - 0
            數(shù)據(jù)加載中……

            轉(zhuǎn)載—網(wǎng)絡(luò)游戲程序中解決加載卡頓的有效方法

            對于3d視頻游戲來說,游戲引擎的性能是至關(guān)重要的。玩家在體驗一款游戲時,游戲的流暢度是最基本的要求。與單機游戲不同,網(wǎng)絡(luò)游戲更需要考慮性能問題,因為無法像單機游戲那樣,控制游戲元素的復(fù)雜度來達到效率的要求。大量玩家涌入同一片區(qū)域,同屏出現(xiàn)大量的游戲角色是無法避免的,因此游戲幀率的大幅下降,系統(tǒng)資源的大量消耗也很難避免,這是網(wǎng)絡(luò)游戲引擎最難處理的問題之一。
                這里要講一下游戲幀率的控制,通常玩家在玩游戲抱怨游戲客戶端卡有兩個意思,一是游戲平均幀率很低,二是游戲的幀率非常不穩(wěn),導(dǎo)致了卡頓。實際上游戲平均幀率低,對玩家心情的影響遠不及卡頓造成的影響。平均10幀的游戲,雖然已經(jīng)很糟糕了,但是依然能玩,但是頻繁的卡頓給人的感覺就糟糕透了,平時40幀左右的游戲忽然因為加載個什么東西卡了一下,幀率掉到了零點幾,然后又恢復(fù)到40幀,這種卡頓給人的感覺就是煩透了。
                游戲引擎首要解決的性能問題就是卡頓的問題。要解決卡頓的話需要做到以下兩點:
                第一,不要在主線程去加載資源,最忌諱的操作就是打開文件,這個操作會掛起當(dāng)前線程,也就是說會讓渲染線程停頓。把所有的資源加載操作全放在加載線程去做,畢竟加載線程隨便停頓也沒什么關(guān)系,對主線程的渲染沒影響,主線程只需要每幀判斷資源是否已經(jīng)加載上來就可以了。一但發(fā)現(xiàn)已經(jīng)加載上來了,就可以用這個數(shù)據(jù)去渲染了。
                第二,也是最重要的一點,把加載的操作攤到多幀去做。通常角色走進人堆里以后,或者在戰(zhàn)場上魔法漫天飛的時候,服務(wù)器會傳來大量消息需要處理,最典型的就是創(chuàng)建消息,無論是創(chuàng)建角色還創(chuàng)建特效,就算是采用多線程加載的方式,在一幀內(nèi)創(chuàng)建對象,通知線程加載底層資源,那么多消息的處理依然會不可避免地造成卡頓。這里有一個非常好的解決辦法,就是這些處理消息的操作不要一幀內(nèi)做完,而是分?jǐn)偟蕉鄮瓿伞?br>    一般說來,處理網(wǎng)絡(luò)消息的過程是這樣一個循環(huán):
            while( 消息隊列中還有消息 )
            {
               從隊列中取出第一條消息;
               處理這條消息;
               將這個消息從隊列中刪除;
            }
                在一幀當(dāng)中,循環(huán)遍歷整個消息隊列,將這一幀收到的消息一個一個處理一遍。
                這樣做忽略了最重要的效率問題,當(dāng)你因為游戲卡頓在焦頭爛額地優(yōu)化資源加載時,不放考慮修改一下消息隊列的處理。
                在這里,我可以加入計時:GetTickCount()

             

            初始時間 = GetTickCount();
            while( 消息隊列中還有消息 )
            {
               從隊列中取出第一條消息;
               處理這條消息;
               當(dāng)前時間 = GetTickCount();
               經(jīng)過時間 = 當(dāng)前時間 - 初始時間;
               if( 經(jīng)過時間 > 20 )
               {
                  break;
               }
            }
                如果這一幀的處理時間超過了20ms,則把剩下的消息放到下一幀處理。通過這種計時的方式,你會發(fā)現(xiàn)游戲的流暢度簡直有了天翻地覆的變化!在優(yōu)化之前,有個幾個人在用群攻魔法攻擊大量的怪物,這些家伙忽然涌入到視野中,幀率便一下掉到了零點幾,游戲出現(xiàn)了非常嚴(yán)重的卡頓,這種狀態(tài)持續(xù)了很短一段時間,幀率又迅速回升上去。而現(xiàn)在,經(jīng)過修改以后,你會發(fā)現(xiàn)那些家伙很平滑地出現(xiàn)在視野中,沒有一絲的卡頓。如此效果簡直是奇跡一般,而這一切僅僅是修改了幾行代碼而已。
                現(xiàn)在考慮這么做所帶來的問題。如果消息量非常大,而機器又慢,平均幀率又很低的話,那麻煩可就大了:每幀處理的消息量還沒有收到的消息量大。這可是個很嚴(yán)重的問題,這會讓客戶端的表現(xiàn)與實際情況嚴(yán)重脫節(jié)。在這里,就需要有一個機制,保證消息在積攢超過一定數(shù)量時,能得到及時的處理:

            初始時間 = GetTickCount();
            while( 消息隊列中還有消息 )
            {
               if( 消息隊列中的消息數(shù)量 > 300 )
               {
                  一次性處理所有的消息;
               }
               else
               {
                  從隊列中取出第一條消息;
                  處理這條消息;
                  當(dāng)前時間 = GetTickCount();
                  經(jīng)過時間 = 當(dāng)前時間 - 初始時間;
                  if( 經(jīng)過時間 > 20 )
                  {
                     break;
                  }
               }   
            }
                這樣就解決了消息越積攢越多的問題,當(dāng)消息越攢越多時,會一次性處理所有的消息。但這樣也會帶來一個問題,那就是在幀率比較低的機器上,當(dāng)需要處理的消息特別多時會出現(xiàn)周期性的卡頓。卡頓的原因就是那步一次性處理所有消息的操作。優(yōu)化的目的就是要避免這樣的卡頓,而對于低端機器來說,這樣的優(yōu)化不但沒有起到效果,反而加重了卡頓現(xiàn)象。為了彌補這個方法帶來的弊端,就要對那個經(jīng)過時間20ms做點手腳:

            static 時間閾值 = 20;     //注意時間閾值是static的
            if( 消息隊列中的消息數(shù)量 > 100 )
            {
               ++時間閾值;
            }
            else
            {
               --時間閾值;
            }
            if( 時間閾值 < 20 )
               時間閾值 = 20;
            if( 時間閾值 > 40 )
               時間閾值 = 40;

            初始時間 = GetTickCount();
            while( 消息隊列中還有消息 )
            {
               if( 消息隊列中的消息數(shù)量 > 300 )
               {
                  一次性處理所有的消息;
               }
               else
               {
                  從隊列中取出第一條消息;
                  處理這條消息;
                  當(dāng)前時間 = GetTickCount();
                  經(jīng)過時間 = 當(dāng)前時間 - 初始時間;
                  if( 經(jīng)過時間 > 時間閾值 )
                  {
                     break;
                  }
               }   
            }
                這里增加了時間閾值這個靜態(tài)變量,替代了之前代碼中的20,使之成為一個由當(dāng)前幀消息包數(shù)量決定的一個可變的值。當(dāng)前幀消息包的數(shù)量超過一個值時,就將這個時間閾值加一,否則減一。這么做的效果就是,消息包來得越多,每幀用于處理消息的時間就越長,也就是說消息處理耗時的比重在逐漸上升。這樣就能很大程度上降低消息數(shù)量超過上限的可能性。
            最差的情況,如果這樣做依然有周期性卡頓的話,這臺機器真的就不適合運行這個游戲了,退一步講,不作這個優(yōu)化的話,這臺機器玩這個游戲也依然會卡得要命。:)
               當(dāng)然,時間閾值的范圍,和消息包的數(shù)量上限可以調(diào)整,以適合于不同的游戲。

            posted on 2009-02-28 13:47 李侃 閱讀(1450) 評論(2)  編輯 收藏 引用 所屬分類: 前臺客戶端

            評論

            # re: 轉(zhuǎn)載—網(wǎng)絡(luò)游戲程序中解決加載卡頓的有效方法  回復(fù)  更多評論   

            對于分?jǐn)偟蕉鄮姆桨福_實身有體會,記得以前做過一個游戲模板數(shù)據(jù)查詢的工具,是用MFC做的,由于一次性查找到的數(shù)據(jù)過多,一下子加載到列表控件中會引起很嚴(yán)重的停頓,于是我便學(xué)游戲的樣子將其放到多個幀中處理,程序仍然是單線程,但立馬流暢了很多。
            2009-08-10 22:01 | 李現(xiàn)民

            # re: 轉(zhuǎn)載—網(wǎng)絡(luò)游戲程序中解決加載卡頓的有效方法  回復(fù)  更多評論   

            嗯,不錯,學(xué)習(xí)了。
            2010-03-10 09:55 | 月隱
            精品久久久久久无码国产| 日本三级久久网| 亚洲精品蜜桃久久久久久| 久久香蕉国产线看观看精品yw| 久久久无码人妻精品无码| 国产精品天天影视久久综合网| 国产激情久久久久影院老熟女| 久久亚洲精品国产精品婷婷| 99久久久国产精品免费无卡顿| 国产亚洲色婷婷久久99精品91| 伊人色综合久久天天人手人婷| 99精品久久精品一区二区| 久久乐国产精品亚洲综合 | 久久综合香蕉国产蜜臀AV| 99久久精品国产毛片| 久久亚洲私人国产精品vA| 欧美久久一区二区三区| 99精品久久久久久久婷婷| 久久精品亚洲一区二区三区浴池 | 久久国产欧美日韩精品| 久久婷婷五月综合成人D啪| 97r久久精品国产99国产精| 亚洲精品无码久久不卡| 久久久久久A亚洲欧洲AV冫| 97久久精品午夜一区二区| 亚洲午夜久久久久妓女影院| 久久影院久久香蕉国产线看观看| 久久精品国产亚洲沈樵| 久久久久亚洲av无码专区导航| 2021国内久久精品| 思思久久好好热精品国产| 三级韩国一区久久二区综合 | 九九久久自然熟的香蕉图片| 无码国内精品久久综合88 | 国内精品久久久久久不卡影院| WWW婷婷AV久久久影片| 欧美va久久久噜噜噜久久| 无码日韩人妻精品久久蜜桃| 色综合久久久久久久久五月| 人妻无码久久一区二区三区免费| 欧洲成人午夜精品无码区久久|