如何重繪「江南百景圖」?近300頁 PPT 免費分享!

大城小胖發表於2021-09-22
去年,古風模擬經營類手遊《江南百景圖》成功破圈,成為年度現象級爆款。如何將它搬到小遊戲平臺?是轉換還是重寫?使用哪些技術方案,能在包體大小僅為原版1/20的同時,達到與 App 版相當的遊戲體驗?椰島小遊戲研發負責人大城小胖,帶著他近300頁的 PPT,在 Cocos 的兩次線下活動中做了全面的技術分享。

如何重繪「江南百景圖」?近300頁 PPT 免費分享!

轉換 or 重寫

《江南百景圖》App 版遊戲包大小有 600+M,上線前期還有部分使用者反映遊戲執行時手機發熱嚴重。而小遊戲版在經過立項選型後,決定使用 Cocos Creator 重寫,僅用了1天就做出了 Demo。經過4個月的優化,我們最後將包體壓縮到 30M 左右,同時保證遊戲體驗與 App 版相當。

如何重繪「江南百景圖」?近300頁 PPT 免費分享!

優化的過程中,我們也做了以下工作,其中 程式碼 部分需要重新設計和編寫。

如何重繪「江南百景圖」?近300頁 PPT 免費分享!

渲染優化

原生版本的《江南百景圖》移植到小遊戲首先需要解決的就是 耗電高、易發燙、Draw Call 高等問題。

合批

合批是降低 Draw Call 最快也是最有效的方式。優化同樣的 Texture,將多張的圖片合併到一張圖集上,這樣不論要生成多少張不同的圖片,都不會打斷合批渲染,Draw Call 也就降低下來了。

但是《江南百景圖》的資源非常多,每個玩家使用資源的順序也不盡相同,如果玩家使用的資源分別在不同的圖集上,還是會導致合批渲染被打斷,產生 Draw Call。因此,針對這一情況,我們採用了 Multi-Texture 的方式進行了優化,其原理是將傳統的判斷是否在同一張圖集,轉換為判斷是否在 同一批圖集,這樣就大大減少了 Draw Call 產生。

如何重繪「江南百景圖」?近300頁 PPT 免費分享!

另外,通過 gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS) 這個指令,可以知道在一臺裝置中 Shader 最多支援幾張圖集。測試發現目前 90% 以上的手機至少支援8張,因此我們將批圖集的數量設定為8張。因為一個批次有 8 張圖集,所以我們是通過這個 idx 判斷某張圖用的哪個圖集,程式碼也很簡單。

如何重繪「江南百景圖」?近300頁 PPT 免費分享!

動態合圖

小遊戲版本採用了 Cocos 的動態合圖機制,這樣在 CDN 下載的圖片也能進行合圖。而為了提高合圖的效率,避免浪費空間,我們會將長度或者寬度特別大的圖片進行裁剪。

如何重繪「江南百景圖」?近300頁 PPT 免費分享!

例如左圖中的旗杆,由於圖片太長,在動態合圖時會導致空間浪費,因此我們將這張旗杆的圖片裁剪成兩張,如右圖所示,再在專案中進行拼接處理。

採用同一個材質資源

在《江南百景圖》中,玩家移動地圖時,原本在顯示範圍外的圖片將從水墨色變為彩色。

如何重繪「江南百景圖」?近300頁 PPT 免費分享!

傳統的方案是改變圖片材質,當地圖移動到要顯示的節點時,節點一個一個地進行材質的切換,達到一個 “淡入淡出” 的效果。但是在專案中嘗試之後,我們發現這樣會導致 Draw Call 上升,而且拖拽地圖又是一個很頻繁的操作,遊戲中實際效果較差。

因此在這裡我們將所有城市物體資源,無論是人物還是建築、常態還是淡入狀態,都用統一的 Material、並使用頂點資料傳遞“時間引數”,以此節約效能消耗,最終達到所有建築和人物的建立、移動、銷燬等全都只需要一個材質就能夠完成。

如何重繪「江南百景圖」?近300頁 PPT 免費分享!

很多人會覺得一個普通的圖片也用這麼複雜的方案,會影響效能,導致效能變差。但是實際測試效果並不差,這也告訴我們,在遊戲開發中還是要以實踐為準,不能想當然。

優化 Shader 的輸入資料

由於《江南百景圖》的圖片資源中不會用到 Color 這個屬性,因此在材質中,我們將原有的 Color 資料去除掉。

