using System.Collections; using System.Collections.Generic; using DG.Tweening; using TMPro; using UnityEngine; namespace Ichni.UI { public class TextCatapult : MonoBehaviour { [Header("UI 元素")] [Tooltip("第一个文本组件")] [SerializeField] private TMP_Text textA; [Tooltip("第二个文本组件")] [SerializeField] private TMP_Text textB; [Header("动画参数")] [Tooltip("文本在中央停留显示的时间")] [SerializeField] private float displayDuration = 4f; [Tooltip("向上移动和淡入淡出的动画时间")] [SerializeField] private float animationDuration = 1f; [Tooltip("文本向上移动的距离")] [SerializeField] private float moveDistance = 200f; // --- 可选: 如果你想让文字内容循环变化 --- [Header("可选的文本内容")] [Tooltip("在这里填入你想循环显示的文字列表")] [SerializeField] private List textContents = new List(); private TMP_Text[] _textElements; private int _currentTextIndex = 0; // 当前显示在中央的文本的索引 (0 或 1) private int _contentIndex = 0; // 当前显示内容的索引 private Vector2 _centerPosition; private Sequence _animationSequence; void Start() { Setup(); // 直接启动第一个动画过渡 AnimateTransition(); } void OnDestroy() { // 安全地杀死所有相关的DoTween动画,防止在场景切换或对象销毁时出错 _animationSequence?.Kill(); } /// /// 初始化设置 /// private void Setup() { _textElements = new[] { textA, textB }; // 记录中心位置 _centerPosition = textA.rectTransform.anchoredPosition; // 设置 textA 的初始状态 textA.rectTransform.anchoredPosition = _centerPosition; textA.alpha = 1f; UpdateTextContent(textA); // 设置初始文字 // 设置 textB 的初始状态 (在下方,透明) textB.rectTransform.anchoredPosition = _centerPosition - new Vector2(0, moveDistance); textB.alpha = 0f; // textB 的内容将在它动画开始前更新,所以这里无需设置 } /// /// 执行一次完整的文本过渡动画,并在完成后自我调用以形成循环 /// private void AnimateTransition() { // 确定哪个文本要移出,哪个要移入 TMP_Text textToAnimateOut = _textElements[_currentTextIndex]; TMP_Text textToAnimateIn = _textElements[(_currentTextIndex + 1) % 2]; // 创建一个新的动画序列 _animationSequence = DOTween.Sequence(); _animationSequence .AppendInterval(displayDuration) // 1. 等待当前文本显示4秒 .AppendCallback(() => { // 2. 在动画开始前,更新即将进入的文本的内容 UpdateTextContent(textToAnimateIn); }) // 3. 同时执行两个文本的动画 .Append(textToAnimateOut.rectTransform.DOAnchorPosY(_centerPosition.y + moveDistance, animationDuration).SetEase(Ease.OutCubic)) .Join(textToAnimateOut.DOFade(0f, animationDuration).SetEase(Ease.OutCubic)) .Join(textToAnimateIn.rectTransform.DOAnchorPosY(_centerPosition.y, animationDuration).SetEase(Ease.OutCubic)) .Join(textToAnimateIn.DOFade(1f, animationDuration).SetEase(Ease.OutCubic)) .OnComplete(() => { // 4. 当整个过渡动画完成时 // 将刚刚移出的文本复位到起始位置,为下一次进入做准备 textToAnimateOut.rectTransform.anchoredPosition = _centerPosition - new Vector2(0, moveDistance); // 切换当前文本的索引,为下个循环做准备 _currentTextIndex = (_currentTextIndex + 1) % 2; // 启动下一次动画过渡,形成循环 AnimateTransition(); }); _animationSequence.Play(); } /// /// 更新文本内容(如果列表不为空) /// private void UpdateTextContent(TMP_Text textElement) { if (textContents == null || textContents.Count == 0) { return; // 如果列表为空,则不改变文字 } textElement.text = textContents[_contentIndex]; _contentIndex = (_contentIndex + 1) % textContents.Count; // 移动到下一个内容索引,并循环 } } }