hch
6 天以前 cb4ec28d83ba847f362392936b20e52e17b03081
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
using UnityEngine;
using System;
 
/// <summary>
/// 战斗飘字UI控制器
/// 职责:处理飘字的缩放、透明度、位置动画逻辑
/// </summary>
[Serializable]
public class BattleFloatingUIController
{
    #region 私有字段
    
    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 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
 
    #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;
        
        // 初始化位置和缩放
        Vector2 beginPos = GetBeginPosition();
        Vector3 beginScale = GetBeginScale();
        
        rectTransform.anchoredPosition = beginPos;
        rectTransform.localScale = beginScale * scaleRatio;
        
        gameObject.SetActive(true);
    }
 
    /// <summary>
    /// 每帧更新
    /// </summary>
    public void Run()
    {
        if (!gameObject.activeSelf || !ValidateConfig()) 
            return;
 
        // 检查是否完成
        if (timer >= config.totalShowTime)
        {
            OnAnimationComplete();
            return;
        }
 
        // 更新动画
        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;
    }
    
    #endregion
}