博客 / 詳情

返回

如何定位遊戲發熱問題

1)如何定位遊戲發熱問題
​2)Unity獲取指定腳本的引用對象
3)如何知道打包時的一個Shader有多少變體
4)如何優化Font.CacheFontForText頻繁造成的耗時峯值


這是第300篇UWA技術知識分享的推送。今天我們繼續為大家精選了若干和開發、優化相關的問題,建議閲讀時間10分鐘,認真讀完必有收穫。

UWA 問答社區:answer.uwa4d.com
UWA QQ羣2:793972859(原羣已滿員)

Performance

Q:目前項目的發熱問題很頭疼,2D遊戲,基於TileMap、SpriteRenderer和UGUI的渲染,關閉了垂直同步,TargetFrameRate設置為60。Android和iOS上發熱都很嚴重,而且在比較好的機型(比如iPhone 12這種),發熱現象甚至更明顯。

跟大部分情況不同,發熱並沒有怎麼影響幀率,在大部分機型上,幀率都不是問題,連iPhone 8機型,都能60幀跑滿。

在Unity Profile和Xcode都進行過性能分析,CPU最明顯的熱點函數就是Spine的骨骼動畫更新計算。然而到一個沒有骨骼動畫的場景,發熱現象稍好點,但還是比預期的要燙不少(場景中除了地面和一些靜態貼圖,基本就沒有多少東西)。

另一個比較普遍的發熱點是網絡測試,在某場景關閉網絡後,發熱依然嚴重。甚至啓動遊戲,停留在登錄界面一會兒,發熱現象都比別的遊戲更明顯。

猜測是否由於每幀的頂點數量過多造成的,在遊戲中Unity的Status面板,頂點數量Verts達到了40KB,三角形Tris也有幾乎20KB。看起來很多,但我不太清楚當前主流遊戲這個數值的級別大概是多少。而且,頂點和三角形的數量很難解釋登錄界面依然容易發熱,畢竟登錄界面這些數值不可能很高。

自己也做了很多測試了,實在搞不清楚問題究竟在哪兒,Unity是還有什麼特別需要優化的,針對發熱的點嗎?

A1:可以用Xcode抓幀看看帶寬,Load Store Action是否合理。

感謝littlesome@UWA問答社區提供了回答

A2:以下是我的建議:

  1. iPhone 8上跑滿60幀,證明CPU、GPU都沒有到達瓶頸,消耗在較為合理的範圍。推薦在Unity Profiler看一下CPU端的消耗,以及查看一下DrawCall數量。
  2. 推薦使用FrameDebugger,看一下是否有冗餘的物體或者後處理在渲染,尤其是你説的啓動界面有發熱。
  3. 如果低級錯誤都排查過了,那麼建議看一下是否用了原生的插件。
  4. 渲染的分辨率是否調整過了,RenderScale的值和FrameDebugger可以查出來分辨率。

感謝張振東@UWA問答社區提供了回答

A3:關於發熱的問題,通常要從幾個角度排查:CPU壓力(耗時)、GPU壓力(耗時和帶寬,可以考慮降低分辨率看看發熱問題是否會有改善)和IO等幾個角度。從題主的問題上看,耗時應該是沒問題,都能跑滿幀(當然60幀本身就是對發熱影響比較大的一點,可以看看限制30幀會不會發熱有下降),所以要看看一些隱形的東西是否有問題。比如帶寬,可以用Snapdragon在高通手機上跑一跑。如果帶寬較高,看看紋理的一些設置是否合理,比如是否壓縮、是否開啓Mipmap,這兩項通常都是需要設置成開啓的。還可以查看是否有不必要的BlitCopy操作,在URP項目中比較容易出現Copy Color和Copy Depth浪費。對於IO,需要看看是否存在子線程裏面有頻繁IO的現象。

感謝Xuan@UWA問答社區提供了回

Script

Q:Unity獲取指定腳本的引用對象:一個GameObject上掛載了一個Script,這個Script引用了很多資源。如何只獲得這個Script引用的資源呢?AssetDatabase是會獲取所有的引用,但是並沒有做區分。

A1:有Guid可以使用以下代碼加載:
var assetPath = AssetDatabase.GUIDToAssetPath(guid);
var texture2D = AssetDatabase.LoadAssetAtPath(assetPath);

感謝蕭小俊@UWA問答社區提供了回答

A2:通過SerializedObject拿到了所有的ObjectReference,然後AssetDatabase.GetAssetPath獲取對應路徑:

var assetObj = AssetDatabase.LoadAssetAtPath<Object>(path);
if (assetObj != null)
{
    GameObject gameObj = assetObj as GameObject;
    if (gameObj != null)
    {
        MeshRendererTextureStreamingData streamingOBJ =
            gameObj.GetComponent<“taget component”>();
        if (streamingOBJ != null)
        {
            SerializedObject so = new SerializedObject(streamingOBJ);
            SerializedProperty it = so.GetIterator();
            bool first = true;


            while (it.Next(true))
            {
                if (it.propertyType == SerializedPropertyType.ObjectReference && it.objectReferenceValue!=null)
                {
                    // Debug.LogError(it.objectReferenceValue);
                    var depPath = AssetDatabase.GetAssetPath(it.objectReferenceValue);
                    streamingAssets.Add(depPath);
                }
            }

        }
    }
}

感謝題主null@UWA問答社區提供了回答

Shader

Q:有什麼方法可以知道打包的時候的一個Shader有多少變體?

A:直接使用Unity提供的回調函數,該函數會在Shader被打包(無論是打AssetBundle包還是Build的時候)時自動調用。

如圖的三個參數:“shader”表示回調函數當前在處理的Shader資源,”data”是參與編譯的變體列表。所以data.count()就是該Shader資源的(參與編譯的)變體數量了。所以,只要在回調函數邏輯裏把這兩個參數Log輸出就行了。

感謝Faust@UWA問答社區提供了回答

UGUI

Q:請問 Font.CacheFontForText頻繁造成耗時峯值,該如何優化?

A:可以參考這篇文章:《Unity3D UGUI 優化:Font.CacheFontForText》

Font.CacheFontForText是在Text渲染時,當對應字體的FontTexture找不到需要渲染的字符時,就會重新生成FontTexture,FontTexture尺寸越大,重新生成這張FontTexture就越耗時。

因此為了減少運行過程中該函數造成的耗時峯值,可以事先渲染所需要的字符,避免在運行時出現重新生成FontTexture。

使用UWA的GOT Online工具驗證了一下上面所提到的解決方案,結果對比如下所示:

優化前


優化後

優化後之所以還是存在部分Font.CacheFontForText的耗時峯值,是因為在測試後期又渲染了之前FontTexture中沒有的字符。

感謝宗卉軒@UWA問答社區提供了回答

封面圖來源於網絡


今天的分享就到這裏。當然,生有涯而知無涯。在漫漫的開發週期中,您看到的這些問題也許都只是冰山一角,我們早已在UWA問答網站上準備了更多的技術話題等你一起來探索和分享。歡迎熱愛進步的你加入,也許你的方法恰能解別人的燃眉之急;而他山之“石”,也能攻你之“玉”。

官網:www.uwa4d.com
官方技術博客:blog.uwa4d.com
官方問答社區:answer.uwa4d.com
UWA學堂:edu.uwa4d.com
官方技術QQ羣:793972859(原羣已滿員)

user avatar mjlong123 頭像 years 頭像
2 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.