2025-01-24 16:21:00 +08:00
|
|
|
|
using System;
|
2025-03-11 21:03:30 +08:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.Linq.Expressions;
|
|
|
|
|
|
using System.Runtime.CompilerServices;
|
2025-03-04 18:40:14 +08:00
|
|
|
|
using AlicizaX.Resource.Runtime;
|
2025-01-24 16:21:00 +08:00
|
|
|
|
using AlicizaX.Runtime;
|
2025-03-04 18:40:14 +08:00
|
|
|
|
using AlicizaX.Timer.Runtime;
|
2025-01-24 16:21:00 +08:00
|
|
|
|
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;
|
|
|
|
|
|
|
2025-03-04 18:40:14 +08:00
|
|
|
|
public const int UIHideLayer = 2; // Ignore Raycast
|
|
|
|
|
|
public const int UIShowLayer = 5; // UI
|
2025-01-24 16:21:00 +08:00
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
{
|
2025-03-04 18:40:14 +08:00
|
|
|
|
throw new GameFrameworkException("UIRoot Prefab is invalid.");
|
2025-01-24 16:21:00 +08:00
|
|
|
|
}
|
2025-03-04 18:40:14 +08:00
|
|
|
|
|
|
|
|
|
|
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>());
|
2025-01-24 16:21:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#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
|
|
|
|
|
|
{
|
2025-03-11 21:03:30 +08:00
|
|
|
|
return (T)ShowUI(MetaTypeCache<T>.Metadata, userDatas);
|
2025-01-24 16:21:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public async UniTask<T> ShowUIAsync<T>(params System.Object[] userDatas) where T : UIWindow
|
|
|
|
|
|
{
|
2025-03-11 21:03:30 +08:00
|
|
|
|
return (T)await ShowUIAsync(MetaTypeCache<T>.Metadata, userDatas);
|
2025-01-24 16:21:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-03-11 21:03:30 +08:00
|
|
|
|
public void CloseUI<T>(bool force = false) where T : UIWindow
|
2025-01-24 16:21:00 +08:00
|
|
|
|
{
|
2025-03-11 21:03:30 +08:00
|
|
|
|
CloseUI(MetaTypeCache<T>.Metadata, force);
|
2025-01-24 16:21:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-03-11 21:03:30 +08:00
|
|
|
|
public T GetUI<T>() where T : UIWindow
|
2025-01-24 16:21:00 +08:00
|
|
|
|
{
|
2025-03-11 21:03:30 +08:00
|
|
|
|
return (T)GetUI(MetaTypeCache<T>.Metadata);
|
2025-01-24 16:21:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-03-11 21:03:30 +08:00
|
|
|
|
// 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)
|
2025-01-24 16:21:00 +08:00
|
|
|
|
{
|
2025-03-11 21:03:30 +08:00
|
|
|
|
return _uiManager.ShowUI(meta, userDatas);
|
2025-01-24 16:21:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-03-11 21:03:30 +08:00
|
|
|
|
public async UniTask<UIBase> ShowUIAsync(UIMetadata meta, params System.Object[] userDatas)
|
2025-01-24 16:21:00 +08:00
|
|
|
|
{
|
2025-03-11 21:03:30 +08:00
|
|
|
|
return await _uiManager.ShowUIAsync(meta, userDatas);
|
2025-03-04 18:40:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-03-11 21:03:30 +08:00
|
|
|
|
private void CloseUI(UIMetadata meta, bool force = false)
|
2025-03-04 18:40:14 +08:00
|
|
|
|
{
|
2025-03-11 21:03:30 +08:00
|
|
|
|
_uiManager.CloseUI(meta, force);
|
2025-03-04 18:40:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-03-11 21:03:30 +08:00
|
|
|
|
private UIBase GetUI(UIMetadata meta)
|
2025-03-04 18:40:14 +08:00
|
|
|
|
{
|
2025-03-11 21:03:30 +08:00
|
|
|
|
return _uiManager.GetUI(meta);
|
2025-01-24 16:21:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-03-11 21:03:30 +08:00
|
|
|
|
private static Dictionary<Type, Action> _triggerCache = new Dictionary<Type, Action>();
|
|
|
|
|
|
|
|
|
|
|
|
private static void TriggerStaticCtor(Type type)
|
2025-01-24 16:21:00 +08:00
|
|
|
|
{
|
2025-03-11 21:03:30 +08:00
|
|
|
|
// 已有缓存直接触发
|
|
|
|
|
|
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();
|
2025-01-24 16:21:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|