Files
aibis-dream/Assets/Scripts/UI/UIManager.cs
T
dingyuntian 4100232162 feat: 添加全局屏幕转场面板
- 新增 ScreenTransitionPanel,支持 fade_in/out、fade_in_white、flash_white、
  wipe_in/out、shutter_in/out 等多种转场效果
- 新增 Screen Transition Panel Prefab 并在 Persistence 场景挂载
- UIManager 启动时自动显示该面板
- BaseYarnCommand 新增对应的 Yarn 全局转场指令
2026-07-16 17:56:33 +08:00

306 lines
9.4 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using AibisDream.Framework;
using AibisDream.UI;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
namespace AibisDream
{
public class UIManager : Kit.Singleton<UIManager>, IUIManager
{
#region 索引
private Dictionary<Type, IUIPanel> _panelPool;
private readonly Stack<IUIPanel> _panelStack = new();
public RectTransform Canvas { get; private set; }
public Vector2 Resolution { get; set; }
public Camera UICamera { get; private set; }
public RectTransform DialogCanvas { get; private set; }
private GraphicRaycaster _raycaster;
private RecordingToolPanel _recordingToolPanel;
#endregion
public override void OnSingletonInit()
{
InitRefs();
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.Escape))
{
if (TryGetPanel<InGameTerminalPanel>(out var inGameTerminal) && inGameTerminal.HandleEscape())
{
return;
}
if (TryGetPanel<TerminalPanel>(out var terminal) && terminal.HandleEscape())
{
return;
}
if (GameManager.Instance != null && GameManager.Instance.state.isInGame)
{
GetPanel<InGameTerminalPanel>()?.Open(InGameTerminalPage.Setting);
}
}
if ((Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl))
&& Input.GetKeyDown(KeyCode.R))
{
_recordingToolPanel?.TogglePanel();
}
}
private void InitRefs()
{
UICamera = transform.Find("UI Camera").GetComponent<Camera>();
// 获取Canvas
Canvas = transform.Find("UI Canvas").GetComponent<RectTransform>();
DialogCanvas = transform.Find("Dialog Canvas").GetComponent<RectTransform>();
// 获取 GraphicRaycaster
_raycaster = Canvas.GetComponent<GraphicRaycaster>();
_recordingToolPanel = GetComponentInChildren<RecordingToolPanel>(true);
if (_recordingToolPanel == null)
{
var infoPanel = transform.Find("UI Canvas/Info Panel");
if (infoPanel != null)
{
_recordingToolPanel = infoPanel.GetComponentInChildren<RecordingToolPanel>(true);
}
}
// 获取Panel
var panels = Canvas.GetComponentsInChildren<IUIPanel>(true);
_panelPool = new Dictionary<Type, IUIPanel>();
foreach (var panel in panels)
{
_panelPool[panel.GetType()] = panel;
panel.Initialize();
}
EnumEventSystem.Global.Register(GameLoopEnum.AppStart, OnAppStart);
EnumEventSystem.Global.Register(GameLoopEnum.GameStart, OnGameStart);
EnumEventSystem.Global.Register(GameLoopEnum.GameQuit, OnGameQuit);
}
private void OnAppStart()
{
// 游戏打开
ShowPanel<TerminalPanel>();
ShowPanel<PlayToolPanel>();
ShowPanel<ScreenTransitionPanel>();
HidePanel<MainPanel>();
HidePanel<InGameTerminalPanel>();
HidePanel<EndPanel>();
}
private void OnGameStart()
{
// 开始游戏
HidePanel<TerminalPanel>();
HidePanel<InGameTerminalPanel>();
ShowPanel<MainPanel>();
}
private void OnGameQuit()
{
HidePanel<MainPanel>();
ShowPanel<TerminalPanel>();
}
public void ShowPanel<T>(Action<T> call = null) where T : class, IUIPanel
{
var type = typeof(T);
// 从缓存中检查panel
if (_panelPool.TryGetValue(type, out var panel))
{
if (panel.IsOpen) return;
if (panel.IsCloseable) _panelStack.Push(panel);
panel.Show();
call?.Invoke(panel as T);
}
else
{
Debug.Log($"panel {type} 未注册");
}
}
public void HidePanel<T>() where T : IUIPanel
{
var type = typeof(T);
// 从缓存中检查panel
if (_panelPool.TryGetValue(type, out var panel))
{
if (!panel.IsOpen) return;
if (panel.IsCloseable) _panelStack.Pop();
panel.Hide();
}
else
{
Debug.Log($"panel {type} 未注册");
}
}
private void HidePanel<T>(T panel) where T : IUIPanel
{
if (_panelPool.ContainsValue(panel))
{
if (!panel.IsOpen) return;
if (panel.IsCloseable) _panelStack.Pop();
panel.Hide();
}
else
{
Debug.Log($"panel {typeof(T)} 未注册");
}
}
public T GetPanel<T>() where T : class, IUIPanel
{
if (_panelPool.TryGetValue(typeof(T), out var panel))
{
return panel as T;
}
Debug.Log($"没有找到{typeof(T)}");
return null;
}
public bool TryGetPanel<T>(out T panel) where T : class, IUIPanel
{
panel = null;
if (_panelPool == null)
{
return false;
}
if (_panelPool.TryGetValue(typeof(T), out var rawPanel))
{
panel = rawPanel as T;
}
return panel != null;
}
public void ShowPreferredLoading()
{
if (TryGetPanel<TerminalLoadingPanel>(out var terminalLoading))
{
ShowPanel<TerminalLoadingPanel>();
terminalLoading.SetProgress(0f);
}
else
{
GetPanel<InfoPanel>()?.ShowLoading();
}
}
public void SetPreferredLoadingProgress(float progress)
{
if (TryGetPanel<TerminalLoadingPanel>(out var terminalLoading))
{
terminalLoading.SetProgress(progress);
}
}
public void HidePreferredLoading()
{
if (TryGetPanel<TerminalLoadingPanel>(out _))
{
HidePanel<TerminalLoadingPanel>();
}
GetPanel<InfoPanel>()?.HideLoading();
}
public static Vector3 GetDialogUIPos(Vector3 screenPos)
{
// 转换为UI坐标
RectTransformUtility.ScreenPointToLocalPointInRectangle(
Instance.DialogCanvas,
screenPos,
Instance.UICamera,
out Vector2 localPoint);
return localPoint;
}
public bool ScreenPointToLocalPointInRectangle(RectTransform rectTransform, Vector2 screenPos, out Vector2 localPoint)
{
return RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, screenPos, UICamera, out localPoint);
}
/// <summary>
/// 检查屏幕坐标是否命中主 UI Canvas 上会接收射线的元素。
/// 对话 Canvas 不在此范围内,因此点击对话画面本身仍可正常推进。
/// </summary>
public bool IsPointerOverUI(Vector2 screenPosition)
{
if (EventSystem.current == null || _raycaster == null)
{
return false;
}
var eventData = new PointerEventData(EventSystem.current)
{
position = screenPosition
};
var results = new List<RaycastResult>();
_raycaster.Raycast(eventData, results);
return results.Count > 0;
}
/// <summary>
/// 检查鼠标是否在指定标记的 UI 区域上
/// </summary>
/// <param name="regionTag">区域标识符,需要在 UIRegionMarker 组件中配置</param>
/// <returns>如果鼠标在指定区域上返回 true,否则返回 false</returns>
public bool IsOverRegion(string regionTag)
{
if (string.IsNullOrEmpty(regionTag))
{
return false;
}
// 如果鼠标不在 UI 上,直接返回 false
if (!EventSystem.current.IsPointerOverGameObject())
{
return false;
}
// 使用 GraphicRaycaster 检测鼠标下的 UI 元素
var eventData = new PointerEventData(EventSystem.current)
{
position = Input.mousePosition
};
var results = new List<RaycastResult>();
_raycaster.Raycast(eventData, results);
return results.Count > 0 && results.Any(result => result.gameObject.CompareTag(regionTag));
}
}
public interface IUIManager
{
void ShowPanel<T>(Action<T> call = null) where T : class, IUIPanel;
void HidePanel<T>() where T : IUIPanel;
T GetPanel<T>() where T : class, IUIPanel;
RectTransform Canvas { get; }
Vector2 Resolution { get; set; }
// 还没想好要不要用栈
// UIPanel Pop();
// void Push<T>(T panel) where T : UIPanel;
}
}