using System.Collections.Generic; using UnityEngine; namespace AibisDream.FrameAnimation { [CreateAssetMenu(fileName = "FrameClip", menuName = "AibisDream/Frame Animation/Frame Clip")] public sealed class FrameClip : ScriptableObject { [SerializeField] private string id = string.Empty; [SerializeField] private string displayName = string.Empty; [SerializeField] private List frames = new List(); [SerializeField] private float speed = 1f; [SerializeField] private FrameClipEndBehavior defaultEndBehavior = FrameClipEndBehavior.HoldLastFrame; [SerializeField] private bool hasImportInfo; [SerializeField] private FrameClipImportInfo importInfo; [SerializeField] private bool hasStandaloneImportSource; [SerializeField] private FrameClipImportSource standaloneImportSource; public string Id => id; public string DisplayName => displayName; public IReadOnlyList Frames => frames; public float Speed => speed; public FrameClipEndBehavior DefaultEndBehavior => defaultEndBehavior; public FrameClipImportInfo ImportInfo => hasImportInfo ? importInfo : null; public bool IsImported => ImportInfo != null; public FrameClipImportSource StandaloneImportSource => hasStandaloneImportSource ? standaloneImportSource : null; public bool HasStandaloneImportSource => StandaloneImportSource != null; public int FrameCount => frames?.Count ?? 0; public long TotalDurationMs { get { long total = 0; if (frames == null) { return total; } foreach (var frame in frames) { if (frame != null) { total += frame.DurationMs; } } return total; } } private void OnEnable() { frames ??= new List(); if (!hasImportInfo && importInfo != null && (!string.IsNullOrEmpty(importInfo.ImportSourceId) || !string.IsNullOrEmpty(importInfo.SourceTagName) || importInfo.IsMissingFromSource)) { // Compatibility for phase-two assets created before the explicit presence flag existed. hasImportInfo = true; } } internal void Configure( string clipId, string clipDisplayName, IEnumerable clipFrames, float clipSpeed = 1f, FrameClipEndBehavior endBehavior = FrameClipEndBehavior.HoldLastFrame, FrameClipImportInfo clipImportInfo = null) { id = clipId ?? string.Empty; displayName = clipDisplayName ?? id; frames = clipFrames != null ? new List(clipFrames) : new List(); speed = clipSpeed; defaultEndBehavior = endBehavior; importInfo = clipImportInfo; hasImportInfo = clipImportInfo != null; } internal void ReplaceImportedFrames( IEnumerable importedFrames, string importSourceId, string sourceTagName) { frames = importedFrames != null ? new List(importedFrames) : new List(); importInfo ??= new FrameClipImportInfo(importSourceId, sourceTagName, false); importInfo.Update(importSourceId, sourceTagName, false); hasImportInfo = true; } internal void SetImportMissingState(bool isMissing) { if (hasImportInfo && importInfo != null) { importInfo.Update(importInfo.ImportSourceId, importInfo.SourceTagName, isMissing); } } internal void SetId(string value) { id = value ?? string.Empty; } internal void SetDisplayName(string value) { displayName = value ?? string.Empty; } internal void SetFrames(IEnumerable value) { frames = value != null ? new List(value) : new List(); } internal FrameClipImportSource GetOrCreateStandaloneImportSource() { standaloneImportSource ??= new FrameClipImportSource(); hasStandaloneImportSource = true; return standaloneImportSource; } internal void ClearStandaloneImportSource() { hasStandaloneImportSource = false; standaloneImportSource = null; } } }