From 6fbe17d7d5e6a4e8a09e0f81bdca1bc3f5d61d5f Mon Sep 17 00:00:00 2001 From: ALEXTANG <574809918@qq.com> Date: Mon, 3 Apr 2023 20:57:10 +0800 Subject: [PATCH] UIWindow --- .../Resource/ResourceComponent.cs | 8 +- .../GameFramework/UI/DefaultUIWindowHelper.cs | 2 +- .../Runtime/GameFramework/UI/IUIBehaviour.cs | 8 +- .../GameFramework/UI/OpenWindowOperation.cs | 73 +++ .../UI/OpenWindowOperation.cs.meta | 3 + .../Runtime/GameFramework/UI/UIBase.cs | 29 +- .../Runtime/GameFramework/UI/UIBase.cs.meta | 4 +- .../Runtime/GameFramework/UI/UIComponent.cs | 449 +++++++++++++++-- .../UI/{ => UIGroup}/IUIGroup.cs | 0 .../UI/{ => UIGroup}/IUIGroup.cs.meta | 0 .../Runtime/GameFramework/UI/UIManager.cs | 7 - .../GameFramework/UI/UIManager.cs.meta | 3 - .../Runtime/GameFramework/UI/UIWidget.cs | 62 +++ .../Runtime/GameFramework/UI/UIWidget.cs.meta | 3 + .../Runtime/GameFramework/UI/UIWindow.cs | 473 +++++++++++++++++- 15 files changed, 1071 insertions(+), 53 deletions(-) create mode 100644 Assets/TEngine/Runtime/GameFramework/UI/OpenWindowOperation.cs create mode 100644 Assets/TEngine/Runtime/GameFramework/UI/OpenWindowOperation.cs.meta rename Assets/TEngine/Runtime/GameFramework/UI/{ => UIGroup}/IUIGroup.cs (100%) rename Assets/TEngine/Runtime/GameFramework/UI/{ => UIGroup}/IUIGroup.cs.meta (100%) delete mode 100644 Assets/TEngine/Runtime/GameFramework/UI/UIManager.cs delete mode 100644 Assets/TEngine/Runtime/GameFramework/UI/UIManager.cs.meta create mode 100644 Assets/TEngine/Runtime/GameFramework/UI/UIWidget.cs create mode 100644 Assets/TEngine/Runtime/GameFramework/UI/UIWidget.cs.meta diff --git a/Assets/TEngine/Runtime/GameFramework/Resource/ResourceComponent.cs b/Assets/TEngine/Runtime/GameFramework/Resource/ResourceComponent.cs index f0d31965..cfd71d95 100644 --- a/Assets/TEngine/Runtime/GameFramework/Resource/ResourceComponent.cs +++ b/Assets/TEngine/Runtime/GameFramework/Resource/ResourceComponent.cs @@ -1,4 +1,5 @@ -using UnityEngine; +using System; +using UnityEngine; namespace TEngine { @@ -25,5 +26,10 @@ namespace TEngine { } + + private void Start() + { + + } } } \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/UI/DefaultUIWindowHelper.cs b/Assets/TEngine/Runtime/GameFramework/UI/DefaultUIWindowHelper.cs index 32550c3e..3ad3063c 100644 --- a/Assets/TEngine/Runtime/GameFramework/UI/DefaultUIWindowHelper.cs +++ b/Assets/TEngine/Runtime/GameFramework/UI/DefaultUIWindowHelper.cs @@ -60,7 +60,7 @@ namespace TEngine rectTransform.anchorMax = m_Half; rectTransform.anchoredPosition = Vector2.zero; // return obj.GetOrAddComponent(); - return new UIWindow(); + return null; } /// diff --git a/Assets/TEngine/Runtime/GameFramework/UI/IUIBehaviour.cs b/Assets/TEngine/Runtime/GameFramework/UI/IUIBehaviour.cs index b2523cb7..7a5ce16b 100644 --- a/Assets/TEngine/Runtime/GameFramework/UI/IUIBehaviour.cs +++ b/Assets/TEngine/Runtime/GameFramework/UI/IUIBehaviour.cs @@ -3,11 +3,11 @@ public interface IUIBehaviour { void ScriptGenerator(); + void BindMemberProperty(); void RegisterEvent(); void OnCreate(); - void OnUpdate(float elapseSeconds, float realElapseSeconds); - void OnClose(bool isShutdown, object userData); - void OnPause(); - void OnResume(); + void OnRefresh(); + void OnUpdate(); + void OnDestroy(); } } \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/UI/OpenWindowOperation.cs b/Assets/TEngine/Runtime/GameFramework/UI/OpenWindowOperation.cs new file mode 100644 index 00000000..e62ca762 --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/UI/OpenWindowOperation.cs @@ -0,0 +1,73 @@ +using YooAsset; + +namespace TEngine +{ + /// + /// 打开窗口操作句柄。 + /// + public class OpenWindowOperation : GameAsyncOperation + { + private enum ESteps + { + None, + Waiting, + Done, + } + + private readonly AssetOperationHandle _handle; + private ESteps _steps = ESteps.None; + + internal OpenWindowOperation(AssetOperationHandle handle) + { + _handle = handle; + } + protected override void OnStart() + { + _steps = ESteps.Waiting; + } + protected override void OnUpdate() + { + if (_steps == ESteps.None || _steps == ESteps.Done) + return; + + if (_steps == ESteps.Waiting) + { + if (_handle.IsValid == false) + { + _steps = ESteps.Done; + Status = EOperationStatus.Failed; + Error = $"{nameof(AssetOperationHandle)} is invalid."; + return; + } + + if (_handle.IsDone == false) + return; + + if (_handle.AssetObject == null) + { + _steps = ESteps.Done; + Status = EOperationStatus.Failed; + Error = $"{nameof(AssetOperationHandle.AssetObject)} is null."; + return; + } + + _steps = ESteps.Done; + Status = EOperationStatus.Succeed; + } + } + + /// + /// 等待异步实例化结束 + /// + public void WaitForAsyncComplete() + { + if (_handle != null) + { + if (_steps == ESteps.Done) + return; + _handle.WaitForAsyncComplete(); + OnUpdate(); + } + } + } +} \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/UI/OpenWindowOperation.cs.meta b/Assets/TEngine/Runtime/GameFramework/UI/OpenWindowOperation.cs.meta new file mode 100644 index 00000000..e9dbddfa --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/UI/OpenWindowOperation.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 8ebdde8ab5014a73983249d935b415e4 +timeCreated: 1680520779 \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/UI/UIBase.cs b/Assets/TEngine/Runtime/GameFramework/UI/UIBase.cs index c6a297ce..9b1d889d 100644 --- a/Assets/TEngine/Runtime/GameFramework/UI/UIBase.cs +++ b/Assets/TEngine/Runtime/GameFramework/UI/UIBase.cs @@ -1,7 +1,34 @@ -namespace TEngine +using YooAsset; + +namespace TEngine { + public enum UIBaseType + { + None, + Window, + Widget, + } + public class UIBase { + /// + /// 所属的window。 + /// + protected UIBase parent = null; + /// + /// UI父节点。 + /// + public UIBase Parent => parent; + + /// + /// UI类型。 + /// + public virtual UIBaseType BaseType => UIBaseType.None; + + /// + /// 资源操作句柄。 + /// + public AssetOperationHandle Handle { protected set; get; } } } \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/UI/UIBase.cs.meta b/Assets/TEngine/Runtime/GameFramework/UI/UIBase.cs.meta index f9bb226a..8dd47bb4 100644 --- a/Assets/TEngine/Runtime/GameFramework/UI/UIBase.cs.meta +++ b/Assets/TEngine/Runtime/GameFramework/UI/UIBase.cs.meta @@ -1,3 +1,3 @@ fileFormatVersion: 2 -guid: 62c603695cbe4b8bb59a94d907ca7f65 -timeCreated: 1680514241 \ No newline at end of file +guid: aa3dc52076b14039bc355c825c7f29de +timeCreated: 1680526167 \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/UI/UIComponent.cs b/Assets/TEngine/Runtime/GameFramework/UI/UIComponent.cs index 84e8ecea..989d806f 100644 --- a/Assets/TEngine/Runtime/GameFramework/UI/UIComponent.cs +++ b/Assets/TEngine/Runtime/GameFramework/UI/UIComponent.cs @@ -1,9 +1,20 @@ using System; +using System.Collections.Generic; using UnityEngine; -using UnityEngine.Serialization; +using UnityEngine.UI; +using YooAsset; namespace TEngine { + public enum EUIGroup + { + Bottom, + UI, + Top, + Tips, + System, + } + /// /// UI组件。 /// @@ -11,51 +22,45 @@ namespace TEngine public sealed partial class UIComponent : GameFrameworkComponent { private const int DefaultPriority = 0; - - [SerializeField] - private Transform m_InstanceRoot = null; - - [SerializeField] - private Camera m_UICamera = null; - - [SerializeField] - private string m_UIWindowHelperTypeName = "TEngine.DefaultUIWindowHelper"; - [SerializeField] - private UIWindowHelperBase mCustomUIWindowHelper = null; + [SerializeField] private Transform m_InstanceRoot = null; - [SerializeField] - private string m_UIGroupHelperTypeName = "TEngine.DefaultUIGroupHelper"; + [SerializeField] private Camera m_UICamera = null; + + [SerializeField] private string m_UIWindowHelperTypeName = "TEngine.DefaultUIWindowHelper"; + + [SerializeField] private UIWindowHelperBase mCustomUIWindowHelper = null; + + [SerializeField] private string m_UIGroupHelperTypeName = "TEngine.DefaultUIGroupHelper"; + + [SerializeField] private UIGroupHelperBase m_CustomUIGroupHelper = null; + + [SerializeField] private UIGroup[] m_UIGroups = null; + + private readonly List _stack = new List(100); - [SerializeField] - private UIGroupHelperBase m_CustomUIGroupHelper = null; - - [SerializeField] - private UIGroup[] m_UIGroups = null; - public const int GROUP_DEEP = 10000; - public const int WINDOWS_DEEP = 100; - + public const int WINDOW_DEEP = 100; + public const int WINDOW_HIDE_LAYER = 2; // Ignore Raycast + public const int WINDOW_SHOW_LAYER = 5; // UI + /// /// UI根节点。 /// public Transform UIRoot => m_InstanceRoot; + public static Transform UIRootStatic; + /// /// UI根节点。 /// public Camera UICamera => m_UICamera; - + /// /// 获取界面组数量。 /// public int UIGroupCount => m_UIGroups?.Length ?? 0; - protected override void Awake() - { - base.Awake(); - } - private void Start() { RootComponent rootComponent = GameEntry.GetComponent(); @@ -64,7 +69,7 @@ namespace TEngine Log.Fatal("Base component is invalid."); return; } - + UIWindowHelperBase uiWindowHelper = Helper.CreateHelper(m_UIWindowHelperTypeName, mCustomUIWindowHelper); if (uiWindowHelper == null) { @@ -76,7 +81,7 @@ namespace TEngine Transform transform = uiWindowHelper.transform; transform.SetParent(this.transform); transform.localScale = Vector3.one; - + if (m_InstanceRoot == null) { m_InstanceRoot = new GameObject("UI Form Instances").transform; @@ -85,6 +90,7 @@ namespace TEngine } m_InstanceRoot.gameObject.layer = LayerMask.NameToLayer("UI"); + UIRootStatic = m_InstanceRoot; for (int i = 0; i < m_UIGroups.Length; i++) { @@ -95,7 +101,27 @@ namespace TEngine } } } - + + private void OnDestroy() + { + CloseAll(); + } + + private void Update() + { + int count = _stack.Count; + for (int i = 0; i < _stack.Count; i++) + { + if (_stack.Count != count) + { + break; + } + + var window = _stack[i]; + window.InternalUpdate(); + } + } + /// /// 增加界面组。 /// @@ -120,5 +146,366 @@ namespace TEngine return true; } + + #region 设置安全区域 + + /// + /// 设置屏幕安全区域(异形屏支持)。 + /// + /// 安全区域 + public static void ApplyScreenSafeRect(Rect safeRect) + { + CanvasScaler scaler = UIRootStatic.GetComponentInParent(); + if (scaler == null) + { + Log.Error($"Not found {nameof(CanvasScaler)} !"); + return; + } + + // Convert safe area rectangle from absolute pixels to UGUI coordinates + float rateX = scaler.referenceResolution.x / Screen.width; + float rateY = scaler.referenceResolution.y / Screen.height; + float posX = (int)(safeRect.position.x * rateX); + float posY = (int)(safeRect.position.y * rateY); + float width = (int)(safeRect.size.x * rateX); + float height = (int)(safeRect.size.y * rateY); + + float offsetMaxX = scaler.referenceResolution.x - width - posX; + float offsetMaxY = scaler.referenceResolution.y - height - posY; + + // 注意:安全区坐标系的原点为左下角 + var rectTrans = UIRootStatic.transform as RectTransform; + if (rectTrans != null) + { + rectTrans.offsetMin = new Vector2(posX, posY); //锚框状态下的屏幕左下角偏移向量 + rectTrans.offsetMax = new Vector2(-offsetMaxX, -offsetMaxY); //锚框状态下的屏幕右上角偏移向量 + } + } + + /// + /// 模拟IPhoneX异形屏 + /// + public static void SimulateIPhoneXNotchScreen() + { + Rect rect; + if (Screen.height > Screen.width) + { + // 竖屏Portrait + float deviceWidth = 1125; + float deviceHeight = 2436; + rect = new Rect(0f / deviceWidth, 102f / deviceHeight, 1125f / deviceWidth, 2202f / deviceHeight); + } + else + { + // 横屏Landscape + float deviceWidth = 2436; + float deviceHeight = 1125; + rect = new Rect(132f / deviceWidth, 63f / deviceHeight, 2172f / deviceWidth, 1062f / deviceHeight); + } + + Rect safeArea = new Rect(Screen.width * rect.x, Screen.height * rect.y, Screen.width * rect.width, Screen.height * rect.height); + ApplyScreenSafeRect(safeArea); + } + + #endregion + + /// + /// 获取所有层级下顶部的窗口名称。 + /// + public string GetTopWindow() + { + if (_stack.Count == 0) + { + return string.Empty; + } + + UIWindow topWindow = _stack[_stack.Count - 1]; + return topWindow.WindowName; + } + + /// + /// 获取指定层级下顶部的窗口名称。 + /// + public string GetTopWindow(int layer) + { + UIWindow lastOne = null; + for (int i = 0; i < _stack.Count; i++) + { + if (_stack[i].WindowLayer == layer) + lastOne = _stack[i]; + } + + if (lastOne == null) + return string.Empty; + + return lastOne.WindowName; + } + + /// + /// 是否有任意窗口正在加载。 + /// + public bool IsAnyLoading() + { + for (int i = 0; i < _stack.Count; i++) + { + var window = _stack[i]; + if (window.IsLoadDone == false) + return true; + } + + return false; + } + + /// + /// 查询窗口是否存在。 + /// + /// 界面类型。 + /// 是否存在。 + public bool HasWindow() + { + return HasWindow(typeof(T)); + } + + /// + /// 查询窗口是否存在。 + /// + /// 界面类型。 + /// 是否存在。 + public bool HasWindow(Type type) + { + return IsContains(type.FullName); + } + + /// + /// 异步打开窗口。 + /// + /// 用户自定义数据。 + /// 打开窗口操作句柄。 + public OpenWindowOperation ShowUIAsync(params System.Object[] userDatas) where T : UIWindow + { + return ShowUIAsync(typeof(T), userDatas); + } + + /// + /// 异步打开窗口。 + /// + /// 界面类型。 + /// 用户自定义数据。 + /// 打开窗口操作句柄。 + public OpenWindowOperation ShowUIAsync(Type type, params System.Object[] userDatas) + { + string windowName = type.FullName; + + // 如果窗口已经存在 + if (IsContains(windowName)) + { + UIWindow window = GetWindow(windowName); + Pop(window); //弹出窗口 + Push(window); //重新压入 + window.TryInvoke(OnWindowPrepare, userDatas); + var operation = new OpenWindowOperation(window.Handle); + YooAssets.StartOperation(operation); + return operation; + } + else + { + UIWindow window = CreateInstance(type); + Push(window); //首次压入 + window.InternalLoad(window.AssetName, OnWindowPrepare, userDatas); + var operation = new OpenWindowOperation(window.Handle); + YooAssets.StartOperation(operation); + return operation; + } + } + + /// + /// 同步打开窗口。 + /// + /// 窗口类。 + /// 用户自定义数据。 + /// 打开窗口操作句柄。 + public OpenWindowOperation ShowUI(params System.Object[] userDatas) where T : UIWindow + { + var operation = ShowUIAsync(typeof(T), userDatas); + operation.WaitForAsyncComplete(); + return operation; + } + + /// + /// 同步打开窗口。 + /// + /// + /// + /// 打开窗口操作句柄。 + public OpenWindowOperation ShowUI(Type type, params System.Object[] userDatas) + { + var operation = ShowUIAsync(type, userDatas); + operation.WaitForAsyncComplete(); + return operation; + } + + /// + /// 关闭窗口 + /// + public void CloseWindow() where T : UIWindow + { + CloseWindow(typeof(T)); + } + + public void CloseWindow(Type type) + { + string windowName = type.FullName; + UIWindow window = GetWindow(windowName); + if (window == null) + return; + + window.InternalDestroy(); + Pop(window); + OnSortWindowDepth(window.WindowLayer); + OnSetWindowVisible(); + } + + /// + /// 关闭所有窗口。 + /// + public void CloseAll() + { + for (int i = 0; i < _stack.Count; i++) + { + UIWindow window = _stack[i]; + window.InternalDestroy(); + } + + _stack.Clear(); + } + + private void OnWindowPrepare(UIWindow window) + { + OnSortWindowDepth(window.WindowLayer); + window.InternalCreate(); + window.InternalRefresh(); + OnSetWindowVisible(); + } + + private void OnSortWindowDepth(int layer) + { + int depth = layer; + for (int i = 0; i < _stack.Count; i++) + { + if (_stack[i].WindowLayer == layer) + { + _stack[i].Depth = depth; + depth += WINDOW_DEEP; + } + } + } + + private void OnSetWindowVisible() + { + bool isHideNext = false; + for (int i = _stack.Count - 1; i >= 0; i--) + { + UIWindow window = _stack[i]; + if (isHideNext == false) + { + window.Visible = true; + if (window.IsPrepare && window.FullScreen) + { + isHideNext = true; + } + } + else + { + window.Visible = false; + } + } + } + + private UIWindow CreateInstance(Type type) + { + UIWindow window = Activator.CreateInstance(type) as UIWindow; + WindowAttribute attribute = Attribute.GetCustomAttribute(type, typeof(WindowAttribute)) as WindowAttribute; + + if (window == null) + throw new Exception($"Window {type.FullName} create instance failed."); + if (attribute == null) + throw new Exception($"Window {type.FullName} not found {nameof(WindowAttribute)} attribute."); + + string assetName = string.IsNullOrEmpty(attribute.AssetName) ? type.Name : attribute.AssetName; + window.Init(type.FullName, attribute.WindowLayer, attribute.FullScreen, assetName); + return window; + } + + private UIWindow GetWindow(string windowName) + { + for (int i = 0; i < _stack.Count; i++) + { + UIWindow window = _stack[i]; + if (window.WindowName == windowName) + { + return window; + } + } + + return null; + } + + private bool IsContains(string windowName) + { + for (int i = 0; i < _stack.Count; i++) + { + UIWindow window = _stack[i]; + if (window.WindowName == windowName) + { + return true; + } + } + + return false; + } + + private void Push(UIWindow window) + { + // 如果已经存在 + if (IsContains(window.WindowName)) + throw new System.Exception($"Window {window.WindowName} is exist."); + + // 获取插入到所属层级的位置 + int insertIndex = -1; + for (int i = 0; i < _stack.Count; i++) + { + if (window.WindowLayer == _stack[i].WindowLayer) + { + insertIndex = i + 1; + } + } + + // 如果没有所属层级,找到相邻层级 + if (insertIndex == -1) + { + for (int i = 0; i < _stack.Count; i++) + { + if (window.WindowLayer > _stack[i].WindowLayer) + { + insertIndex = i + 1; + } + } + } + + // 如果是空栈或没有找到插入位置 + if (insertIndex == -1) + { + insertIndex = 0; + } + + // 最后插入到堆栈 + _stack.Insert(insertIndex, window); + } + + private void Pop(UIWindow window) + { + // 从堆栈里移除 + _stack.Remove(window); + } } } \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/UI/IUIGroup.cs b/Assets/TEngine/Runtime/GameFramework/UI/UIGroup/IUIGroup.cs similarity index 100% rename from Assets/TEngine/Runtime/GameFramework/UI/IUIGroup.cs rename to Assets/TEngine/Runtime/GameFramework/UI/UIGroup/IUIGroup.cs diff --git a/Assets/TEngine/Runtime/GameFramework/UI/IUIGroup.cs.meta b/Assets/TEngine/Runtime/GameFramework/UI/UIGroup/IUIGroup.cs.meta similarity index 100% rename from Assets/TEngine/Runtime/GameFramework/UI/IUIGroup.cs.meta rename to Assets/TEngine/Runtime/GameFramework/UI/UIGroup/IUIGroup.cs.meta diff --git a/Assets/TEngine/Runtime/GameFramework/UI/UIManager.cs b/Assets/TEngine/Runtime/GameFramework/UI/UIManager.cs deleted file mode 100644 index 87b5f8a6..00000000 --- a/Assets/TEngine/Runtime/GameFramework/UI/UIManager.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace TEngine -{ - public class UIManager - { - - } -} \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/UI/UIManager.cs.meta b/Assets/TEngine/Runtime/GameFramework/UI/UIManager.cs.meta deleted file mode 100644 index 78bd6faa..00000000 --- a/Assets/TEngine/Runtime/GameFramework/UI/UIManager.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 5bd4d4995a5241e3b2df04687b757f47 -timeCreated: 1680511416 \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/UI/UIWidget.cs b/Assets/TEngine/Runtime/GameFramework/UI/UIWidget.cs new file mode 100644 index 00000000..968f01b3 --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/UI/UIWidget.cs @@ -0,0 +1,62 @@ +namespace TEngine +{ + public abstract class UIWidget:UIBase,IUIBehaviour + { + /// + /// 所属的窗口。 + /// + public UIWindow OwnerWindow + { + get + { + var parentUI = base.parent; + while (parentUI != null) + { + if (parentUI.BaseType == UIBaseType.Window) + { + return parentUI as UIWindow; + } + + parentUI = parentUI.Parent; + } + + return null; + } + } + + public virtual void ScriptGenerator() + { + throw new System.NotImplementedException(); + } + + public virtual void BindMemberProperty() + { + throw new System.NotImplementedException(); + } + + public virtual void RegisterEvent() + { + throw new System.NotImplementedException(); + } + + public virtual void OnCreate() + { + throw new System.NotImplementedException(); + } + + public virtual void OnRefresh() + { + throw new System.NotImplementedException(); + } + + public virtual void OnUpdate() + { + throw new System.NotImplementedException(); + } + + public virtual void OnDestroy() + { + throw new System.NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/UI/UIWidget.cs.meta b/Assets/TEngine/Runtime/GameFramework/UI/UIWidget.cs.meta new file mode 100644 index 00000000..215b8d8e --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/UI/UIWidget.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 497ec3b265be4f81aead015ca3aa91d7 +timeCreated: 1680526019 \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/UI/UIWindow.cs b/Assets/TEngine/Runtime/GameFramework/UI/UIWindow.cs index 02b9a877..32d7ced6 100644 --- a/Assets/TEngine/Runtime/GameFramework/UI/UIWindow.cs +++ b/Assets/TEngine/Runtime/GameFramework/UI/UIWindow.cs @@ -1,7 +1,474 @@ -namespace TEngine +using System; +using UnityEngine; +using UnityEngine.UI; +using YooAsset; +using Object = UnityEngine.Object; + +namespace TEngine { - public class UIWindow + public abstract class UIWindow : UIBase, IUIBehaviour { - + private System.Action _prepareCallback; + + private System.Object[] _userDatas; + + private bool _isCreate = false; + + private GameObject _panel; + + private Canvas _canvas; + + private Canvas[] _childCanvas; + + private GraphicRaycaster _raycaster; + + private GraphicRaycaster[] _childRaycaster; + + public override UIBaseType BaseType => UIBaseType.Window; + + /// + /// 窗口矩阵位置组件。 + /// + public RectTransform transform => _panel.transform as RectTransform; + + /// + /// 窗口的实例资源对象。 + /// + public GameObject gameObject => _panel; + + /// + /// 窗口名称。 + /// + public string WindowName { private set; get; } + + /// + /// 窗口层级。 + /// + public int WindowLayer { private set; get; } + + /// + /// 资源定位地址。 + /// + public string AssetName { private set; get; } + + /// + /// 是否为全屏窗口 + /// + public bool FullScreen { private set; get; } + + /// + /// 自定义数据。 + /// + public System.Object UserData + { + get + { + if (_userDatas != null && _userDatas.Length >= 1) + { + return _userDatas[0]; + } + else + { + return null; + } + } + } + + /// + /// 自定义数据集。 + /// + public System.Object[] UserDatas => _userDatas; + + /// + /// 窗口深度值 + /// + public int Depth + { + get + { + if (_canvas != null) + { + return _canvas.sortingOrder; + } + else + { + return 0; + } + } + + set + { + if (_canvas != null) + { + if (_canvas.sortingOrder == value) + { + return; + } + + // 设置父类 + _canvas.sortingOrder = value; + + // 设置子类 + int depth = value; + for (int i = 0; i < _childCanvas.Length; i++) + { + var canvas = _childCanvas[i]; + if (canvas != _canvas) + { + depth += 5; //注意递增值 + canvas.sortingOrder = depth; + } + } + + // 虚函数 + if (_isCreate) + { + OnSortDepth(value); + } + } + } + } + + /// + /// 窗口可见性 + /// + public bool Visible + { + get + { + if (_canvas != null) + { + return _canvas.gameObject.layer == UIComponent.WINDOW_SHOW_LAYER; + } + else + { + return false; + } + } + + set + { + if (_canvas != null) + { + int setLayer = value ? UIComponent.WINDOW_SHOW_LAYER : UIComponent.WINDOW_HIDE_LAYER; + if (_canvas.gameObject.layer == setLayer) + return; + + // 显示设置 + _canvas.gameObject.layer = setLayer; + for (int i = 0; i < _childCanvas.Length; i++) + { + _childCanvas[i].gameObject.layer = setLayer; + } + + // 交互设置 + Interactable = value; + + // 虚函数 + if (_isCreate) + { + OnSetVisible(value); + } + } + } + } + + /// + /// 窗口交互性 + /// + private bool Interactable + { + get + { + if (_raycaster != null) + { + return _raycaster.enabled; + } + else + { + return false; + } + } + + set + { + if (_raycaster != null) + { + _raycaster.enabled = value; + for (int i = 0; i < _childRaycaster.Length; i++) + { + _childRaycaster[i].enabled = value; + } + } + } + } + + /// + /// 是否加载完毕 + /// + internal bool IsLoadDone => Handle.IsDone; + + /// + /// 是否准备完毕 + /// + public bool IsPrepare { private set; get; } + + + public void Init(string name, int layer, bool fullScreen, string assetName) + { + WindowName = name; + WindowLayer = layer; + FullScreen = fullScreen; + AssetName = assetName; + } + + /// + /// 代码自动生成绑定。 + /// + public virtual void ScriptGenerator() + { + } + + /// + /// 绑定UI成员元素。 + /// + public virtual void BindMemberProperty() + { + } + + /// + /// 注册事件。 + /// + public virtual void RegisterEvent() + { + } + + /// + /// 窗口创建。 + /// + public virtual void OnCreate() + { + } + + /// + /// 窗口刷新 + /// + public virtual void OnRefresh() + { + } + + /// + /// 窗口更新 + /// + public virtual void OnUpdate() + { + } + + /// + /// 窗口销毁 + /// + public virtual void OnDestroy() + { + } + + protected virtual void Close() + { + GameModule.UI.CloseWindow(this.GetType()); + } + + /// + /// 当触发窗口的层级排序 + /// + protected virtual void OnSortDepth(int depth) + { + } + + /// + /// 当因为全屏遮挡触发窗口的显隐 + /// + protected virtual void OnSetVisible(bool visible) + { + } + + internal void TryInvoke(System.Action prepareCallback, System.Object[] userDatas) + { + _userDatas = userDatas; + if (IsPrepare) + { + prepareCallback?.Invoke(this); + } + else + { + _prepareCallback = prepareCallback; + } + } + + internal void InternalLoad(string location, System.Action prepareCallback, System.Object[] userDatas) + { + if (Handle != null) + { + return; + } + + _prepareCallback = prepareCallback; + _userDatas = userDatas; + Handle = YooAssets.LoadAssetAsync(location); + Handle.Completed += Handle_Completed; + } + + internal void InternalCreate() + { + if (_isCreate == false) + { + _isCreate = true; + OnCreate(); + } + } + + internal void InternalRefresh() + { + OnRefresh(); + } + + internal void InternalUpdate() + { + if (IsPrepare) + { + OnUpdate(); + } + } + + internal void InternalDestroy() + { + _isCreate = false; + + RemoveAllUIEvent(); + + // 注销回调函数 + _prepareCallback = null; + + // 卸载面板资源 + if (Handle != null) + { + Handle.Release(); + Handle = null; + } + + // 销毁面板对象 + if (_panel != null) + { + OnDestroy(); + Object.Destroy(_panel); + _panel = null; + } + } + + private void Handle_Completed(AssetOperationHandle handle) + { + if (handle.AssetObject == null) + { + return; + } + + // 实例化对象 + _panel = handle.InstantiateSync(UIComponent.UIRootStatic); + _panel.transform.localPosition = Vector3.zero; + + // 获取组件 + _canvas = _panel.GetComponent(); + if (_canvas == null) + { + throw new Exception($"Not found {nameof(Canvas)} in panel {WindowName}"); + } + + _canvas.overrideSorting = true; + _canvas.sortingOrder = 0; + _canvas.sortingLayerName = "Default"; + + // 获取组件 + _raycaster = _panel.GetComponent(); + _childCanvas = _panel.GetComponentsInChildren(true); + _childRaycaster = _panel.GetComponentsInChildren(true); + + // 通知UI管理器 + IsPrepare = true; + _prepareCallback?.Invoke(this); + } + + #region FindChildComponent + + public Transform FindChild(string path) + { + return DUnityUtil.FindChild(transform, path); + } + + public Transform FindChild(Transform trans, string path) + { + return DUnityUtil.FindChild(trans, path); + } + + public T FindChildComponent(string path) where T : Component + { + return DUnityUtil.FindChildComponent(transform, path); + } + + public T FindChildComponent(Transform trans, string path) where T : Component + { + return DUnityUtil.FindChildComponent(trans, path); + } + + #endregion + + #region UIEvent + + private GameEventMgr _eventMgr; + + protected GameEventMgr EventMgr + { + get + { + if (_eventMgr == null) + { + _eventMgr = MemoryPool.Acquire(); + } + + return _eventMgr; + } + } + + public void AddUIEvent(int eventType, Action handler) + { + EventMgr.AddUIEvent(eventType, handler); + } + + protected void AddUIEvent(int eventType, Action handler) + { + EventMgr.AddUIEvent(eventType, handler); + } + + protected void AddUIEvent(int eventType, Action handler) + { + EventMgr.AddUIEvent(eventType, handler); + } + + protected void AddUIEvent(int eventType, Action handler) + { + EventMgr.AddUIEvent(eventType, handler); + } + + protected void AddUIEvent(int eventType, Action handler) + { + EventMgr.AddUIEvent(eventType, handler); + } + + private void RemoveAllUIEvent() + { + if (_eventMgr != null) + { + MemoryPool.Release(_eventMgr); + } + } + + #endregion } } \ No newline at end of file