ZMUIL6——高性能系统 
高性能系统要做的工作 
高性能系统是为了解决UI性能问题,增加游戏的流畅度而设计的一系列性能解决方案。 
主要针对渲染、重绘、顶点、UI组件、等多个方面进行性能的优化处理。 
他的功能有以下几点:
一键优化合批。  自动根据图集图片和相邻组件的特征进行重新排序。 
避免使用SetActive引起的UI重绘和GC垃圾。  用CanvasGroup和Scale进行代替。 
使用UI对象池。  避免频繁的克隆物体导致的卡顿和GC。(在之前的UIModule里已经实现了,隐藏窗口不会直接销毁窗口对象,可复用) 
智能化禁用不必要的组件属性。  从而来避免一些不必要的性能开销。 
界面预加载。  针对复杂一些的界面我们可以使用预加载进行提前加载物体,来确保在真正使用界面时,能够流畅度加载出界面。 
**高性能文字描边。 **Unity描边组件是拷贝4份相同的文本顶点数占用量巨大。 
一个字母的Text加上Untiy的描边一共占用30个顶点。 
而我们的Text同样是一个字母加上描边能做到只占用6个顶点。性能是Unity组件的5倍。  (这课没讲,不用记辣) 
组件自动序列化。  避免掉使用Find接口查找组件带来的性能消耗,而使用自动化序列化的方式拿到组件,将性能消耗尽可能的降至最低。 
(编写自动化系统时就已经实现了,使用组件数据脚本即可) 
 
特点:最大的程度去降低UI在游戏中所消耗的性能问题,让我们做出来的游戏质量更好,游戏流畅度更高。
窗口预加载 
就是将窗口的加载和窗口的弹出分离,在特定时间提前加载窗口而不显示它,达到预加载的结果,之后再显示窗口,使显示窗口更加顺畅
预加载在UIModule内实现,会调用窗口的OnAwake方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 #region  预加载相关 public  void  PreLoadWindow <T >() where  T : WindowBase, new (){          System.Type type = typeof (T);     string  windowName = type.Name;     T windowBase = new  T();          GameObject newWindowObj = TempLoadWindow(windowName);          if  (newWindowObj != null )     {                  windowBase.gameObject = newWindowObj;         windowBase.transform = newWindowObj.transform;         windowBase.Canvas = newWindowObj.GetComponent<Canvas>();         windowBase.Canvas.worldCamera = mUICamera;         windowBase.Name = newWindowObj.name;         windowBase.OnAwake();         windowBase.SetVisible(false );                  RectTransform rectTrans = newWindowObj.GetComponent<RectTransform>();         rectTrans.anchorMax = Vector2.one;         rectTrans.offsetMax = Vector2.zero;         rectTrans.offsetMin = Vector2.zero;                  mAllWindowDic.Add(windowName, windowBase);         mAllWindowList.Add(windowBase);     }     Debug.Log($"预加载窗口:{windowName} " ); } #endregion  
 
优化显隐逻辑 
检测网格重建行为 
可以通过下面的脚本直接检查网格重建行为,随意挂载到一个对象上即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 using  System;using  System.Collections.Generic;using  System.Reflection;using  UnityEngine;using  UnityEngine.UI;public  class  CanvasRebuildTest  : MonoBehaviour {     IList<ICanvasElement> mLayoutRebuildQueue;           IList<ICanvasElement> mGraphicRebuildQueue;          void  Start ()     {         Type type = typeof (CanvasUpdateRegistry);         FieldInfo field = type.GetField("m_LayoutRebuildQueue" , BindingFlags.NonPublic | BindingFlags.Instance);         mLayoutRebuildQueue = (IList<ICanvasElement>)field.GetValue(CanvasUpdateRegistry.instance);         field = type.GetField("m_GraphicRebuildQueue" , BindingFlags.NonPublic | BindingFlags.Instance);         mGraphicRebuildQueue = (IList<ICanvasElement>)field.GetValue(CanvasUpdateRegistry.instance);     }     void  Update ()     {         for  (int  i = 0 ; i < mLayoutRebuildQueue.Count; i++)         {             var  rebuild = mLayoutRebuildQueue[i];             if  (ObjectValidForUpdate(rebuild))             {                 Debug.LogFormat("{0}引起{1}网格重建" , rebuild.transform.name, rebuild.transform.GetComponent<Graphic>().canvas.name);             }         }         for  (int  i = 0 ; i < mGraphicRebuildQueue.Count; i++)         {             var  rebuild = mGraphicRebuildQueue[i];             if  (ObjectValidForUpdate(rebuild))             {                 Debug.LogFormat("{0}引起{1}网格重建" , rebuild.transform.name, rebuild.transform.GetComponent<Graphic>().canvas.name);             }         }     }                              private  bool  ObjectValidForUpdate (ICanvasElement element )     {         var  valid = element != null ;         var  isUnityObject = element is  UnityEngine.Object;         if  (isUnityObject)         {             valid = (element as  object ) != null ;         }         return  valid;     } } 
 
