Files
2026-08-05 21:14:06 +08:00

441 lines
16 KiB
C#

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;
}
}
/// <summary>
/// Editor-time source settings owned by an external FrameClip asset.
/// This is intentionally separate from graph-owned FrameClipImportInfo.
/// </summary>
[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 AnimationFlowAsyncCompletionMode asyncCompletionMode =
AnimationFlowAsyncCompletionMode.CompleteOnTerminalLoopStart;
[SerializeField] private bool hasEndBehaviorOverride;
[SerializeField] private FrameClipEndBehavior endBehaviorOverride;
public string Id => id;
public string DisplayName => displayName;
public string EntryNodeId => entryNodeId;
public AnimationFlowAsyncCompletionMode AsyncCompletionMode => asyncCompletionMode;
public FrameClipEndBehavior? EndBehaviorOverride =>
hasEndBehaviorOverride ? endBehaviorOverride : (FrameClipEndBehavior?)null;
public AnimationFlow()
{
}
internal AnimationFlow(
string id,
string displayName,
string entryNodeId,
FrameClipEndBehavior? endBehaviorOverride = null,
AnimationFlowAsyncCompletionMode asyncCompletionMode =
AnimationFlowAsyncCompletionMode.CompleteOnTerminalLoopStart)
{
this.id = id ?? string.Empty;
this.displayName = displayName ?? this.id;
this.entryNodeId = entryNodeId ?? string.Empty;
this.asyncCompletionMode = asyncCompletionMode;
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;
}
internal void SetAsyncCompletionMode(AnimationFlowAsyncCompletionMode value)
{
asyncCompletionMode = value;
}
}
[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<AnimationNodeEditorData> nodeEditorData = new List<AnimationNodeEditorData>();
[SerializeField] private List<AnimationFlowEditorData> flowEditorData = new List<AnimationFlowEditorData>();
public IReadOnlyList<AnimationNodeEditorData> NodeEditorData => nodeEditorData;
public IReadOnlyList<AnimationFlowEditorData> FlowEditorData => flowEditorData;
internal AnimationNodeEditorData GetOrCreateNodeData(string nodeId, Vector2 position)
{
nodeEditorData ??= new List<AnimationNodeEditorData>();
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<AnimationNodeEditorData>();
return nodeEditorData.RemoveAll(data => data != null && data.NodeId == nodeId) > 0;
}
internal AnimationFlowEditorData GetOrCreateFlowData(string flowId, Color color)
{
flowEditorData ??= new List<AnimationFlowEditorData>();
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<AnimationFlowEditorData>();
return flowEditorData.RemoveAll(data => data != null && data.FlowId == flowId) > 0;
}
}
}