《刀塔傳奇》主程:我們踩過的那些坑

>>>  創業先鋒 眾人拾柴火焰高  >>> 簡體     傳統

10月28日,在Cocos開發者大會(秋季)上,《刀塔傳奇》的主程張振新做了客戶端技術經驗分享。他們選擇的是Cocos2d-x結合小巧高效的Lua,它提高了開發效率,對手游面臨的復雜版本更新問題也頗有助力。


《刀塔傳奇》是張振新與團隊從端游轉入手游開發的第一款產品,在他的演講中,還提到一些眾多初創手游團隊可能都會遇到的“坑”。


比如:“刀塔傳奇是在win環境下面開發,早期經常碰到一個問題就是開發環境下面功能都ok,可到版本一發布,在手機上面就各種不對,也不知道是哪里錯誤。”他們使用錯誤信息框的方式來解決這種調試問題。


又比如一個數據,他們每次用全量包更新,當日DAU就會大概減少10%。優化方案是,制作低清版本來解決包大小的問題,或者在游戲內集成下載器,在啟動游戲的過程中,完成全量包的下載和安裝。


更多張振新的分享,見以下整理內容。


大家好,我叫張振新,來自莉莉絲科技,我從2009年開始負責PC端游的開發,2013年非常榮幸的進入莉莉絲科技,參與了《刀塔傳奇》的開發,這是我開發的第一款手游,非常的高興。下面給大家分享一下《刀塔傳奇》客戶端技術經驗。


首先介紹一下《刀塔傳奇》的一個客戶端的整個結構,我們在Cocos2d-x下面分裝了一個數據模塊,一個是數據模塊,還有一個網絡模塊,最大的一塊就是業務邏輯模塊,里面分UI系統、戰斗系統和事件系統,大家玩的游戲都是通過這些模塊搭建起來。



我們這個模塊除了Cocos2dx是在C++層面實現的,《刀塔傳奇》很大程度上都是通過Lua實現的。


開發效率


Lua是一個小巧的成本語言,它的設計目的是為了嵌入應用程序中,從而為應用程序提供靈活的擴展和定制功能。它有以下幾個特點。


一個是輕量級,官方版本只提供了一個精簡的核心和最劇本的庫,因此Lua體積小,啟動速度快,因此非常適合嵌入在別的程序里。


二是可擴展性,Lua并不象其它許多"大而全"的語言那樣,包括很多功能,比如網絡通訊、圖形界面等。但是Lua提供了非常易于使用的擴展接口和機制:由宿主語言(通常是C或C++)提供這些功能。


三是非常容易上手,而且簡單靈活,這一點對用戶有幫助,因為它學習比較方便,操作靈活。


Lua是一門解釋性語言,沒有編譯和鏈接生產二進制代碼的過程,它是由lua虛擬機直接解釋執行的。開發中,修改完代碼后,直接運行程序即可看見效果。因此可以節省大量的開發時間,項目越大,效果越明顯。


刀塔傳奇支持游戲過程中動態加載lua代碼,省去重新打開程序的時間。我們在腳本層封裝了一個reload函數,用于在游戲運行時重新加載lua代碼,實現原理也非常簡單:先將先前required過的module從刪除,然后重新require該所有module,相當于重新初始化腳本代碼。這樣我們又省去了關閉和重新打開程序的時間。刀塔傳奇的UI場景是使用cocos2dx的場景scene,并在腳本層維護了一個scene的stack,reload的時候將原先的scene先pop,然后使用reload后的代碼和資源重新創建新的scene,還原原先的場景,省去了重復操作的時間。


版本更新


除了開發效率,Lua給我們《刀塔傳奇》帶來的好處就是版本更新。版本更新是手游上市之后的非常重要的環節,直接影響到手游產品的質量。手游版本的更新比PC更加的惡劣,因為iOS的AppStore有一段忽長忽短的審核期,而Andriod更糟糕,渠道眾多,版本分發流程復雜。刀塔傳奇在立項的階段就考慮到版本更新的問題,程序的整個框架也給版本更新帶來一些便利性。


1) 線上熱更


loadstring:功能非常簡單,效果非常明顯。先前做PC游戲的時候,每次發現線上緊急問題,采取的辦法都是緊急停服,然后出更新包,測試,發布,周期長,體驗差。曾經無數次線上緊急問題都是通過這個方式修復的。


刀塔傳奇在程序啟動的時候,會從服務器上面拉一段代碼下來,然后執行這段代碼。由于這個邏輯是在發生在lua代碼加載之后,于是就可以用拉取下來的代碼段覆蓋版本里面的lua代碼。


