在 2026 年 1 月 24 日的 Unity User Group 深圳站活動(dòng)中,詩悅網(wǎng)絡(luò)星辰工作室技術(shù)專項(xiàng)組負(fù)責(zé)人黎其桂帶來演講《永遠(yuǎn)的蔚藍(lán)星球》小游戲性能優(yōu)化實(shí)戰(zhàn),與大家分享在保證高度還原 app 端的效果的前提下,主創(chuàng)團(tuán)隊(duì)在包體、內(nèi)存、渲染等方面付出的心力與經(jīng)驗(yàn),以及如何利用團(tuán)結(jié)引擎的優(yōu)勢(shì)進(jìn)一步減少引擎?zhèn)鹊拈_銷。本文為演講全程實(shí)錄,點(diǎn)擊閱讀原文,可下載演講資料。
![]()
黎其桂:大家好,我是來自詩悅網(wǎng)絡(luò)星辰工作室技術(shù)專項(xiàng)組的黎其桂。很高興今天能來到這里,跟大家一起分享《永遠(yuǎn)的蔚藍(lán)星球》這款微信小游戲的性能優(yōu)化實(shí)戰(zhàn)經(jīng)驗(yàn)。
首先簡(jiǎn)單介紹一下產(chǎn)品。《永遠(yuǎn)的蔚藍(lán)星球》是一款隨機(jī)合成萌系塔防手游,好玩上頭,已于去年 6 月底公測(cè)上線。在完成 APP 端上線后,我們迅速投入到微信小游戲的適配與上線準(zhǔn)備中。從 7 月到 8 月,游戲在微信小游戲平臺(tái)上取得了出色的成績(jī),最高曾登頂暢銷榜第 4 名,目前穩(wěn)定保持在暢銷榜前 20 名。
這次分享,我將重點(diǎn)介紹我們針對(duì)微信小游戲平臺(tái)所做的性能優(yōu)化工作。在微信平臺(tái)開發(fā),往往會(huì)遇到平臺(tái)本身的局限性,我們?yōu)榇嗽诎w、性能、渲染等多個(gè)方面投入了大量精力。
Part 1:WASM 包體
包體優(yōu)化的第一步是常規(guī)手段:清理無用模塊,并開啟最高級(jí)別的代碼裁減。但開啟高級(jí)別裁減后,有時(shí)會(huì)錯(cuò)誤地裁掉一些必要代碼。這時(shí)我們就需要結(jié)合 LinkXML 文件,將需要保留的關(guān)鍵代碼標(biāo)記出來,同時(shí)將代碼裁減級(jí)別調(diào)到最高。
![]()
因?yàn)槲覀兪褂昧?Lua 來實(shí)現(xiàn)系統(tǒng)和界面的邏輯,在 Lua 的封裝(Wrap)代碼中,往往存在一些在 Lua 端很少用到的 C# 接口或代碼。我們也將這部分內(nèi)容進(jìn)行了優(yōu)化和裁減。具體做法是,先收集所有已導(dǎo)出 wrap 的類型和成員到一個(gè)列表中,再遍歷所有 lua 文件,通過字符串匹配的方式判斷已導(dǎo)出的 wrap 是否有調(diào)用,剔除掉未被調(diào)用的 wrap,最終把需要過濾的成員函數(shù)接入到 tolua 的導(dǎo)出過濾里面,最終把整包大小(webgl.wasm)優(yōu)化到了 30m 以內(nèi)。
![]()
最后,我們使用了Wasm Analysis工具(團(tuán)結(jié)引擎中已經(jīng)集成了此工具)來校驗(yàn)裁減結(jié)果是否符合預(yù)期。這個(gè)工具非常好用,能夠幫助我們精準(zhǔn)定位問題,高效解決包體體積的問題。
![]()
Part 2:WASM 代碼分包
第二,代碼分包。代碼分包在一定意義上來說,是小游戲平臺(tái)再進(jìn)一步的代碼裁剪的手段,因?yàn)樗前l(fā)布前,通過真實(shí)跑游戲去收集過程中使用到的函數(shù),這些收集到的代碼函數(shù)就作為首包,在進(jìn)入游戲時(shí)加載出來,剩下的用到才會(huì)去線上拉取。
代碼分包對(duì)開發(fā)者而言有利有弊。好處在于,分包能大大降低首包大小,提升啟動(dòng)速度和有效轉(zhuǎn)化,同時(shí)也能降低首包編譯內(nèi)存;壞處在于,既要“小”,又要“全”,這在工程上比較麻煩。微信平臺(tái)的分包機(jī)制在安卓和 iOS 上有所不同:安卓可以在后臺(tái)完整加載分包,而 iOS 則需要運(yùn)行時(shí)逐函數(shù)拉取。如果 iOS 分包不完整,觸發(fā)了連續(xù) 5 個(gè)以上的分包拉取,用戶體驗(yàn)會(huì)非常糟糕。因此,我們必須做到“既小又全”。
我們的做法是:
首次收集后進(jìn)行人工微調(diào)。
通過工具實(shí)現(xiàn)增量管理。
遵循經(jīng)驗(yàn):首包占比 30% 是一個(gè)較優(yōu)的比例。
這里有必要介紹一下我們的分包輔助工具。在做微信分包時(shí),我們發(fā)現(xiàn)分包界面提供了兩個(gè)手動(dòng)管理的按鈕。出于好奇點(diǎn)擊后,發(fā)現(xiàn)“新增函數(shù)”和“總收集函數(shù)”的格式與 symbol 符號(hào)表文件里面非常相似。于是我們基于這個(gè)思路,通過反序列化了 symbol 文件,用新包的 symbol 去 diff 舊包,導(dǎo)出一份新增的函數(shù)列表,用這份列表導(dǎo)入到微信分包工具中就可以保證業(yè)務(wù)迭代的代碼都能加到首包里面。這樣能做到版本增量更新后腳本自動(dòng)跑增量分包,提高效率提高準(zhǔn)確率。最終也能保持分包后,首包函數(shù)占比 30% 左右,整體運(yùn)行流暢。
![]()
Part 3:大量同屏元素
對(duì)于肉鴿塔防游戲來說,“爽感”至關(guān)重要,而這往往意味著滿屏的怪物、滿屏的特效和滿屏的飄字。這種感官體驗(yàn)帶來的是巨大的性能挑戰(zhàn)。
以《永遠(yuǎn)的蔚藍(lán)星球》為例,游戲場(chǎng)景中存在高達(dá) 5000+ 的粒子系統(tǒng)(Particle System)、同屏超過 200+ 怪物以及 3000+ 的傷害飄字。為了保證還原 app 的效果和品質(zhì),數(shù)量不能減少,要在小游戲平臺(tái)做到大量級(jí),同時(shí)兼顧性能。面對(duì)這種同屏場(chǎng)景,無論是在內(nèi)存、CPU 還是渲染層面,都帶來了巨大的壓力。
![]()
Part 3-1: GPU 序列幀
面對(duì)原生 Particle System 高昂的性能消耗(每個(gè)組件約占用 10KB),我們的第一反應(yīng)是:盡量不用粒子系統(tǒng)。雖然它的表現(xiàn)力好,但 DC 過高,性能代價(jià)過大。
我們轉(zhuǎn)而采用GPU 序列幀方案。之所以叫 “GPU 序列幀”,是因?yàn)槌R?guī)序列幀(如使用 Animate 組件或腳本輪詢圖集)是運(yùn)行在 CPU 上的。而我們的優(yōu)化思路是,在微信小程序這樣的平臺(tái),CPU 壓力本就很大,因此要把能轉(zhuǎn)移到 GPU 的邏輯都轉(zhuǎn)移到 GPU 上去執(zhí)行,所以就定義它為 GPU 序列幀。
![]()
具體實(shí)現(xiàn)上,關(guān)鍵點(diǎn)是利用了GPU Instancing。我們將粒子的表現(xiàn)效果烘焙到圖集或圖片中,然后在 GPU 中進(jìn)行計(jì)算和還原。優(yōu)化后,原來 1500+ DrawCall 的場(chǎng)景可以通過 GPU Instancing 合批到 233 個(gè) batches,簡(jiǎn)單而有效。
![]()
Part 3-2: GPU粒子
既然序列幀很有效,為什么還要做 GPU 粒子?因?yàn)樾蛄袔衅渚窒扌浴.?dāng)面對(duì)全屏播放、幀數(shù)很高的特效時(shí),序列幀圖集會(huì)變得異常龐大,得不償失。另外不支持 3D,比如立體透視的龍卷風(fēng)、拖尾等,而且存在填充率問題,overdraw 很高。
![]()
因此,我們引入了GPU 粒子的概念,使用VAT(Vertex Animation Texture,頂點(diǎn)動(dòng)畫貼圖)。它的核心思想是,把大網(wǎng)格、ID 索引、粒子的屬性和材質(zhì)的屬性都烘焙下來,裝到頂點(diǎn)著色器里,在頂點(diǎn)著色器中用 SV_VertexID 去逐步解析出粒子和粒子系統(tǒng)的 ID,再去獲取對(duì)應(yīng)下標(biāo)的各項(xiàng)數(shù)據(jù),從而將計(jì)算壓力完全轉(zhuǎn)移到 GPU。
![]()
在實(shí)踐中,我們克服了兩個(gè)主要難點(diǎn):
動(dòng)態(tài)變化的粒子記錄:粒子系統(tǒng)的一大特點(diǎn)是粒子隨著時(shí)間會(huì)不斷的生成和消失,常規(guī) VAT 做法以橫坐標(biāo)為粒子數(shù)、縱坐標(biāo)為幀數(shù),會(huì)導(dǎo)致貼圖空間浪費(fèi)。我們?cè)诤姹毫W有畔r(shí),舍棄了粒子的固定順序,把貼圖寬度壓縮為同時(shí)存在的最大粒子數(shù)目。雖然放棄了粒子的嚴(yán)格先后順序,不能采樣相鄰幀做動(dòng)畫的平滑,但極大地壓縮了貼圖大小,降低了內(nèi)存和帶寬消耗。
![]()
多粒子系統(tǒng)網(wǎng)格烘焙:另一個(gè)難點(diǎn)是,特效可能包含多個(gè)粒子系統(tǒng),而每一個(gè)粒子系統(tǒng)可能又由多種網(wǎng)格組成。這時(shí)候可以以單個(gè)粒子系統(tǒng)的單種網(wǎng)格為最小單元,把每個(gè)單元的貼圖進(jìn)行水平拼接。
![]()
實(shí)現(xiàn)時(shí),在將資源烘焙好之后,需要記錄兩張索引表來還原粒子 ID 和粒子系統(tǒng) ID,再由此讀出烘焙好的各類信息。其中粒子 ID 可以用來讀取逐粒子的信息,比如每個(gè)粒子的 Transform;粒子系統(tǒng) ID 可以用來讀取逐粒子系統(tǒng)的信息,比如材質(zhì)參數(shù)。最終在頂點(diǎn)渲染階段完成所有計(jì)算,這樣邏輯、渲染都在 GPU 層面。
![]()
這一優(yōu)化不僅降低了 CPU 負(fù)荷,還讓我們可以精確控制渲染層級(jí),解決了原生粒子系統(tǒng)在層級(jí)穿插上難以把控的問題。
![]()
Part 3-3: 怪物 GPU 動(dòng)畫
三是怪物 GPU 動(dòng)畫,還是繼續(xù)壓榨 GPU。在這個(gè)場(chǎng)景里有同屏 200+ 的怪物,對(duì)于 2D 游戲,動(dòng)畫常用 Spine 組件。但 Spine 組件存在內(nèi)存占用高、CPU 開銷大、實(shí)例無法共享的問題(200 個(gè)相同怪物就要 200 份消耗)。
![]()
我們的思路依然是 “CPU 轉(zhuǎn)移到 GPU”,即將怪物動(dòng)畫也變成頂點(diǎn)動(dòng)畫(VAT)形式。在將 Spine 動(dòng)畫烘焙為 VAT 的過程中,同樣遇到了挑戰(zhàn):
動(dòng)畫部件顯隱記錄:Spine 動(dòng)畫播放過程中會(huì)實(shí)時(shí)改變部件的顯示和隱藏,例如武器投擲,特效附件的出現(xiàn)和隱藏,部件的切換等,為解決該問題,我們?cè)谧灾频?Spine 導(dǎo)出工具中,把所有附件預(yù)烘焙到一個(gè)靜態(tài) Tpose 網(wǎng)格中(右圖),當(dāng)某一幀某個(gè)附件需要隱藏時(shí),VAT 會(huì)在對(duì)應(yīng)位置記錄一個(gè)非法值(如負(fù)無窮大),播放時(shí)讀取到非法的頂點(diǎn)位置信息就會(huì)自動(dòng)隱藏。
![]()
部位之間的層級(jí)變化:動(dòng)畫播放過程中還會(huì)出現(xiàn)部件之間渲染順序變化的問題,如左圖,渲染順序從右手-->身體-->左手,變成身體-->左手-->右手。原生 Spine 對(duì)部件之間渲染順序的控制是通過頂點(diǎn)的 Z 軸坐標(biāo)實(shí)現(xiàn)的,因此在烘培 VAT 時(shí),會(huì)根據(jù) SlotIndex 和 zSpacing 計(jì)算出每幀對(duì)應(yīng)部件的 Z 軸坐標(biāo)。
![]()
最終導(dǎo)出的核心資源:
TPose 網(wǎng)格(包括所有部件)
vat 貼圖(黑色部分就是寫入的非法值)
同個(gè) Spine 的多個(gè)實(shí)例可以合成一個(gè) DC。所以我們實(shí)現(xiàn)了能合批的 spine GPU 動(dòng)畫,而且上游制作流程無感。
![]()
優(yōu)化后,GPU Spine 對(duì)比原生 spine 在耗時(shí)、drawcall 和內(nèi)存上都有很大的優(yōu)化。
![]()
![]()
Part 4:高性能飄字
我們的舊版飄字存在一個(gè)問題,它是通過一個(gè)大網(wǎng)格管理同屏中所有出現(xiàn)的飄字,并通過網(wǎng)格通道傳遞飄字信息的,在每次生成飄字的時(shí)候需要重建整個(gè)大網(wǎng)格,一方面重建網(wǎng)格會(huì)造成 CPU 耗時(shí),另一方面重建后需要把網(wǎng)格重新從 CPU 傳到 GPU 上,會(huì)造成帶寬浪費(fèi)。
![]()
我們最初的優(yōu)化方案是預(yù)分一個(gè)大網(wǎng)格,將所有字體烘焙進(jìn)去。但這仍是“舊版方案”,每飄一個(gè)字就要重建一次網(wǎng)格,仍然會(huì)造成性能峰值,并且在 CPU 向 GPU 傳遞大量數(shù)據(jù)時(shí)會(huì)產(chǎn)生嚴(yán)重的帶寬鋸齒。
![]()
我們的“新版方案”核心是使用 CBuffer 傳遞飄字信息,拋棄了每次更新都重建網(wǎng)格的方式,只需要通過材質(zhì)修改對(duì)應(yīng) Cbuffer 即可完成。
![]()
改版后,不再通過網(wǎng)格通道記錄數(shù)據(jù),現(xiàn)在的網(wǎng)格只使用了一個(gè) 4 字節(jié)大小的 SV_VertexID。以往逐頂點(diǎn)記錄的數(shù)據(jù)現(xiàn)在可以逐文本記錄,并且對(duì)數(shù)據(jù)進(jìn)行了充分壓縮,充分利用了總量為 16k 的 Cbuffer,大幅降低帶寬浪費(fèi),極致地完成了飄字效果還原。
![]()
優(yōu)化后,新版飄字保證了原有飄字?jǐn)?shù)量 3000+ 的前提下,可以看到左上角的帶寬更穩(wěn)定了,帶寬峰值從每幀 8mb 降到每幀 3mb。
![]()
不僅線條平滑,在游戲高壓場(chǎng)景下(合作模式第十層)最低幀率從 32 上升到 48。
![]()
Part 5:團(tuán)結(jié)引擎?zhèn)葍?yōu)化
團(tuán)結(jié)引擎帶來的直接收益
除了業(yè)務(wù)層面的優(yōu)化,《永遠(yuǎn)的蔚藍(lán)星球》也受益于團(tuán)結(jié)引擎的優(yōu)化升級(jí)。團(tuán)結(jié)引擎為小游戲提供了針對(duì)性的優(yōu)化,帶來了立竿見影的性能收益:
il2cpp 虛擬機(jī)優(yōu)化:減少約 30MB 內(nèi)存占用。
內(nèi)存分配器 overhead 優(yōu)化:減少約 7-11MB 內(nèi)存。
粒子系統(tǒng)內(nèi)存減少約 15MB:剔除未使用的粒子系統(tǒng)模塊內(nèi)存占用,粒子系統(tǒng)播放結(jié)束釋放內(nèi)存,數(shù)量越多收益越大,該功能默認(rèn)開啟。
TypeTree 內(nèi)存優(yōu)化:小游戲可只加載 MonoBehaviour 對(duì)應(yīng)的 TypeTree,節(jié)省序列化文件內(nèi)存,減少約 25MB 內(nèi)存。
此外,Slim Global-Metadata、String Intern Pool、Dynamic Buff Reuse 等優(yōu)化也貢獻(xiàn)了可觀的增益。
總計(jì),從 Unity 引擎切換到團(tuán)結(jié)引擎,我們直接獲得了約 100MB 的內(nèi)存下降。這對(duì)于開發(fā)者是巨大的好處,并且線上閃退率也直接砍半。
![]()
WebGL Metal
我們?cè)?iOS 平臺(tái)上還使用了Metal渲染后端。Metal 通過直接調(diào)用 iOS 原生 API,避免了 WebGL 到 Metal 的中間轉(zhuǎn)換層,減少了 GPU 帶寬消耗和功耗。從實(shí)際測(cè)試數(shù)據(jù)看,在幀率和亮度一致的情況下,使用 Metal API 在 iOS 機(jī)上的功耗下降了 17%,帶來了更好的體感和設(shè)備溫度表現(xiàn)。
![]()
以上就是我今天分享的小游戲優(yōu)化。雖然是小游戲,但其中的優(yōu)化技術(shù)卻并不“小”。我們對(duì)包體、渲染、引擎底層等多個(gè)層面進(jìn)行了深度探索和實(shí)踐。未來,我們也期待著像團(tuán)結(jié)引擎正在開發(fā)的Infinity 粒子系統(tǒng)這樣的新技術(shù)能盡快上線,這能極大地減輕我們手動(dòng)定制 VAT 的工作負(fù)擔(dān)。
最后,希望大家還是要以一個(gè)精品游戲探索者的角色,堅(jiān)持做難而正確的事情。
以上就是我的分享,希望大家可以一起交流學(xué)習(xí),謝謝。
UUG(Unity用戶組織)是一個(gè)連接本地 Unity 開發(fā)者的社群網(wǎng)絡(luò)。十年來,全國各地的志愿者們?cè)诒本⑸虾!V州、深圳、杭州、武漢、成都、廈門、沈陽等城市成功舉辦了 50+ 線下交流活動(dòng)。2026 年,我們希望能構(gòu)建一個(gè)具備更多可能性的場(chǎng)域,讓創(chuàng)作的能量在這里流動(dòng)。
深圳 UUG 的演講資料已上傳至網(wǎng)盤,點(diǎn)擊閱讀原文下載,持續(xù)關(guān)注學(xué)習(xí)。
Unity 官方微信
第一時(shí)間了解Unity引擎動(dòng)向,學(xué)習(xí)進(jìn)階開發(fā)技能
每一個(gè)“點(diǎn)贊”、“在看”,都是我們前進(jìn)的動(dòng)力
![]()
特別聲明:以上內(nèi)容(如有圖片或視頻亦包括在內(nèi))為自媒體平臺(tái)“網(wǎng)易號(hào)”用戶上傳并發(fā)布,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.