using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using YooAsset; namespace UniFramework.Window { public static class UniWindow { public struct WindowInfo { public string WindowName; public int WindowLayer; public bool IsLoadDone; } private static bool _isInitialize = false; private static GameObject _driver = null; private static readonly List _stack = new List(100); internal static GameObject Desktop { private set; get; } /// /// 初始化界面系统 /// public static void Initalize(GameObject desktop) { if (_isInitialize) throw new Exception($"{nameof(UniWindow)} is initialized !"); if (desktop == null) throw new ArgumentNullException(); if (_isInitialize == false) { // 创建驱动器 _isInitialize = true; _driver = new UnityEngine.GameObject($"[{nameof(UniWindow)}]"); _driver.AddComponent(); UnityEngine.Object.DontDestroyOnLoad(_driver); UniLogger.Log($"{nameof(UniWindow)} initalize !"); Desktop = desktop; } } /// /// 销毁界面系统 /// public static void Destroy() { if (_isInitialize) { CloseAll(); _isInitialize = false; if (_driver != null) GameObject.Destroy(_driver); UniLogger.Log($"{nameof(UniWindow)} destroy all !"); } } /// /// 更新界面系统 /// internal static void Update() { if (_isInitialize) { int count = _stack.Count; for (int i = 0; i < _stack.Count; i++) { if (_stack.Count != count) break; var window = _stack[i]; window.InternalUpdate(); } } } /// /// 设置屏幕安全区域(异形屏支持) /// /// 安全区域 public static void ApplyScreenSafeRect(Rect safeRect) { CanvasScaler scaler = Desktop.GetComponentInParent(); if (scaler == null) { UniLogger.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 = Desktop.transform as RectTransform; 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); } /// /// 获取窗口堆栈信息 /// public static void GetWindowInfos(List output) { if (output == null) output = new List(); else output.Clear(); for (int i = 0; i < _stack.Count; i++) { var window = _stack[i]; WindowInfo info = new WindowInfo(); info.WindowName = window.WindowName; info.WindowLayer = window.WindowLayer; info.IsLoadDone = window.IsLoadDone; output.Add(info); } } /// /// 获取所有层级下顶部的窗口名称 /// public static string GetTopWindow() { if (_stack.Count == 0) return string.Empty; UIWindow topWindow = _stack[_stack.Count - 1]; return topWindow.WindowName; } /// /// 获取指定层级下顶部的窗口名称 /// public static 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 static bool IsAnyLoading() { for (int i = 0; i < _stack.Count; i++) { var window = _stack[i]; if (window.IsLoadDone == false) return true; } return false; } /// /// 查询窗口是否存在 /// public static bool HasWindow() { return HasWindow(typeof(T)); } public static bool HasWindow(Type type) { return IsContains(type.FullName); } /// /// 异步打开窗口 /// /// 资源定位地址 /// 用户自定义数据 public static OpenWindowOperation OpenWindowAsync(string location, params System.Object[] userDatas) where T : UIWindow { return OpenWindowAsync(typeof(T), location, userDatas); } public static OpenWindowOperation OpenWindowAsync(Type type, string location, 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(location, OnWindowPrepare, userDatas); var operation = new OpenWindowOperation(window.Handle); YooAssets.StartOperation(operation); return operation; } } /// /// 同步打开窗口 /// /// 窗口类 /// 资源定位地址 /// 用户自定义数据 public static OpenWindowOperation OpenWindowSync(string location, params System.Object[] userDatas) where T : UIWindow { var operation = OpenWindowAsync(typeof(T), location, userDatas); operation.WaitForAsyncComplete(); return operation; } public static OpenWindowOperation OpenWindowSync(Type type, string location, params System.Object[] userDatas) { var operation = OpenWindowAsync(type, location, userDatas); operation.WaitForAsyncComplete(); return operation; } /// /// 关闭窗口 /// public static void CloseWindow() where T : UIWindow { CloseWindow(typeof(T)); } public static 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 static void CloseAll() { for (int i = 0; i < _stack.Count; i++) { UIWindow window = _stack[i]; window.InternalDestroy(); } _stack.Clear(); } private static void OnWindowPrepare(UIWindow window) { OnSortWindowDepth(window.WindowLayer); window.InternalCreate(); window.InternalRefresh(); OnSetWindowVisible(); } private static 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 += 100; //注意:每次递增100深度 } } } private static 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 static 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."); window.Init(type.FullName, attribute.WindowLayer, attribute.FullScreen); return window; } private static UIWindow GetWindow(string name) { for (int i = 0; i < _stack.Count; i++) { UIWindow window = _stack[i]; if (window.WindowName == name) return window; } return null; } private static bool IsContains(string name) { for (int i = 0; i < _stack.Count; i++) { UIWindow window = _stack[i]; if (window.WindowName == name) return true; } return false; } private static 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 static void Pop(UIWindow window) { // 从堆栈里移除 _stack.Remove(window); } } }