端鎖幀優(yōu)化:降低發(fā)熱、穩(wěn)定幀率的實(shí)操指南)
1. 鎖幀這件事為什么值得單獨(dú)拎出來講做移動(dòng)端項(xiàng)目的人大概都有過這種體驗(yàn)游戲邏輯明明不重Draw Call 也壓到了兩百以內(nèi)Shader 也沒用什么花哨的東西可手機(jī)一跑起來背面燙得能煎雞蛋十分鐘后幀率從 60 掉到 40再過一會(huì)兒直接卡成 PPT。你去查 ProfilerCPU 和 GPU 的耗時(shí)看起來都還行就是溫度壓不住。這種情況我遇到過太多次了最后發(fā)現(xiàn)問題往往不在渲染本身而在于一個(gè)很多人壓根沒在意的東西——幀率上限。這篇是發(fā)燙優(yōu)化系列的第七篇前面幾篇聊過 Draw Call 合并、紋理壓縮、Shader 簡化、GC 控制、物理更新頻率、UI 重建這些方向今天專門把鎖幀這個(gè)手段拿出來單獨(dú)講。原因很簡單它是所有發(fā)熱優(yōu)化手段里改動(dòng)成本最低、見效最快、副作用最可控的一個(gè)。你不需要重構(gòu)渲染管線不需要換 Shader甚至不需要美術(shù)配合只要在代碼里加一行Application.targetFrameRate 30很多項(xiàng)目的發(fā)熱問題就能緩解一大半。但鎖幀這件事說簡單也簡單說坑也真不少。鎖多少合適什么時(shí)候鎖鎖了之后動(dòng)畫會(huì)不會(huì)卡不同平臺(tái)行為一樣嗎Unity 的targetFrameRate和QualitySettings.vSyncCount到底誰說了算這些問題如果不想清楚鎖幀可能不但沒優(yōu)化發(fā)熱反而把體驗(yàn)搞得更糟。我見過有項(xiàng)目直接鎖 30結(jié)果 UI 滑動(dòng)像幻燈片也見過鎖了幀但沒關(guān)垂直同步實(shí)際根本沒生效的。所以這篇不打算只給你一個(gè)結(jié)論而是把鎖幀背后的邏輯、參數(shù)選擇、平臺(tái)差異、實(shí)操步驟和踩坑經(jīng)驗(yàn)完整地講一遍。不管你是剛接觸 Unity 的新手還是已經(jīng)做過幾個(gè)上線項(xiàng)目的老手只要你的項(xiàng)目在移動(dòng)端有發(fā)熱困擾這篇內(nèi)容都能直接拿去用。核心關(guān)鍵詞就幾個(gè)鎖幀、幀率、發(fā)熱、Unity、targetFrameRate圍繞它們把這件事講透。2. 幀率與發(fā)熱的底層關(guān)系拆解2.1 為什么幀率越高手機(jī)越燙要理解鎖幀為什么能降溫得先搞清楚手機(jī)發(fā)熱的來源。手機(jī) SoC 里跟游戲幀率直接相關(guān)的主要是 CPU 和 GPU 兩大塊它們每渲染一幀都要做大量運(yùn)算CPU 負(fù)責(zé)邏輯更新、物理模擬、動(dòng)畫計(jì)算、提交渲染命令GPU 負(fù)責(zé)頂點(diǎn)變換、光柵化、像素著色。這些運(yùn)算本質(zhì)上都是電流在晶體管里跑來跑去有電流就有功耗有功耗就有熱量。關(guān)鍵點(diǎn)在于功耗和運(yùn)算量基本成正比而運(yùn)算量和幀率成正比。你跑 60 幀意味著每秒要做 60 次完整的邏輯加渲染循環(huán)跑 30 幀運(yùn)算量直接砍半。功耗降下來發(fā)熱自然就少了。這不是線性的簡單關(guān)系因?yàn)檫€有漏電流、電壓調(diào)節(jié)等復(fù)雜因素但大方向上幀率減半GPU 負(fù)載和 CPU 邏輯負(fù)載大致減半整機(jī)功耗能降 30% 到 40%。更麻煩的是熱積累的惡性循環(huán)。手機(jī)沒有風(fēng)扇散熱全靠被動(dòng)傳導(dǎo)。當(dāng) SoC 溫度升到某個(gè)閾值通常 40 到 45 攝氏度左右系統(tǒng)會(huì)主動(dòng)降頻保護(hù)硬件這時(shí)候幀率會(huì)突然掉下來玩家感受到的就是卡頓。而降頻之后如果負(fù)載還是很高溫度繼續(xù)升降頻更狠最后就是持續(xù)卡頓加燙手。鎖幀的作用就是在源頭把負(fù)載壓下去讓溫度維持在一個(gè)不會(huì)觸發(fā)降頻的區(qū)間幀率反而更穩(wěn)定。我做過一個(gè)實(shí)測對(duì)比同一款中度休閑游戲在中端安卓機(jī)上跑不鎖幀時(shí)前 3 分鐘穩(wěn)定 60 幀第 5 分鐘開始掉到 50 左右第 10 分鐘穩(wěn)定在 38 到 42 幀波動(dòng)機(jī)身背面最高溫度 46 度鎖 30 幀后全程穩(wěn)定 30 幀10 分鐘后溫度只有 38 度左右手感明顯溫和很多。幀率數(shù)字是低了但體驗(yàn)的一致性反而更好因?yàn)椴粫?huì)出現(xiàn)那種忽高忽低的抖動(dòng)。2.2 幀率、刷新率、垂直同步三者的關(guān)系很多人把幀率、刷新率、垂直同步混為一談這里必須理清楚否則鎖幀參數(shù)很容易設(shè)錯(cuò)。幀率Frame Rate是你的游戲每秒實(shí)際渲染出多少幀由你的代碼和硬件性能決定。刷新率Refresh Rate是屏幕硬件每秒刷新多少次畫面現(xiàn)在手機(jī)普遍是 60Hz、90Hz、120Hz 甚至 144Hz。垂直同步VSync是一種同步機(jī)制讓游戲的幀輸出跟屏幕刷新對(duì)齊避免畫面撕裂。Unity 里控制這三者的關(guān)鍵參數(shù)有兩個(gè)Application.targetFrameRate和QualitySettings.vSyncCount。它們的關(guān)系有個(gè)很多人不知道的規(guī)則當(dāng) vSyncCount 不為 0 時(shí)targetFrameRate 會(huì)被忽略。也就是說如果你在 Quality Settings 里開了垂直同步代碼里設(shè)的 targetFrameRate 根本不生效實(shí)際幀率會(huì)被鎖定到屏幕刷新率的整數(shù)分之一。舉個(gè)例子屏幕 60HzvSyncCount 設(shè)為 1那游戲幀率就被鎖在 60vSyncCount 設(shè)為 2鎖在 30。屏幕如果是 120HzvSyncCount 為 1 時(shí)鎖 120為 2 時(shí)鎖 60。這就是為什么有些人設(shè)了 targetFrameRate 30 卻發(fā)現(xiàn)幀率還是 60因?yàn)榇怪蓖桨阉采w了。所以正確的鎖幀姿勢是先把 vSyncCount 設(shè)為 0再設(shè)置 targetFrameRate。這樣 targetFrameRate 才能真正生效。當(dāng)然關(guān)掉垂直同步可能會(huì)帶來畫面撕裂但在移動(dòng)端小屏幕上撕裂通常不明顯而且發(fā)熱優(yōu)化的收益遠(yuǎn)大于撕裂帶來的視覺影響。如果你確實(shí)在意撕裂那就用 vSyncCount 來控制幀率但靈活性會(huì)差很多因?yàn)橹荒苕i到刷新率的整數(shù)分之一。2.3 鎖幀不等于降畫質(zhì)這是兩碼事有個(gè)常見的誤解需要澄清鎖幀不是降低畫質(zhì)。畫質(zhì)取決于分辨率、紋理精度、Shader 復(fù)雜度、后處理效果這些鎖幀只是控制每秒渲染多少次。你完全可以在鎖 30 幀的同時(shí)保持高畫質(zhì)只是每幀之間的間隔變長了。這帶來一個(gè)很重要的策略空間當(dāng)性能預(yù)算有限時(shí)你可以選擇高畫質(zhì)低幀率或者低畫質(zhì)高幀率。對(duì)于不同類型的游戲最優(yōu)解完全不同。休閑益智類、卡牌類、模擬經(jīng)營類30 幀完全夠用把省下來的性能預(yù)算堆到畫質(zhì)上畫面更精致玩家更買賬。而競技類、動(dòng)作類、音游類幀率就是生命線寧可降畫質(zhì)也要保 60 幀。我個(gè)人的經(jīng)驗(yàn)是大部分手游其實(shí)并不需要 60 幀。玩家對(duì)幀率的敏感度遠(yuǎn)低于開發(fā)者想象尤其是非動(dòng)作類游戲。你鎖 30 幀只要幀生成時(shí)間穩(wěn)定沒有明顯卡頓絕大多數(shù)玩家根本察覺不到。反倒是發(fā)熱導(dǎo)致的降頻卡頓玩家一玩就能感覺到。所以鎖幀在很多時(shí)候是用玩家感知不到的東西換玩家能明顯感知到的東西這筆賬很劃算。3. Unity 中鎖幀的具體實(shí)現(xiàn)與參數(shù)選擇3.1 targetFrameRate 的正確設(shè)置方式在 Unity 里設(shè)置目標(biāo)幀率最直接的就是這一行Application.targetFrameRate 30;但這一行放在哪里、什么時(shí)候調(diào)用是有講究的。我一般會(huì)把它放在游戲啟動(dòng)的初始化腳本里比如一個(gè)專門的PerformanceManager或者GameBootstrap腳本的Awake方法中。不要放在每幀都執(zhí)行的Update里那樣雖然也能生效但沒必要反復(fù)設(shè)置。一個(gè)完整的初始化邏輯大概長這樣using UnityEngine; public class PerformanceManager : MonoBehaviour { [SerializeField] private int targetFrameRate 30; [SerializeField] private bool disableVSync true; void Awake() { if (disableVSync) { QualitySettings.vSyncCount 0; } Application.targetFrameRate targetFrameRate; DontDestroyOnLoad(gameObject); } }這里把vSyncCount設(shè)為 0 是關(guān)鍵前面講過不設(shè)的話 targetFrameRate 會(huì)被忽略。DontDestroyOnLoad保證切換場景時(shí)這個(gè)設(shè)置不會(huì)被重置。有些項(xiàng)目在場景切換后會(huì)重新初始化 QualitySettings導(dǎo)致鎖幀失效加上這個(gè)能避免。另外要注意targetFrameRate 的值必須是整數(shù)而且不能是負(fù)數(shù)-1 表示不限制讓系統(tǒng)自己決定。設(shè)成 0 或者負(fù)數(shù)在部分平臺(tái)上行為不一致建議老老實(shí)實(shí)用正數(shù)。3.2 鎖 30 還是鎖 60怎么選這是最核心的問題。我的判斷邏輯分三層第一層看游戲類型。動(dòng)作、射擊、競速、音游、MOBA 這類對(duì)操作響應(yīng)要求高的優(yōu)先保 60 幀實(shí)在保不住再考慮 45 或者 40。休閑、卡牌、模擬、放置、文字類30 幀足夠甚至 24 幀在某些場景下也能接受。第二層看目標(biāo)機(jī)型。如果你的用戶主要是中低端機(jī)那 60 幀本身就是奢望不如直接鎖 30把穩(wěn)定性做上去。如果是旗艦機(jī)用戶為主可以嘗試 60 幀但要做好溫控策略比如溫度升高后動(dòng)態(tài)降到 45 或 30。第三層看發(fā)熱預(yù)算。你可以做個(gè)簡單的測試不鎖幀跑 10 分鐘記錄溫度和幀率曲線鎖 60 跑 10 分鐘記錄鎖 30 跑 10 分鐘記錄。對(duì)比下來哪個(gè)檔位能在你設(shè)定的溫度上限內(nèi)保持穩(wěn)定就選哪個(gè)。我整理了一個(gè)簡單的參考表不同游戲類型和機(jī)型檔位的建議幀率游戲類型低端機(jī)中端機(jī)高端機(jī)動(dòng)作/射擊/競速306060卡牌/模擬/休閑303030-60放置/文字/棋牌24-303030音游/格斗306060這個(gè)表不是絕對(duì)的只是給個(gè)起點(diǎn)。實(shí)際項(xiàng)目里還要結(jié)合具體的性能測試來定。3.3 動(dòng)態(tài)鎖幀根據(jù)溫度實(shí)時(shí)調(diào)整固定鎖幀有個(gè)問題低端機(jī)鎖 30 可能還是燙高端機(jī)鎖 30 又浪費(fèi)了性能。更聰明的做法是動(dòng)態(tài)鎖幀根據(jù)設(shè)備溫度或者性能表現(xiàn)實(shí)時(shí)調(diào)整目標(biāo)幀率。Unity 本身沒有直接讀取設(shè)備溫度的跨平臺(tái) API但可以通過一些間接方式判斷。比如監(jiān)測一段時(shí)間內(nèi)的平均幀率如果持續(xù)低于目標(biāo)值說明設(shè)備在降頻這時(shí)候可以主動(dòng)降低 targetFrameRate減少負(fù)載讓溫度回落。等溫度降下來再逐步提回去。一個(gè)簡單的動(dòng)態(tài)調(diào)整邏輯using UnityEngine; public class DynamicFrameRate : MonoBehaviour { [SerializeField] private int highFrameRate 60; [SerializeField] private int lowFrameRate 30; [SerializeField] private float checkInterval 5f; [SerializeField] private float lowFpsThreshold 0.85f; private float timer; private int frameCount; private float elapsed; void Start() { QualitySettings.vSyncCount 0; Application.targetFrameRate highFrameRate; } void Update() { frameCount; elapsed Time.unscaledDeltaTime; timer Time.unscaledDeltaTime; if (timer checkInterval) { float avgFps frameCount / elapsed; if (avgFps Application.targetFrameRate * lowFpsThreshold) { Application.targetFrameRate lowFrameRate; } else if (avgFps highFrameRate * 0.95f Application.targetFrameRate ! highFrameRate) { Application.targetFrameRate highFrameRate; } timer 0f; frameCount 0; elapsed 0f; } } }這個(gè)邏輯的核心是如果實(shí)際幀率明顯低于目標(biāo)說明設(shè)備扛不住主動(dòng)降檔如果實(shí)際幀率能穩(wěn)定達(dá)到高檔位再升回去。這樣既能在高性能設(shè)備上跑滿又能在低性能設(shè)備上保穩(wěn)定。注意動(dòng)態(tài)調(diào)整幀率時(shí)切換頻率不要太快否則玩家會(huì)感覺到明顯的幀率跳變。我一般設(shè) 5 到 10 秒檢查一次而且降檔要快、升檔要慢避免在臨界點(diǎn)反復(fù)橫跳。4. 不同平臺(tái)的鎖幀行為差異4.1 Android 平臺(tái)的坑Android 是鎖幀行為最復(fù)雜的平臺(tái)因?yàn)樵O(shè)備碎片化太嚴(yán)重。同樣一行targetFrameRate 30在不同廠商、不同系統(tǒng)版本上的表現(xiàn)可能完全不同。最大的坑是部分廠商的系統(tǒng)會(huì)強(qiáng)制覆蓋應(yīng)用的幀率設(shè)置。比如某些游戲手機(jī)有性能模式開啟后會(huì)強(qiáng)制拉滿幀率你的鎖幀代碼根本不生效。還有些系統(tǒng)在檢測到游戲運(yùn)行時(shí)會(huì)自動(dòng)啟用高刷新率模式把屏幕刷新率提到 120Hz這時(shí)候即使你鎖了 30 幀系統(tǒng)層面的調(diào)度也可能讓功耗下不來。應(yīng)對(duì)辦法有幾個(gè)一是在游戲內(nèi)提供幀率選項(xiàng)讓玩家自己選同時(shí)說明低幀率更省電二是通過Application.targetFrameRate配合QualitySettings.vSyncCount 0雙管齊下三是在關(guān)鍵場景比如戰(zhàn)斗、加載臨時(shí)調(diào)整幀率非關(guān)鍵場景比如主菜單、劇情直接鎖低幀率。另外Android 上還有個(gè)Screen.currentResolution.refreshRate可以讀取當(dāng)前屏幕刷新率但注意這個(gè)值在運(yùn)行時(shí)可能變化不要緩存一次就一直用。4.2 iOS 平臺(tái)的特性iOS 相對(duì)規(guī)范很多targetFrameRate的行為比較一致。但 iOS 有個(gè) ProMotion 技術(shù)在支持的高端機(jī)型上屏幕刷新率可以在 24Hz 到 120Hz 之間自適應(yīng)。這種情況下如果你設(shè)了 targetFrameRate 60系統(tǒng)可能會(huì)根據(jù)內(nèi)容自動(dòng)調(diào)整刷新率實(shí)際表現(xiàn)跟你想的不完全一樣。iOS 上還有個(gè)要注意的點(diǎn)低電量模式下系統(tǒng)會(huì)主動(dòng)限制幀率。當(dāng)用戶開啟低電量模式系統(tǒng)可能把幀率壓到 30 甚至更低這時(shí)候你的鎖幀設(shè)置會(huì)被系統(tǒng)覆蓋。這個(gè)行為是系統(tǒng)級(jí)的應(yīng)用無法改變只能接受。所以做 iOS 優(yōu)化時(shí)要考慮到低電量模式下的表現(xiàn)不要把幀率相關(guān)的邏輯寫得太死。4.3 PC 和主機(jī)平臺(tái)的差異PC 平臺(tái)的情況又不一樣。PC 上玩家對(duì)幀率極其敏感而且硬件性能差異巨大。在 PC 上鎖幀更多是為了減少 GPU 滿載導(dǎo)致的發(fā)熱和風(fēng)扇噪音而不是像手機(jī)那樣為了溫控。很多 PC 玩家會(huì)主動(dòng)開垂直同步或者用驅(qū)動(dòng)層面的幀率限制所以游戲內(nèi)的鎖幀選項(xiàng)要做得靈活最好提供 30、60、120、不限制等多個(gè)檔位。主機(jī)平臺(tái)相對(duì)封閉幀率目標(biāo)通常由平臺(tái)方和游戲類型決定鎖幀策略比較固定。這里就不展開講了重點(diǎn)還是放在移動(dòng)端。5. 鎖幀實(shí)操從測試到上線的完整流程5.1 第一步建立發(fā)熱基線測試在動(dòng)手鎖幀之前必須先知道當(dāng)前項(xiàng)目的發(fā)熱情況。沒有基線你無法判斷鎖幀到底有沒有效果。測試方法我一般這樣做選三臺(tái)代表性設(shè)備低端、中端、高端各一臺(tái)安裝一個(gè)帶性能監(jiān)控的 Debug 包跑同一個(gè)場景或者同一段游戲流程持續(xù) 15 到 20 分鐘記錄以下數(shù)據(jù)每秒幀率用Time.deltaTime或者1 / Time.unscaledDeltaTime統(tǒng)計(jì)CPU 和 GPU 耗時(shí)通過 Profiler 或者平臺(tái)工具設(shè)備溫度Android 可以用adb shell dumpsys thermalserviceiOS 用 Xcode 的 Energy Log電池消耗速率把這些數(shù)據(jù)整理成曲線圖你就能看到幀率和溫度隨時(shí)間的變化趨勢。如果溫度在 5 分鐘內(nèi)就沖到 42 度以上那基本可以確定需要鎖幀了。5.2 第二步確定鎖幀檔位并驗(yàn)證根據(jù)基線數(shù)據(jù)選一個(gè)目標(biāo)幀率通常是 30 或 60。然后修改代碼重新跑同樣的測試對(duì)比數(shù)據(jù)。這里有個(gè)細(xì)節(jié)要注意鎖幀后要觀察幀生成時(shí)間的穩(wěn)定性不能只看平均幀率。有些項(xiàng)目鎖了 30 幀平均幀率確實(shí)是 30但幀生成時(shí)間忽長忽短玩家感受到的是卡頓。這種情況說明 CPU 或 GPU 有突發(fā)負(fù)載需要進(jìn)一步優(yōu)化光靠鎖幀解決不了。我一般會(huì)統(tǒng)計(jì)幀生成時(shí)間的 95 分位值也就是 95% 的幀都在這個(gè)時(shí)間內(nèi)完成如果這個(gè)值明顯超過 33.3ms30 幀的幀預(yù)算說明有卡頓幀需要排查。5.3 第三步加入動(dòng)態(tài)調(diào)整和玩家選項(xiàng)固定鎖幀驗(yàn)證有效后可以進(jìn)一步做成動(dòng)態(tài)的并且給玩家留一個(gè)選項(xiàng)。設(shè)置界面里加一個(gè)幀率模式選項(xiàng)提供省電30幀、均衡45幀、流暢60幀三檔讓玩家根據(jù)自己的設(shè)備和偏好選。這樣做的好處是既照顧了低端機(jī)用戶又不會(huì)讓高端機(jī)用戶覺得被限制了。而且玩家自己選的即使發(fā)熱了也不會(huì)怪游戲心理接受度更高。代碼上把 targetFrameRate 的賦值跟設(shè)置項(xiàng)綁定玩家切換時(shí)實(shí)時(shí)生效。同時(shí)保留動(dòng)態(tài)調(diào)整邏輯作為兜底當(dāng)檢測到設(shè)備過熱時(shí)即使玩家選了 60 幀也臨時(shí)降到 30 幀并在界面上給出提示。5.4 第四步上線后的監(jiān)控與迭代鎖幀不是設(shè)完就完事了。上線后要通過埋點(diǎn)收集玩家的實(shí)際幀率和設(shè)備溫度數(shù)據(jù)如果平臺(tái)允許看看不同機(jī)型的表現(xiàn)是否符合預(yù)期。如果發(fā)現(xiàn)某些機(jī)型鎖幀后依然發(fā)熱嚴(yán)重可能需要針對(duì)這些機(jī)型做特殊處理比如進(jìn)一步降到 24 幀或者關(guān)閉某些特效。我踩過的一個(gè)坑是測試階段用的都是主流機(jī)型表現(xiàn)很好上線后發(fā)現(xiàn)某幾個(gè)小眾機(jī)型鎖幀完全無效原因是這些機(jī)型的系統(tǒng)層面對(duì)幀率做了強(qiáng)制管理。后來針對(duì)這些機(jī)型加了白名單單獨(dú)處理才解決問題。所以上線后的數(shù)據(jù)監(jiān)控比測試階段更重要因?yàn)檎鎸?shí)用戶設(shè)備的多樣性遠(yuǎn)超你的測試覆蓋。6. 常見問題與排查技巧實(shí)錄6.1 鎖幀不生效的幾種原因這是被問得最多的問題。鎖幀代碼寫了但幀率還是原來的樣子通常有這幾個(gè)原因現(xiàn)象可能原因排查方法幀率完全沒變vSyncCount 不為 0檢查 QualitySettings.vSyncCount設(shè)為 0幀率是目標(biāo)值的整數(shù)倍垂直同步在起作用同上或改用 vSyncCount 控制部分場景生效部分不生效場景切換重置了設(shè)置用 DontDestroyOnLoad 保持設(shè)置編輯器里生效真機(jī)不生效平臺(tái)差異真機(jī)測試檢查系統(tǒng)幀率管理鎖 30 但實(shí)際跑 60系統(tǒng)強(qiáng)制高刷檢查設(shè)備刷新率設(shè)置考慮用 vSyncCount排查時(shí)建議按這個(gè)順序先確認(rèn) vSyncCount 是否為 0再確認(rèn) targetFrameRate 是否被正確賦值然后確認(rèn)設(shè)置是否在場景切換后丟失最后確認(rèn)設(shè)備系統(tǒng)層面是否有干預(yù)。6.2 鎖幀后動(dòng)畫變卡怎么辦鎖 30 幀后有些動(dòng)畫看起來一頓一頓的尤其是 UI 動(dòng)畫和相機(jī)移動(dòng)。這不是鎖幀的錯(cuò)而是動(dòng)畫更新頻率跟幀率綁定導(dǎo)致的。解決辦法是讓動(dòng)畫更新跟幀率解耦。Unity 的 Animator 默認(rèn)是按幀更新的鎖幀后動(dòng)畫采樣頻率降低自然就卡??梢栽?Animator 組件上把Update Mode改成Animate Physics或者用Unscaled Time讓動(dòng)畫按固定時(shí)間步長更新。UI 動(dòng)畫則可以用 DOTween 這類補(bǔ)間庫它們內(nèi)部用的是時(shí)間插值跟幀率無關(guān)鎖幀后依然平滑。相機(jī)移動(dòng)也是同理用Time.deltaTime做插值的移動(dòng)在鎖幀后依然平滑但如果用了固定步長或者跟幀率綁定的邏輯就會(huì)卡。檢查一下你的移動(dòng)代碼確保所有跟時(shí)間相關(guān)的計(jì)算都乘了Time.deltaTime。6.3 鎖幀和 GC 的關(guān)系鎖幀會(huì)降低 GC 的觸發(fā)頻率因?yàn)槊繋a(chǎn)生的垃圾少了累積到觸發(fā)閾值的時(shí)間變長了。這對(duì)發(fā)熱優(yōu)化是好事但要注意不要因?yàn)殒i幀就放松 GC 優(yōu)化。鎖幀只是減少了 GC 次數(shù)單次 GC 的耗時(shí)并沒有變?nèi)绻麊未?GC 時(shí)間過長依然會(huì)造成卡頓。我見過有項(xiàng)目鎖了 30 幀后覺得 GC 不是問題了結(jié)果在某些復(fù)雜場景下還是出現(xiàn)明顯卡頓一查發(fā)現(xiàn)是單次 GC 耗時(shí)超過 30ms直接吃掉一整幀的預(yù)算。所以鎖幀和 GC 優(yōu)化要并行做不能互相替代。6.4 獨(dú)家避坑技巧匯總最后分享幾個(gè)我在實(shí)際項(xiàng)目中總結(jié)的小技巧都是文檔里不會(huì)寫的技巧一在加載場景鎖低幀率。加載場景時(shí)玩家注意力在進(jìn)度條上幀率高低無所謂直接鎖 15 甚至 10 幀能顯著降低加載期間的發(fā)熱。等進(jìn)入游戲場景再恢復(fù)。技巧二后臺(tái)時(shí)鎖 1 幀。游戲切到后臺(tái)時(shí)把 targetFrameRate 設(shè)為 1幾乎不消耗性能?;謴?fù)前臺(tái)時(shí)再設(shè)回去。這個(gè)對(duì)省電和降溫效果極好尤其是玩家頻繁切后臺(tái)的場景。技巧三用幀率選項(xiàng)做 A/B 測試。上線時(shí)給一部分玩家默認(rèn) 30 幀一部分默認(rèn) 60 幀觀察留存和付費(fèi)數(shù)據(jù)。如果 30 幀組的留存沒有明顯下降那就大膽推廣 30 幀發(fā)熱問題迎刃而解。技巧四注意 targetFrameRate 的賦值時(shí)機(jī)。不要在Update里反復(fù)賦值雖然開銷不大但沒必要。在Awake或Start里設(shè)一次需要?jiǎng)討B(tài)調(diào)整時(shí)再改。技巧五測試時(shí)用真機(jī)別信編輯器。Unity 編輯器里的幀率表現(xiàn)跟真機(jī)差異巨大編輯器里鎖幀生效不代表真機(jī)生效。所有幀率相關(guān)的測試必須在真機(jī)上做而且要多機(jī)型覆蓋。我個(gè)人在實(shí)際操作中的體會(huì)是鎖幀這件事最大的價(jià)值不在于技術(shù)本身而在于它逼著你去思考玩家到底需要多少幀這個(gè)問題。很多時(shí)候我們默認(rèn) 60 幀是標(biāo)配但實(shí)際上對(duì)大部分游戲來說穩(wěn)定的 30 幀比波動(dòng)的 60 幀體驗(yàn)更好。把幀率降下來把穩(wěn)定性提上去把發(fā)熱控制住玩家玩得久留存自然就好。這個(gè)賬值得每個(gè)做移動(dòng)端的人認(rèn)真算一算。