hch
昨天 a19acb609721b89419fe55785643a0d4f1959368
Main/System/Battle/UIComp/BattleFloatingUIController.cs
@@ -2,117 +2,307 @@
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
}