using System; using UnityEngine; using UnityEngine.UI; [DisallowMultipleComponent] public class IntensifySmoothSlider : MonoBehaviour { [SerializeField] Slider m_Slider; public Slider slider { get { return m_Slider; } } [SerializeField] [Range(0, 10)] float m_Delay = 0.2f; public float delay { get { return m_Delay; } set { m_Delay = Mathf.Clamp(value, 0, 10); } } [SerializeField] [Range(0, 1)] float m_Value = 0f; public float value { get { return m_Value; } set { m_Value = Mathf.Clamp01(value); } } // 值更新事件(ValueChangeAction)的触发分段数。 // 用于降低 ValueChangeAction 的触发频率,防止UI文本等监听组件频繁刷新。 // // 工作原理: // 将滑块的 [0, 1] 范围划分为 'm_ValueUpdateSegments' 个数量相等的分段。 // ValueChangeAction 事件仅在 slider.value 跨越这些分段边界时才会被触发。 // // 示例: // - 设为 10: 则滑块值每变化 0.1 (10%) 触发一次事件 (如从 0.19 跨到 0.21 时)。 // - 设为 100: 则滑块值每变化 0.01 (1%) 触发一次事件。 [SerializeField] [Range(1, 1000)] int m_ValueUpdateSegments = 10; public int ValueUpdateSegments { get { return m_ValueUpdateSegments; } set { m_ValueUpdateSegments = Mathf.Max(1, value); } // 至少为1 } // 跟踪上一次触发事件时,滑块值所处的分段索引 (范围从 0 到 m_ValueUpdateSegments-1)。 private int m_LastNotifiedSegment = -1; int m_Stage = 0; public int stage { get { return m_Stage; } set { m_Stage = value; } } private int presentStage; public int CurrentStage { get { return presentStage; } } float refSpeed = 0f; public event Action StageUpAction; public event Action ValueChangeAction; public event Action ChangeEndAction; public void ResetStage() { presentStage = stage; } public void ResetValue(float _value) { value = _value; if (slider != null) { slider.value = Mathf.Clamp01(_value); } // 重置值时,同步更新最后通知的分段索引 m_LastNotifiedSegment = GetCurrentSegment(slider.value); } void OnEnable() { refSpeed = 0f; // 启用时,初始化最后通知的分段索引 m_LastNotifiedSegment = GetCurrentSegment(slider != null ? slider.value : m_Value); } // 根据当前滑块值(0-1),计算其所属的分段索引(0 到 m_ValueUpdateSegments-1) private int GetCurrentSegment(float currentValue) { // 将 [0, 1] 范围的浮点数值 映射到 [0, m_ValueUpdateSegments-1] 范围的整数索引 float preciseSegment = currentValue * m_ValueUpdateSegments; // 注意:当值为1.0f时,我们希望它落在最后一个分段索引上 (即 segments - 1) if (currentValue >= 1.0f) { return m_ValueUpdateSegments - 1; } return Mathf.FloorToInt(preciseSegment); } // 用于跟踪滑块是否正在缓动 private bool isMoving = false; void LateUpdate() { if (slider == null) { return; } if (presentStage < m_Stage) { slider.value = Mathf.SmoothDamp(slider.value, 1, ref refSpeed, delay / 2); if (slider.value >= 0.99f) { slider.value = 0f; presentStage++; StageUpAction?.Invoke(presentStage); m_LastNotifiedSegment = 0;// 当血条循环时,重置分段索引 } isMoving = true; CheckForValueChangeNotification(slider.value); } else { if (Mathf.Abs(slider.value - value) > 0.001f) { slider.value = Mathf.SmoothDamp(slider.value, value, ref refSpeed, delay); isMoving = true; CheckForValueChangeNotification(slider.value); } else { if (isMoving) { // 确保值在停止时完全精确 if (slider.value != value) { slider.value = value; ValueChangeAction?.Invoke(slider.value, CurrentStage); m_LastNotifiedSegment = GetCurrentSegment(slider.value); } ChangeEndAction?.Invoke(); isMoving = false; } } } } // 检查是否需要触发ValueChangeAction的逻辑 private void CheckForValueChangeNotification(float currentSliderValue) { int currentSegment = GetCurrentSegment(currentSliderValue); // 如果当前的分段索引与上次触发事件时的索引不同,说明已跨越分段边界。 if (currentSegment != m_LastNotifiedSegment) { ValueChangeAction?.Invoke(currentSliderValue, CurrentStage); // 更新“上次触发”的索引为当前索引,防止本分段内重复触发。 m_LastNotifiedSegment = currentSegment; } } /// /// 立即设置滑块的状态(值和阶段),而不触发任何事件或平滑动画。 /// /// 要设置的新目标值 (0-1)。 /// 要设置的新目标阶段和当前阶段。 public void SetStateInstantly(float newValue, int newStage) { this.value = newValue; this.stage = newStage; this.presentStage = newStage; if (m_Slider != null) { m_Slider.value = this.value; } refSpeed = 0f; isMoving = false; // 确保下次 LateUpdate 不会因为索引不匹配而错误触发 ValueChangeAction m_LastNotifiedSegment = GetCurrentSegment(this.value); } }