ZMUIL2——遮罩系统
遮罩系统要做的工作
开启单遮模式时,每当调用UIModule来控制UI窗口显示隐藏时,
UIModule的遮罩系统都要计算显示的窗口中哪个窗口需要开启遮罩,并隐藏其他窗口的遮罩,以达到单遮的效果
UI配置文件可设定是否开启单遮模式
- 单遮模式:无论打开多少界面,遮罩只有一层。
 
- 叠遮模式:即每一个界面都有一层遮罩,打开的界面越多,遮罩就越黑。
 
UIModule的遮罩系统相关
配置文件
首先需要配置文件来控制遮罩系统的开关,使用继承ScriptableObject的单例配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
   | using UnityEngine;
  [CreateAssetMenu(fileName = "UISetting", menuName = "UISetting", order = 0)] public class UISetting : ScriptableObject {     private static UISetting instance;     public static UISetting Instance     {         get         {             if (instance == null)             {                 instance = Resources.Load<UISetting>("UISetting");             }             return instance;         }     }
      public bool SINGMASK_SYSTEM;     }
   | 
 
右键创建UISetting配置文件,即可在选择是否开启单遮模式
UI对象结构
为了便于UIModule管理各个窗口的遮罩,窗口对象的基本结构规定为:

其中,UIMask对象为一个覆盖全屏的黑色Image,黑色Image的透明度由CanvasGroup决定
简单来说,一个Window模板对象会包括UIMask对象作为背景遮罩,UIContent作为所有UI控件的父对象
值得一提的是:
- 使用这里的UIMask遮罩对象会使用Canvas Group进行控制遮罩的显示与隐藏
不使用SetActive设置的原因是,使用它会造成UI对象重绘,浪费性能 
- 这里创建UIContent作为UI控件父节点的原因是,便于缩放动画的制作,
可以通过缩放UIContent来制作缩放动画,同时也避免UIMask也跟着缩放。 
遮罩系统思路及其实现
遮罩系统主要在UI窗口显示隐藏时工作,而UIModule负责窗口显隐的执行,因此遮罩系统主要在UIModule内实现
首先需要在WindowBase内实现对UIMask显隐的控制,并对UIModule提供控制窗口UIMask显隐的接口
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
   | public class WindowBase : WindowBehaviour {          
      private CanvasGroup mUIMask;             protected Transform mUIContent;     
           private void InitializeBaseComponent()     {         mUIMask = transform.Find("UIMask").GetComponent<CanvasGroup>();         mUIContent = transform.Find("UIContent").transform;     }
      #region 生命周期函数的声明     public override void OnAwake()     {         base.OnAwake();         InitializeBaseComponent();     }
           #endregion
      public override void SetVisible(bool isVisible)     {              }
           public void SetMaskVisible(bool isVisible)     {                  if (!UISetting.Instance.SINGMASK_SYSTEM) return;         mUIMask.alpha = isVisible ? 1f : 0f;     }
      #region 事件管理方法          #endregion }
   | 
 
接下来需要在UIModule实现一套调节UI遮罩的算法,原理较复杂,使用伪代码阐述思路
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
   | 目的:通过调用WindowBase类的SetMaskVisible方法开启唯一符合条件的窗口的遮罩,关闭所有不符合条件的窗口的遮罩,实现单遮效果 符合开启遮罩的窗口的条件:窗口的Canvas的sortingOrder最大时,位于兄弟窗口排序最靠后的窗口
  private void 设置窗口遮罩() {     if 未开启单遮模式         return     var 要开启遮罩的窗口 = null     for 遍历所有的可见的窗口         if 遍历到的窗口不为空且对应的UI对象也不为空             关闭当前循环到的窗口的遮罩             if 要开启遮罩的窗口 == null                 要开启遮罩的窗口 = 当前循环到的窗口             else                 if 要开启遮罩的窗口的渲染层级 < 当前循环到的窗口的渲染层级                     要开启遮罩的窗口 = 当前循环到的窗口                 else if 要开启遮罩的窗口的渲染层级 == 当前循环到的窗口的渲染层级 && 要开启遮罩的窗口比当前循环到的窗口在同层级排序中更靠下                     要开启遮罩的窗口 = 当前循环到的窗口     if 要开启遮罩的窗口不为空         开启唯一符合条件的窗口的遮罩 }
   | 
 
SetWindowMaskVisible()会在UIModule的弹出窗口,隐藏窗口,销毁窗口里调用,达到每次显示隐藏面板时都会计算哪个面板需要开启遮罩,实现单遮
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
   | 
  #region 控制遮罩相关
  private void SetWindowMaskVisible() {          if (!UISetting.Instance.SINGMASK_SYSTEM) return;     WindowBase maxOrderWindowBase = null;        int maxOrder = 0;                            int maxIndex = 0;                                      for (int i = 0; i < mVisibleWindowList.Count; i++)     {         WindowBase window = mVisibleWindowList[i];                  if (window != null && window.gameObject != null)         {                          window.SetMaskVisible(false);             if (maxOrderWindowBase == null)             {                 maxOrderWindowBase = window;                 maxOrder = window.Canvas.sortingOrder;                 maxIndex = window.transform.GetSiblingIndex();             }             else             {                                  if (maxOrder < window.Canvas.sortingOrder)                 {                     maxOrderWindowBase = window;                     maxOrder = window.Canvas.sortingOrder;                 }                                  else if (maxOrder == window.Canvas.sortingOrder && maxIndex < window.transform.GetSiblingIndex())                 {                     maxOrderWindowBase = window;                     maxIndex = window.transform.GetSiblingIndex();                 }             }         }     }          maxOrderWindowBase?.SetMaskVisible(true); } #endregion
 
  |