遊戲引擎開發中需要注意的循環結構 原

我們在遊戲設計和開發中,尤其是引擎開發中,邏輯循環是一個重要組成部分,循環決定了遊戲的基礎邏輯和運行方式,在不同的開發環境和語言下,對於循環的釋義甚至相差甚遠,那麼我想和大家分享的是在Silverlight遊戲開發中,循環的設計方式和做法。

以下內容來自以往的遊戲開發經驗,可能在其他語言中的相關文章更加詳細,謹在這裏討論有關在Silverlight遊戲開發中的應用。

在傳統的開發觀念中,無論任何開發環境,它都逃不出While,代碼一般是這樣:

while (true)
{
    if (GameExit() == true)
        break;
    else
        GameLoop();
}

這個代碼方式只是一個模型,可以將其理解成爲一個不停在檢查狀態的狀態機。

那麼在Silverlight遊戲開發中,我們是否也可以這樣應用呢?道理上差不多,目前來說大部分的做法只是實現了一個根(Root)部,這方面深藍色右手以及很多其他朋友都有了各種各樣的解決方案,有興趣的朋友可以找他們的文章,無論是用線程(Thread)、故事板(Storyboard)、Rendering、DispatcherTimer,都是一個好的循環體的開始。

對於遊戲而言,尤其是網絡遊戲,我們將面臨着大量的交互,這些交互可能來自用戶,也可能來自自身的遊戲邏輯,就如最現實的問題是,當一個遊戲的同屏幕呈現100個以上的角色的時候,遊戲是否因此而“卡”住,在早期的時候,我陷入了一個誤區,呈現足夠多的角色就是最大的性能體現(3.0呈現600個角色不卡),現在看來卻是不然,因爲單純的角色呈現,有幾百個不算什麼,在一個整體遊戲的執行時候,它是否還能保持足夠的流暢纔是最重要的,因爲戰鬥邏輯、界面邏輯、場景管理器無時不刻在佔用着系統資源,並且此時的角色也絕非幾張圖片那麼簡單,他們身上的裝備、部件、特效都將成爲遊戲開發者的負擔。

在此種情況下,優化循環過程相當重要,作爲團隊經驗積累,今次拿出來大家一起研討,有什麼好的想法和建議歡迎一起交流一下:)下面的五種循環設計模式名字自己亂起的不好,還請見諒。

一、自身式循環

自身式循環比較容易理解,比如一個精靈控件,自己內部實現一個循環,來不停的檢測和執行邏輯,開發者都不需要去單獨做什麼,只需要new出來它們自己就會執行邏輯了,這種方式非常便捷和方便,開發起來也相對容易,互相之間沒有任何關係,此時需要藉助單例之類的設計模式來解決互相的結合問題。圖示如下:

很顯然,我們自身邏輯有一個最大的問題是獨自的性能佔用,如果一個場景(不是同屏)有幾百個這樣的循環時,那麼遊戲各個線程就會吃掉大量的CPU,尤其是用Thread、Storyboard、DispatcherTimer的時候。

二、鏈條式循環

自身邏輯存在各自的循環消耗問題,那麼有沒有辦法將各自的循環邏輯統一到一個循環中呢,如果學過數據結構,我們可以透過鏈表的形式來做,基本的原理是將各個循環體放入到一個大循環中,然後從第一個開始執行循環邏輯,只執行一次,然後下一個,到底以後回來繼續執行,模型如下:

這是一種常見的處理方法,能夠大大降低系統消耗,而且C#提供了迭代器等好用的遍歷,使得我們結合面向對象的思路更方便。示意代碼如下:

public class obj
{
    public virtual void OnLogic();
}
List<obj> ObjList = new List<obj>();
public void OnLoop()
{
    foreach (var item in ObjList)
    {
        item.OnLogic();
    }
}

鏈條式循環最大的優點是將所有的獨立循環全部集中到一個大循環邏輯中,需要注意的有一個問題,那就是動態處理,因爲遊戲當中的物體生成和銷燬是非常頻繁的,正在循環的時候發生了集合改變,那麼就危險了,我們的做法有兩種,分別是數組轉換和回收判斷,數組轉換非常容易,將集合拷貝到一個數組中,然後循環數組的各個元素;回收判斷是通過標識將物體,在合適的時機摘出到一個回收列表中,然後在安全的時機清理。

鏈條式循環的優點可以創造一個遊戲的RootHead,將所有的元素加入到這個RootHead當中,創建一個主循環然後遍歷即可,當然了,你需要通過基類的方式來達到目的。

這是一種好的方式嗎?在某種情況是的,它能解決性能損耗,當然了,要是內部實現的邏輯過於複雜,有的時候可能還得藉助一下另外線程。在遊戲產品中,有一個更加直接的需求,那就是級別層次問題,也就是說,有的循環體是系統級別,而有的可能只是一張圖片,那麼遊戲的循環到底有多大才能包容一切,比如場景管理中的那麼多場景物體,如果有邏輯循環就直接加入到這個大循環中嗎?在遊戲運行時,有一些循環在特定的時候是不需要使用的,或者不需要執行的,也爲後續開發造成了障礙,所以,在我們的MMOROG引擎中,最多應用是下面的這種循環模式。

