using System; using Data; using Prefab; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.SceneManagement; using UnityEngine.UI; using Utils; namespace Managers { public class TemporaryAnimationManager : MonoSingleton { [SerializeField] private TemporaryAnimatorImageUI temporaryAnimatorImageUIPrefab; [SerializeField] private TemporaryAnimatorSprite temporaryAnimatorSpritePrefab; [SerializeField] private TemporaryAnimatorText temporaryAnimatorTextPrefab; [SerializeField] private TemporaryAnimatorText temporaryAnimatorUITextPrefab; private GameObject _temporaryAnimationLevel; // 非UI临时动画的父级GameObject private GameObject _temporaryAnimationUILevel; // UI临时动画的父级GameObject private void OnDestroy() { SceneManager.sceneLoaded -= OnSceneLoaded; // 管理器销毁时,销毁所有创建的动画层级。 Destroy(_temporaryAnimationLevel); Destroy(_temporaryAnimationUILevel); } protected override void OnStart() { SceneManager.sceneLoaded += OnSceneLoaded; // 首次启动时为当前场景创建动画层级,确保动画容器在首次使用前存在。 CreateAnimationLayersForCurrentScene(SceneManager.GetActiveScene()); } private void OnSceneLoaded(Scene scene, LoadSceneMode mode) { // 在新场景加载时重新创建动画层级。 CreateAnimationLayersForCurrentScene(scene); } /// /// 为当前活动场景创建或重新创建临时动画的父级层级。 /// /// 当前加载的场景。 private void CreateAnimationLayersForCurrentScene(Scene scene) { // 销毁场景切换时可能存在的旧动画层级。 if (_temporaryAnimationLevel != null) Destroy(_temporaryAnimationLevel); if (_temporaryAnimationUILevel != null) Destroy(_temporaryAnimationUILevel); // 创建非UI临时动画的根GameObject。 _temporaryAnimationLevel = new GameObject("TemporaryAnimations"); // 将非UI动画根GameObject移动到当前场景。 SceneManager.MoveGameObjectToScene(_temporaryAnimationLevel, scene); // 获取场景主Canvas,如果不存在则创建。 var mainCanvas = GetOrCreateMainCanvas(); // 创建UI临时动画的根GameObject。 _temporaryAnimationUILevel = new GameObject("TemporaryUIAnimations"); // 将UI动画根GameObject设为主Canvas的子对象。 _temporaryAnimationUILevel.transform.SetParent(mainCanvas.transform, false); _temporaryAnimationUILevel.transform.localPosition = Vector3.zero; _temporaryAnimationUILevel.transform.localScale = Vector3.one; _temporaryAnimationUILevel.transform.localRotation = Quaternion.identity; } /// /// 生成一个非UI文本临时动画。 /// /// 文本内容。 /// 世界坐标系中的初始位置。 /// 生命周期(秒)。 /// 动画帧率。 /// X轴位移函数。 /// Y轴位移函数。 /// 创建的 TemporaryAnimatorText 对象。 public TemporaryAnimatorText GenerateTemporaryAnimation(string str, Vector3 position, float lifeTime = 3, float fps = 3, Func xShift = null, Func yShift = null) { if (!_temporaryAnimationLevel) { Debug.LogError("TemporaryAnimationManager: 非UI动画层未初始化。请确保管理器在生成非UI动画前已正确初始化。"); return null; } 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); return textObj; } /// /// 生成一个非UI文本临时动画,可指定父级Transform。 /// /// 文本内容。 /// 世界坐标系中的初始位置。 /// 父级Transform。如果为null,则使用默认非UI动画层。 /// 生命周期(秒)。 /// 动画帧率。 /// X轴位移函数。 /// Y轴位移函数。 /// 创建的 TemporaryAnimatorText 对象。 public TemporaryAnimatorText GenerateTemporaryAnimation(string str, Vector3 position, Transform parent, float lifeTime = 3, float fps = 3, Func xShift = null, Func yShift = null) { if (!_temporaryAnimationLevel) { Debug.LogError("TemporaryAnimationManager: 非UI动画层未初始化。请确保管理器在生成非UI动画前已正确初始化。"); return null; } // 优先使用传入的父级,否则使用默认的非UI动画层。 var actualParent = parent ?? _temporaryAnimationLevel.transform; var textObj = Instantiate(temporaryAnimatorTextPrefab, actualParent); textObj.transform.position = position; textObj.Init(str, fps); textObj.SetAnimationFunctions(xShift, yShift); textObj.lifeTime = lifeTime; textObj.gameObject.SetActive(true); return textObj; } /// /// 生成一个非UI精灵临时动画。 /// /// 精灵帧数组。 /// 世界坐标系中的初始位置。 /// 生命周期(秒)。 /// 动画帧率。 /// X轴位移函数。 /// Y轴位移函数。 /// 创建的 TemporaryAnimatorSprite 对象。 public TemporaryAnimatorSprite GenerateTemporaryAnimation(Sprite[] sprites, Vector3 position, float lifeTime = 3, float fps = 3, Func xShift = null, Func yShift = null) { if (!_temporaryAnimationLevel) { Debug.LogError("TemporaryAnimationManager: 非UI动画层未初始化。请确保管理器在生成非UI动画前已正确初始化。"); return null; } 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); return obj; } /// /// 生成一个非UI精灵临时动画,可指定父级Transform。 /// /// 精灵帧数组。 /// 世界坐标系中的初始位置。 /// 父级Transform。如果为null,则使用默认非UI动画层。 /// 生命周期(秒)。 /// 动画帧率。 /// X轴位移函数。 /// Y轴位移函数。 /// 创建的 TemporaryAnimatorSprite 对象。 public TemporaryAnimatorSprite GenerateTemporaryAnimation(Sprite[] sprites, Vector3 position, Transform parent, float lifeTime = 3, float fps = 3, Func xShift = null, Func yShift = null) { if (!_temporaryAnimationLevel) { Debug.LogError("TemporaryAnimationManager: 非UI动画层未初始化。请确保管理器在生成非UI动画前已正确初始化。"); return null; } // 优先使用传入的父级,否则使用默认的非UI动画层。 var actualParent = parent ?? _temporaryAnimationLevel.transform; var obj = Instantiate(temporaryAnimatorSpritePrefab, actualParent); obj.transform.position = position; obj.Init(sprites, fps); obj.SetAnimationFunctions(xShift, yShift); obj.lifeTime = lifeTime; obj.gameObject.SetActive(true); return obj; } /// /// 生成一个非UI DrawNodeDef 临时动画及其子动画。 /// /// 绘制节点定义。 /// 世界坐标系中的初始位置。 /// 生命周期(秒)。 /// 动画帧率。 /// X轴位移函数。 /// Y轴位移函数。 /// 创建的根 TemporaryAnimatorSprite 对象。 public TemporaryAnimatorSprite GenerateTemporaryAnimation(DrawNodeDef drawNode, Vector3 position, float lifeTime = 3, float fps = 3, Func xShift = null, Func yShift = null) { if (_temporaryAnimationLevel == null) { Debug.LogError("TemporaryAnimationManager: 非UI动画层未初始化。请确保管理器在生成非UI动画前已正确初始化。"); return null; } var textureSprite = PackagesImageManager.Instance.GetSprites(drawNode.textures); if (textureSprite == null) { Debug.LogWarning($"TemporaryAnimationManager: 无法找到DrawNodeDef纹理 '{drawNode.textures}' 的精灵。跳过动画。"); return null; } var obj = Instantiate(temporaryAnimatorSpritePrefab, _temporaryAnimationLevel.transform); obj.transform.position = new Vector2(position.x, position.y) + StringUtils.StringToVector2( drawNode.position); obj.Init(textureSprite, drawNode.FPS); obj.SetAnimationFunctions(xShift, yShift); obj.lifeTime = lifeTime; obj.gameObject.SetActive(true); if (drawNode.nodes == null) return obj; foreach (var childNode in drawNode.nodes) // 递归生成子动画,使用默认的非UI动画层。 GenerateTemporaryAnimation(childNode, position, _temporaryAnimationLevel.transform, lifeTime, fps, xShift, yShift); return obj; } /// /// 生成一个非UI DrawNodeDef 临时动画及其子动画,可指定父级Transform。 /// /// 绘制节点定义。 /// 世界坐标系中的初始位置。 /// 父级Transform。如果为null,则使用默认非UI动画层。 /// 生命周期(秒)。 /// 动画帧率。 /// X轴位移函数。 /// Y轴位移函数。 /// 创建的根 TemporaryAnimatorSprite 对象。 public TemporaryAnimatorSprite GenerateTemporaryAnimation(DrawNodeDef drawNode, Vector3 position, Transform parent, float lifeTime = 3, float fps = 3, Func xShift = null, Func yShift = null) { if (_temporaryAnimationLevel == null) { Debug.LogError("TemporaryAnimationManager: 非UI动画层未初始化。请确保管理器在生成非UI动画前已正确初始化。"); return null; } // 优先使用传入的父级,否则使用默认的非UI动画层。 var actualParent = parent ?? _temporaryAnimationLevel.transform; var textureSprite = PackagesImageManager.Instance.GetSprites(drawNode.textures); if (textureSprite == null) { Debug.LogWarning($"TemporaryAnimationManager: 无法找到DrawNodeDef纹理 '{drawNode.textures}' 的精灵。跳过动画。"); return null; } var obj = Instantiate(temporaryAnimatorSpritePrefab, actualParent); obj.transform.position = new Vector2(position.x, position.y) + StringUtils.StringToVector2(drawNode.position); obj.Init(textureSprite, drawNode.FPS); obj.SetAnimationFunctions(xShift, yShift); obj.lifeTime = lifeTime; obj.gameObject.SetActive(true); if (drawNode.nodes == null) return obj; foreach (var childNode in drawNode.nodes) // 递归生成子动画,子节点位置基于父节点位置加上自身偏移,父级为当前对象。 GenerateTemporaryAnimation(childNode, position + (Vector3)StringUtils.StringToVector2(drawNode.position), obj.transform, lifeTime, fps, xShift, yShift); return obj; } public TemporaryAnimatorImageUI GenerateTemporaryAnimationUI(DrawNodeDef drawNode, Vector3 worldPosition, Transform parent, float lifeTime = 3, float fps = 3, Func xShift = null, Func yShift = null) { if (_temporaryAnimationLevel == null) { Debug.LogError("TemporaryAnimationManager: 非UI动画层未初始化。请确保管理器在生成非UI动画前已正确初始化。"); return null; } // 优先使用传入的父级,否则使用默认的非UI动画层。 var actualParent = parent ?? _temporaryAnimationLevel.transform; var textureSprite = PackagesImageManager.Instance.GetSprites(drawNode.textures); if (textureSprite == null) { Debug.LogWarning($"TemporaryAnimationManager: 无法找到DrawNodeDef纹理 '{drawNode.textures}' 的精灵。跳过动画。"); return null; } var obj = Instantiate(temporaryAnimatorImageUIPrefab, actualParent); obj.transform.position = new Vector2(worldPosition.x, worldPosition.y) + StringUtils.StringToVector2(drawNode.position); obj.Init(textureSprite, drawNode.FPS); obj.SetAnimationFunctions(xShift, yShift); obj.lifeTime = lifeTime; obj.gameObject.SetActive(true); if (drawNode.nodes == null) return obj; foreach (var childNode in drawNode.nodes) // 递归生成子动画,子节点位置基于父节点位置加上自身偏移,父级为当前对象。 GenerateTemporaryAnimation(childNode, worldPosition + (Vector3)StringUtils.StringToVector2(drawNode.position), obj.transform, lifeTime, fps, xShift, yShift); return obj; } /// /// 生成一个UI文本临时动画。 /// 此重载将传入的 视为屏幕坐标 (如鼠标位置),并将其自动转换为Canvas局部坐标。 /// /// 文本内容。 /// 屏幕坐标系中的初始位置。 /// 生命周期(秒)。 /// 动画帧率。 /// X轴位移函数。 /// Y轴位移函数。 /// 创建的 TemporaryAnimatorText 对象。 public TemporaryAnimatorText GenerateTemporaryAnimationUI(string str, Vector2 position, float lifeTime = 3, float fps = 3, Func xShift = null, Func yShift = null) { if (_temporaryAnimationUILevel == null) { Debug.LogError("TemporaryAnimationManager: UI动画层未初始化。无法生成UI动画。"); return null; } var textObj = Instantiate(temporaryAnimatorUITextPrefab, _temporaryAnimationUILevel.transform); var canvasRectTransform = _temporaryAnimationUILevel.transform as RectTransform; if (canvasRectTransform != null) { var rootCanvas = canvasRectTransform.root.GetComponent(); Camera eventCamera = null; // 根据Canvas的RenderMode确定用于屏幕坐标转换的相机。 if (rootCanvas.renderMode == RenderMode.ScreenSpaceOverlay) { } else if (rootCanvas.renderMode == RenderMode.ScreenSpaceCamera || rootCanvas.renderMode == RenderMode.WorldSpace) { eventCamera = rootCanvas.worldCamera; if (eventCamera == null) { eventCamera = Camera.main; if (eventCamera == null) Debug.LogWarning( $"Canvas '{rootCanvas.name}' 处于 {rootCanvas.renderMode} 模式但世界相机为null且未找到主相机。UI动画位置可能不正确。"); } } Vector2 localPoint; if (RectTransformUtility.ScreenPointToLocalPointInRectangle(canvasRectTransform, position, eventCamera, out localPoint)) textObj.rectTransform.anchoredPosition = localPoint; else Debug.LogWarning( $"未能将屏幕点 {position} 转换为UI动画在Canvas '{rootCanvas.name}' 上的局部点。将使用默认锚点位置 (0,0)。请检查Canvas的RenderMode和世界相机设置。"); } else { Debug.LogError("TemporaryAnimationManager: UI动画层不是RectTransform。无法转换屏幕点。UI动画无法正确放置。"); } textObj.Init(str, fps); textObj.SetAnimationFunctions(xShift, yShift); textObj.lifeTime = lifeTime; textObj.gameObject.SetActive(true); return textObj; } /// /// 生成一个UI文本临时动画,可指定父级RectTransform。 /// 此重载将传入的 视为相对于父级RectTransform的 anchoredPosition。 /// /// 文本内容。 /// UI坐标系(RectTransform.anchoredPosition)中的初始位置。 /// 父级RectTransform。如果为null,则使用默认的UI动画层。 /// 生命周期(秒)。 /// 动画帧率。 /// X轴位移函数。 /// Y轴位移函数。 /// 创建的 TemporaryAnimatorText 对象。 public TemporaryAnimatorText GenerateTemporaryAnimationUI(string str, Vector2 position, RectTransform parent, float lifeTime = 3, float fps = 3, Func xShift = null, Func yShift = null) { if (_temporaryAnimationUILevel == null) { Debug.LogError("TemporaryAnimationManager: UI动画层未初始化。请确保管理器在生成UI动画前已正确初始化。"); return null; } var defaultUIParent = _temporaryAnimationUILevel.transform as RectTransform; if (defaultUIParent == null) { Debug.LogError("TemporaryAnimationManager: 默认UI动画层不是RectTransform。无法生成UI动画。"); return null; } var actualParent = parent ?? defaultUIParent; var textObj = Instantiate(temporaryAnimatorUITextPrefab, actualParent, false); textObj.rectTransform.anchoredPosition = position; textObj.Init(str, fps); textObj.SetAnimationFunctions(xShift, yShift); textObj.lifeTime = lifeTime; textObj.gameObject.SetActive(true); return textObj; } /// /// 生成一个UI精灵临时动画。 /// 此重载将传入的 视为屏幕坐标 (如鼠标位置),并将其自动转换为Canvas局部坐标。 /// /// 精灵帧数组。 /// 屏幕坐标系中的初始位置。 /// 生命周期(秒)。 /// 动画帧率。 /// X轴位移函数。 /// Y轴位移函数。 /// 创建的 TemporaryAnimatorImageUI 对象。 public TemporaryAnimatorImageUI GenerateTemporaryAnimationUI(Sprite[] sprites, Vector2 position, float lifeTime = 3, float fps = 3, Func xShift = null, Func yShift = null) { if (_temporaryAnimationUILevel == null) { Debug.LogError("TemporaryAnimationManager: UI动画层未初始化。无法生成UI动画。"); return null; } var obj = Instantiate(temporaryAnimatorImageUIPrefab, _temporaryAnimationUILevel.transform); var canvasRectTransform = _temporaryAnimationUILevel.transform as RectTransform; if (canvasRectTransform != null) { var rootCanvas = canvasRectTransform.root.GetComponent(); Camera eventCamera = null; // 根据Canvas的RenderMode确定用于屏幕坐标转换的相机。 if (rootCanvas.renderMode == RenderMode.ScreenSpaceOverlay) { } else if (rootCanvas.renderMode == RenderMode.ScreenSpaceCamera || rootCanvas.renderMode == RenderMode.WorldSpace) { eventCamera = rootCanvas.worldCamera; if (eventCamera == null) { eventCamera = Camera.main; if (eventCamera == null) Debug.LogWarning( $"Canvas '{rootCanvas.name}' 处于 {rootCanvas.renderMode} 模式但世界相机为null且未找到主相机。UI动画位置可能不正确。"); } } Vector2 localPoint; if (RectTransformUtility.ScreenPointToLocalPointInRectangle(canvasRectTransform, position, eventCamera, out localPoint)) obj.rectTransform.anchoredPosition = localPoint; else Debug.LogWarning( $"未能将屏幕点 {position} 转换为UI动画在Canvas '{rootCanvas.name}' 上的局部点。将使用默认锚点位置 (0,0)。请检查Canvas的RenderMode和世界相机设置。"); } else { Debug.LogError("TemporaryAnimationManager: UI动画层不是RectTransform。无法转换屏幕点。UI动画无法正确放置。"); } obj.Init(sprites, fps); obj.SetAnimationFunctions(xShift, yShift); obj.lifeTime = lifeTime; obj.gameObject.SetActive(true); return obj; } /// /// 生成一个UI精灵临时动画,可指定父级RectTransform。 /// 此重载将传入的 视为相对于父级RectTransform的 anchoredPosition。 /// /// 精灵帧数组。 /// UI坐标系(RectTransform.anchoredPosition)中的初始位置。 /// 父级RectTransform。如果为null,则使用默认的UI动画层。 /// 生命周期(秒)。 /// 动画帧率。 /// X轴位移函数。 /// Y轴位移函数。 /// 创建的 TemporaryAnimatorImageUI 对象。 public TemporaryAnimatorImageUI GenerateTemporaryAnimationUI(Sprite[] sprites, Vector2 position, RectTransform parent, float lifeTime = 3, float fps = 3, Func xShift = null, Func yShift = null) { if (_temporaryAnimationUILevel == null) { Debug.LogError("TemporaryAnimationManager: UI动画层未初始化。请确保管理器在生成UI动画前已正确初始化。"); return null; } var defaultUIParent = _temporaryAnimationUILevel.transform as RectTransform; if (defaultUIParent == null) { Debug.LogError("TemporaryAnimationManager: 默认UI动画层不是RectTransform。无法生成UI动画。"); return null; } var actualParent = parent ?? defaultUIParent; var obj = Instantiate(temporaryAnimatorImageUIPrefab, actualParent, false); obj.rectTransform.anchoredPosition = position; obj.Init(sprites, fps); obj.SetAnimationFunctions(xShift, yShift); obj.lifeTime = lifeTime; obj.gameObject.SetActive(true); return obj; } /// /// 寻找场景中现有的 Canvas,如果不存在则创建一个新的。 /// 同时确保场景中存在 EventSystem。 /// /// 返回场景中的 Canvas 组件。 private static Canvas GetOrCreateMainCanvas() { var existingCanvas = FindFirstObjectByType(); if (existingCanvas != null) { // 如果现有Canvas需要WorldCamera但未设置,尝试赋值Camera.main。 if (existingCanvas.renderMode == RenderMode.ScreenSpaceCamera || existingCanvas.renderMode == RenderMode.WorldSpace) if (existingCanvas.worldCamera == null) { var mainCamera = Camera.main; if (mainCamera == null) Debug.LogWarning("现有Canvas处于屏幕相机/世界空间模式但未设置世界相机,且主相机为空。UI动画可能无法正确渲染。"); existingCanvas.worldCamera = mainCamera; } EnsureEventSystemExists(); return existingCanvas; } // 在场景中创建一个新的Canvas GameObject。 var canvasGO = new GameObject("Canvas"); // 将新创建的Canvas移动到当前场景根目录。 SceneManager.MoveGameObjectToScene(canvasGO, SceneManager.GetActiveScene()); canvasGO.layer = LayerMask.NameToLayer("UI"); var newCanvas = canvasGO.AddComponent(); newCanvas.renderMode = RenderMode.ScreenSpaceOverlay; canvasGO.AddComponent(); canvasGO.AddComponent(); EnsureEventSystemExists(); return newCanvas; } /// /// 检查场景中是否存在 EventSystem,如果不存在则创建一个。 /// private static void EnsureEventSystemExists() { var existingEventSystem = FindFirstObjectByType(); if (existingEventSystem != null) return; var eventSystemGO = new GameObject("EventSystem"); eventSystemGO.AddComponent(); eventSystemGO.AddComponent(); // 将新的EventSystem移动到当前场景。 SceneManager.MoveGameObjectToScene(eventSystemGO, SceneManager.GetActiveScene()); } } }