如何重繪「江南百景圖」?近300頁 PPT 免費分享!

下圖是一個正常的頂點資料:

如何重繪「江南百景圖」?近300頁 PPT 免費分享!

接下來將原有的 Color 資料去除掉,用來存放專案中所需要的其它資訊,這樣可以減少 CPU 與 GPU 互相傳輸的資料量。

如何重繪「江南百景圖」?近300頁 PPT 免費分享!

層級規劃

我們將不同的型別的資源,分別放置在對應的層級中。《江南百景圖》共分了13個層級,下圖只展示了部分比較重要的層級:

如何重繪「江南百景圖」?近300頁 PPT 免費分享!

其中比較有意思的是旗幟層。旗幟是《江南百景圖》中的一個常見元素,但因為專案實際技術限制,無法將一個旗幟製作在一個完整龍骨動畫中,如果強行放在一起,就會導致在渲染到旗幟的時候出現斷批。我們採用了 動態組織層級關係 的方式來解決這個問題。例如這是一個原來的旗幟預製體:

如何重繪「江南百景圖」?近300頁 PPT 免費分享!

採用 動態組織層級關係 的方式,將旗杆與旗面拆開,旗杆放在下面的普通建築物層,旗面則單獨分為一層旗幟層放在上層,這樣就很好地避免了渲染時一直被打斷合批的情況。

如何重繪「江南百景圖」?近300頁 PPT 免費分享!

UI 渲染優化

UI 部分我們沒有使用動態合圖或者 MultiTexture,動態合圖我們留給了遊戲中的人物和建築、而沒有使用 MultiTexture 主要是開發成本的原因。但在我們的優化下,現在遊戲的 Draw Call 可以降得很低。

如何重繪「江南百景圖」?近300頁 PPT 免費分享!

UI 方面我們也是做了分層,比如下面左邊的圖上我們的 button 層,裡面都是按鈕部分,右邊是我們的標籤牌層級。這樣我們就可以根據功能區去劃分圖集,然後和遊戲裡的層級對應起來,而不會打斷合批。

如何重繪「江南百景圖」?近300頁 PPT 免費分享!

自定義引擎

Cocos 是個開源引擎,我們可以根據專案的實際需要,對引擎進行定製、修改,從而達到更好的效果。

增強 TiledMap

我們在 Cocos Creator 原有的 TiledMap 元件的基礎上,擴充了新的功能,下圖是 Cocos 自帶的元件。

如何重繪「江南百景圖」?近300頁 PPT 免費分享!

這裡就不詳細說了,有興趣的可以去官方文件查閱,我們主要來說一下經過擴充的新功能。

如何重繪「江南百景圖」?近300頁 PPT 免費分享!

1.  Diamond Tile:遊戲中使用了很多 TiledMap 中的圖塊菱形方塊, 但是引擎預設的傳遞方式是矩形,這樣就會造成資料浪費和冗餘。

如何重繪「江南百景圖」?近300頁 PPT 免費分享!

這些圖片首先都是 規則的菱形,所以很簡單,直接 根據寬高進行進行計算。

如何重繪「江南百景圖」?近300頁 PPT 免費分享!

將菱形周圍多餘的部分切割,這樣很明顯圖片大小減少了一半,這裡注意一下非標準圖形就不能這麼用了。

2.  Share Culling:《江南百景圖》共有三層 TiledMap 地圖層,勾選時 將只對 TiledMap 的第一個地圖層進行處理判斷可視區域的範圍,而其他的地圖層將直接照搬第一個地圖層的處理結果,這樣能夠節約不少效能。

3.  With Color:如果不需要顏色資料就可以勾選,減少資料量的傳輸。

將道路轉為 Tile

遊戲中的道路是不需要進行淡入淡出效果的,如果當作普通建築物資源來用之前的材質進行渲染,會消耗相當多的效能。因此我們將道路作為 Tile Map 地圖的一部分,讓道路不需要用之前提到的材質進行渲染。

還有一個小細節,在 Tiled Map Editor 中設定的寬高,與實際專案中使用是無關的,因此在生成的時候可以將地圖塊按照實際專案需求進行縮小,減少資源使用。

如何重繪「江南百景圖」?近300頁 PPT 免費分享!

資源壓縮

