- 新增 ScreenTransitionPanel,支持 fade_in/out、fade_in_white、flash_white、 wipe_in/out、shutter_in/out 等多种转场效果 - 新增 Screen Transition Panel Prefab 并在 Persistence 场景挂载 - UIManager 启动时自动显示该面板 - BaseYarnCommand 新增对应的 Yarn 全局转场指令
306 lines
9.4 KiB
C#
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;
|
|
}
|
|
}
|