using System; using System.Collections.Generic; using UnityEngine; namespace SLSUtilities.Feedback { /// /// 反馈播放器状态枚举。 /// public enum FeedbackPlayerState { Idle, Playing, Paused } /// /// 运行时反馈播放器(纯 C# 类,非 MonoBehaviour),管理一个 FeedbackData 的播放生命周期。 /// 由 FeedbackManager 的 Update 集中驱动,也可由外部(如 FeedbackSubcontroller)手动驱动。 /// public class FeedbackPlayer { private const float MIN_DURATION = 0.001f; private FeedbackData _data; private FeedbackPlayerState _state; private float _currentTime; private IFeedbackTimeProvider _timeProvider; private Transform _ownerTransform; private bool _isCompleted; // 每个 Clip 的运行时状态 private enum ClipState { Pending, Active, Finished } private ClipState[,] _clipStates; // [trackIndex, clipIndex] private float[,] _clipElapsedTimes; // [trackIndex, clipIndex] private bool _hasSoloTracks; public FeedbackData Data => _data; public FeedbackPlayerState State => _state; public float CurrentTime => _currentTime; public IFeedbackTimeProvider TimeProvider => _timeProvider; public Transform OwnerTransform => _ownerTransform; /// /// 播放完毕事件。 /// public event Action OnComplete; /// /// 被打断事件。 /// public event Action OnInterrupt; /// /// 是否已播放完毕(自然结束)。 /// public bool IsCompleted => _isCompleted; /// /// 是否处于活跃状态(Playing 或 Paused)。 /// public bool IsActive => _state == FeedbackPlayerState.Playing || _state == FeedbackPlayerState.Paused; public FeedbackPlayer(FeedbackData data, IFeedbackTimeProvider timeProvider, Transform ownerTransform) { _data = data; _timeProvider = timeProvider; _ownerTransform = ownerTransform; _state = FeedbackPlayerState.Idle; _currentTime = 0f; _isCompleted = false; } /// /// 开始播放。 /// public void Play() { if (_data == null) { Debug.LogWarning("[FeedbackPlayer] Cannot play: FeedbackData is null."); return; } _currentTime = 0f; _isCompleted = false; _state = FeedbackPlayerState.Playing; InitializeClipStates(); } /// /// 暂停播放。 /// public void Pause() { if (_state == FeedbackPlayerState.Playing) { _state = FeedbackPlayerState.Paused; } } /// /// 恢复播放。 /// public void Resume() { if (_state == FeedbackPlayerState.Paused) { _state = FeedbackPlayerState.Playing; } } /// /// 立即停止并复位所有已启动的 Action。 /// public void Stop() { if (_state == FeedbackPlayerState.Idle) return; InterruptAllActiveClips(); _state = FeedbackPlayerState.Idle; OnInterrupt?.Invoke(); ClearEvents(); } /// /// 每帧由外部驱动调用(FeedbackManager 或 Subcontroller)。 /// public void Tick(float deltaTime) { if (_state != FeedbackPlayerState.Playing) return; if (_data == null || _data.tracks == null) return; float totalDuration = _data.TotalDuration; // 处理 TotalDuration 为 0 的边界情况 if (totalDuration <= 0f) { _state = FeedbackPlayerState.Idle; _isCompleted = true; OnComplete?.Invoke(); ClearEvents(); return; } List tracks = _data.tracks; for (int trackIdx = 0; trackIdx < tracks.Count; trackIdx++) { FeedbackTrack track = tracks[trackIdx]; if (!ShouldPlayTrack(track)) continue; for (int clipIdx = 0; clipIdx < track.clips.Count; clipIdx++) { FeedbackClip clip = track.clips[clipIdx]; if (clip?.action == null) continue; float clipTimeScale = ComputeClipTimeScale(clip); float clipDeltaTime = deltaTime * clipTimeScale; ProcessClip(trackIdx, clipIdx, clip, clipDeltaTime, clipTimeScale); } } _currentTime += deltaTime; // 仅当时间线游标超过总时长 且 所有 Clip 已结束时才完成。 // 这避免了因时间缩放导致 Clip 尚在播放就被提前完成的问题。 if (_currentTime >= totalDuration && AllClipsFinished()) { _state = FeedbackPlayerState.Idle; _isCompleted = true; OnComplete?.Invoke(); ClearEvents(); } } /// /// 清除所有事件订阅,防止完成/停止后残留引用。 /// private void ClearEvents() { OnComplete = null; OnInterrupt = null; } /// /// 初始化所有 Clip 的运行时状态数组。 /// private void InitializeClipStates() { List tracks = _data.tracks; if (tracks == null || tracks.Count == 0) return; int maxClips = 0; _hasSoloTracks = false; for (int i = 0; i < tracks.Count; i++) { if (tracks[i].clips != null && tracks[i].clips.Count > maxClips) maxClips = tracks[i].clips.Count; if (tracks[i].solo) _hasSoloTracks = true; } _clipStates = new ClipState[tracks.Count, maxClips]; _clipElapsedTimes = new float[tracks.Count, maxClips]; } /// /// 判断轨道是否应该播放:处理 Mute 和 Solo 逻辑。 /// private bool ShouldPlayTrack(FeedbackTrack track) { if (track.mute) return false; if (_hasSoloTracks && !track.solo) return false; return true; } /// /// 根据 Clip 的时间设置计算综合时间缩放系数。 /// private float ComputeClipTimeScale(FeedbackClip clip) { if (_timeProvider == null) return 1f; if (clip?.action == null || clip.action.IgnoreTimeScale) return 1f; FeedbackTimeSettings settings = clip.overrideTimeSettings ? clip.timeSettings : _data.defaultTimeSettings; if (settings == null || settings.timeScaleType == FeedbackTimeSettings.TimeScaleType.Unscaled) return 1f; return _timeProvider.GetTimeScale(settings); } /// /// 处理单个 Clip 的生命周期状态转换和回调调用。 /// private void ProcessClip(int trackIdx, int clipIdx, FeedbackClip clip, float deltaTime, float timeScale) { ref ClipState clipState = ref _clipStates[trackIdx, clipIdx]; ref float elapsed = ref _clipElapsedTimes[trackIdx, clipIdx]; float safeDuration = Mathf.Max(clip.duration, MIN_DURATION); switch (clipState) { case ClipState.Pending: if (_currentTime >= clip.startTime) { clipState = ClipState.Active; elapsed = _currentTime - clip.startTime; FeedbackContext ctx = CreateContext(deltaTime, elapsed, safeDuration, timeScale, clip.timeSettings); clip.action.OnStart(ctx); float normalizedTime = Mathf.Clamp01(elapsed / safeDuration); clip.action.OnUpdate(CreateContext(deltaTime, elapsed, safeDuration, timeScale, clip.timeSettings), normalizedTime); } break; case ClipState.Active: // 如果启用动态时间缩放,每帧重新获取当前的时间缩放 FeedbackTimeSettings settings = clip.overrideTimeSettings ? clip.timeSettings : _data.defaultTimeSettings; float currentTimeScale = timeScale; if (settings.applyDynamicTimeScale) { currentTimeScale = ComputeClipTimeScale(clip); } // 使用调整后的deltaTime进行累加 float adjustedDeltaTime = deltaTime * currentTimeScale; elapsed += adjustedDeltaTime; if (elapsed >= safeDuration) { elapsed = safeDuration; clipState = ClipState.Finished; FeedbackContext ctx = CreateContext(deltaTime, elapsed, safeDuration, currentTimeScale, clip.timeSettings); clip.action.OnUpdate(ctx, 1f); clip.action.OnEnd(ctx); } else { float normalizedTime = Mathf.Clamp01(elapsed / safeDuration); FeedbackContext ctx = CreateContext(deltaTime, elapsed, safeDuration, currentTimeScale, clip.timeSettings); clip.action.OnUpdate(ctx, normalizedTime); } break; case ClipState.Finished: break; } } /// /// 打断所有已激活的 Clip,调用其 OnInterrupt 进行复位。 /// private void InterruptAllActiveClips() { if (_data?.tracks == null || _clipStates == null) return; List tracks = _data.tracks; for (int trackIdx = 0; trackIdx < tracks.Count; trackIdx++) { if (tracks[trackIdx].clips == null) continue; for (int clipIdx = 0; clipIdx < tracks[trackIdx].clips.Count; clipIdx++) { if (_clipStates[trackIdx, clipIdx] == ClipState.Active) { FeedbackClip clip = tracks[trackIdx].clips[clipIdx]; if (clip?.action == null) continue; float elapsed = _clipElapsedTimes[trackIdx, clipIdx]; float safeDuration = Mathf.Max(clip.duration, MIN_DURATION); FeedbackContext ctx = CreateContext(0f, elapsed, safeDuration, 1f, clip.timeSettings); clip.action.OnInterrupt(ctx); } _clipStates[trackIdx, clipIdx] = ClipState.Finished; } } } /// /// 检查所有应播放的 Clip 是否都已完成。 /// 用于时间缩放场景下,避免 Clip 尚在播放就提前完成整个 Feedback。 /// private bool AllClipsFinished() { if (_clipStates == null) return true; List tracks = _data.tracks; for (int trackIdx = 0; trackIdx < tracks.Count; trackIdx++) { FeedbackTrack track = tracks[trackIdx]; if (!ShouldPlayTrack(track)) continue; if (track.clips == null) continue; for (int clipIdx = 0; clipIdx < track.clips.Count; clipIdx++) { if (_clipStates[trackIdx, clipIdx] == ClipState.Active) return false; } } return true; } /// /// 创建 FeedbackContext 实例。 /// private FeedbackContext CreateContext(float deltaTime, float elapsedTime, float duration, float timeScale, FeedbackTimeSettings timeSettings) { return new FeedbackContext { player = this, owner = _ownerTransform, deltaTime = deltaTime, elapsedTime = elapsedTime, duration = duration, timeScale = timeScale, timeSettings = timeSettings }; } } }