446 lines
13 KiB
C#
446 lines
13 KiB
C#
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<MessageItem> msgPool = new Stack<MessageItem>();
|
||
private List<MessageItem> activeMessages = new List<MessageItem>();
|
||
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<RectTransform>();
|
||
if (prefabRect != null)
|
||
{
|
||
messageHeight = prefabRect.rect.height;
|
||
}
|
||
|
||
// 获取Image组件的高度(如果有)
|
||
Image image = prefab.GetComponent<Image>();
|
||
if (image != null)
|
||
{
|
||
RectTransform imageRect = image.GetComponent<RectTransform>();
|
||
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<RectTransform>();
|
||
text = go.transform.GetChild(0).GetComponent<Text>();
|
||
|
||
// 获取背景图片
|
||
backgroundImage = go.GetComponent<Image>();
|
||
|
||
// 添加或获取CanvasGroup
|
||
canvasGroup = go.GetComponent<CanvasGroup>();
|
||
if (canvasGroup == null)
|
||
canvasGroup = go.AddComponent<CanvasGroup>();
|
||
}
|
||
|
||
// 初始化消息
|
||
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<Text>(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();
|
||
}
|
||
} |