using System; using System.Collections.Generic; using UnityEngine; namespace AibisDream.FrameAnimation { [Serializable] public sealed class FrameAnimationFrame { [SerializeField] private Sprite sprite; [SerializeField] private int durationMs = 100; [SerializeField] private string frameName = string.Empty; [SerializeField] private int sourceIndex = -1; public Sprite Sprite => sprite; public int DurationMs => durationMs; public string FrameName => frameName; public int SourceIndex => sourceIndex; public FrameAnimationFrame() { } internal FrameAnimationFrame(Sprite sprite, int durationMs, string frameName, int sourceIndex) { this.sprite = sprite; this.durationMs = durationMs; this.frameName = frameName ?? string.Empty; this.sourceIndex = sourceIndex; } } [Serializable] public sealed class FrameClipImportInfo { [SerializeField] private string importSourceId = string.Empty; [SerializeField] private string sourceTagName = string.Empty; [SerializeField] private bool isMissingFromSource; public string ImportSourceId => importSourceId; public string SourceTagName => sourceTagName; public bool IsMissingFromSource => isMissingFromSource; internal FrameClipImportInfo(string importSourceId, string sourceTagName, bool isMissingFromSource) { this.importSourceId = importSourceId ?? string.Empty; this.sourceTagName = sourceTagName ?? string.Empty; this.isMissingFromSource = isMissingFromSource; } internal void Update(string sourceId, string tagName, bool missingFromSource) { importSourceId = sourceId ?? string.Empty; sourceTagName = tagName ?? string.Empty; isMissingFromSource = missingFromSource; } } /// /// Editor-time source settings owned by an external FrameClip asset. /// This is intentionally separate from graph-owned FrameClipImportInfo. /// [Serializable] public sealed class FrameClipImportSource { [SerializeField] private Texture2D texture; [SerializeField] private TextAsset asepriteJson; [SerializeField] private Vector2 pivot = new Vector2(0.5f, 0.5f); [SerializeField] private bool manageSpriteSlicing; [SerializeField] private string lastSourceHash = string.Empty; [SerializeField] private string lastImportedTagName = string.Empty; public Texture2D Texture => texture; public TextAsset AsepriteJson => asepriteJson; public Vector2 Pivot => pivot; public bool ManageSpriteSlicing => manageSpriteSlicing; public string LastSourceHash => lastSourceHash; public string LastImportedTagName => lastImportedTagName; internal void SetLastImport(string sourceHash, string tagName) { lastSourceHash = sourceHash ?? string.Empty; lastImportedTagName = tagName ?? string.Empty; } } [Serializable] public sealed class AnimationFlow { [SerializeField] private string id = string.Empty; [SerializeField] private string displayName = string.Empty; [SerializeField] private string entryNodeId = string.Empty; [SerializeField] private bool hasEndBehaviorOverride; [SerializeField] private FrameClipEndBehavior endBehaviorOverride; public string Id => id; public string DisplayName => displayName; public string EntryNodeId => entryNodeId; public FrameClipEndBehavior? EndBehaviorOverride => hasEndBehaviorOverride ? endBehaviorOverride : (FrameClipEndBehavior?)null; public AnimationFlow() { } internal AnimationFlow( string id, string displayName, string entryNodeId, FrameClipEndBehavior? endBehaviorOverride = null) { this.id = id ?? string.Empty; this.displayName = displayName ?? this.id; this.entryNodeId = entryNodeId ?? string.Empty; SetEndBehaviorOverride(endBehaviorOverride); } internal void SetEndBehaviorOverride(FrameClipEndBehavior? value) { hasEndBehaviorOverride = value.HasValue; endBehaviorOverride = value.GetValueOrDefault(); } internal void SetId(string value) { id = value ?? string.Empty; } internal void SetDisplayName(string value) { displayName = value ?? string.Empty; } internal void SetEntryNodeId(string value) { entryNodeId = value ?? string.Empty; } } [Serializable] public sealed class AnimationNode { [SerializeField] private string internalId = string.Empty; [SerializeField] private string displayName = string.Empty; [SerializeField] private AnimationNodeType type = AnimationNodeType.Clip; [SerializeField] private string clipId = string.Empty; [SerializeField] private bool hasEndBehaviorOverride; [SerializeField] private FrameClipEndBehavior endBehaviorOverride; [SerializeField] private bool hasSpeedOverride; [SerializeField] private float speedOverride = 1f; public string InternalId => internalId; public string DisplayName => displayName; public AnimationNodeType Type => type; public string ClipId => clipId; public FrameClipEndBehavior? EndBehaviorOverride => hasEndBehaviorOverride ? endBehaviorOverride : (FrameClipEndBehavior?)null; public float? SpeedOverride => hasSpeedOverride ? speedOverride : (float?)null; public AnimationNode() { } internal AnimationNode( string clipId, string displayName = null, FrameClipEndBehavior? endBehaviorOverride = null, float? speedOverride = null, string internalId = null) { this.internalId = string.IsNullOrEmpty(internalId) ? FrameAnimationValueUtility.NewInternalId() : internalId; this.displayName = displayName ?? clipId ?? string.Empty; type = AnimationNodeType.Clip; this.clipId = clipId ?? string.Empty; SetEndBehaviorOverride(endBehaviorOverride); SetSpeedOverride(speedOverride); } internal void EnsureInternalId() { if (string.IsNullOrWhiteSpace(internalId)) { internalId = FrameAnimationValueUtility.NewInternalId(); } } internal void SetEndBehaviorOverride(FrameClipEndBehavior? value) { hasEndBehaviorOverride = value.HasValue; endBehaviorOverride = value.GetValueOrDefault(); } internal void SetSpeedOverride(float? value) { hasSpeedOverride = value.HasValue; speedOverride = value.GetValueOrDefault(1f); } internal void SetClipId(string value) { clipId = value ?? string.Empty; } internal void SetDisplayName(string value) { displayName = value ?? string.Empty; } } [Serializable] public sealed class AnimationEdge { [SerializeField] private string internalId = string.Empty; [SerializeField] private string fromNodeId = string.Empty; [SerializeField] private string toNodeId = string.Empty; [SerializeField] private string exitName = "default"; [SerializeField] private AnimationEdgeCondition condition = AnimationEdgeCondition.Always; public string InternalId => internalId; public string FromNodeId => fromNodeId; public string ToNodeId => toNodeId; public string ExitName => exitName; public AnimationEdgeCondition Condition => condition; public AnimationEdge() { } internal AnimationEdge(string fromNodeId, string toNodeId, string internalId = null) { this.internalId = string.IsNullOrEmpty(internalId) ? FrameAnimationValueUtility.NewInternalId() : internalId; this.fromNodeId = fromNodeId ?? string.Empty; this.toNodeId = toNodeId ?? string.Empty; exitName = "default"; condition = AnimationEdgeCondition.Always; } internal void EnsureInternalId() { if (string.IsNullOrWhiteSpace(internalId)) { internalId = FrameAnimationValueUtility.NewInternalId(); } } } [Serializable] public sealed class FrameAnimationImportSource { [SerializeField] private string internalId = string.Empty; [SerializeField] private string displayName = string.Empty; [SerializeField] private bool isEnabled = true; [SerializeField] private Texture2D texture; [SerializeField] private TextAsset asepriteJson; [SerializeField] private Vector2 pivot = new Vector2(0.5f, 0.5f); [SerializeField] private bool manageSpriteSlicing; [SerializeField] private FrameClipEndBehavior defaultNewClipEndBehavior = FrameClipEndBehavior.HoldLastFrame; [SerializeField] private string lastSourceHash = string.Empty; public string InternalId => internalId; public string DisplayName => displayName; public bool IsEnabled => isEnabled; public Texture2D Texture => texture; public TextAsset AsepriteJson => asepriteJson; public Vector2 Pivot => pivot; public bool ManageSpriteSlicing => manageSpriteSlicing; public FrameClipEndBehavior DefaultNewClipEndBehavior => defaultNewClipEndBehavior; public string LastSourceHash => lastSourceHash; public FrameAnimationImportSource() { } internal FrameAnimationImportSource( string sourceDisplayName, Texture2D sourceTexture, TextAsset sourceJson, Vector2 sourcePivot, bool ownsSpriteSlicing, FrameClipEndBehavior newClipEndBehavior = FrameClipEndBehavior.HoldLastFrame, string sourceInternalId = null) { internalId = string.IsNullOrWhiteSpace(sourceInternalId) ? FrameAnimationValueUtility.NewInternalId() : sourceInternalId; displayName = sourceDisplayName ?? string.Empty; isEnabled = true; texture = sourceTexture; asepriteJson = sourceJson; pivot = sourcePivot; manageSpriteSlicing = ownsSpriteSlicing; defaultNewClipEndBehavior = newClipEndBehavior; } internal void EnsureInternalId() { if (string.IsNullOrWhiteSpace(internalId)) { internalId = FrameAnimationValueUtility.NewInternalId(); } } internal void SetLastSourceHash(string value) { lastSourceHash = value ?? string.Empty; } } [Serializable] public sealed class FrameAnimationGraphSettings { [SerializeField] private string defaultPlayableId = string.Empty; [SerializeField] private FrameClipEndBehavior newManualClipDefaultEndBehavior = FrameClipEndBehavior.HoldLastFrame; public string DefaultPlayableId => defaultPlayableId; public FrameClipEndBehavior NewManualClipDefaultEndBehavior => newManualClipDefaultEndBehavior; internal void SetDefaultPlayableId(string value) { defaultPlayableId = value ?? string.Empty; } internal void SetNewManualClipDefaultEndBehavior(FrameClipEndBehavior value) { newManualClipDefaultEndBehavior = value; } } [Serializable] public sealed class AnimationNodeEditorData { [SerializeField] private string nodeId = string.Empty; [SerializeField] private Vector2 position; public string NodeId => nodeId; public Vector2 Position => position; internal AnimationNodeEditorData(string nodeId, Vector2 position) { this.nodeId = nodeId ?? string.Empty; this.position = position; } internal void SetPosition(Vector2 value) { position = value; } } [Serializable] public sealed class AnimationFlowEditorData { [SerializeField] private string flowId = string.Empty; [SerializeField] private Color color = Color.white; public string FlowId => flowId; public Color Color => color; internal AnimationFlowEditorData(string flowId, Color color) { this.flowId = flowId ?? string.Empty; this.color = color; } internal void SetFlowId(string value) { flowId = value ?? string.Empty; } internal void SetColor(Color value) { color = value; } } [Serializable] public sealed class FrameAnimationGraphEditorData { [SerializeField] private List nodeEditorData = new List(); [SerializeField] private List flowEditorData = new List(); public IReadOnlyList NodeEditorData => nodeEditorData; public IReadOnlyList FlowEditorData => flowEditorData; internal AnimationNodeEditorData GetOrCreateNodeData(string nodeId, Vector2 position) { nodeEditorData ??= new List(); var existing = nodeEditorData.Find(data => data != null && data.NodeId == nodeId); if (existing != null) { return existing; } var created = new AnimationNodeEditorData(nodeId, position); nodeEditorData.Add(created); return created; } internal bool RemoveNodeData(string nodeId) { nodeEditorData ??= new List(); return nodeEditorData.RemoveAll(data => data != null && data.NodeId == nodeId) > 0; } internal AnimationFlowEditorData GetOrCreateFlowData(string flowId, Color color) { flowEditorData ??= new List(); var existing = flowEditorData.Find(data => data != null && data.FlowId == flowId); if (existing != null) { return existing; } var created = new AnimationFlowEditorData(flowId, color); flowEditorData.Add(created); return created; } internal bool RemoveFlowData(string flowId) { flowEditorData ??= new List(); return flowEditorData.RemoveAll(data => data != null && data.FlowId == flowId) > 0; } } }