將一個原版 600+M 的遊戲壓縮到最終的 30M 左右,資源的壓縮工作必不可少。我們需要將遊戲資源進行合理的壓縮,使其更加適合小遊戲執行,並且不影響遊戲最終的顯示效果。

圖片縮放

對不同型別、不同清晰度的資源,我們可以設定不同的縮放比例。我們將大部分的建築縮放到原來的 0.65 倍,背景中的山川則被縮放到原來的 0.3 倍。另外,就算是相同位置上使用的人物立繪,由於每個人物的自身和背景的顏色、精度不同,也都可以給它們設定不同的縮放比例。

如何重繪「江南百景圖」?近300頁 PPT 免費分享!

於是我們將所有 Sprite 元件採用 Custom 模式,可以自由控制比例。不同的圖片使用差異化配置,設定不同的縮放比例,用指令碼控制縮放比例,這樣便可以打包出任意畫質和體積的各種版本,並且還提升了動態合圖的利用率和部分效能。

如何重繪「江南百景圖」?近300頁 PPT 免費分享!

如何重繪「江南百景圖」?近300頁 PPT 免費分享!

圖片減色

綜合比較了大家比較熟知的 tinypng 和 pngquant 兩種工具之後,專案最終選擇使用 pngquant 對 PNG 圖片進行批量壓縮。pngquant 可以自定義壓縮品質,而且 pngquant 開源,容易維護,風險可控。pngquant 也提供像 ImageAlpha 這樣的工具,可以實時檢視圖片減色後的效果,方便調整引數。

pngquant 地址:

https://pngquant.org/

需要注意的是,由於 Cocos 會進行合圖處理,如果對 Build 前的圖片做壓縮,合圖時前期的一些壓縮工作可能就此無效化,所以我們要對 Build 後的圖片 做壓縮處理。

另外我們也建議程式多瞭解一下圖片格式以及其原理。不是所有圖片都要使用 PNG 格式,也會有使用 JPG 的情況。

場景剔除

這部分我們的需求是 只渲染可視物體。那麼用什麼方法確定哪些物體是可見的呢?最開始我們使用了四叉樹,但是在 JS 語言中的效果並不好。所以我們給地圖劃分格子,Grid 的單元格大小要適中,但單元格的邊長應為 2的整次冪,便於利用 位運算 提升效能。

如下圖所示,紅框就是鏡頭,所以需要渲染的也就是這個紅框裡出現的格子。然後我們再根據建築物的座標、大小去進行計算,判斷建築在哪一行哪一列的格子裡,從而確定該建築物是否是需要被渲染的物體。

如何重繪「江南百景圖」?近300頁 PPT 免費分享!

如何重繪「江南百景圖」?近300頁 PPT 免費分享!

這是一段簡單的檢測函式 大家可以根據自己的專案需求去進行擴充套件。

如何重繪「江南百景圖」?近300頁 PPT 免費分享!

除此之外,為了防止特殊情況出現,判斷的可視範圍需要比實際範圍更大一些。

如何重繪「江南百景圖」?近300頁 PPT 免費分享!

尋路

《江南百景圖》使用的尋路演算法,有針對單源單點的 A* 和單源多點的 Dijkstra。但這裡我們要講的不是尋路演算法,而是在遊戲中的用法優化。

針對地圖很大、建築物和人物都很多的情況下,這些演算法一起執行就會很損耗效能。所以我們用了 分時尋路,就是把尋路過程由一幀分到若干幀去進行計算,這樣就不會在某一個時間段集中進行大量運算,對遊戲效能也不會有太大的影響。

如何重繪「江南百景圖」?近300頁 PPT 免費分享!

除此之外我們還在遊戲裡做了一個大膽的優化,就是統一管理尋路任務,同一時間只為一個角色服務。也許有人會問,那豈不是一個角色在哪裡走、其他物件都在那邊等著?其實真正在遊戲裡不會有這種奇怪的表現。首先每個角色尋路的起始和結束時間都不一樣,再者這個同一時間是非常短的,就等於把角色尋路分配到了不同幀裡,交替進行執行。

再談效能

模糊特效

玩家在開啟《江南百景圖》的任意介面時,遊戲的背景需要做模糊處理,而背景中的人物動畫等仍需要正常播放。

如何重繪「江南百景圖」?近300頁 PPT 免費分享!

在經過一系列的研究(可參考 PPT 中資料)後,我們選擇了一個較好的方案,將場景渲染到一個小的 RenderTexture 上,然後將其通過 Kawase 模糊後再放大 顯示,如下圖所示。

