AlicizaX/Client/Packages/com.alicizax.unity.ui/Runtime/UI/UIComponent.cs
2025-03-11 21:03:30 +08:00

209 lines
7.3 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 System.Linq.Expressions;
using System.Runtime.CompilerServices;
using AlicizaX.Resource.Runtime;
using AlicizaX.Runtime;
using AlicizaX.Timer.Runtime;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;
using Object = UnityEngine.Object;
namespace AlicizaX.UI.Runtime
{
[DisallowMultipleComponent]
[AddComponentMenu("Game Framework/UI")]
[UnityEngine.Scripting.Preserve]
public sealed partial class UIComponent : GameFrameworkComponent
{
[SerializeField] private GameObject uiRoot = null;
private Transform m_InstanceRoot = null;
private IUIManager _uiManager;
public const int UIHideLayer = 2; // Ignore Raycast
public const int UIShowLayer = 5; // UI
public Camera UICamera => _uiManager?.UICamera;
public Transform UIRoot => _uiManager.UICanvasRoot;
protected override void Awake()
{
ImplementationComponentType = Utility.Assembly.GetType(componentType);
InterfaceComponentType = typeof(IUIManager);
base.Awake();
_uiManager = SysModuleCenter.GetModule<IUIManager>();
if (uiRoot == null)
{
throw new GameFrameworkException("UIRoot Prefab is invalid.");
}
GameObject obj = Instantiate(uiRoot, Vector3.zero, Quaternion.identity);
obj.name = "------UI Root------";
m_InstanceRoot = obj.transform;
Object.DontDestroyOnLoad(m_InstanceRoot);
_uiManager.Initlize(m_InstanceRoot);
}
private void Start()
{
_uiManager.SetTimerManager(SysModuleCenter.GetModule<ITimerManager>());
}
#region
/// <summary>
/// 设置屏幕安全区域(异形屏支持)。
/// </summary>
/// <param name="safeRect">安全区域</param>
public void ApplyScreenSafeRect(Rect safeRect)
{
CanvasScaler scaler = _uiManager.UICanvasRoot.GetComponent<CanvasScaler>();
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 = _uiManager.UICanvasRoot.transform as RectTransform;
if (rectTrans != null)
{
rectTrans.offsetMin = new Vector2(posX, posY); //锚框状态下的屏幕左下角偏移向量
rectTrans.offsetMax = new Vector2(-offsetMaxX, -offsetMaxY); //锚框状态下的屏幕右上角偏移向量
}
}
/// <summary>
/// 模拟IPhoneX异形屏
/// </summary>
public 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 T ShowUI<T>(params System.Object[] userDatas) where T : UIWindow
{
return (T)ShowUI(MetaTypeCache<T>.Metadata, userDatas);
}
public async UniTask<T> ShowUIAsync<T>(params System.Object[] userDatas) where T : UIWindow
{
return (T)await ShowUIAsync(MetaTypeCache<T>.Metadata, userDatas);
}
public void CloseUI<T>(bool force = false) where T : UIWindow
{
CloseUI(MetaTypeCache<T>.Metadata, force);
}
public T GetUI<T>() where T : UIWindow
{
return (T)GetUI(MetaTypeCache<T>.Metadata);
}
// public UIBase ShowUI(string windowName, params System.Object[] userDatas)
// {
// Type uiType = UITypeCollector.GetUIInst(windowName);
// GameFrameworkGuard.NotNull(uiType, nameof(uiType));
// return ShowUI(uiType, userDatas);
// }
//
// public async UniTask<UIBase> ShowUIAsync(string windowName, params System.Object[] userDatas)
// {
// Type uiType = UITypeCollector.GetUIInst(windowName);
// GameFrameworkGuard.NotNull(uiType, nameof(uiType));
// return await ShowUIAsync(uiType, userDatas);
// }
public UIBase ShowUI(UIMetadata meta, params System.Object[] userDatas)
{
return _uiManager.ShowUI(meta, userDatas);
}
public async UniTask<UIBase> ShowUIAsync(UIMetadata meta, params System.Object[] userDatas)
{
return await _uiManager.ShowUIAsync(meta, userDatas);
}
private void CloseUI(UIMetadata meta, bool force = false)
{
_uiManager.CloseUI(meta, force);
}
private UIBase GetUI(UIMetadata meta)
{
return _uiManager.GetUI(meta);
}
private static Dictionary<Type, Action> _triggerCache = new Dictionary<Type, Action>();
private static void TriggerStaticCtor(Type type)
{
// 已有缓存直接触发
if (_triggerCache.TryGetValue(type, out var trigger))
{
trigger();
return;
}
// 无缓存时动态编译并缓存 -------------------------------------------------
// 1. 构造泛型类型 MetaTypeCache<T>
var genericType = typeof(MetaTypeCache<>).MakeGenericType(type);
// 2. 构建表达式树RuntimeHelpers.RunClassConstructor(typeof(MetaTypeCache<T>).TypeHandle)
var method = typeof(RuntimeHelpers).GetMethod(
nameof(RuntimeHelpers.RunClassConstructor),
new[] { typeof(RuntimeTypeHandle) }
);
var typeHandleExpr = Expression.Constant(genericType.TypeHandle);
var callExpr = Expression.Call(method, typeHandleExpr);
var lambda = Expression.Lambda<Action>(callExpr);
// 3. 编译为原生委托(性能关键!)
var compiledTrigger = lambda.Compile();
// 4. 缓存并触发(非线程安全)
_triggerCache[type] = compiledTrigger;
compiledTrigger();
}
}
}