最近的作業跟 UI 打交道比較多, 各種坑.
今天從 Prefab 的序列化功能來說說 System.Diagnostics.Conditional 的妙用.
我們做 UI 面對各種按鈕, 組件的獲取方式大致也就兩種, 一種直接序列化到 Prefab 中, 另一種是在代碼中去獲取 :
序列化
tagObj = transform.Find("Child/Cube").gameObject; // 代碼獲取
各有各的好處 :
序列化很直接, 代碼都不用寫了, 并且隨便你拖動 UI 到哪個節點, 都能正確參考. 不過問題也很明顯, 過了幾天連自己都找不著北了, 有過長期維護經驗的人應該了解.
代碼獲取的方式從維護性來說, 直接就能讓看的人知道那個組件在哪個節點上, 理解起來更簡單. 可是如果節點路徑發生變更, 就要出問題了, 這在頻繁改動的 UI 設計上來說很要命. 特別是如果使用封裝好的函式進行安全獲取, 就更難發現問題了, 比如:
// 封裝好的函式, 連錯都不報 public static GameObject GetGameObject(Transform from, string find) { var trans = from.Find(find); if(trans) { return trans.gameObject; } return null; }
如果從效率的角度來看, 應該是序列化 Prefab 的效率要高于使用代碼查找的效率的, 因為序列化物件是一個唯一ID, 而代碼查找時 Transform 的子物件是在串列中的, 所以 Find 函式查找效率是跟 List 一樣的 :

這里用 tagObj 參考了自己, 看到序列化物件的 ID 就是指向上面的 m_GameObject 的, 應該就是個內部 GUID 了. 從效率上看略勝一籌, 因為我在之前的專案碰到過有幾千個 Child 的節點, 然后通過資料庫得到的幾千個資料對節點下面的物件進行查找, 那效率簡直酸爽, 有這種需求的一定要先厲遍 Transform 把所有節點都放到 Dictionary 里參考啊, 血的教訓...
第二點, 存盤大小 :
如果序列化到 Prefab 里面, 它使用了 ID 作為參考, 增加的存盤大小基本是個定值, 并且打包之后還能進行壓縮等, 實際占用空間非常小, 而且作為資源它可以熱更.
代碼如果寫在 C# 中, Find("XXX/ooo") 里面的字符會被放到靜態域中, 這就很要命了, 因為到專案后期不僅由于過多的代碼, 更多的字串導致程式包過大, 我們真的碰到過超過 IOS 50多M限制的單個 DLL 問題...當然如果寫在 Lua 中的話就跟資源一樣了, 你能編譯成二進制, 你能壓縮, 萬能的 Lua. 不過按照一般情況, "XXX/ooo" 字符一般都會比 ID 更長特別是 UI 層級很深的情況, 有中文的時候就更糟了.
假設我們有了 Lua, 你問問開發人員他們愿意寫一大堆 Find 代碼, 還是簡單拖一拖了事? 因為 Lua 在使用上跟 C# 沒有什么不同, 如果使用了序列化誰都懶得再去寫代碼了... 這真的是少有的 Unity 官方功能比民間方法更牛的特例.
這樣看來序列化真是又簡單又高效, 還省空間, 主要問題還是在后期維護上, 比如我們的變數叫 tagObj, 制作的人可能參考的物件名稱是 Cube, 名稱上完全沒有關聯性, 在一些不可預知的情況下如果參考丟失了, 連制作者都不知道原來參考的是哪個物件的情況多的是. 我們能把兩種模式結合起來的話就好了, 比如在組件上面加個注釋 :

恩, 不錯, 還有更好的 :

System.Diagnostics.Conditional 可以根據編譯條件決定是否編譯, 它在非編輯器下就跟注釋一樣是不會加進去的, 而在編輯器下你是可以通過 GetCustomAttributes 獲取的, 那么它的用處就大了 :
1. 在編輯器下檢查序列化物件是不是丟了, 丟了的話找 Attribute 然后通過 loadPath 去自動獲取.
2. 在編輯器下檢查序列化物件跟 loadPath 的物件是不是一致, 檢查資料正確性.
這些看似簡單的功能, 在發布版本之前可能救不少人的命呢, 避免了每次抓人去祭天. 通過這個應用方法添加了一些資訊, 并能提供上述功能支持.
測驗一下 :

-------- 編輯器 --------

-------- 發布后 --------

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/43389.html
標籤:其他
下一篇:打開pip.exe后自動關閉
