| | |
| | | using System; |
| | | |
| | | /// <summary> |
| | | /// 战斗飘字UI控制器:处理缩放、透明度、位置动画逻辑 |
| | | /// 战斗飘字UI控制器 |
| | | /// 职责:处理飘字的缩放、透明度、位置动画逻辑 |
| | | /// </summary> |
| | | [SerializeField] |
| | | [Serializable] |
| | | public class BattleFloatingUIController |
| | | { |
| | | // Position Settings |
| | | public Vector2 beginPos = Vector2.zero; |
| | | public Vector2 endPos = new Vector2(0, 150); |
| | | |
| | | // Time Settings |
| | | public float scaleChangeTime = 1f / BattleConst.skillMotionFps * 16f + 0.1f; // 7~8帧 (8/30=0.2667秒) |
| | | public float totalShowTime = 1f / BattleConst.skillMotionFps * 48f + 0.1f; // 总时间约24帧 (8+16=24帧) |
| | | |
| | | // Normal Settings |
| | | public Vector3 normalBeginScale = new Vector3(2f, 2f, 2f); |
| | | public Vector3 normalEndScale = new Vector3(1f, 1f, 1f); |
| | | |
| | | // Critical Settings |
| | | public Vector3 critBeginScale = new Vector3(3f, 3f, 3f); |
| | | public Vector3 critEndScale = new Vector3(1.5f, 1.5f, 1.5f); |
| | | #region 私有字段 |
| | | |
| | | // Color Settings |
| | | public Color beginColor = new Color(1f, 1f, 1f, 0.5f); |
| | | public Color endColor = new Color(1f, 1f, 1f, 1f); |
| | | |
| | | private FloatingConfig config; |
| | | private RectTransform rectTransform; |
| | | private GameObject gameObject; |
| | | private Action<Color> applyColorCallback; |
| | | |
| | | // 运行时状态 |
| | | private float timer = 0f; |
| | | private float speedRatio = 1f; |
| | | private float scaleRatio = 1f; |
| | | private bool isCritical = false; |
| | | private Action onFinishCallback; |
| | | private Action<Color> applyColorCallback; |
| | | private GameObject gameObject; |
| | | |
| | | // 运行时位置覆盖(优先级高于配置) |
| | | private Vector2? runtimeBeginPos = null; |
| | | private Vector2? runtimeEndPos = null; |
| | | |
| | | // 运行时颜色覆盖(优先级高于配置) |
| | | private Color? runtimeBeginColor = null; |
| | | private Color? runtimeEndColor = null; |
| | | |
| | | #endregion |
| | | |
| | | // 添加只读属性以对外暴露 |
| | | #region 公共属性 |
| | | |
| | | public float Timer => timer; |
| | | public float SpeedRatio => speedRatio; |
| | | public float ScaleRatio => scaleRatio; |
| | | |
| | | #endregion |
| | | |
| | | public BattleFloatingUIController(RectTransform rect, GameObject go, Action<Color> applyColor) |
| | | #region 构造函数 |
| | | |
| | | public BattleFloatingUIController( |
| | | RectTransform rect, |
| | | GameObject go, |
| | | Action<Color> applyColor, |
| | | FloatingConfig cfg) |
| | | { |
| | | rectTransform = rect; |
| | | gameObject = go; |
| | | applyColorCallback = applyColor; |
| | | config = cfg; |
| | | } |
| | | |
| | | #endregion |
| | | |
| | | #region 配置管理 |
| | | |
| | | /// <summary> |
| | | /// 设置或更新配置 |
| | | /// </summary> |
| | | public void SetConfig(FloatingConfig cfg) |
| | | { |
| | | config = cfg; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 设置运行时位置覆盖(会覆盖配置中的位置) |
| | | /// </summary> |
| | | public void SetRuntimePosition(Vector2 beginPos, Vector2 endPos) |
| | | { |
| | | runtimeBeginPos = beginPos; |
| | | runtimeEndPos = endPos; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 清除运行时位置覆盖,恢复使用配置中的位置 |
| | | /// </summary> |
| | | public void ClearRuntimePosition() |
| | | { |
| | | runtimeBeginPos = null; |
| | | runtimeEndPos = null; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 设置运行时颜色覆盖(会覆盖配置中的颜色) |
| | | /// </summary> |
| | | public void SetRuntimeColor(Color beginColor, Color endColor) |
| | | { |
| | | runtimeBeginColor = beginColor; |
| | | runtimeEndColor = endColor; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 清除运行时颜色覆盖,恢复使用配置中的颜色 |
| | | /// </summary> |
| | | public void ClearRuntimeColor() |
| | | { |
| | | runtimeBeginColor = null; |
| | | runtimeEndColor = null; |
| | | } |
| | | |
| | | #endregion |
| | | |
| | | #region 播放控制 |
| | | |
| | | /// <summary> |
| | | /// 设置速度和缩放比例 |
| | | /// </summary> |
| | | public void SetRatio(float speed, float scale) |
| | | { |
| | | speedRatio = speed; |
| | | scaleRatio = scale; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 开始播放动画 |
| | | /// </summary> |
| | | public void Play(bool isCrit, Action onComplete = null) |
| | | { |
| | | if (!ValidateConfig()) return; |
| | | |
| | | isCritical = isCrit; |
| | | onFinishCallback = onComplete; |
| | | timer = 0f; |
| | | |
| | | Vector3 beginScale = isCritical ? critBeginScale : normalBeginScale; |
| | | // 初始化位置和缩放 |
| | | Vector2 beginPos = GetBeginPosition(); |
| | | Vector3 beginScale = GetBeginScale(); |
| | | |
| | | rectTransform.anchoredPosition = beginPos; |
| | | rectTransform.localScale = beginScale * scaleRatio; |
| | | |
| | | gameObject.SetActive(true); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 每帧更新 |
| | | /// </summary> |
| | | public void Run() |
| | | { |
| | | if (!gameObject.activeSelf) |
| | | if (!gameObject.activeSelf || !ValidateConfig()) |
| | | return; |
| | | |
| | | if (timer >= totalShowTime) |
| | | // 检查是否完成 |
| | | if (timer >= config.totalShowTime) |
| | | { |
| | | gameObject.SetActive(false); |
| | | onFinishCallback?.Invoke(); |
| | | onFinishCallback = null; |
| | | OnAnimationComplete(); |
| | | return; |
| | | } |
| | | |
| | | // 整个过程都往上飘 |
| | | float moveProgress = timer / totalShowTime; |
| | | rectTransform.anchoredPosition = Vector2.Lerp(beginPos, endPos, moveProgress); |
| | | |
| | | Vector3 currentBeginScale = isCritical ? critBeginScale : normalBeginScale; |
| | | Vector3 currentEndScale = isCritical ? critEndScale : normalEndScale; |
| | | |
| | | // 阶段1: 7~8帧内缩放和透明度变化 |
| | | if (timer < scaleChangeTime) |
| | | { |
| | | float scaleProgress = timer / scaleChangeTime; |
| | | rectTransform.localScale = Vector3.Lerp(currentBeginScale, currentEndScale, scaleProgress) * scaleRatio; |
| | | |
| | | Color currentColor = Color.Lerp(beginColor, endColor, scaleProgress); |
| | | applyColorCallback?.Invoke(currentColor); |
| | | } |
| | | // 阶段2: 保持缩放和透明度,继续往上飘 |
| | | else |
| | | { |
| | | rectTransform.localScale = currentEndScale * scaleRatio; |
| | | applyColorCallback?.Invoke(endColor); |
| | | } |
| | | |
| | | timer += 1f / BattleConst.skillMotionFps * speedRatio; |
| | | // 更新动画 |
| | | UpdatePosition(); |
| | | UpdateScaleAndColor(); |
| | | |
| | | // 增加计时器 |
| | | timer += GetDeltaTime(); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 暂停(预留接口) |
| | | /// </summary> |
| | | public void Stop() |
| | | { |
| | | // 可以添加暂停逻辑 |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 恢复(预留接口) |
| | | /// </summary> |
| | | public void Resume() |
| | | { |
| | | // 可以添加恢复逻辑 |
| | | } |
| | | |
| | | #endregion |
| | | |
| | | #region 私有方法 |
| | | |
| | | /// <summary> |
| | | /// 验证配置有效性 |
| | | /// </summary> |
| | | private bool ValidateConfig() |
| | | { |
| | | if (config == null) |
| | | { |
| | | Debug.LogError("[BattleFloatingUIController] FloatingConfig 配置为空"); |
| | | return false; |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 获取起始位置(运行时位置优先) |
| | | /// </summary> |
| | | private Vector2 GetBeginPosition() |
| | | { |
| | | return runtimeBeginPos ?? config.beginPos; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 获取结束位置(运行时位置优先) |
| | | /// </summary> |
| | | private Vector2 GetEndPosition() |
| | | { |
| | | return runtimeEndPos ?? config.endPos; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 获取起始缩放 |
| | | /// </summary> |
| | | private Vector3 GetBeginScale() |
| | | { |
| | | return isCritical ? config.critBeginScale : config.normalBeginScale; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 获取结束缩放 |
| | | /// </summary> |
| | | private Vector3 GetEndScale() |
| | | { |
| | | return isCritical ? config.critEndScale : config.normalEndScale; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 获取时间增量 |
| | | /// </summary> |
| | | private float GetDeltaTime() |
| | | { |
| | | return 1f / BattleConst.skillMotionFps * speedRatio; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 更新位置 |
| | | /// </summary> |
| | | private void UpdatePosition() |
| | | { |
| | | float timeProgress = timer / config.totalShowTime; |
| | | // 使用曲线来调整插值进度 |
| | | float curveProgress = config.positionCurve.Evaluate(timeProgress); |
| | | Vector2 currentPos = Vector2.Lerp(GetBeginPosition(), GetEndPosition(), curveProgress); |
| | | rectTransform.anchoredPosition = currentPos; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 获取起始颜色(运行时颜色优先) |
| | | /// </summary> |
| | | private Color GetBeginColor() |
| | | { |
| | | return runtimeBeginColor ?? config.beginColor; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 获取结束颜色(运行时颜色优先) |
| | | /// </summary> |
| | | private Color GetEndColor() |
| | | { |
| | | return runtimeEndColor ?? config.endColor; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 更新缩放和颜色 |
| | | /// </summary> |
| | | private void UpdateScaleAndColor() |
| | | { |
| | | // 阶段1: 缩放和透明度变化 |
| | | if (timer < config.scaleChangeTime) |
| | | { |
| | | float timeProgress = timer / config.scaleChangeTime; |
| | | |
| | | // 根据是否暴击选择对应的缩放曲线 |
| | | AnimationCurve scaleCurve = isCritical ? config.critScaleCurve : config.normalScaleCurve; |
| | | float scaleProgress = scaleCurve.Evaluate(timeProgress); |
| | | Vector3 currentScale = Vector3.Lerp(GetBeginScale(), GetEndScale(), scaleProgress); |
| | | rectTransform.localScale = currentScale * scaleRatio; |
| | | |
| | | // 使用曲线来调整颜色插值进度 |
| | | float colorProgress = config.colorCurve.Evaluate(timeProgress); |
| | | Color currentColor = Color.Lerp(GetBeginColor(), GetEndColor(), colorProgress); |
| | | applyColorCallback?.Invoke(currentColor); |
| | | } |
| | | // 阶段2: 保持最终缩放和透明度 |
| | | else |
| | | { |
| | | rectTransform.localScale = GetEndScale() * scaleRatio; |
| | | applyColorCallback?.Invoke(GetEndColor()); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 动画完成回调 |
| | | /// </summary> |
| | | private void OnAnimationComplete() |
| | | { |
| | | gameObject.SetActive(false); |
| | | onFinishCallback?.Invoke(); |
| | | onFinishCallback = null; |
| | | } |
| | | |
| | | public bool IsValid() |
| | | { |
| | | return ValidateConfig() && rectTransform != null && gameObject != null; |
| | | } |
| | | |
| | | #endregion |
| | | } |