606 lines
26 KiB
C#
606 lines
26 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using UnityEditor;
|
|
using UnityEngine;
|
|
|
|
namespace AibisDream.FrameAnimation.Editor
|
|
{
|
|
internal static class FrameAnimationImportService
|
|
{
|
|
public static FrameAnimationImportPreview PreviewSource(
|
|
FrameAnimationGraph graph,
|
|
string sourceId)
|
|
{
|
|
var source = graph?.ImportSources.FirstOrDefault(item => item != null && item.InternalId == sourceId);
|
|
return PreviewInternal(graph, source != null ? new[] { source } : Array.Empty<FrameAnimationImportSource>());
|
|
}
|
|
|
|
public static FrameAnimationImportPreview PreviewAll(FrameAnimationGraph graph)
|
|
{
|
|
return PreviewInternal(
|
|
graph,
|
|
graph?.ImportSources.Where(source => source != null && source.IsEnabled).ToArray() ??
|
|
Array.Empty<FrameAnimationImportSource>());
|
|
}
|
|
|
|
public static bool RefreshSource(
|
|
FrameAnimationGraph graph,
|
|
string sourceId,
|
|
bool allowSpriteChanges,
|
|
out FrameAnimationImportPreview preview,
|
|
out string error)
|
|
{
|
|
preview = PreviewSource(graph, sourceId);
|
|
return Apply(preview, allowSpriteChanges, out error);
|
|
}
|
|
|
|
public static bool RefreshAll(
|
|
FrameAnimationGraph graph,
|
|
bool allowSpriteChanges,
|
|
out FrameAnimationImportPreview preview,
|
|
out string error)
|
|
{
|
|
preview = PreviewAll(graph);
|
|
return Apply(preview, allowSpriteChanges, out error);
|
|
}
|
|
|
|
public static bool Apply(
|
|
FrameAnimationImportPreview preview,
|
|
bool allowSpriteChanges,
|
|
out string error)
|
|
{
|
|
error = string.Empty;
|
|
if (preview == null || preview.Graph == null)
|
|
{
|
|
error = "导入预览或 Graph 为空。";
|
|
return false;
|
|
}
|
|
|
|
if (preview.HasErrors)
|
|
{
|
|
error = "导入预览包含阻断错误,未修改任何资产。";
|
|
return false;
|
|
}
|
|
|
|
if (preview.HasSpriteChanges && !allowSpriteChanges)
|
|
{
|
|
error = "导入需要修改 TextureImporter,但尚未获得确认。";
|
|
return false;
|
|
}
|
|
|
|
var graphPath = AssetDatabase.GetAssetPath(preview.Graph);
|
|
if (string.IsNullOrEmpty(graphPath))
|
|
{
|
|
error = "FrameAnimationGraph 必须先保存为资产才能创建 Imported Clip sub-asset。";
|
|
return false;
|
|
}
|
|
|
|
var snapshots = new List<FrameAnimationTextureSnapshot>();
|
|
Undo.IncrementCurrentGroup();
|
|
var undoGroup = Undo.GetCurrentGroup();
|
|
Undo.SetCurrentGroupName("Refresh Frame Animation Imports");
|
|
|
|
try
|
|
{
|
|
Undo.RecordObject(preview.Graph, "Refresh Frame Animation Imports");
|
|
foreach (var sourcePreview in preview.Sources.Where(item =>
|
|
item.Source.ManageSpriteSlicing && item.HasSpriteChanges))
|
|
{
|
|
var snapshot = FrameAnimationSpriteUtility.CaptureSnapshot(sourcePreview.SpritePlan);
|
|
snapshots.Add(snapshot);
|
|
FrameAnimationSpriteUtility.ApplyPlan(sourcePreview.SpritePlan);
|
|
}
|
|
|
|
foreach (var sourcePreview in preview.Sources)
|
|
{
|
|
ApplySource(preview.Graph, sourcePreview);
|
|
sourcePreview.Source.SetLastSourceHash(
|
|
FrameAnimationSpriteUtility.ComputeSourceHash(sourcePreview.Source));
|
|
}
|
|
|
|
EditorUtility.SetDirty(preview.Graph);
|
|
AssetDatabase.SaveAssets();
|
|
Undo.CollapseUndoOperations(undoGroup);
|
|
return true;
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
try
|
|
{
|
|
Undo.RevertAllDownToGroup(undoGroup);
|
|
for (var index = snapshots.Count - 1; index >= 0; index--)
|
|
{
|
|
FrameAnimationSpriteUtility.RestoreSnapshot(snapshots[index]);
|
|
}
|
|
AssetDatabase.SaveAssets();
|
|
}
|
|
catch (Exception rollbackException)
|
|
{
|
|
Debug.LogError($"Frame Animation 导入回滚失败:{rollbackException}");
|
|
}
|
|
|
|
error = $"导入应用失败,已尝试回滚:{exception.Message}";
|
|
Debug.LogException(exception);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private static FrameAnimationImportPreview PreviewInternal(
|
|
FrameAnimationGraph graph,
|
|
IReadOnlyList<FrameAnimationImportSource> targets)
|
|
{
|
|
var preview = new FrameAnimationImportPreview(graph);
|
|
if (graph == null)
|
|
{
|
|
return preview;
|
|
}
|
|
|
|
var targetSet = new HashSet<FrameAnimationImportSource>(targets);
|
|
var targetPreviews = new Dictionary<FrameAnimationImportSource, FrameAnimationImportSourcePreview>();
|
|
foreach (var source in targets)
|
|
{
|
|
var sourcePreview = new FrameAnimationImportSourcePreview(source);
|
|
targetPreviews[source] = sourcePreview;
|
|
preview.AddSource(sourcePreview);
|
|
}
|
|
|
|
ValidateSourceIds(graph, targetPreviews);
|
|
var contextDocuments = ParseEnabledContext(graph, targetPreviews);
|
|
ValidateCrossSourceTags(graph, contextDocuments, targetPreviews);
|
|
ValidateTextureOwnership(graph, targetPreviews);
|
|
|
|
foreach (var pair in targetPreviews)
|
|
{
|
|
var source = pair.Key;
|
|
var sourcePreview = pair.Value;
|
|
if (!contextDocuments.TryGetValue(source, out var document))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
sourcePreview.Document = document;
|
|
ValidateSource(graph, source, document, sourcePreview);
|
|
if (sourcePreview.HasErrors)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
sourcePreview.SpritePlan = FrameAnimationSpriteUtility.BuildPlan(source, document, sourcePreview);
|
|
sourcePreview.CurrentHash = FrameAnimationSpriteUtility.ComputeSourceHash(source);
|
|
if (!string.IsNullOrEmpty(source.LastSourceHash) && source.LastSourceHash != sourcePreview.CurrentHash)
|
|
{
|
|
sourcePreview.AddIssue(new FrameAnimationImportIssue(
|
|
FrameAnimationValidationSeverity.Warning,
|
|
FrameAnimationImportIssueCode.SourceChanged,
|
|
source.InternalId,
|
|
source.InternalId,
|
|
"来源内容或切图设置自上次刷新后已发生变化。"));
|
|
}
|
|
|
|
if (!sourcePreview.HasErrors)
|
|
{
|
|
BuildClipDiffs(graph, source, document, sourcePreview);
|
|
}
|
|
}
|
|
|
|
return preview;
|
|
}
|
|
|
|
private static Dictionary<FrameAnimationImportSource, AsepriteSourceDocument> ParseEnabledContext(
|
|
FrameAnimationGraph graph,
|
|
IReadOnlyDictionary<FrameAnimationImportSource, FrameAnimationImportSourcePreview> targetPreviews)
|
|
{
|
|
var result = new Dictionary<FrameAnimationImportSource, AsepriteSourceDocument>();
|
|
foreach (var source in graph.ImportSources.Where(source => source != null && source.IsEnabled))
|
|
{
|
|
targetPreviews.TryGetValue(source, out var sourcePreview);
|
|
if (source.AsepriteJson == null)
|
|
{
|
|
sourcePreview?.AddIssue(Error(source, FrameAnimationImportIssueCode.JsonMissing, "ImportSource 缺少 Aseprite JSON。"));
|
|
continue;
|
|
}
|
|
|
|
if (!AsepriteJsonParser.TryParse(source.AsepriteJson.text, source.InternalId, out var document, out var issue))
|
|
{
|
|
sourcePreview?.AddIssue(issue);
|
|
continue;
|
|
}
|
|
result[source] = document;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private static void ValidateSourceIds(
|
|
FrameAnimationGraph graph,
|
|
IReadOnlyDictionary<FrameAnimationImportSource, FrameAnimationImportSourcePreview> targetPreviews)
|
|
{
|
|
foreach (var source in graph.ImportSources.Where(source => source != null))
|
|
{
|
|
if (!FrameAnimationValueUtility.IsValidInternalId(source.InternalId) &&
|
|
targetPreviews.TryGetValue(source, out var sourcePreview))
|
|
{
|
|
sourcePreview.AddIssue(Error(source, FrameAnimationImportIssueCode.ImportSourceIdInvalid,
|
|
"ImportSource internalId 必须是 N 格式 GUID。"));
|
|
}
|
|
}
|
|
|
|
foreach (var duplicate in graph.ImportSources
|
|
.Where(source => source != null)
|
|
.GroupBy(source => source.InternalId)
|
|
.Where(group => group.Count() > 1))
|
|
{
|
|
foreach (var source in duplicate)
|
|
{
|
|
if (targetPreviews.TryGetValue(source, out var sourcePreview))
|
|
{
|
|
sourcePreview.AddIssue(Error(source, FrameAnimationImportIssueCode.ImportSourceIdDuplicate,
|
|
$"ImportSource internalId '{source.InternalId}' 重复。"));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void ValidateSource(
|
|
FrameAnimationGraph graph,
|
|
FrameAnimationImportSource source,
|
|
AsepriteSourceDocument document,
|
|
FrameAnimationImportSourcePreview preview)
|
|
{
|
|
if (source.Texture == null)
|
|
{
|
|
preview.AddIssue(Error(source, FrameAnimationImportIssueCode.TextureMissing, "ImportSource 缺少 Texture。"));
|
|
return;
|
|
}
|
|
|
|
if (document.Frames.Count == 0)
|
|
{
|
|
preview.AddIssue(Error(source, FrameAnimationImportIssueCode.FramesMissing, "Aseprite JSON 的 frames 为空。"));
|
|
}
|
|
|
|
foreach (var duplicate in document.Frames.GroupBy(frame => frame.FrameName).Where(group => group.Count() > 1))
|
|
{
|
|
preview.AddIssue(Error(source, FrameAnimationImportIssueCode.FrameNameDuplicate,
|
|
$"frameName '{duplicate.Key}' 在同一来源内重复。"));
|
|
}
|
|
|
|
for (var index = 0; index < document.Frames.Count; index++)
|
|
{
|
|
var frame = document.Frames[index];
|
|
if (string.IsNullOrWhiteSpace(frame.FrameName))
|
|
{
|
|
preview.AddIssue(Error(source, FrameAnimationImportIssueCode.FrameNameInvalid,
|
|
$"SourceFrame[{index}] 的 frameName 为空。"));
|
|
}
|
|
if (frame.DurationMs <= 0)
|
|
{
|
|
preview.AddIssue(Error(source, FrameAnimationImportIssueCode.FrameDurationInvalid,
|
|
$"SourceFrame '{frame.FrameName}' 的 duration 必须大于 0。"));
|
|
}
|
|
if (frame.Rect.width <= 0 || frame.Rect.height <= 0 || frame.Rect.x < 0 || frame.Rect.y < 0 ||
|
|
frame.Rect.xMax > source.Texture.width || frame.Rect.yMax > source.Texture.height)
|
|
{
|
|
preview.AddIssue(Error(source, FrameAnimationImportIssueCode.FrameRectInvalid,
|
|
$"SourceFrame '{frame.FrameName}' 的 rect 越出 Texture 范围。"));
|
|
}
|
|
if (frame.Trimmed)
|
|
{
|
|
preview.AddIssue(Error(source, FrameAnimationImportIssueCode.TrimmedUnsupported,
|
|
$"SourceFrame '{frame.FrameName}' 使用了第一版不支持的 trimmed。"));
|
|
}
|
|
if (frame.Rotated)
|
|
{
|
|
preview.AddIssue(Error(source, FrameAnimationImportIssueCode.RotatedUnsupported,
|
|
$"SourceFrame '{frame.FrameName}' 使用了第一版不支持的 rotated。"));
|
|
}
|
|
}
|
|
|
|
if (document.TextureSize.x != source.Texture.width || document.TextureSize.y != source.Texture.height)
|
|
{
|
|
preview.AddIssue(Error(source, FrameAnimationImportIssueCode.TextureSizeMismatch,
|
|
$"meta.size {document.TextureSize.x}x{document.TextureSize.y} 与 Texture {source.Texture.width}x{source.Texture.height} 不一致。"));
|
|
}
|
|
|
|
var textureFileName = Path.GetFileName(AssetDatabase.GetAssetPath(source.Texture));
|
|
if (!string.IsNullOrEmpty(document.ImageName) &&
|
|
!string.Equals(document.ImageName, textureFileName, StringComparison.Ordinal))
|
|
{
|
|
preview.AddIssue(Warning(source, FrameAnimationImportIssueCode.ImageNameMismatch,
|
|
$"meta.image '{document.ImageName}' 与绑定 Texture '{textureFileName}' 不一致。"));
|
|
}
|
|
|
|
if (document.Tags.Count == 0)
|
|
{
|
|
preview.AddIssue(Warning(source, FrameAnimationImportIssueCode.NoTags,
|
|
"JSON 中没有 frameTags,此来源不会生成 Clip。"));
|
|
}
|
|
|
|
foreach (var duplicate in document.Tags.GroupBy(tag => tag.Name).Where(group => group.Count() > 1))
|
|
{
|
|
preview.AddIssue(Error(source, FrameAnimationImportIssueCode.TagNameDuplicate,
|
|
$"Tag '{duplicate.Key}' 在同一来源内重复。"));
|
|
}
|
|
|
|
foreach (var tag in document.Tags)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(tag.Name))
|
|
{
|
|
preview.AddIssue(Error(source, FrameAnimationImportIssueCode.TagNameInvalid, "Tag 名不能为空。"));
|
|
continue;
|
|
}
|
|
if (!AsepriteJsonParser.TryExpandTag(tag, document.Frames.Count, source.InternalId, out _, out var issue))
|
|
{
|
|
preview.AddIssue(issue);
|
|
}
|
|
}
|
|
|
|
ValidateClipAssociations(graph, source, preview);
|
|
}
|
|
|
|
private static void ValidateClipAssociations(
|
|
FrameAnimationGraph graph,
|
|
FrameAnimationImportSource source,
|
|
FrameAnimationImportSourcePreview preview)
|
|
{
|
|
var graphPath = AssetDatabase.GetAssetPath(graph);
|
|
foreach (var clip in graph.Clips.Where(clip => clip != null && clip.IsImported))
|
|
{
|
|
if (!graph.ImportSources.Any(item => item != null && item.InternalId == clip.ImportInfo.ImportSourceId))
|
|
{
|
|
preview.AddIssue(Error(source, FrameAnimationImportIssueCode.ImportSourceReferenceInvalid,
|
|
$"Imported Clip '{clip.Id}' 指向不存在的 ImportSource。"));
|
|
}
|
|
if (!AssetDatabase.IsSubAsset(clip) || AssetDatabase.GetAssetPath(clip) != graphPath)
|
|
{
|
|
preview.AddIssue(Error(source, FrameAnimationImportIssueCode.ImportedClipOwnershipInvalid,
|
|
$"Imported Clip '{clip.Id}' 必须是所属 Graph 的 sub-asset。"));
|
|
}
|
|
}
|
|
|
|
foreach (var duplicate in graph.Clips
|
|
.Where(clip => clip != null && clip.ImportInfo?.ImportSourceId == source.InternalId)
|
|
.GroupBy(clip => clip.ImportInfo.SourceTagName)
|
|
.Where(group => group.Count() > 1))
|
|
{
|
|
preview.AddIssue(Error(source, FrameAnimationImportIssueCode.ImportSourceReferenceInvalid,
|
|
$"来源 Tag '{duplicate.Key}' 同时关联了多个 Imported Clip。"));
|
|
}
|
|
}
|
|
|
|
private static void ValidateCrossSourceTags(
|
|
FrameAnimationGraph graph,
|
|
IReadOnlyDictionary<FrameAnimationImportSource, AsepriteSourceDocument> documents,
|
|
IReadOnlyDictionary<FrameAnimationImportSource, FrameAnimationImportSourcePreview> targetPreviews)
|
|
{
|
|
var tags = documents.SelectMany(pair => pair.Value.Tags.Select(tag => (source: pair.Key, tag)));
|
|
foreach (var duplicate in tags.Where(item => !string.IsNullOrWhiteSpace(item.tag.Name))
|
|
.GroupBy(item => item.tag.Name).Where(group => group.Count() > 1))
|
|
{
|
|
foreach (var item in duplicate)
|
|
{
|
|
if (targetPreviews.TryGetValue(item.source, out var sourcePreview))
|
|
{
|
|
sourcePreview.AddIssue(Error(item.source, FrameAnimationImportIssueCode.TagConflict,
|
|
$"Tag '{duplicate.Key}' 与其他启用 ImportSource 重名。"));
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach (var pair in targetPreviews)
|
|
{
|
|
if (!documents.TryGetValue(pair.Key, out var document))
|
|
{
|
|
continue;
|
|
}
|
|
foreach (var tag in document.Tags)
|
|
{
|
|
var existing = graph.Clips.Count(clip => clip != null &&
|
|
clip.ImportInfo?.ImportSourceId == pair.Key.InternalId &&
|
|
clip.ImportInfo.SourceTagName == tag.Name);
|
|
if (existing > 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (graph.Clips.Any(clip => clip != null && clip.Id == tag.Name) ||
|
|
graph.Flows.Any(flow => flow != null && flow.Id == tag.Name))
|
|
{
|
|
pair.Value.AddIssue(Error(pair.Key, FrameAnimationImportIssueCode.PlayableIdConflict,
|
|
$"新 Tag '{tag.Name}' 与现有 Clip / Flow playable id 冲突。"));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void ValidateTextureOwnership(
|
|
FrameAnimationGraph currentGraph,
|
|
IReadOnlyDictionary<FrameAnimationImportSource, FrameAnimationImportSourcePreview> targetPreviews)
|
|
{
|
|
var owners = new List<(FrameAnimationGraph graph, FrameAnimationImportSource source)>();
|
|
foreach (var guid in AssetDatabase.FindAssets("t:FrameAnimationGraph"))
|
|
{
|
|
var graph = AssetDatabase.LoadAssetAtPath<FrameAnimationGraph>(AssetDatabase.GUIDToAssetPath(guid));
|
|
if (graph == null)
|
|
{
|
|
continue;
|
|
}
|
|
owners.AddRange(graph.ImportSources.Where(source => source != null && source.IsEnabled &&
|
|
source.ManageSpriteSlicing && source.Texture != null).Select(source => (graph, source)));
|
|
}
|
|
if (!owners.Any(item => item.graph == currentGraph))
|
|
{
|
|
owners.AddRange(currentGraph.ImportSources.Where(source => source != null && source.IsEnabled &&
|
|
source.ManageSpriteSlicing && source.Texture != null).Select(source => (currentGraph, source)));
|
|
}
|
|
|
|
foreach (var pair in targetPreviews.Where(pair => pair.Key.ManageSpriteSlicing && pair.Key.Texture != null))
|
|
{
|
|
var conflicts = owners.Where(item => item.source.Texture == pair.Key.Texture &&
|
|
!(item.graph == currentGraph && ReferenceEquals(item.source, pair.Key))).ToArray();
|
|
if (conflicts.Length > 0)
|
|
{
|
|
pair.Value.AddIssue(Error(pair.Key, FrameAnimationImportIssueCode.TextureOwnershipConflict,
|
|
$"Texture '{pair.Key.Texture.name}' 已被其他可写 ImportSource 占用:" +
|
|
string.Join(", ", conflicts.Select(item => $"{item.graph.name}/{item.source.DisplayName}"))));
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void BuildClipDiffs(
|
|
FrameAnimationGraph graph,
|
|
FrameAnimationImportSource source,
|
|
AsepriteSourceDocument document,
|
|
FrameAnimationImportSourcePreview preview)
|
|
{
|
|
var seenTags = new HashSet<string>();
|
|
foreach (var tag in document.Tags)
|
|
{
|
|
if (!AsepriteJsonParser.TryExpandTag(tag, document.Frames.Count, source.InternalId, out var indices, out _))
|
|
{
|
|
continue;
|
|
}
|
|
seenTags.Add(tag.Name);
|
|
var clip = graph.Clips.SingleOrDefault(item => item != null &&
|
|
item.ImportInfo?.ImportSourceId == source.InternalId &&
|
|
item.ImportInfo.SourceTagName == tag.Name);
|
|
if (clip == null)
|
|
{
|
|
preview.AddClipDiff(new FrameAnimationClipDiff(
|
|
FrameAnimationImportChangeKind.Added, tag.Name, null, indices, "创建 Imported Clip sub-asset"));
|
|
continue;
|
|
}
|
|
|
|
var changed = clip.ImportInfo.IsMissingFromSource || !FramesEqual(clip, document, indices);
|
|
preview.AddClipDiff(new FrameAnimationClipDiff(
|
|
changed ? FrameAnimationImportChangeKind.Updated : FrameAnimationImportChangeKind.Unchanged,
|
|
tag.Name,
|
|
clip,
|
|
indices,
|
|
changed ? "原地更新导入帧" : "无变化"));
|
|
}
|
|
|
|
foreach (var clip in graph.Clips.Where(item => item != null &&
|
|
item.ImportInfo?.ImportSourceId == source.InternalId &&
|
|
!seenTags.Contains(item.ImportInfo.SourceTagName)))
|
|
{
|
|
preview.AddClipDiff(new FrameAnimationClipDiff(
|
|
clip.ImportInfo.IsMissingFromSource
|
|
? FrameAnimationImportChangeKind.Unchanged
|
|
: FrameAnimationImportChangeKind.Missing,
|
|
clip.ImportInfo.SourceTagName,
|
|
clip,
|
|
Array.Empty<int>(),
|
|
clip.ImportInfo.IsMissingFromSource ? "保持 Missing" : "标记 Missing,不删除 Clip"));
|
|
}
|
|
}
|
|
|
|
private static bool FramesEqual(
|
|
FrameClip clip,
|
|
AsepriteSourceDocument document,
|
|
IReadOnlyList<int> indices)
|
|
{
|
|
if (clip.FrameCount != indices.Count)
|
|
{
|
|
return false;
|
|
}
|
|
for (var index = 0; index < indices.Count; index++)
|
|
{
|
|
var sourceIndex = indices[index];
|
|
var sourceFrame = document.Frames[sourceIndex];
|
|
var frame = clip.Frames[index];
|
|
if (frame == null || frame.SourceIndex != sourceIndex ||
|
|
frame.FrameName != sourceFrame.FrameName || frame.DurationMs != sourceFrame.DurationMs ||
|
|
frame.Sprite == null || frame.Sprite.name != sourceFrame.FrameName)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private static void ApplySource(
|
|
FrameAnimationGraph graph,
|
|
FrameAnimationImportSourcePreview preview)
|
|
{
|
|
var source = preview.Source;
|
|
var sprites = source.ManageSpriteSlicing
|
|
? FrameAnimationSpriteUtility.LoadSpritesByName(AssetDatabase.GetAssetPath(source.Texture))
|
|
: preview.SpritePlan.ReadOnlySprites;
|
|
|
|
foreach (var diff in preview.ClipDiffs)
|
|
{
|
|
if (diff.Kind == FrameAnimationImportChangeKind.Missing)
|
|
{
|
|
Undo.RecordObject(diff.Clip, "Mark Imported Clip Missing");
|
|
diff.Clip.SetImportMissingState(true);
|
|
EditorUtility.SetDirty(diff.Clip);
|
|
continue;
|
|
}
|
|
if (diff.Kind == FrameAnimationImportChangeKind.Unchanged && diff.Clip != null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var frames = diff.SourceIndices.Select(sourceIndex =>
|
|
{
|
|
var sourceFrame = preview.Document.Frames[sourceIndex];
|
|
if (!sprites.TryGetValue(sourceFrame.FrameName, out var sprite) || sprite == null)
|
|
{
|
|
throw new InvalidOperationException($"刷新后找不到 Sprite '{sourceFrame.FrameName}'。 ");
|
|
}
|
|
return new FrameAnimationFrame(sprite, sourceFrame.DurationMs, sourceFrame.FrameName, sourceIndex);
|
|
}).ToArray();
|
|
|
|
if (diff.Clip != null)
|
|
{
|
|
Undo.RecordObject(diff.Clip, "Update Imported Frame Clip");
|
|
diff.Clip.ReplaceImportedFrames(frames, source.InternalId, diff.SourceTagName);
|
|
EditorUtility.SetDirty(diff.Clip);
|
|
continue;
|
|
}
|
|
|
|
var clip = ScriptableObject.CreateInstance<FrameClip>();
|
|
clip.name = diff.SourceTagName;
|
|
clip.Configure(
|
|
diff.SourceTagName,
|
|
diff.SourceTagName,
|
|
frames,
|
|
1f,
|
|
source.DefaultNewClipEndBehavior,
|
|
new FrameClipImportInfo(source.InternalId, diff.SourceTagName, false));
|
|
AssetDatabase.AddObjectToAsset(clip, graph);
|
|
Undo.RegisterCreatedObjectUndo(clip, "Create Imported Frame Clip");
|
|
graph.AddImportedClip(clip);
|
|
EditorUtility.SetDirty(clip);
|
|
}
|
|
}
|
|
|
|
private static FrameAnimationImportIssue Error(
|
|
FrameAnimationImportSource source,
|
|
FrameAnimationImportIssueCode code,
|
|
string message)
|
|
{
|
|
return new FrameAnimationImportIssue(
|
|
FrameAnimationValidationSeverity.Error,
|
|
code,
|
|
source?.InternalId,
|
|
source?.InternalId,
|
|
message);
|
|
}
|
|
|
|
private static FrameAnimationImportIssue Warning(
|
|
FrameAnimationImportSource source,
|
|
FrameAnimationImportIssueCode code,
|
|
string message)
|
|
{
|
|
return new FrameAnimationImportIssue(
|
|
FrameAnimationValidationSeverity.Warning,
|
|
code,
|
|
source?.InternalId,
|
|
source?.InternalId,
|
|
message);
|
|
}
|
|
}
|
|
}
|