注意:用loadstring只能覆蓋全局域下面能訪問到的函數,從程序設計方面來說,一個模塊應該對外暴露盡可能少的接口,但是從線上熱更來看,應該讓全局域下面能訪問到盡可能多的函數,這樣可以提高線上熱更代碼的覆蓋率。


2)游戲內更新包


這是刀塔傳奇的常規更新方式,一般用來更新大特性版本。更新的內容包括lua腳本,美術資源,策劃資源。由于代碼都是lua腳本,更新完成后,可以通過重新初始化整個lua state,達到更新包在游戲運行時生效。


注意:更新包不能用于解決更新流程之前的bug;當一個版本有連續幾個比較大的更新包之后,可以考慮出一個非強制更新的全量包給到渠道,這樣有助于提高新進玩家的轉化率。


3)全量包更新


這個是我們最不愿意采用的版本更新方式,有一些時候不得不用。比如《刀塔傳奇》剛剛上線的時候跨服藍牙堆棧功能。


這邊每個數據跟大家分享一下,每次完整包更新的時候,當日的日活躍大概會少10多萬。它的優化方案,制作低清版本,解決包大小的問題,還有一種是在游戲內準備繼承一個下載器的功能,就是在游戲啟動過程當中,跟游戲更新包類似,在游戲啟動過程當中完成一個全量包的下載和安裝,達到優化更新體驗的目的。


調試問題


Lua的開發者經常會碰到的感覺,就是開發效率極快,一下子做起來了,但是維護特別的困難,其中很重要的原因就是調試困難,很難找到bug。


除了常規的print和log的方法,刀塔傳奇使用lua的debug庫開發了一個DEBUG函數,作用類似ide環境下面的斷點。該函數會先輸出當前的調用堆棧,并進入循環等待用戶輸入調試語句,在該狀態下,通過debug庫提供的getinfo等函數可查看指定level堆棧下的local和upvalue。


錯誤提示框


我們還做了一個游戲錯誤框,類似這樣的效果圖。我們《刀塔傳奇》是在PC下面開發的,等版本發布出來,到Andriod、iOS一跑就各種問題,也不知道出現了什么問題,然后到各種下面去查非常的麻煩。然后我們就在手機發布上面去添加了這么一個框,當有錯誤的時候它可以以這個形式展現過來,我們在發布內部的測試版方面,我們發這么一個框,可以很容易的定位到手機版本的問題,他們提交的bug單可以讓我們找到錯誤的信息,這對bug來說是很方便的事情。


Snapshot:解決Lua腳本內存泄漏


使用lua開發也會存在內存泄漏的問題,snapshot可以對當前的Lua State做一個完整的快照,并記錄對象的引用關系,我們可以在不同的手機端對Lua進行兩次快照,通過兩次的對比可以的出新增加的內存處于何處。


它在第一行做了一個快照,分配了一個Test1,然后他又做了一個S2,這對我們定位的內部發生了斜路都有很好的解決。特別是對Lua問題發生的不太容易發現的,通過添加這種方式可以解決。


性能問題


把性能瓶頸相關的代碼用C或C++實現。


為了解決draglist渲染效率低下的問題,添加了可見性裁剪的功能,將不在顯示區域的item項設置為不可見。第一版的做法是通過通過lua導出接口,在lua層實現裁剪,確實減少了draw call的調用,不過卻增加了cpu的負担,主要是因為產生了大量的lua調用導出接口原因。第二版就直接在node層面上現實自動裁剪,在visit的函數里面判斷是否可見。


對table預先分配大小,減少rehash。


當我們把新的鍵值賦給table的時候,若數組和哈希表已經滿了,更會觸發一個再哈希(rehash),再哈希的代價是高昂的,首先會在內存中分配一個新的長度的數組,然后將所有記錄再全部哈希一遍,將原來的記錄轉移到新數組中。


Lua的創新跟其他的腳本實現方式不太一樣,它采用了內例化的實現方式,所有的字符串在Lua中都只儲存一份拷貝,并且都以引用的方式保存。這邊可以考慮一下,Lua保存的方式可以用table保存。


我的分享結束,謝謝大家!



游戲葡萄 2015-08-23 08:41:30

[新一篇] 一年前的王信文談《刀塔傳奇》(上)

[舊一篇] 獨立之光(1)從講故事開始
回頂部
寫評論


評論集


暫無評論。

稱謂:

内容:

驗證:


返回列表