using System;
using System.Collections.Generic;
using System.Linq;
using Sirenix.OdinInspector;
using UniRx;
using UnityEngine;
namespace SLSUtilities.Feedback
{
///
/// 单个反馈序列的完整数据定义,是 Feedback 系统的核心 ScriptableObject。
/// 包含多条轨道(Track),每条轨道包含按时间排列的片段(Clip)。
///
[CreateAssetMenu(fileName = "NewFeedbackData", menuName = "SLSUtilities/Feedback/FeedbackData")]
public partial class FeedbackData : SerializedScriptableObject
{
///
/// 父级集合引用,由 FeedbackDataCollection 自动维护。
///
[ReadOnly, ShowInInspector]
public FeedbackDataCollection parentCollection;
///
/// 用于字典索引的名称,在 FeedbackDataCollection 中按此名称查找。
///
[Title("Editor Settings")]
public string feedbackName;
[Title("Play Limits")]
[LabelText("限制播放频次")]
[Tooltip("如果勾选,将限制此 Feedback 的播放频次。默认每帧最多播放1次")]
public bool limitPlay = true;
[ShowIf("limitPlay")]
[LabelText("最低播放间隔 (秒)")]
[Tooltip("两次播放之间的最低时间间隔(以 unscaledTime 计算)。为 0 代表每帧最多播放 1 次。")]
public float minPlayInterval = 0f;
[System.NonSerialized]
private float _lastPlayTime = -9999f;
[System.NonSerialized]
private int _lastPlayFrame = -1;
///
/// 全局默认的时间设置。Clip 可选择覆盖此设置。
///
public FeedbackTimeSettings defaultTimeSettings = new FeedbackTimeSettings();
///
/// 反馈轨道列表,多条轨道天然并行播放。
///
[Title("Feedback Tracks")]
[ListDrawerSettings(ShowFoldout = true, ListElementLabelName = "trackName")]
public List tracks = new List();
///
/// 所有轨道的最大时长。
///
public float TotalDuration => tracks.Count > 0 ? tracks.Max(t => t.TotalDuration) : 0f;
///
/// 运行时预览:通过 FeedbackManager 播放此反馈。
/// 仅在 Play 模式下可用。
///
[Button("Preview", ButtonSizes.Medium)]
[EnableIf("@UnityEngine.Application.isPlaying")]
public void Preview()
{
if (!Application.isPlaying)
{
Debug.LogWarning("[FeedbackData] Preview is only available in Play mode.");
return;
}
if (FeedbackManager.Instance == null)
{
Debug.LogWarning("[FeedbackData] Preview failed: FeedbackManager not found in scene. " +
"Add a GameObject with FeedbackManager component.");
return;
}
FeedbackPlayer player = FeedbackManager.Instance.Play(this);
Debug.Log($"[FeedbackData] Previewing '{feedbackName}' (Duration: {TotalDuration:F2}s)");
}
}
public partial class FeedbackData
{
public FeedbackTrack Track(string name)
{
FeedbackTrack track = tracks.FirstOrDefault(t => t.trackName == name);
if (track == null)
{
Debug.LogWarning($"[FeedbackData] Track '{name}' not found in FeedbackData '{feedbackName}'.");
}
return track;
}
public FeedbackClip Clip(string trackName, Func predicate)
{
FeedbackTrack track = Track(trackName);
if (track == null) return null;
FeedbackClip clip = track.clips.FirstOrDefault(predicate);
if (clip == null)
{
Debug.LogWarning($"[FeedbackData] Clip matching predicate not found in Track '{trackName}' of FeedbackData '{feedbackName}'.");
}
return clip;
}
public FeedbackClip Clip(string trackName) where T : FeedbackActionBase
{
return Clip(trackName, c => c.action.GetType() == typeof(T));
}
public FeedbackClip Clip(string trackName, string clipName)
{
return Clip(trackName, c => c.clipName == clipName);
}
public T Action(string trackName) where T : FeedbackActionBase
{
FeedbackTrack track = Track(trackName);
FeedbackClip clip = track?.clips.FirstOrDefault(c => c.action.GetType() == typeof(T));
return clip?.action as T;
}
public bool CanPlay()
{
if (!limitPlay) return true;
// 防御性检查:当在 Unity 编辑器中不重新加载域运行游戏,或者重开 Run 导致运行时间倒退时,重置记录
if (Time.unscaledTime < _lastPlayTime)
{
_lastPlayTime = -9999f;
_lastPlayFrame = -1;
}
if (minPlayInterval <= 0f)
{
return Time.frameCount != _lastPlayFrame;
}
else
{
return Time.unscaledTime - _lastPlayTime >= minPlayInterval;
}
}
public void RecordPlay()
{
if (!limitPlay) return;
_lastPlayFrame = Time.frameCount;
_lastPlayTime = Time.unscaledTime;
}
}
}