Files
plugin-library/Assets/UI/Scripts/UIFramework/Runtime/UIManager.cs
2025-09-18 18:25:40 +08:00

402 lines
14 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.EventSystems;
using DG.Tweening;
using YooAsset;
namespace SkierFramework
{
public enum UIBlackType
{
None, // 无黑边,全适应
Height, // 保持高度填满,两边黑边
Width, // 保持宽度填满, 上下黑边
AutoBlack, // 自动黑边(选中左右或上下黑边最少的一方)
}
public class UIManager : SingletonMono<UIManager>
{
public int width = 1920;
public int height = 1080;
public UIBlackType uiBlackType = UIBlackType.None;
private Transform _root;
private Camera _worldCamera;
private Camera _uiCamera;
/// <summary>
/// 屏幕渐变遮罩
/// </summary>
private CanvasGroup _blackMask;
private CanvasGroup _backgroundMask;
private Tweener _fadeTweener;
/// <summary>
/// 黑边
/// </summary>
private RectTransform[] _blacks = new RectTransform[2];
private Dictionary<string, UIViewController> _viewControllers;
private Dictionary<UILayer, UILayerLogic> _layers;
private HashSet<string> _openViews;
private HashSet<string> _residentViews;
public EventSystem EventSystem { get; private set; }
private void Awake()
{
Initialize();
}
private void Initialize()
{
_layers = new Dictionary<UILayer, UILayerLogic>();
_viewControllers = new Dictionary<string, UIViewController>();
_openViews = new HashSet<string>();
_residentViews = new HashSet<string>();
_worldCamera = Camera.main;
_worldCamera.cullingMask &= int.MaxValue ^ (1 << Layer.UI);
var root = GameObject.Find("UIRoot");
if (root == null)
{
root = new GameObject("UIRoot");
}
root.layer = Layer.UI;
GameObject.DontDestroyOnLoad(root);
_root = root.transform;
var camera = GameObject.Find("UICamera");
if (camera == null)
{
camera = new GameObject("UICamera");
}
_uiCamera = camera.GetOrAddComponent<Camera>();
_uiCamera.cullingMask = 1 << Layer.UI;
_uiCamera.transform.SetParent(_root);
_uiCamera.orthographic = true;
_uiCamera.clearFlags = CameraClearFlags.Depth;
EventSystem = EventSystem.current;
var layers = Enum.GetValues(typeof(UILayer));
foreach (UILayer layer in layers)
{
bool is3d = layer == UILayer.SceneLayer;
Canvas layerCanvas = UIExtension.CreateLayerCanvas(layer, is3d, _root, is3d ? _worldCamera : _uiCamera,
width, height);
UILayerLogic uILayerLogic = new UILayerLogic(layer, layerCanvas);
_layers.Add(layer, uILayerLogic);
}
_blackMask = UIExtension.CreateBlackMask(_layers[UILayer.BlackMaskLayer].canvas.transform);
_backgroundMask = UIExtension.CreateBlackMask(_layers[UILayer.BackgroundLayer].canvas.transform);
}
private void Update()
{
// TODO 不应该Update设置应该放在屏幕状态变动事件里
ChangeOrCreateBlack();
}
/// <summary>
/// 创建或者调整黑边,需间隔触发,由于有些设备屏幕是可以转动,是动态的
/// </summary>
private void ChangeOrCreateBlack()
{
if (_layers == null) return;
var parent = _layers[UILayer.BackgroundLayer].canvas.transform as RectTransform;
var uIBlackType = GetUIBlackType();
switch (uIBlackType)
{
case UIBlackType.Height:
// 高度适配时的左右黑边
var rect = _blacks[0];
if (rect == null)
{
_blacks[0] = rect = UIExtension.CreateBlackMask(parent, 1, "right").transform as RectTransform;
}
else if (Mathf.Abs(rect.anchoredPosition.x * 2 + parent.rect.width - width) < 1)
{
return;
}
rect.pivot = new Vector2(0, 0.5f);
rect.anchorMin = new Vector2(1, 0);
rect.anchorMax = new Vector2(1, 1);
rect.sizeDelta = new Vector2(Mathf.Abs(width - parent.rect.width), 0);
rect.anchoredPosition = new Vector2((width - parent.rect.width) / 2, 0);
rect = _blacks[1];
if (rect == null)
{
_blacks[1] = rect = UIExtension.CreateBlackMask(parent, 1, "left").transform as RectTransform;
}
rect.pivot = new Vector2(1, 0.5f);
rect.anchorMin = new Vector2(0, 0);
rect.anchorMax = new Vector2(0, 1);
rect.sizeDelta = new Vector2(Mathf.Abs(width - parent.rect.width), 0);
rect.anchoredPosition = new Vector2(-(width - parent.rect.width) / 2, 0);
break;
case UIBlackType.Width:
// 宽度适配时的上下黑边
rect = _blacks[0];
if (rect == null)
{
_blacks[0] = rect = UIExtension.CreateBlackMask(parent, 1, "top").transform as RectTransform;
}
else if (Mathf.Abs(rect.anchoredPosition.y * 2 + parent.rect.height - height) < 1)
{
return;
}
rect.pivot = new Vector2(0.5f, 0);
rect.anchorMin = new Vector2(0, 1);
rect.anchorMax = new Vector2(1, 1);
rect.sizeDelta = new Vector2(0, Mathf.Abs(height - parent.rect.height));
rect.anchoredPosition = new Vector2(0, (height - parent.rect.height) / 2);
rect = _blacks[1];
if (rect == null)
{
_blacks[1] = rect = UIExtension.CreateBlackMask(parent, 1, "bottom").transform as RectTransform;
}
rect.pivot = new Vector2(0.5f, 1);
rect.anchorMin = new Vector2(0, 0);
rect.anchorMax = new Vector2(1, 0);
rect.sizeDelta = new Vector2(0, Mathf.Abs(height - parent.rect.height));
rect.anchoredPosition = new Vector2(0, -(height - parent.rect.height) / 2);
break;
default:
break;
}
}
public UIBlackType GetUIBlackType()
{
var uIBlackType = uiBlackType;
if (uIBlackType == UIBlackType.AutoBlack)
{
var parent = _layers[UILayer.BackgroundLayer].canvas.transform as RectTransform;
float widthDis = Mathf.Abs(width - parent.rect.width);
float heightDis = Mathf.Abs(height - parent.rect.height);
if (widthDis < 1 && heightDis < 1)
uIBlackType = UIBlackType.None;
else if (widthDis > heightDis)
uIBlackType = UIBlackType.Height;
else
uIBlackType = UIBlackType.Width;
}
return uIBlackType;
}
public Rect GetSafeArea()
{
Rect rect = Screen.safeArea;
if (uiBlackType == UIBlackType.Width)
{
var parent = _layers[UILayer.BackgroundLayer].canvas.transform as RectTransform;
float blackArea = Mathf.Abs(height - parent.rect.height) / 2;
rect.yMin = Mathf.Max(0, rect.yMin - blackArea);
rect.yMax = Mathf.Min(rect.yMax + blackArea, Screen.height);
}
else if (uiBlackType == UIBlackType.Height)
{
var parent = _layers[UILayer.BackgroundLayer].canvas.transform as RectTransform;
float blackArea = Mathf.Abs(width - parent.rect.width) / 2;
rect.xMin = Mathf.Max(0, rect.xMin - blackArea);
rect.xMax = Mathf.Min(rect.xMax + blackArea, Screen.width);
}
return rect;
}
public void EnableBackgroundMask(bool enable)
{
_backgroundMask.alpha = enable ? 1 : 0;
}
public async UniTask InitUIConfig()
{
// 初始化需要加载所有UI的配置
var list = await UIConfig.GetAllConfigs();
foreach (var cfg in list)
{
if (_viewControllers.ContainsKey(cfg.uiType))
{
Debug.LogErrorFormat("存在相同的uiType:{0} 请检查UIConfig是否重复", cfg.uiType.ToString());
continue;
}
_viewControllers.Add(cfg.uiType, new UIViewController
{
uiPath = cfg.path,
uiType = cfg.uiType,
uiLayer = _layers[cfg.uiLayer],
uiViewType = cfg.viewType,
isWindow = cfg.isWindow,
});
}
}
/// <summary>
/// 注册常驻UI
/// </summary>
public void AddResidentUI<T>() where T : UIView
{
_residentViews.Add(typeof(T).Name);
}
public void Open<T>(object userData = null, Action callback = null) where T : UIView
{
if (!_viewControllers.ContainsKey(typeof(T).Name))
{
Debug.LogErrorFormat("未配置uiType:{0} 请检查UIConfig.cs", typeof(T).Name);
return;
}
_openViews.Add(typeof(T).Name);
_viewControllers[typeof(T).Name].Open(userData, callback);
}
public async UniTask Preload<T>() where T : UIView
{
if (!_viewControllers.TryGetValue(typeof(T).Name, out var controller))
{
Debug.LogErrorFormat("未配置uiType:{0} 请检查UIConfig.cs", typeof(T).Name);
return;
}
await controller.Load();
}
public async UniTask PreloadAll()
{
foreach (var controller in _viewControllers.Values)
{
await ResourceManager.Instance.GetResourceCore(UIConfig.UIPackageName)
.LoadAssetAsync<GameObject>(controller.uiPath);
}
}
public bool IsOpen<T>() where T : UIView
{
return _openViews.Contains(typeof(T).Name);
}
public void Close<T>(Action callback = null) where T : UIView
{
if (!_viewControllers.ContainsKey(typeof(T).Name))
{
Debug.LogErrorFormat("未配置uiType:{0} 请检查UIConfig.cs", typeof(T).Name);
return;
}
_openViews.Remove(typeof(T).Name);
_viewControllers[typeof(T).Name].Close(callback);
}
public void Close<T>(T view, Action callback = null) where T : UIView
{
Close<T>();
}
/// <summary>
/// UI建议都用事件进行交互最好不使用该接口
/// </summary>
public T GetView<T>() where T : UIView
{
if (!_viewControllers.ContainsKey(typeof(T).Name))
{
Debug.LogErrorFormat("未配置uiType:{0} 请检查UIConfig.cs", typeof(T).Name);
return null;
}
return _viewControllers[typeof(T).Name].uiView as T;
}
public void CloseAll(bool closeResidentView = false)
{
var list = ListPool<string>.Get();
foreach (var uiType in _openViews)
{
if (closeResidentView || !_residentViews.Contains(uiType))
{
_viewControllers[uiType].Close();
list.Add(uiType);
}
}
foreach (var uiType in list)
{
_openViews.Remove(uiType);
}
ListPool<string>.Release(list);
}
public void ReleaseAll()
{
foreach (var controller in _viewControllers.Values)
{
if (!_residentViews.Contains(controller.uiType))
{
_openViews.Remove(controller.uiType);
controller.Release();
}
}
}
public void FadeIn(float duration = 0.5f, TweenCallback callback = null)
{
if (_fadeTweener != null && _fadeTweener.IsPlaying())
_fadeTweener.Complete();
_fadeTweener = _blackMask.DOFade(1.0f, duration);
_fadeTweener.onComplete = callback;
}
public void FadeOut(float duration = 0.5f, TweenCallback callback = null)
{
if (_fadeTweener != null && _fadeTweener.IsPlaying())
_fadeTweener.Complete();
_fadeTweener = _blackMask.DOFade(0.0f, duration);
_fadeTweener.onComplete = callback;
}
public void FadeInOut(float duration = 1.0f, TweenCallback callback = null)
{
if (_fadeTweener != null && _fadeTweener.IsPlaying())
_fadeTweener.Complete();
_fadeTweener = _blackMask.DOFade(1.0f, duration * 0.5f);
_fadeTweener.onComplete += () =>
{
_fadeTweener = _blackMask.DOFade(0.0f, duration * 0.5f);
_fadeTweener.onComplete = callback;
};
}
public void Cancel()
{
if (_layers.TryGetValue(UILayer.NormalLayer, out var layer) && layer.openedViews.Count > 0)
{
var viewController = layer.openedViews.Peek();
if (viewController.uiView != null)
{
viewController.uiView.OnCancel();
}
}
}
}
}