231 lines
10 KiB
C#
231 lines
10 KiB
C#
![]() |
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;
|
|||
|
|
|||
|
private Func<float, float> defaultXoffset = f => Mathf.Sin(f * 3);
|
|||
|
private Func<float, float> defaultYoffset = f => f;
|
|||
|
|
|||
|
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;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
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;
|
|||
|
}
|
|||
|
|
|||
|
xShift ??= defaultXoffset;
|
|||
|
yShift ??= defaultYoffset;
|
|||
|
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;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 生成一个非UI精灵临时动画。
|
|||
|
/// </summary>
|
|||
|
/// <param name="sprites">动画的精灵帧数组。</param>
|
|||
|
/// <param name="position">动画在世界坐标系中的初始位置。</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 == null)
|
|||
|
{
|
|||
|
Debug.LogError("TemporaryAnimationLevel is not initialized. Cannot generate non-UI animation.");
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
xShift ??= defaultXoffset;
|
|||
|
yShift ??= defaultYoffset;
|
|||
|
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;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 生成一个UI文本临时动画。
|
|||
|
/// </summary>
|
|||
|
/// <param name="str">动画的文本内容。</param>
|
|||
|
/// <param name="position">动画在UI坐标系(RectTransform.anchoredPosition)中的初始位置。</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;
|
|||
|
}
|
|||
|
|
|||
|
xShift ??= defaultXoffset;
|
|||
|
yShift ??= defaultYoffset;
|
|||
|
var textObj = Instantiate(temporaryAnimatorUITextPrefab, _temporaryAnimationUILevel.transform);
|
|||
|
textObj.transform.position = position;
|
|||
|
textObj.Init(str, fps);
|
|||
|
textObj.SetAnimationFunctions(xShift, yShift);
|
|||
|
textObj.lifeTime = lifeTime;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 生成一个UI精灵临时动画。
|
|||
|
/// </summary>
|
|||
|
/// <param name="sprites">动画的精灵帧数组。</param>
|
|||
|
/// <param name="position">动画在UI坐标系(RectTransform.anchoredPosition)中的初始位置。</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;
|
|||
|
}
|
|||
|
|
|||
|
xShift ??= defaultXoffset;
|
|||
|
yShift ??= defaultYoffset;
|
|||
|
var obj = Instantiate(temporaryAnimatorImageUIPrefab, _temporaryAnimationUILevel.transform);
|
|||
|
obj.transform.position = position;
|
|||
|
obj.Init(sprites, fps);
|
|||
|
obj.SetAnimationFunctions(xShift, yShift);
|
|||
|
obj.lifeTime = lifeTime;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 寻找场景中现有的 Canvas,如果不存在则创建一个新的。
|
|||
|
/// 同时确保场景中存在 EventSystem。
|
|||
|
/// </summary>
|
|||
|
/// <returns>返回场景中的 Canvas 组件。</returns>
|
|||
|
private static Canvas GetOrCreateMainCanvas()
|
|||
|
{
|
|||
|
var existingCanvas = FindFirstObjectByType<Canvas>();
|
|||
|
if (existingCanvas != null)
|
|||
|
{
|
|||
|
EnsureEventSystemExists(); // 即使Canvas已存在,也要确保EventSystem存在
|
|||
|
return existingCanvas;
|
|||
|
}
|
|||
|
|
|||
|
// 创建 Canvas GameObject
|
|||
|
var canvasGO = new GameObject("Canvas");
|
|||
|
canvasGO.layer = LayerMask.NameToLayer("UI"); // 建议将UI设置为UI层
|
|||
|
// 添加 Canvas 组件
|
|||
|
var newCanvas = canvasGO.AddComponent<Canvas>();
|
|||
|
newCanvas.renderMode = RenderMode.ScreenSpaceOverlay; // 最常见的UI渲染模式
|
|||
|
// 添加其他必要的UI组件
|
|||
|
canvasGO.AddComponent<CanvasScaler>();
|
|||
|
canvasGO.AddComponent<GraphicRaycaster>();
|
|||
|
// 确保场景中存在 EventSystem,以便UI交互(按钮点击等)能正常工作
|
|||
|
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>(); // 最常见的输入模块
|
|||
|
}
|
|||
|
}
|
|||
|
}
|