優化 Unity uGUI 在 Raycast 效能消耗 – A Optimizing Tip of uGUI Raycast

這篇文章的內容是關於 Unity 的優化,針對的優化對象是 uGUI 在射線 (Raycast)。

由於 Raycast 在 Unity 當中算是相當消耗運算資源的物件,但是又被大量應用於點擊偵測之中。在 Unity 5.2 版之後,uGUI 多了一個選項 (raycastTarget) 可以選擇關閉 UI 物件上作為可被 Raycast 掃描之物件的屬性,以降低 Raycast 的負擔。所以後期優化的動作便多了一個:將不會被點擊的 uGUI 上的 raycastTarget 屬性取消掉。

相關資訊我是在 Unity3D研究院之UGUI一個優化效率小技巧 這片文章中得知的,文章中也提出了一個建議:與其開發後期才一個個檢查 uGUI 並取消 raycastTarget,不如一開始就預設關閉它。同時該文章作者針對這想法起了一個自訂 Editor 的頭,我在其中加上了自己的想法補全並改善這個自訂 Editor,形成這篇文章。

優化後的 uGUI 建立方法

Editor/OptimizedUGUICreation.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
using UnityEngine;
using UnityEditor;
using UnityEngine.UI;

public class OptimizeEditor
{
[MenuItem("GameObject/Optimized UI/Image", false, 10)]
static void CreatImage(MenuCommand menuCommand)
{
EditorApplication.ExecuteMenuItem("GameObject/UI/Image");
GameObject go = Selection.activeGameObject;
GameObjectUtility.SetParentAndAlign(go, menuCommand.context as GameObject);
go.GetComponent<Image>().raycastTarget = false;
}

[MenuItem("GameObject/Optimized UI/Text", false, 10)]
static void CreatText(MenuCommand menuCommand)
{
EditorApplication.ExecuteMenuItem("GameObject/UI/Text");
GameObject go = Selection.activeGameObject;
GameObjectUtility.SetParentAndAlign(go, menuCommand.context as GameObject);
go.GetComponent<Text>().raycastTarget = false;
}
}

這份短短的程式必須被放置於 Asset 中的 Editor 資料夾發揮作用,在 hierarchy 中建立物件的列表中新增一個 Optimized UI 的選項,由此建立的 Text 及 Image 將預設 raycastTarget = false,除此之外跟 Unity 所建立的物件一模一樣。

  • Line7:在 hierarchy 右鍵列表中建立新的物件選項。
  • Line10:先執行一次原生的 Unity Image 物件建立動作 (執行原生的選項)。
  • Line11:取得新建立的物件 (新建立的物件會處於被選擇的狀態,藉此取得之)。
  • Line12:設定 parent 為右鍵點選的對象 (仿照原生的建立動作)。
  • Line13:設定 raycastTarget = false。

接下來 Text 的建立動作類似處理。

如此設計 Editor 的思路過程

在動手撰寫 Editor 前,我希望這個 Editor 要達成幾個目標:

  1. 除了設定 raycastTarget = false,建立出來的 uGUI 物件其他部分要跟 Unity 預設的一樣。
  2. 建立出來的新物件會以點選的物件作為 parent,這也是 Unity 預設的建立結果。
  3. 這個 Editor 要能夠方便修改,增減其他參數的自訂預設 (例如將預設的 size 做改變)。
  4. Unity 假如改版後修改了物件的建立,我的 Editor 必須盡量降低需要維護的比例。

要達成第 1、2 點,可以重新實作所有 Unity 建立物件所做的工作,相當直覺的作法,難度也不高。不過這樣會使程式碼增加不少維護難度,尤其與第 4 點相當矛盾,只要 Unity 一改相關的物件便要重新撰寫。

我對 3、4 點的目標相當堅持,因為這兩點與整份程式碼的 “延展性”、“可維護性” 的提升有相當大的幫助,雖然只是一個小小功能的程式碼,我還是希望它未來可以一直被使用下去而不需要整個重新撰寫。

於是我去尋求能否繼承 Unity 原有的 Editor 功能,而不是 Override 掉整個功能,結果這個方向是錯誤的,我之後發現無法繼承原生的選單選項。念一轉,那我是否能夠呼叫原生的建構函式,而不是自己再寫一個呢?幸運地,我在官方文件中挖掘到了 EditorApplication.ExecuteMenuItem() 這個函式,可以程式化地呼叫 Editor 選單,讓我實現了上方的 Editor 設計。

目前這個 Editor 的設計,在運作自訂功能前先呼叫了原生的 “建立 UI 物件” 選項,可以保證設計目標的第 1 點與第 4 點,然後用最少的程式碼完成第 2 點,未來要增減不同的預設值,也只要在 Line13 之後做簡單的補充即可。

這樣的實作成果,相當程度的兼顧了 “功能” 與 “未來維護”,在我學習程式開發的路上,我漸漸重視並努力將這兩項課題兼具於我的開發之中,越是去思考如何同時完善這兩件事,我就越了解自己的程式,並對它的穩定性有這更高的信心。