using Base; using TMPro; using UnityEngine; namespace Prefab { /// /// 临时动画文本组件,用于在指定时间内逐帧显示一系列文本动画。 /// 该组件假设 TemporaryAnimator 实现了 ITickable 接口,因此可以在游戏循环中接收更新。 /// public class TemporaryAnimatorText : TemporaryAnimator { /// /// 用于显示动画文本的 TMP_Text 组件。 /// 允许在编辑器中赋值,以增加灵活性和鲁棒性。 /// [SerializeField] public TMP_Text text; /// /// 当前播放的动画帧索引。 /// private int currentFrameIndex = 0; /// /// 存储所有动画帧文本的字符串数组。每个元素代表一帧动画内容。 /// private string[] animationsKey; /// /// 每帧动画的持续时间(秒)。根据帧率(FPS)计算得出。 /// private float frameDuration; /// /// 用于跟踪当前帧已持续时间的计时器。 /// private float timer; /// /// 初始化动画文本组件。 /// /// 包含所有动画帧文本的字符串,多个帧之间用逗号分隔。 /// 动画播放的帧率(Frames Per Second)。 public void Init(string s, float fps=3) { // 如果文本组件未赋值,尝试在子对象中查找。 // 如果仍未找到,则记录错误并提前退出。 if (!text) { text = GetComponentInChildren(); if (!text) { Debug.LogError("TemporaryAnimatorText: 未在子对象中找到或未指定 TMP_Text 组件。动画无法进行。", this); return; // 提前退出 Init 方法,避免后续操作使用未初始化的 text } } // 处理输入字符串为 null 或空字符串的情况。 // 在这种情况下,动画将显示空字符串或停止。 if (string.IsNullOrEmpty(s)) { Debug.LogWarning("TemporaryAnimatorText: 输入的动画字符串为空或为 null。动画将显示空字符串或停止。", this); animationsKey = new string[] { "" }; // 确保 animationsKey 不为 null 且至少包含一个空字符串元素 } else { animationsKey = s.Split(','); // 如果 s.Split(',') 结果为空数组(这种情况极少见,但在某些 Split 重载下可能发生),也应进行处理。 if (animationsKey.Length == 0) { Debug.LogWarning("TemporaryAnimatorText: 输入的动画字符串导致动画帧数组为空。动画将显示空字符串或停止。", this); animationsKey = new string[] { "" }; } } // 检查帧率(FPS)的合法性。FPS 必须是大于 0 的正值。 // 如果帧率不合法,则设置为默认值,防止除以零或动画速度异常。 if (fps <= 0) { Debug.LogError("TemporaryAnimatorText: FPS 必须是大于 0 的正值。将动画帧持续时间设置为 1 秒。", this); this.frameDuration = 1f; // 默认每帧1秒,实际效果是动画暂停或非常慢 } else { this.frameDuration = 1f / fps; } // 重新初始化动画时,将当前帧索引重置为第一帧。 currentFrameIndex = 0; // 在 animationsKey 准备好后,设置初始文本为第一帧的内容。 if (text && animationsKey.Length > 0) { text.text = animationsKey[currentFrameIndex]; } // 如果动画帧数组为空,但文本组件存在,则清空文本显示。 else if (text) { text.text = ""; } // 重置计时器,从零开始计算新帧的持续时间。 timer = 0f; } /// /// 每帧更新动画显示。该方法将在游戏循环中被 Clock 调用。 /// public override void Tick() { base.Tick(); if (!text) { Debug.LogWarning("TemporaryAnimatorText: 在 Tick 方法执行期间 TMP_Text 组件为 null。正在从 Clock 中移除。", this); return; // 无法执行动画,直接返回 } // 检查动画帧数组是否已初始化或为空。 // 如果没有动画帧,则无需更新,直接返回。 if (animationsKey == null || animationsKey.Length == 0) { // Debug.LogWarning("TemporaryAnimatorText: 动画键未初始化或为空。跳过 Tick 更新。", this); // 这条日志已被注释掉,避免频繁输出不必要的警告 return; // 没有动画帧,直接返回 } // 增加计时器。 timer += Time.deltaTime; // 如果当前帧的持续时间已达到或超过预设的帧持续时间,则更新到下一帧。 if (!(timer >= frameDuration)) return; // 减去一帧的持续时间,以便计算下一帧的剩余时间,或者处理帧率不精确导致的累计误差。 timer -= frameDuration; // 移动到下一帧。 currentFrameIndex += 1; // 如果当前帧索引超出动画帧数组的范围,则循环回到第一帧。 if (currentFrameIndex >= animationsKey.Length) { currentFrameIndex = 0; } // 更新文本组件显示为当前帧的文本内容。 text.text = animationsKey[currentFrameIndex]; } } }