using LuaInterface; using System; using System.Collections; using System.Collections.Generic; using System.Linq; using UnityEngine; using UnityEngine.UI; public class LuaUIHelper : MonoBehaviour { #region 消息提示 private float messageDuration = 2f; // 单条消息显示时间 private float appearDuration = 0.3f; // 出现动画时间 private float disappearDuration = 0.5f; // 消失动画时间 private float startYPosition = -100f; // 起始Y位置(底部) private float moveSpeed = 8f; // 位置移动速度 private float spacingMultiplier = 1.2f; // 间距倍数(基于消息高度) private GameObject msgPrefab; private Stack msgPool = new Stack(); private List activeMessages = new List(); private float messageHeight = 60f; // 消息预制体的高度(运行时获取) private bool isInitialized = false; // 消息全部消失时的回调 private LuaFunction onAllMessagesDisappeared; // 初始化消息预制体,并设置消息全部消失时的回调 public void SetMsgPrefab(GameObject prefab, LuaFunction onMsgEnd) { msgPrefab = prefab; // 设置回调 onAllMessagesDisappeared = onMsgEnd; // 获取预制体的高度 RectTransform prefabRect = prefab.GetComponent(); if (prefabRect != null) { messageHeight = prefabRect.rect.height; } // 获取Image组件的高度(如果有) Image image = prefab.GetComponent(); if (image != null) { RectTransform imageRect = image.GetComponent(); messageHeight = imageRect.rect.height; } isInitialized = true; } // 显示消息 public void ShowInfo(string strMsg) { if (!isInitialized) { return; } MessageItem msg = GetMessageItem(); msg.Initialize(strMsg, this, messageDuration); // 新消息总是在列表的开头(索引0),表示最底部 activeMessages.Insert(0, msg); // 更新所有消息的目标位置 UpdateAllTargetPositions(); // 开始显示新消息 StartCoroutine(msg.Show()); } // 从池中获取消息项 private MessageItem GetMessageItem() { MessageItem msg; if (msgPool.Count > 0) { msg = msgPool.Pop(); msg.gameObject.SetActive(true); } else { GameObject go = Instantiate(msgPrefab, msgPrefab.transform.parent, false); msg = new MessageItem(go); } return msg; } // 计算消息之间的间距 private float GetSpacing() { return messageHeight * spacingMultiplier; } // 更新所有消息的目标位置 private void UpdateAllTargetPositions() { float spacing = GetSpacing(); // 列表索引0是最底部的消息(最新) // 列表索引越大位置越高(越靠上) for (int i = 0; i < activeMessages.Count; i++) { // 第i条消息的位置 = 起始位置 + (从底部算起的索引 * 间距) float targetY = startYPosition + (i * spacing); activeMessages[i].SetTargetY(targetY); } } private void Update() { // 更新所有活跃消息的位置 for (int i = 0; i < activeMessages.Count; i++) { var msg = activeMessages[i]; // 更新消息的生命周期 msg.UpdateMessage(Time.deltaTime); // 如果消息应该消失,开始消失动画 if (msg.ShouldDisappear() && msg.currentState != MessageState.Disappearing) { StartCoroutine(HandleMessageDisappear(msg)); } // 更新消息位置(平滑移动) if (msg.currentState == MessageState.Active || msg.currentState == MessageState.Appearing) { msg.UpdatePosition(Time.deltaTime * moveSpeed); } } } // 处理消息消失 private IEnumerator HandleMessageDisappear(MessageItem msg) { // 开始消失动画 yield return StartCoroutine(msg.Disappear()); // 从活跃列表中移除 if (activeMessages.Contains(msg)) { activeMessages.Remove(msg); // 回收消息到对象池 ReturnToPool(msg); // 重新计算剩余消息的位置 UpdateAllTargetPositions(); // 检查是否所有消息都消失了 CheckAllMessagesDisappeared(); } } // 检查是否所有消息都消失了 private void CheckAllMessagesDisappeared() { if (activeMessages.Count == 0 && onAllMessagesDisappeared != null) { onAllMessagesDisappeared.Call(); } } // 回收消息项到池中 private void ReturnToPool(MessageItem msg) { msg.gameObject.SetActive(false); msg.Reset(); msgPool.Push(msg); } // 消息状态枚举 private enum MessageState { WaitingToShow, // 等待显示 Appearing, // 出现中 Active, // 活跃中 Disappearing // 消失中 } // 消息项类 private class MessageItem { public GameObject gameObject; public RectTransform rect; public Text text; public CanvasGroup canvasGroup; public Image backgroundImage; // 背景图片 private LuaUIHelper manager; private MessageState _currentState = MessageState.WaitingToShow; public MessageState currentState => _currentState; private float currentY; // 当前Y位置 private float targetY; // 目标Y位置 private float appearProgress; // 出现进度 private float lifeTime; // 生命周期计时器 private float maxLifeTime; // 最大生命周期 public MessageItem(GameObject go) { gameObject = go; rect = go.GetComponent(); text = go.transform.GetChild(0).GetComponent(); // 获取背景图片 backgroundImage = go.GetComponent(); // 添加或获取CanvasGroup canvasGroup = go.GetComponent(); if (canvasGroup == null) canvasGroup = go.AddComponent(); } // 初始化消息 public void Initialize(string message, LuaUIHelper manager, float duration) { this.manager = manager; this.maxLifeTime = duration; text.text = message; // 重置状态 _currentState = MessageState.WaitingToShow; appearProgress = 0f; lifeTime = 0f; // 新消息从屏幕外更底部开始 float spacing = manager.GetSpacing(); currentY = manager.startYPosition - spacing - 50f; rect.anchoredPosition = new Vector2(0, currentY); canvasGroup.alpha = 0f; // 确保消息可见 if (backgroundImage != null) { backgroundImage.enabled = true; } gameObject.SetActive(true); } // 设置目标Y位置 public void SetTargetY(float y) { targetY = y; } // 更新消息生命周期 public void UpdateMessage(float deltaTime) { if (_currentState == MessageState.Active) { lifeTime += deltaTime; } } // 检查是否应该消失 public bool ShouldDisappear() { return _currentState == MessageState.Active && lifeTime >= maxLifeTime; } // 显示消息 public IEnumerator Show() { _currentState = MessageState.Appearing; // 出现动画:从下方滑入并淡入 float timer = 0f; float startY = currentY; while (timer < manager.appearDuration) { timer += Time.deltaTime; appearProgress = timer / manager.appearDuration; // 缓动函数:EaseOutCubic float easedProgress = 1f - Mathf.Pow(1f - appearProgress, 3); // 位置动画(从下方滑入) float newY = Mathf.Lerp(startY, targetY, easedProgress); currentY = newY; rect.anchoredPosition = new Vector2(0, newY); // 透明度动画 canvasGroup.alpha = Mathf.Lerp(0f, 1f, easedProgress); yield return null; } // 进入活跃状态 _currentState = MessageState.Active; currentY = targetY; rect.anchoredPosition = new Vector2(0, targetY); canvasGroup.alpha = 1f; // 重置生命周期计时器 lifeTime = 0f; } // 消失动画 public IEnumerator Disappear() { _currentState = MessageState.Disappearing; // 向上淡出消失 float timer = 0f; float startY = currentY; float disappearY = startY + 50f; // 向上移动消失 while (timer < manager.disappearDuration) { timer += Time.deltaTime; float progress = timer / manager.disappearDuration; // 缓动函数:EaseInCubic float easedProgress = Mathf.Pow(progress, 3); // 位置动画(向上移动) float newY = Mathf.Lerp(startY, disappearY, easedProgress); rect.anchoredPosition = new Vector2(0, newY); // 透明度动画 canvasGroup.alpha = Mathf.Lerp(1f, 0f, easedProgress); yield return null; } // 完全消失 canvasGroup.alpha = 0f; rect.anchoredPosition = new Vector2(0, disappearY); } // 更新位置(平滑移动到目标位置) public void UpdatePosition(float speed) { if ((_currentState == MessageState.Active || _currentState == MessageState.Appearing) && Mathf.Abs(currentY - targetY) > 0.1f) { currentY = Mathf.Lerp(currentY, targetY, speed); rect.anchoredPosition = new Vector2(0, currentY); } else if (_currentState == MessageState.Active) { currentY = targetY; rect.anchoredPosition = new Vector2(0, targetY); } } // 重置状态 public void Reset() { _currentState = MessageState.WaitingToShow; appearProgress = 0f; lifeTime = 0f; canvasGroup.alpha = 0f; // 重置位置到初始位置 rect.anchoredPosition = Vector2.zero; } } #endregion public Text[] GetAllText(GameObject go) { Text[] allTexts = go.GetComponentsInChildren(true); return allTexts.Where(text => !text.CompareTag("untext")).ToArray(); } public void DelayAction(float floTime, LuaFunction func) { StartCoroutine(_delayAction(floTime, func)); } IEnumerator _delayAction(float floTime, LuaFunction func) { yield return new WaitForSeconds(floTime); func.Call(); } public void AddButtonClick(Button button, LuaFunction func) { if (button == null || func == null) return; button.onClick.AddListener(() => { func.Call(); }); } public void AddButtonClickWithGameObject(Button button, LuaFunction func) { if (button == null || func == null) return; GameObject go = button.gameObject; button.onClick.AddListener(() => { func.Call(go); }); } public void AddButtonClick(Button button, LuaFunction func, int index) { if (button == null || func == null) return; button.onClick.AddListener(() => { func.Call(index); }); } public void AddButtonClick(Button button, LuaFunction func, LuaTable ta) { if (button == null || func == null) return; button.onClick.AddListener(() => { func.Call(ta); }); } public float GetAnimatorAormalizedTime(Animator a, bool boo) { AnimatorStateInfo infor = a.GetCurrentAnimatorStateInfo(0); if (boo) { return infor.normalizedTime; } return infor.normalizedTime % 1; } public float GetAnimtorTotalTime(Animator a) { AnimatorStateInfo infor = a.GetCurrentAnimatorStateInfo(0); return infor.length; } public bool GetAnimatorIsName(Animator a, string strName) { return a.GetCurrentAnimatorStateInfo(0).IsName(strName); } public void SetGameObjectLastSibling(GameObject go) { go.transform.SetAsLastSibling(); } }