# 3-5.UI模块 - UIModule
一个游戏70%都是UI，剩下30%才是GamePlay，所以有一套简洁强大的商业化UI模块以及UI开发工作流将是项目的一大利器，能够提高至少一倍的开发效率。(配合事件模块实现MVE[Model - View - Event]事件驱动架构)

UI脚本为纯C#实现，脱离Mono的生命周期，由UIModule的帧更新驱动并管理UI的生命周期。

IUIBehaviour为UI通用行为接口、UIBase为UI基类、UIWindow为UI窗口基类，UIWidget为UI组件基类。

## 前期配置：
注意！！！！m_item节点为特殊节点表示是UI下的UIWidget组件，不会继续往下遍历生成UI代码。若需要这个UIWidget组件m_item的代码则在m_item右键生成这个组件的UI脚本。

Scene窗口下右键ScriptGenerator菜单下About目录有默认UI命名前缀规范。
![image](src/3-5-1.png)

有自定义需求可以在TEngineSetting下进行自定义。
![image](src/3-5-2.png)
### 开发工作流：
1.遵守前期默认配置或者自定义配置进行UI编排
![image](src/3-5-3.png)

2.在UI的根节点右键ScriptGenerator生成UI代码到剪贴板上！！！（注-使用-UniTask的生成代码可以做异步事件流驱动的UI。）
![image](src/3-5-4.png)

3.自行创建UI脚本到需要的目录下并复制UI脚本。

## 举例示范
``` csharp
// 同步打开面板
GameModule.UI.ShowUI<GameMainUI>([nullable]userData);

// 异步打开面板
GameModule.UI.ShowUIAsync<GameMainUI>([nullable]userData);

namespace GameLogic
{
    /// <summary>
    /// BattleMainUI面板
    /// <remarks>UIWindow需要以下特性，UILayer可以自行定义，fullScreen表示为全屏面板会停止和隐藏这个面板堆栈后面的面板。</remarks>
    /// </summary>
    [Window(UILayer.Bottom,fullScreen:true)]
    class BattleMainUI : UIWindow
    {
        private TouchMove m_touchView;
        
        #region 脚本工具生成的代码
        private RectTransform m_rectContainer;
        private GameObject m_itemTouch;
        private Button m_btnLeaveBattle;
        private GameObject m_goTopInfo;
        private Button m_btnPause;
        public override void ScriptGenerator()
        {
            m_rectContainer = FindChildComponent<RectTransform>("m_rectContainer");
            m_itemTouch = FindChild("m_rectContainer/m_itemTouch").gameObject;
            m_btnLeaveBattle = FindChildComponent<Button>("m_btnLeaveBattle");
            m_goTopInfo = FindChild("m_goTopInfo").gameObject;
            m_btnPause = FindChildComponent<Button>("m_goTopInfo/m_btnPause");
            m_btnLeaveBattle.onClick.AddListener(OnClickLeaveBattleBtn);
            m_btnPause.onClick.AddListener(OnClickPauseBtn);
        }
        #endregion

        #region 事件

        private void OnClickPauseBtn()
        {
            BattleSys.Instance.Pause = !BattleSys.Instance.Pause;
        }

        private void OnClickLeaveBattleBtn()
        {
            BattleSys.Instance.StopBattle(isBattleEnd:false,isWin:false);
        }
        #endregion

        //注册事件举例
        public override void RegisterEvent()
        {
            //通过AddUIEvent这样注册事件会把事件的生命周期绑定给面板，面板销毁的时候自动移除监听。
            AddUIEvent(ActorLogicEventDefined.OnMainPlayerBagDataChange, RefreshUI);
        }

        public override void BindMemberProperty()
        {
            //特殊的m_item节点的域不属于父级UIWindows，所以如注意所说需要同样创建这个UIWidget的脚本并生成代码过去。 可以如下创建或者走type、path创建。
            m_touchView = CreateWidget<TouchMove>(m_itemTouch);
        }

        ......
    }
}

namespace GameLogic
{
    /// <summary>
    /// 移动操作UIWidget。
    /// </summary>
    class TouchMove : UIWidget, IUICtrlMove
    {
        public override void BindMemberProperty()
        {
        }

        //注意Update只有在重写了此方法才会驱动这个Widget或者面板的Update。
        public override void OnUpdate()
        {
            TProfiler.BeginSample("CheckMoveTouchFinger");
            CheckMoveTouchFinger();
            TProfiler.EndSample();

            TProfiler.BeginSample("UpdateTouchMovePos");
            UpdateTouchMovePos();
            TProfiler.EndSample();

            TProfiler.BeginSample("UpdateKeyMove");
            UpdateKeyMove();
            TProfiler.EndSample();
        }
        ......
    }
}
```

## UI进阶
UI面板需要标记UIWindowAttribute，以标识层级(可以自行定义)和是否全屏。全屏面板则会把下层面板的Visible设置为false。
![image](src/3-5-5.png)