检查发现,直接使用SetActive方法控制窗口的显隐会触发网格重建,导致不必要的性能消耗, 
而CanvasGroup改变alpha值和改变Transform的缩放不会触发网格重建
因此,我们可以调整CanvasGroup改变alpha值和改变Transform的缩放来控制UI对象的显示隐藏,性能更好
窗口的显隐 
我们为每个窗口都添加一个CanvasGroup,通过改变其alpha值为0或1,来控制窗口的隐藏或显示
将WindowBase的设置可见性方法改成调整CanvasGroup的逻辑
1 2 3 4 5 6 7 8 9 10 private  CanvasGroup mCanvasGroup;       public  override  void  SetVisible (bool  isVisible ){          mCanvasGroup.alpha = isVisible ? 1f  : 0f ;        mCanvasGroup.blocksRaycasts = isVisible;         Visible = isVisible; } 
 
UI对象的显隐 
我们可以编写一个UGUIAgent,在其中编写一系列扩展方法,拓展一种调整缩放来控制显隐的方法,为UI控件的显隐提供新的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 using  UnityEngine;using  UnityEngine.UI;public  static  class  UGUIAgent {                              public  static  void  SetVisible (this  GameObject obj, bool  visible )     {         obj.transform.localScale = visible ? Vector3.one : Vector3.zero;     }                              public  static  void  SetVisible (this  Transform transform, bool  visible )     {         transform.localScale = visible ? Vector3.one : Vector3.zero;     }     public  static  void  SetVisible (this  Button button, bool  visible )     {         button.transform.localScale = visible ? Vector3.one : Vector3.zero;     }     public  static  void  SetVisible (this  Text text, bool  visible )     {         text.transform.localScale = visible ? Vector3.one : Vector3.zero;     }     public  static  void  SetVisible (this  Slider slider, bool  visible )     {         slider.transform.localScale = visible ? Vector3.one : Vector3.zero;     }     public  static  void  SetVisible (this  Toggle toggle, bool  visible )     {         toggle.transform.localScale = visible ? Vector3.one : Vector3.zero;     }     public  static  void  SetVisible (this  InputField inputField, bool  visible )     {         inputField.transform.localScale = visible ? Vector3.one : Vector3.zero;     }     public  static  void  SetVisible (this  RawImage image, bool  visible )     {         image.transform.localScale = visible ? Vector3.one : Vector3.zero;     }     public  static  void  SetVisible (this  ScrollRect scrollRect, bool  visible )     {         scrollRect.transform.localScale = visible ? Vector3.one : Vector3.zero;     } } 
 
智能禁用RaycastTarget 
很多单纯的Image和Text是不接收用户输入的,因此将其RaycastTarget开着只能浪费性能 
我们可以检测新创建出来的Text, Image, Raw Image,将其RaycastTarget属性自动关闭,省去我们手动关闭的麻烦 
也不影响其他用于Button等的Image的射线检测正常执行
实现思路如下:
在编辑器模式下,每当Hierarchy窗口发生改变,就检测选中的物体(一般都是新创建的UI对象)名字是否包含Text, Image字样,一旦存在自动关闭其RaycastTarget属性
 
注:不对Text Mesh Pro生效
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 using  System.Collections;using  System.Collections.Generic;using  UnityEditor;using  UnityEngine;using  UnityEngine.UI;public  class  SystemUIEditor  : Editor {          [InitializeOnLoadMethod ]     private  static  void  InitEditor ()     {                  EditorApplication.hierarchyChanged += HanderTextOrImageRaycast;     }          private  static  void  HanderTextOrImageRaycast ()     {         GameObject obj = Selection.activeGameObject;         if  (obj != null )         {             if  (obj.name.Contains("Text" ))             {                 Text text = obj.GetComponent<Text>();                 if  (text != null )                 {                     text.raycastTarget = false ;                 }             }             else  if  (obj.name.Contains("Image" ))             {                 Image image = obj.GetComponent<Image>();                 if  (image != null )                 {                     image.raycastTarget = false ;                 }                 else                  {                     RawImage rawImage = obj.GetComponent<RawImage>();                     if  (rawImage != null )                     {                         rawImage.raycastTarget = false ;                     }                 }             }         }     }          private  static  void  LoadWindowCamera ()     {         if  (Selection.activeGameObject != null )         {             GameObject uiCameraObj = GameObject.Find("UICamera" );             if  (uiCameraObj != null )             {                 Camera camera = uiCameraObj.GetComponent<Camera>();                 if  (Selection.activeGameObject.name.Contains("Window" ))                 {                     Canvas canvas = Selection.activeGameObject.GetComponent<Canvas>();                     if  (canvas != null )                     {                         canvas.worldCamera = camera;                     }                 }             }         }     } } 
 
一键自动优化Batchs 
与NGUI的优化Batchs类似,它会将重新为同层级的对象进行排序,将同图集的对象排列到一起,将Text对象统一排列到末尾 
以最大化的降低Batchs
通过导入UGUI-Editor这个开源库,即可使用该功能
 
 
 
悲报: 
看了一眼这个UGUI-Editor在GitHub上的库,已经久未更新了,将该库导入到2021版的Unity出现了不少过时警告,但还是能用 
使用其PrafabWin窗口也有轻微显示问题,笔者难以保证这个库的其他功能还能在未来的版本里正常使用