Files
Gen_Hack-and-Slash-Roguelit…/Client/Assets/Scripts/Managers/TemporaryAnimationManager.cs

428 lines
21 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 Prefab;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using Utils;
namespace Managers
{
public class TemporaryAnimationManager : MonoSingleton<TemporaryAnimationManager>
{
// 将包装图层改为由代码动态创建和管理
private GameObject _temporaryAnimationLevel;
private GameObject _temporaryAnimationUILevel;
[SerializeField] private TemporaryAnimatorImageUI temporaryAnimatorImageUIPrefab;
[SerializeField] private TemporaryAnimatorSprite temporaryAnimatorSpritePrefab;
[SerializeField] private TemporaryAnimatorText temporaryAnimatorTextPrefab;
[SerializeField] private TemporaryAnimatorText temporaryAnimatorUITextPrefab;
protected override void OnStart()
{
SceneManager.sceneLoaded += OnSceneLoaded;
CreateAnimationLayersForCurrentScene(SceneManager.GetActiveScene(), LoadSceneMode.Single);
}
private void OnDestroy()
{
SceneManager.sceneLoaded -= OnSceneLoaded;
// 在Manager销毁时也销毁创建的图层防止残留
if (_temporaryAnimationLevel != null)
{
Destroy(_temporaryAnimationLevel);
}
if (_temporaryAnimationUILevel != null)
{
Destroy(_temporaryAnimationUILevel);
}
}
private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
// 在新场景加载时,重新创建动画图层
CreateAnimationLayersForCurrentScene(scene, mode);
}
/// <summary>
/// 为当前活动场景创建或重新创建临时动画的父级图层。
/// </summary>
/// <param name="scene">当前加载的场景。</param>
/// <param name="mode">场景加载模式。</param>
private void CreateAnimationLayersForCurrentScene(Scene scene, LoadSceneMode mode)
{
// 销毁旧的图层(如果存在),确保每个场景都有独立的图层
// 注意如果TemporaryAnimationManager是DontDestroyOnLoad
// 那么旧的图层可能还在,需要显式销毁。
if (_temporaryAnimationLevel != null)
{
Destroy(_temporaryAnimationLevel);
}
if (_temporaryAnimationUILevel != null)
{
Destroy(_temporaryAnimationUILevel);
}
// 创建非UI动画的包装图层
_temporaryAnimationLevel = new GameObject("TemporaryAnimations");
// 将其移动到当前加载的场景的根目录,使其成为场景的一部分
SceneManager.MoveGameObjectToScene(_temporaryAnimationLevel, scene);
// 1. 获取或创建 Canvas
var mainCanvas = GetOrCreateMainCanvas();
// 2. 创建 _temporaryAnimationUILevel GameObject
_temporaryAnimationUILevel = new GameObject("TemporaryUIAnimations");
// 3. 将其设置为 Canvas 的子对象
// SetParent(parentTransform, worldPositionStay: false) 会将子对象的局部位置重置为 (0,0,0)
// 这对于新的UI元素通常是期望的行为
_temporaryAnimationUILevel.transform.SetParent(mainCanvas.transform, false);
// 4. 重置新创建的UI容器的局部变换
// 确保它相对于父级 Canvas 处于一个干净的初始状态
_temporaryAnimationUILevel.transform.localPosition = Vector3.zero;
_temporaryAnimationUILevel.transform.localScale = Vector3.one;
_temporaryAnimationUILevel.transform.localRotation = Quaternion.identity;
}
/// <summary>
/// 生成一个非UI文本临时动画。
/// </summary>
/// <param name="str">动画的文本内容。</param>
/// <param name="position">动画在世界坐标系中的初始位置。</param>
/// <param name="lifeTime">动画的生命周期(秒)。</param>
/// <param name="fps">动画帧率。</param>
/// <param name="xShift">X轴位移函数。</param>
/// <param name="yShift">Y轴位移函数。</param>
public void GenerateTemporaryAnimation(string str, Vector3 position, float lifeTime = 3, float fps = 3,
Func<float, float> xShift = null,
Func<float, float> yShift = null)
{
if (!_temporaryAnimationLevel)
{
Debug.LogError("TemporaryAnimationLevel is not initialized. Cannot generate non-UI animation.");
return;
}
var textObj = Instantiate(temporaryAnimatorTextPrefab, _temporaryAnimationLevel.transform);
textObj.transform.position = new Vector3(position.x,position.y);
textObj.Init(str, fps);
textObj.SetAnimationFunctions(xShift, yShift);
textObj.lifeTime = lifeTime;
textObj.gameObject.SetActive(true);
}
/// <summary>
/// 生成一个非UI文本临时动画可指定父级Transform。
/// </summary>
/// <param name="str">动画的文本内容。</param>
/// <param name="position">动画在世界坐标系中的初始位置。</param>
/// <param name="parent">动画的父级Transform。如果为null则使用默认的非UI动画层。</param>
/// <param name="lifeTime">动画的生命周期(秒)。</param>
/// <param name="fps">动画帧率。</param>
/// <param name="xShift">X轴位移函数。</param>
/// <param name="yShift">Y轴位移函数。</param>
public void GenerateTemporaryAnimation(string str, Vector3 position, Transform parent, float lifeTime = 3, float fps = 3,
Func<float, float> xShift = null,
Func<float, float> yShift = null)
{
Transform actualParent = parent ?? _temporaryAnimationLevel?.transform;
if (actualParent == null)
{
Debug.LogError("TemporaryAnimationLevel or specified parent is not initialized. Cannot generate non-UI animation.");
return;
}
var textObj = Instantiate(temporaryAnimatorTextPrefab, actualParent);
textObj.transform.position = position; // 外部传入的position应视为世界坐标
textObj.Init(str, fps);
textObj.SetAnimationFunctions(xShift, yShift);
textObj.lifeTime = lifeTime;
textObj.gameObject.SetActive(true);
}
/// <summary>
/// 生成一个非UI精灵临时动画。
/// </summary>
/// <param name="sprites">动画的精灵帧数组。</param>
/// <param name="position">动画在世界坐标系中的初始位置。</param>
/// <param name="lifeTime">动画的生命周期(秒)。</param>
/// <param name="fps">动画帧率。</param>
/// <param name="xShift">X轴位移函数。</param>
/// <param name="yShift">Y轴位移函数。</param>
public void GenerateTemporaryAnimation(Sprite[] sprites, Vector3 position,float lifeTime = 3, float fps = 3, Func<float, float> xShift = null,
Func<float, float> yShift = null)
{
if (!_temporaryAnimationLevel)
{
Debug.LogError("TemporaryAnimationLevel is not initialized. Cannot generate non-UI animation.");
return;
}
var obj = Instantiate(temporaryAnimatorSpritePrefab, _temporaryAnimationLevel.transform);
obj.transform.position = new Vector3(position.x,position.y);
obj.Init(sprites, fps);
obj.SetAnimationFunctions(xShift, yShift);
obj.lifeTime = lifeTime;
obj.gameObject.SetActive(true);
}
/// <summary>
/// 生成一个非UI精灵临时动画可指定父级Transform。
/// </summary>
/// <param name="sprites">动画的精灵帧数组。</param>
/// <param name="position">动画在世界坐标系中的初始位置。</param>
/// <param name="parent">动画的父级Transform。如果为null则使用默认的非UI动画层。</param>
/// <param name="lifeTime">动画的生命周期(秒)。</param>
/// <param name="fps">动画帧率。</param>
/// <param name="xShift">X轴位移函数。</param>
/// <param name="yShift">Y轴位移函数。</param>
public void GenerateTemporaryAnimation(Sprite[] sprites, Vector3 position, Transform parent, float lifeTime = 3, float fps = 3, Func<float, float> xShift = null,
Func<float, float> yShift = null)
{
Transform actualParent = parent ?? _temporaryAnimationLevel?.transform;
if (actualParent == null)
{
Debug.LogError("TemporaryAnimationLevel or specified parent is not initialized. Cannot generate non-UI animation.");
return;
}
var obj = Instantiate(temporaryAnimatorSpritePrefab, actualParent);
obj.transform.position = position; // 外部传入的position应视为世界坐标
obj.Init(sprites, fps);
obj.SetAnimationFunctions(xShift, yShift);
obj.lifeTime = lifeTime;
obj.gameObject.SetActive(true);
}
/// <summary>
/// 生成一个UI文本临时动画。
/// 此重载将传入的 <paramref name="position"/> 视为屏幕坐标 (如鼠标位置)并将其自动转换为Canvas局部坐标。
/// </summary>
/// <param name="str">动画的文本内容。</param>
/// <param name="position">动画在屏幕坐标系中的初始位置(如鼠标位置)。</param>
/// <param name="lifeTime">动画的生命周期(秒)。</param>
/// <param name="fps">动画帧率。</param>
/// <param name="xShift">X轴位移函数。</param>
/// <param name="yShift">Y轴位移函数。</param>
public void GenerateTemporaryAnimationUI(string str, Vector2 position,float lifeTime = 3, float fps = 3, Func<float, float> xShift = null,
Func<float, float> yShift = null)
{
if (!_temporaryAnimationUILevel)
{
Debug.LogError("TemporaryAnimationUILevel is not initialized. Cannot generate UI animation.");
return;
}
var textObj = Instantiate(temporaryAnimatorUITextPrefab, _temporaryAnimationUILevel.transform);
RectTransform canvasRectTransform = _temporaryAnimationUILevel.transform as RectTransform;
if (canvasRectTransform != null)
{
// 获取Canvas组件特别是RenderMode为ScreenSpaceCamera时需要worldCamera
Canvas rootCanvas = canvasRectTransform.root.GetComponent<Canvas>();
Camera eventCamera = null;
if (rootCanvas.renderMode == RenderMode.ScreenSpaceCamera || rootCanvas.renderMode == RenderMode.WorldSpace)
{
eventCamera = rootCanvas.worldCamera; // 使用Canvas指定的相机
if (eventCamera == null) // 如果Canvas没有指定相机尝试主相机
{
eventCamera = Camera.main;
}
}
if (RectTransformUtility.ScreenPointToLocalPointInRectangle(canvasRectTransform, position, eventCamera, out var localPoint))
{
textObj.rectTransform.anchoredPosition = localPoint;
}
else
{
textObj.rectTransform.anchoredPosition = position;
Debug.LogWarning("Failed to convert screen point to local point for UI text animation. Using raw position.");
}
}
else
{
textObj.rectTransform.anchoredPosition = position;
Debug.LogWarning("TemporaryAnimationUILevel is not a RectTransform. Unable to convert screen point. Using raw position.");
}
textObj.Init(str, fps);
textObj.SetAnimationFunctions(xShift, yShift);
textObj.lifeTime = lifeTime;
textObj.gameObject.SetActive(true);
}
/// <summary>
/// 生成一个UI文本临时动画可指定父级RectTransform。
/// 此重载将传入的 <paramref name="position"/> 视为相对于父级RectTransform的 anchoredPosition。
/// </summary>
/// <param name="str">动画的文本内容。</param>
/// <param name="position">动画在UI坐标系(RectTransform.anchoredPosition)中的初始位置。</param>
/// <param name="parent">动画的父级RectTransform。如果为null则使用默认的UI动画层。</param>
/// <param name="lifeTime">动画的生命周期(秒)。</param>
/// <param name="fps">动画帧率。</param>
/// <param name="xShift">X轴位移函数。</param>
/// <param name="yShift">Y轴位移函数。</param>
public void GenerateTemporaryAnimationUI(string str, Vector2 position, RectTransform parent, float lifeTime = 3, float fps = 3, Func<float, float> xShift = null,
Func<float, float> yShift = null)
{
RectTransform actualParent = parent ?? (_temporaryAnimationUILevel?.transform as RectTransform);
if (actualParent == null)
{
Debug.LogError("TemporaryAnimationUILevel or specified parent is not initialized or not a RectTransform. Cannot generate UI animation.");
return;
}
var textObj = Instantiate(temporaryAnimatorUITextPrefab);
textObj.rectTransform.SetParent(actualParent, false); // SetParent with worldPositionStay: false
textObj.rectTransform.anchoredPosition = position;
textObj.Init(str, fps);
textObj.SetAnimationFunctions(xShift, yShift);
textObj.lifeTime = lifeTime;
textObj.gameObject.SetActive(true);
}
/// <summary>
/// 生成一个UI精灵临时动画。
/// 此重载将传入的 <paramref name="position"/> 视为屏幕坐标 (如鼠标位置)并将其自动转换为Canvas局部坐标。
/// </summary>
/// <param name="sprites">动画的精灵帧数组。</param>
/// <param name="position">动画在屏幕坐标系中的初始位置(如鼠标位置)。</param>
/// <param name="lifeTime">动画的生命周期(秒)。</param>
/// <param name="fps">动画帧率。</param>
/// <param name="xShift">X轴位移函数。</param>
/// <param name="yShift">Y轴位移函数。</param>
public void GenerateTemporaryAnimationUI(Sprite[] sprites, Vector2 position,float lifeTime = 3, float fps = 3, Func<float, float> xShift = null,
Func<float, float> yShift = null)
{
if (_temporaryAnimationUILevel == null)
{
Debug.LogError("TemporaryAnimationUILevel is not initialized. Cannot generate UI animation.");
return;
}
var obj = Instantiate(temporaryAnimatorImageUIPrefab, _temporaryAnimationUILevel.transform);
RectTransform canvasRectTransform = _temporaryAnimationUILevel.transform as RectTransform;
if (canvasRectTransform != null)
{
// 获取Canvas组件特别是RenderMode为ScreenSpaceCamera时需要worldCamera
Canvas rootCanvas = canvasRectTransform.root.GetComponent<Canvas>();
Camera eventCamera = null;
if (rootCanvas.renderMode == RenderMode.ScreenSpaceCamera || rootCanvas.renderMode == RenderMode.WorldSpace)
{
eventCamera = rootCanvas.worldCamera; // 使用Canvas指定的相机
if (eventCamera == null) // 如果Canvas没有指定相机尝试主相机
{
eventCamera = Camera.main;
}
}
Vector2 localPoint;
if (RectTransformUtility.ScreenPointToLocalPointInRectangle(canvasRectTransform, position, eventCamera, out localPoint))
{
obj.rectTransform.anchoredPosition = localPoint;
}
else
{
obj.rectTransform.anchoredPosition = position;
Debug.LogWarning("Failed to convert screen point to local point for UI sprite animation. Using raw position.");
}
}
else
{
obj.rectTransform.anchoredPosition = position;
Debug.LogWarning("TemporaryAnimationUILevel is not a RectTransform. Unable to convert screen point. Using raw position.");
}
obj.Init(sprites, fps);
obj.SetAnimationFunctions(xShift, yShift);
obj.lifeTime = lifeTime;
obj.gameObject.SetActive(true);
}
/// <summary>
/// 生成一个UI精灵临时动画可指定父级RectTransform。
/// 此重载将传入的 <paramref name="position"/> 视为相对于父级RectTransform的 anchoredPosition。
/// </summary>
/// <param name="sprites">动画的精灵帧数组。</param>
/// <param name="position">动画在UI坐标系(RectTransform.anchoredPosition)中的初始位置。</param>
/// <param name="parent">动画的父级RectTransform。如果为null则使用默认的UI动画层。</param>
/// <param name="lifeTime">动画的生命周期(秒)。</param>
/// <param name="fps">动画帧率。</param>
/// <param name="xShift">X轴位移函数。</param>
/// <param name="yShift">Y轴位移函数。</param>
public void GenerateTemporaryAnimationUI(Sprite[] sprites, Vector2 position, RectTransform parent, float lifeTime = 3, float fps = 3, Func<float, float> xShift = null,
Func<float, float> yShift = null)
{
RectTransform actualParent = parent ?? (_temporaryAnimationUILevel?.transform as RectTransform);
if (actualParent == null)
{
Debug.LogError("TemporaryAnimationUILevel or specified parent is not initialized or not a RectTransform. Cannot generate UI animation.");
return;
}
var obj = Instantiate(temporaryAnimatorImageUIPrefab);
obj.rectTransform.SetParent(actualParent, false); // SetParent with worldPositionStay: false
obj.rectTransform.anchoredPosition = position;
obj.Init(sprites, fps);
obj.SetAnimationFunctions(xShift, yShift);
obj.lifeTime = lifeTime;
obj.gameObject.SetActive(true);
}
/// <summary>
/// 寻找场景中现有的 Canvas如果不存在则创建一个新的。
/// 同时确保场景中存在 EventSystem。
/// </summary>
/// <returns>返回场景中的 Canvas 组件。</returns>
private static Canvas GetOrCreateMainCanvas()
{
var existingCanvas = FindFirstObjectByType<Canvas>();
if (existingCanvas != null)
{
EnsureEventSystemExists();
return existingCanvas;
}
// 创建 Canvas GameObject
var canvasGO = new GameObject("Canvas");
canvasGO.layer = LayerMask.NameToLayer("UI");
var newCanvas = canvasGO.AddComponent<Canvas>();
newCanvas.renderMode = RenderMode.ScreenSpaceOverlay;
// 确保有相机以便RectTransformUtility.ScreenPointToLocalPointInRectangle可以工作
// 对于ScreenSpaceOverlayworldCamera可以为null但为了通用性和健壮性这里设置为Camera.main
// 以防Canvas.renderMode将来调整为ScreenSpaceCamera或WorldSpace
newCanvas.worldCamera = Camera.main;
canvasGO.AddComponent<CanvasScaler>();
canvasGO.AddComponent<GraphicRaycaster>();
EnsureEventSystemExists();
return newCanvas;
}
/// <summary>
/// 检查场景中是否存在 EventSystem如果不存在则创建一个。
/// </summary>
private static void EnsureEventSystemExists()
{
var existingEventSystem = FindFirstObjectByType<EventSystem>();
if (existingEventSystem != null) return;
var eventSystemGO = new GameObject("EventSystem");
eventSystemGO.AddComponent<EventSystem>();
eventSystemGO.AddComponent<StandaloneInputModule>(); // 最常见的输入模块
}
}
}