每個Unity開發者都經歷過這種落差:開發機上絲滑流暢,真機一跑幀率暴跌、手機發燙、電量肉眼可見地往下掉。Ocean View Games在Domi Online項目里處理過10萬棵GPU實例化樹木的渲染,創始人曾在Jagex負責RuneScape手游優化——這套內部清單就是他們用血淚換來的。
優化不是最后打補丁,而是貫穿開發全程的紀律。但即便小心翼翼,問題還是會冒出來:Draw Call堆積、紋理膨脹、GC(垃圾回收) spikes在Profiler里準時打卡。這份清單按優先級排序,從分析工具開始,因為盲目優化是災難。
![]()
第0步:先分析,再動手
![]()
這是鐵律。永遠不要憑直覺優化。必須在真機上分析,定位真實瓶頸,再針對性解決。
核心工具包:Unity Profiler(CPU/GPU/內存/渲染模塊,USB或WiFi連接真機,Editor數據不可信)、Frame Debugger(逐幀查看渲染順序,揪出冗余Draw Call和Overdraw)、Memory Profiler(Package Manager單獨安裝,追蹤紋理/網格/音頻內存)、Xcode Instruments(iOS Metal System Trace看GPU底層)、Android GPU Inspector/Snapdragon Profiler(高通芯片專項)。
分析前回答三個問題:CPU瓶頸還是GPU瓶頸?Profiler時間軸一目了然——CPU幀時超過GPU,問題在腳本/物理/動畫,而非渲染。Spikes在哪?穩定20fps和周期性200ms卡頓是兩種病,后者通常是GC或一次性高開銷操作。內存 footprint 多少?移動端有硬上限,超了就靜默崩潰,連報錯日志都沒有。
關鍵原則:永遠在最低配目標設備上分析。iPhone 15 Pro跑60fps、三年前的Android閃退——這不叫優化,叫沒測試。
渲染與Draw Call
渲染是移動端最吃資源的環節。Unity默認設置對PC友好,對手機是災難。策略分三層:減少Draw Call、降低Overdraw、控制著色器復雜度。
Draw Call合并:Static Batching對靜態場景有效,Dynamic Batching適合小網格但CPU開銷大,GPU Instancing是大規模重復物體的最優解(Domi Online的10萬棵樹靠這個)。SRP Batcher在URP/HDRP下能大幅降低CPU準備時間,但要求Shader兼容。
Overdraw控制:移動端填充率有限,半透明疊加是殺手。用Frame Debugger檢查,確保UI和粒子系統不堆疊。Opaque物體從前到后渲染,Unity會自動Early-Z剔除;如果順序亂了,在URP里調整Render Queue。
Shader精簡:復雜光照計算、逐像素效果在移動端是奢侈品。用Simple Lit代替Lit,移動端專用Shader變體,關閉不必要的Keyword。在Xcode Instruments里看GPU時間分布,Fragment Shader占比過高就是像素殺手的信號。
紋理與內存
紋理是內存大戶。原則:用得上才加載,用完立刻釋放。
導入設置:移動端強制ASTC(iOS/Android通用,4x4到12x12質量可選)、ETC2(Android fallback)、PVRTC(舊iOS)。禁用Read/Write Enabled(內存翻倍),非UI紋理關閉Mip Maps(省30%內存),UI紋理開啟(避免遠處鋸齒)。Max Size按實際屏幕占比設置,1024x1024的圖標在手機上可能是浪費。
圖集策略:Sprite Atlas減少Draw Call,但尺寸失控會爆內存。按場景/功能拆分,運行時動態加載。Memory Profiler里盯緊Texture2D的Native內存,和Profiler里的Total Reserved對比,差距大說明有泄漏。
資源流送:Addressables或AssetBundle按需加載,場景切換時UnloadUnusedAssets——但注意這會觸發GC,避開性能敏感期。
![]()
代碼與GC
托管內存的GC spikes是30fps游戲的幀率殺手。C#的便利是有代價的。
零分配原則:Update里不new對象,用對象池(對象池本身用Queue/Stack預分配)。LINQ和閉包在循環里禁用,它們背后偷偷裝箱。字符串用StringBuilder拼接,或者直接用插值(C# 10起分配優化了,但仍要Profiler驗證)。
結構體陷阱:非只讀結構體傳參會復制,大結構體改class。但class有GC壓力,權衡看Profiler的GC Alloc列。
異步加載:AssetBundle.LoadAssetAsync、SceneManager.LoadSceneAsync不阻塞主線程,但回調里別做重活。AsyncOperation.completed += 是C#事件,記得取消訂閱防泄漏。
Job System:Burst編譯的并行Job把CPU密集型工作從主線程剝離,適合大規模計算(尋路、物理模擬)。但Job數據布局要連續,指針和托管對象進不了Job。
物理與動畫
Physics.Simulate默認在FixedUpdate跑,移動端降到30fps或更低。減少Rigidbody數量,靜態碰撞器用Mesh Collider是性能自殺——凸包分解或干脆用Box/Sphere近似。Layer Collision Matrix關掉不交互的層,Broad Phase剔除效率翻倍。
動畫:Mecanim的Blend Tree在運行時計算,簡單動畫用Simple Animation組件或DOTween。骨骼數量直接關聯GPU Skinning開銷,LOD級別里砍低級模型的骨骼數。Animator的Culling Mode設成Cull Completely,屏幕外不更新。
音頻與雜項
AudioClip的Load Type:短音效Decompress On Load(CPU換內存),長音樂Streaming(內存換IO),中等Compressed In Memory。Sample Rate強制22050或更低,移動端聽不出44100的區別。
最后檢查項:Quality Settings按平臺覆蓋,移動端關掉抗鋸齒或只用FXAA;V Sync Count設成Every Second V Blank(30fps目標)或Don't Sync(自己控制);Target Frame Rate顯式設置,Unity默認300fps燒電。
發布前在最低配設備上跑完整流程:冷啟動、連續玩30分鐘、后臺切回、接打電話后恢復。記錄溫度、電量、幀率曲線。Profiler里沒問題的代碼,真機熱節流后會暴露。
這份清單不是一次性任務,是迭代循環。每加一個功能,重新Profile。性能預算和美術預算一樣,超支就得砍。
特別聲明:以上內容(如有圖片或視頻亦包括在內)為自媒體平臺“網易號”用戶上傳并發布,本平臺僅提供信息存儲服務。
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.