Files
plugin-library/Assets/06.UIFarme/RunTime/UIFramework/UIManager.cs

412 lines
14 KiB
C#
Raw Normal View History

2025-09-23 11:18:38 +08:00
using System;
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.EventSystems;
using DG.Tweening;
using YooAsset;
namespace Stary.Evo
{
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 handle = YooAssets.LoadAssetSync<UIConfig>("Config_UIConfig");
await handle.Task;
UIConfig uiConfig = handle.GetAssetObject<UIConfig>();
if (uiConfig == null)
{
Debug.LogError($"UnityEvo:UIConfig初始化失败请检查配置文件");
return;
}
var list = uiConfig.uiConfigJsons;
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.pathId,
uiType = cfg.uiType,
uiLayer = _layers[cfg.uiLayer],
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<T>(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<T>();
}
public async UniTask PreloadAll()
{
foreach (var controller in _viewControllers.Values)
{
var handle = YooAssets.LoadAssetSync<GameObject>(controller.uiPath);
await handle.Task;
GameObject gameObject = handle.GetAssetObject<GameObject>();
}
}
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();
}
}
}
}
}