三、子樹式循環

子樹循環顧名思義,使用樹狀結構來處理循環邏輯,我們實際應用中還有可以分爲:活動子樹式循環和固定子樹循環,爲了方便講解,主要講固定子樹循環的模式。

我們知道在一個遊戲中,有很多的系統,比如場景系統、戰鬥系統、隊伍系統、公會系統、聊天系統……N多系統,它們自己內部是否有一個循環呢?如果從直觀角度上,上述系統可能不需要循環,但是事實不然,比如隊伍系統,可能爲了完成組隊、移動等行爲,專門有一個循環來處理判斷邏輯,雖然這個邏輯很簡單,再比如公會系統是否有每10秒鐘刷新一下公會列表的需求……

如上圖所示,我們利用子對象的方式創造一顆樹,然後逐一進行遍歷,在執行過程可以使用迭代,也可以使用遞歸,不管那種方式,對於子樹而言沒有太大的區別,但是對於性能而言,我們可以做一些有趣的優化,當一個系統關閉的時候,它在樹中就不執行了——具體用什麼方法,看情況而定,無論是拆枝還是邏輯判定都行。我們得到的效果是,關閉的子樹下面的元素也不會執行循環,多麼簡單,比鏈條式的容易多了,一斷全都斷。

子樹式循環在系統級別非常常用,對於那些比較頻繁的更換的邏輯比較實用,比如特效動畫、地圖系統等等,具體的算法和操作在《數據結構》中有明確的答案,可以在其中找到想要的東西:)。

四、區間式循環

區間式循環嚴格意義上是循環中的一個判定方式,而不是實現模式,原理是將遊戲系統各個部分拆分開,掛入不同的循環結構中,如果說鏈條式和子樹式是一種Object集中,區間式可以說是一種Objects集合打散,釋義圖如下:

區間式在大系統級別,可以分拆最消耗性能的部分,到另外一個線程(或循環結構)中完成循環,比如說戰鬥系統、地圖處理、場景管理器,而場景管理器下也可以帶入一個區間式循環,將場景分割,然後對一個區域範圍的物體進行處理(如果想想上面的圖是否可以做成一個二維數組呢?),對於超出區域範圍內的循環邏輯完全視而不見就行了,否則的話,要處理一片大場景中的N多個角色,無論是在自身、鏈條、子樹都會一筆不小的開銷。

區間式最大的優點是加強了範圍判定,如果寫的好,還可以多重結合,使用二維(三維也行)數組完成各個需要循環邏輯的分配,將不需要的拆分出去,這裏的算法可能稍微有點意思,類似哈希和List的結合,要注意的是當一個物體(OBJ)從區間1到區間2的時候,會發生什麼事情:)

五、組合式循環

其實組合式循環是一個非常偷懶的部分,因爲組合的是前四種而已,在遊戲開發中,並不是上述的那種方式最好,而是因地制宜,什麼樣的模式滿足什麼樣的需求,不能只是單純爲了達到高效而高效,更加要注意未來開發的順利程度,避免返工。

如上圖所示,我們可以很清楚的分析不同循環方式在不同的環境下的應用:

自身式循環比較適合界面,因爲比較固定,而且複雜邏輯不多,當然了這只是在Silverlight的UI當中比較適用,其實主邏輯就是一個很大的自身循環,Root的循環方式就是一個自身式循環。

鏈條式循環比較使用與第二級的遊戲系統,將系統全部串起來,以達到快速遍歷目的,但是在系統的下一級,就是子樹式循環,系統元素全部在一個系統下,下面的子樹中也可能會出現鏈條式,很顯然是一種最頻繁的組合方式。

區間式循環主要是應用在場景系統,可能需要一個鏈條循環或者子樹循環帶動,具體情況需要看遊戲的設計模式,如果單個場景(比如地圖)是使用單例的方式,那麼使用鏈條式循環帶動循環邏輯比較合適,如果單個場景是通過new出來的,那麼使用子樹方式來切換銜接更加容易明瞭。

以上是我們在做MMORPG時候的一些小小經驗總結,上述中我們用的最多是組合式循環(廢話,組合式全包了),但是對於一些小型的遊戲,建議還是不要設計這麼複雜,對於大型的網絡遊戲而言,程序設計這部分的重要性非比尋常,最後,看過很多這樣或那樣的說法,網頁遊戲對於性能是不行的,我想大部分的性能問題並不是技術本身,而是開發者沒有將一個遊戲本質思考清楚,什麼時候我們用什麼方法可以達到什麼目的,能爲各位開發者多能從中找到一點靈感,那爲我自己的想法也有了一個交代,也歡迎小夥伴們在評論下方留言,共同討論。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章