如何重繪「江南百景圖」?近300頁 PPT 免費分享!

RenderTexture 池

在小遊戲或 Web 端 建立 RenderTexture 時,比較損耗效能。所以我們在遊戲中使用完 RenderTexture 後,不是直接銷燬,而是將其放在一個 快取池 中,下次從快取池中呼叫符合要求的 RenderTexture 即可。

點選檢測

《江南百景圖》中有很多建築物,而在使用者點選時,並非簡單地通過地形上的塊做判斷,而是給每個建築物畫了一個 多邊形檢測區域。但是建築物是移動的,如果 多邊形檢測區域 也隨之移動,從效能和邏輯上都不是好的處理方式。

如何重繪「江南百景圖」?近300頁 PPT 免費分享!

於是在實際操作中,我們讓建築物移動,而對應的 多邊形檢測區域 不做移動,並將其設定在原點座標上。使用者點選操作時,將點選的座標減去建築物相對原點的座標,就可以進行點選檢測了。同理如果建築物是反轉狀態,可以將點選座標進行映象,而 多邊形檢測區域 仍然可以不做調整。類似還有其他情況,大家也可以去了解一下各情況下對多邊形的處理方式。

陣列排序

陣列排序是大家容易忽略的一個優化模組,Array.sort() 這樣的 快速排序 演算法,更適用於混亂無章的資料。而在《江南百景圖》中,每幀都會對場景中的人物和建築物進行排序,而連續的兩幀之間差異不會很大,也就是 相對有序 的資料,而這更適合使用 插入排序 演算法。

如何重繪「江南百景圖」?近300頁 PPT 免費分享!

其他優化

「閱後即焚」

遊戲中存在一些低頻顯示的大圖,例如進入遊戲時的公告、抽到的卡片等,玩家在遊戲中看一遍就不會再出現了,對於這一類我們用了“閱後即焚”的思路。

如何重繪「江南百景圖」?近300頁 PPT 免費分享!

像這些大圖,我們通常先從遠端伺服器下載到本地快取,產生 Image 物件,還有cc.Texture2D、renderer.Texture2D。

如何重繪「江南百景圖」?近300頁 PPT 免費分享!

我們通過虛擬碼來簡單講解一下。載入圖片時,將圖片新增到我們自己建立的回收用工具類 TextureRecycle 中。

如何重繪「江南百景圖」?近300頁 PPT 免費分享!

檢視關閉時,通過工具類回收這些圖片。

如何重繪「江南百景圖」?近300頁 PPT 免費分享!

在圖片的回收階段中,就可以將以上所有用到的物件都清理乾淨了。

如何重繪「江南百景圖」?近300頁 PPT 免費分享!

構建優化

在構建釋出流程中,專案使用了大量的自動化指令碼來優化構建流程。包括 全平臺構建、上傳遊戲平臺、資源預處理和後處理、CDN 同步和版本控制和二次混淆加密 等。但成也指令碼敗也指令碼,過長的構建時間也造成了不少困擾,因此我們也需要做一些額外優化。

Cocos 新版本新增了一個第三方開源壓縮工具 Sharp,壓縮級別是0-9,數值越大壓縮越久,Cocos 的預設引數是 6。由於我們已經進行過 圖片減色 處理,因此我們將引數改為 0,這樣就能減少很多構建的時間。

如何重繪「江南百景圖」?近300頁 PPT 免費分享!

而各平臺構建時間總是格外漫長,原因是在每次平臺構建時,Creator 都要重新生成對應的平臺圖集。找到原因後,我們在每次構建前,將對應目錄中的 info.json 中的 actualPlatform 引數先修改為 對應的平臺名稱 再打包,這個改動使我們的構建時間由之前的 15 分鐘縮短到 10 分鐘左右,提升了 30% 效率。

如何重繪「江南百景圖」?近300頁 PPT 免費分享!

如何重繪「江南百景圖」?近300頁 PPT 免費分享!

在不懈的優化下,我們看到在現場演示時,這個用於官方演示遊戲的高階賬號,在遊戲場景人物都很豐富的情況下,仍然只有 6個 Draw Call。

如何重繪「江南百景圖」?近300頁 PPT 免費分享!


來源: COCOS
原文:https://mp.weixin.qq.com/s/4s-xK0pAP972tDxJ3VchqA


相關文章