429 lines
24 KiB
C#
429 lines
24 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using AibisDream.FrameAnimation.Editor;
|
|
using NUnit.Framework;
|
|
using UnityEditor;
|
|
using UnityEngine;
|
|
|
|
namespace AibisDream.FrameAnimation.Tests.EditMode
|
|
{
|
|
public sealed class FrameAnimationImportTests
|
|
{
|
|
private const string TestRoot = "Assets/Tests/FrameAnimation/GeneratedImportTests";
|
|
private const string TexturePath = TestRoot + "/Atlas.png";
|
|
private const string JsonPath = TestRoot + "/Atlas.json";
|
|
private const string GraphPath = TestRoot + "/ImportGraph.asset";
|
|
|
|
[SetUp]
|
|
public void SetUp()
|
|
{
|
|
AssetDatabase.DeleteAsset(TestRoot);
|
|
EnsureFolder(TestRoot);
|
|
}
|
|
|
|
[TearDown]
|
|
public void TearDown()
|
|
{
|
|
AssetDatabase.DeleteAsset(TestRoot);
|
|
AssetDatabase.Refresh();
|
|
}
|
|
|
|
[Test]
|
|
public void Parser_PreservesObjectAndArrayOrderAndUnicode()
|
|
{
|
|
Assert.That(AsepriteJsonParser.TryParse(ObjectJson(), "source", out var objectDocument, out var objectIssue),
|
|
Is.True, objectIssue?.Message);
|
|
Assert.That(AsepriteJsonParser.TryParse(ArrayJson(), "source", out var arrayDocument, out var arrayIssue),
|
|
Is.True, arrayIssue?.Message);
|
|
|
|
CollectionAssert.AreEqual(
|
|
new[] { "第二帧.png", "第一帧.png" },
|
|
objectDocument.Frames.Select(frame => frame.FrameName));
|
|
CollectionAssert.AreEqual(
|
|
objectDocument.Frames.Select(frame => frame.FrameName),
|
|
arrayDocument.Frames.Select(frame => frame.FrameName));
|
|
CollectionAssert.AreEqual(new[] { "待机", "眨眼" }, objectDocument.Tags.Select(tag => tag.Name));
|
|
}
|
|
|
|
[Test]
|
|
public void Parser_GroupsSharedRectsWithoutCollapsingLogicalFrames()
|
|
{
|
|
Assert.That(AsepriteJsonParser.TryParse(
|
|
SharedRectJson(), "source", out var document, out var issue), Is.True, issue?.Message);
|
|
|
|
Assert.That(document.Frames.Count, Is.EqualTo(3));
|
|
Assert.That(document.SpriteSlots.Count, Is.EqualTo(2));
|
|
Assert.That(document.SpriteSlots[0].CanonicalFrameName, Is.EqualTo("FrameA"));
|
|
CollectionAssert.AreEqual(new[] { 0, 1 }, document.SpriteSlots[0].SourceIndices);
|
|
CollectionAssert.AreEqual(new[] { "FrameA", "FrameB" }, document.SpriteSlots[0].FrameNames);
|
|
Assert.That(document.SpriteSlotBySourceIndex[0], Is.SameAs(document.SpriteSlotBySourceIndex[1]));
|
|
Assert.That(document.SpriteSlotBySourceIndex[2], Is.Not.SameAs(document.SpriteSlotBySourceIndex[0]));
|
|
}
|
|
|
|
[TestCase("forward", new[] { 0, 1, 2, 3 })]
|
|
[TestCase("reverse", new[] { 3, 2, 1, 0 })]
|
|
[TestCase("pingpong", new[] { 0, 1, 2, 3, 2, 1 })]
|
|
[TestCase("pingpong_reverse", new[] { 3, 2, 1, 0, 1, 2 })]
|
|
public void TagExpansion_IsDeterministic(string direction, int[] expected)
|
|
{
|
|
var tag = new AsepriteSourceTag("测试", 0, 3, direction);
|
|
Assert.That(AsepriteJsonParser.TryExpandTag(tag, 4, "source", out var actual, out var issue),
|
|
Is.True, issue?.Message);
|
|
CollectionAssert.AreEqual(expected, actual);
|
|
}
|
|
|
|
[TestCase("pingpong")]
|
|
[TestCase("pingpong_reverse")]
|
|
public void TagExpansion_SingleFrameDoesNotDuplicateEndpoint(string direction)
|
|
{
|
|
var tag = new AsepriteSourceTag("单帧", 1, 1, direction);
|
|
Assert.That(AsepriteJsonParser.TryExpandTag(tag, 3, "source", out var actual, out _), Is.True);
|
|
CollectionAssert.AreEqual(new[] { 1 }, actual);
|
|
}
|
|
|
|
[Test]
|
|
public void WritableRefresh_CreatesAndUpdatesStableSubAssetsAndMissingState()
|
|
{
|
|
var graph = CreateWritableGraph(ObjectJson());
|
|
var source = graph.ImportSources.Single();
|
|
|
|
var firstPreview = FrameAnimationImportService.PreviewAll(graph);
|
|
Assert.That(firstPreview.HasErrors, Is.False, JoinIssues(firstPreview));
|
|
Assert.That(firstPreview.Sources.Single().SpriteDiffs.Count(diff =>
|
|
diff.Kind == FrameAnimationSpriteChangeKind.Added), Is.EqualTo(2));
|
|
Assert.That(FrameAnimationImportService.Apply(firstPreview, true, out var firstError), Is.True, firstError);
|
|
|
|
Assert.That(graph.Clips.Count, Is.EqualTo(2));
|
|
var idle = graph.Clips.Single(clip => clip.ImportInfo.SourceTagName == "待机");
|
|
Assert.That(AssetDatabase.IsSubAsset(idle), Is.True);
|
|
Assert.That(AssetDatabase.GetAssetPath(idle), Is.EqualTo(GraphPath));
|
|
Assert.That(idle.Frames[0].FrameName, Is.EqualTo("第二帧.png"));
|
|
Assert.That(source.LastSourceHash, Is.Not.Empty);
|
|
Assert.That(AssetDatabase.TryGetGUIDAndLocalFileIdentifier(idle, out _, out long firstLocalId), Is.True);
|
|
|
|
var idleSerialized = new SerializedObject(idle);
|
|
idleSerialized.FindProperty("id").stringValue = "用户重命名待机";
|
|
idleSerialized.FindProperty("displayName").stringValue = "自定义显示名";
|
|
idleSerialized.FindProperty("speed").floatValue = 1.75f;
|
|
idleSerialized.ApplyModifiedPropertiesWithoutUndo();
|
|
WriteJson(ObjectJson(duration: 180));
|
|
|
|
var updatePreview = FrameAnimationImportService.PreviewAll(graph);
|
|
Assert.That(updatePreview.HasErrors, Is.False, JoinIssues(updatePreview));
|
|
Assert.That(FrameAnimationImportService.Apply(updatePreview, true, out var updateError), Is.True, updateError);
|
|
Assert.That(AssetDatabase.TryGetGUIDAndLocalFileIdentifier(idle, out _, out long updatedLocalId), Is.True);
|
|
Assert.That(updatedLocalId, Is.EqualTo(firstLocalId));
|
|
Assert.That(idle.Id, Is.EqualTo("用户重命名待机"));
|
|
Assert.That(idle.DisplayName, Is.EqualTo("自定义显示名"));
|
|
Assert.That(idle.Speed, Is.EqualTo(1.75f));
|
|
Assert.That(idle.Frames[0].DurationMs, Is.EqualTo(180));
|
|
|
|
WriteJson(ObjectJson(includeIdle: false));
|
|
var missingPreview = FrameAnimationImportService.PreviewAll(graph);
|
|
Assert.That(FrameAnimationImportService.Apply(missingPreview, true, out var missingError), Is.True, missingError);
|
|
Assert.That(idle.ImportInfo.IsMissingFromSource, Is.True);
|
|
|
|
WriteJson(ObjectJson(duration: 220));
|
|
var restorePreview = FrameAnimationImportService.PreviewAll(graph);
|
|
Assert.That(FrameAnimationImportService.Apply(restorePreview, true, out var restoreError), Is.True, restoreError);
|
|
Assert.That(idle.ImportInfo.IsMissingFromSource, Is.False);
|
|
Assert.That(idle.Frames[0].DurationMs, Is.EqualTo(220));
|
|
Assert.That(AssetDatabase.TryGetGUIDAndLocalFileIdentifier(idle, out _, out long restoredLocalId), Is.True);
|
|
Assert.That(restoredLocalId, Is.EqualTo(firstLocalId));
|
|
}
|
|
|
|
[Test]
|
|
public void WritableRefresh_SharedRectsCreateOneSpriteAndPreserveLogicalFrames()
|
|
{
|
|
var graph = CreateWritableGraph(SharedRectJson());
|
|
var firstPreview = FrameAnimationImportService.PreviewAll(graph);
|
|
|
|
Assert.That(firstPreview.HasErrors, Is.False, JoinIssues(firstPreview));
|
|
var sourcePreview = firstPreview.Sources.Single();
|
|
Assert.That(sourcePreview.Document.Frames.Count, Is.EqualTo(3));
|
|
Assert.That(sourcePreview.Document.SpriteSlots.Count, Is.EqualTo(2));
|
|
Assert.That(sourcePreview.SpriteDiffs.Count(diff =>
|
|
diff.Kind == FrameAnimationSpriteChangeKind.Added), Is.EqualTo(2));
|
|
Assert.That(sourcePreview.SpriteDiffs.Single(diff => diff.SourceFrameCount == 2).FrameName,
|
|
Is.EqualTo("FrameA"));
|
|
Assert.That(FrameAnimationImportService.Apply(firstPreview, true, out var firstError),
|
|
Is.True, firstError);
|
|
|
|
var sprites = AssetDatabase.LoadAllAssetsAtPath(TexturePath).OfType<Sprite>().ToArray();
|
|
Assert.That(sprites.Length, Is.EqualTo(2));
|
|
var clip = graph.Clips.Single(item => item.ImportInfo.SourceTagName == "Shared");
|
|
Assert.That(clip.FrameCount, Is.EqualTo(3));
|
|
Assert.That(clip.Frames[0].Sprite, Is.SameAs(clip.Frames[1].Sprite));
|
|
Assert.That(clip.Frames[2].Sprite, Is.Not.SameAs(clip.Frames[0].Sprite));
|
|
CollectionAssert.AreEqual(new[] { "FrameA", "FrameB", "FrameC" },
|
|
clip.Frames.Select(frame => frame.FrameName));
|
|
CollectionAssert.AreEqual(new[] { 0, 1, 2 }, clip.Frames.Select(frame => frame.SourceIndex));
|
|
CollectionAssert.AreEqual(new[] { 100, 300, 80 }, clip.Frames.Select(frame => frame.DurationMs));
|
|
|
|
var firstIds = sprites.ToDictionary(sprite => sprite.name, GetLocalFileId);
|
|
var secondPreview = FrameAnimationImportService.PreviewAll(graph);
|
|
Assert.That(secondPreview.HasErrors, Is.False, JoinIssues(secondPreview));
|
|
Assert.That(secondPreview.Sources.Single().SpriteDiffs.All(diff =>
|
|
diff.Kind == FrameAnimationSpriteChangeKind.Unchanged), Is.True);
|
|
Assert.That(secondPreview.Sources.Single().ClipDiffs.All(diff =>
|
|
diff.Kind == FrameAnimationImportChangeKind.Unchanged), Is.True);
|
|
Assert.That(FrameAnimationImportService.Apply(secondPreview, true, out var secondError),
|
|
Is.True, secondError);
|
|
|
|
var secondIds = AssetDatabase.LoadAllAssetsAtPath(TexturePath).OfType<Sprite>()
|
|
.ToDictionary(sprite => sprite.name, GetLocalFileId);
|
|
CollectionAssert.AreEquivalent(firstIds, secondIds);
|
|
Assert.That(clip.Frames[0].Sprite, Is.SameAs(clip.Frames[1].Sprite));
|
|
}
|
|
|
|
[Test]
|
|
public void WritableRefresh_SharedSlotSplitAndMergeKeepDeterministicSpriteIdentity()
|
|
{
|
|
var graph = CreateWritableGraph(SharedPairJson(true));
|
|
var mergedPreview = FrameAnimationImportService.PreviewAll(graph);
|
|
Assert.That(FrameAnimationImportService.Apply(mergedPreview, true, out var mergedError),
|
|
Is.True, mergedError);
|
|
var canonical = AssetDatabase.LoadAllAssetsAtPath(TexturePath).OfType<Sprite>().Single();
|
|
var canonicalId = GetLocalFileId(canonical);
|
|
|
|
WriteJson(SharedPairJson(false));
|
|
var splitPreview = FrameAnimationImportService.PreviewAll(graph);
|
|
Assert.That(splitPreview.HasErrors, Is.False, JoinIssues(splitPreview));
|
|
Assert.That(splitPreview.Sources.Single().SpriteDiffs.Count(diff =>
|
|
diff.Kind == FrameAnimationSpriteChangeKind.Added), Is.EqualTo(1));
|
|
Assert.That(FrameAnimationImportService.Apply(splitPreview, true, out var splitError),
|
|
Is.True, splitError);
|
|
var splitSprites = AssetDatabase.LoadAllAssetsAtPath(TexturePath).OfType<Sprite>()
|
|
.ToDictionary(sprite => sprite.name);
|
|
Assert.That(splitSprites.Keys, Is.EquivalentTo(new[] { "FrameA", "FrameB" }));
|
|
Assert.That(GetLocalFileId(splitSprites["FrameA"]), Is.EqualTo(canonicalId));
|
|
var clip = graph.Clips.Single(item => item.ImportInfo.SourceTagName == "SharedPair");
|
|
Assert.That(clip.Frames[0].Sprite, Is.Not.SameAs(clip.Frames[1].Sprite));
|
|
|
|
WriteJson(SharedPairJson(true));
|
|
var mergedAgainPreview = FrameAnimationImportService.PreviewAll(graph);
|
|
Assert.That(mergedAgainPreview.HasErrors, Is.False, JoinIssues(mergedAgainPreview));
|
|
Assert.That(mergedAgainPreview.Sources.Single().SpriteDiffs.Any(diff =>
|
|
diff.Kind == FrameAnimationSpriteChangeKind.Retained && diff.FrameName == "FrameB"), Is.True);
|
|
Assert.That(FrameAnimationImportService.Apply(mergedAgainPreview, true, out var mergedAgainError),
|
|
Is.True, mergedAgainError);
|
|
Assert.That(GetLocalFileId(AssetDatabase.LoadAllAssetsAtPath(TexturePath).OfType<Sprite>()
|
|
.Single(sprite => sprite.name == "FrameA")), Is.EqualTo(canonicalId));
|
|
Assert.That(clip.Frames[0].Sprite, Is.SameAs(clip.Frames[1].Sprite));
|
|
}
|
|
|
|
[Test]
|
|
public void ReadOnlyPreview_MatchesExistingSpritesWithoutChangingImporter()
|
|
{
|
|
var writableGraph = CreateWritableGraph(ObjectJson());
|
|
var writablePreview = FrameAnimationImportService.PreviewAll(writableGraph);
|
|
Assert.That(FrameAnimationImportService.Apply(writablePreview, true, out var applyError), Is.True, applyError);
|
|
var importer = (TextureImporter)AssetImporter.GetAtPath(TexturePath);
|
|
var before = EditorJsonUtility.ToJson(importer);
|
|
|
|
var readOnlyGraph = ScriptableObject.CreateInstance<FrameAnimationGraph>();
|
|
readOnlyGraph.Configure("ReadOnly", "Read Only", Array.Empty<FrameClip>(),
|
|
Array.Empty<AnimationNode>(), Array.Empty<AnimationEdge>(), Array.Empty<AnimationFlow>(), string.Empty);
|
|
readOnlyGraph.AddImportSource(new FrameAnimationImportSource(
|
|
"Read Only", AssetDatabase.LoadAssetAtPath<Texture2D>(TexturePath),
|
|
AssetDatabase.LoadAssetAtPath<TextAsset>(JsonPath), new Vector2(0.5f, 0.5f), false));
|
|
AssetDatabase.CreateAsset(readOnlyGraph, TestRoot + "/ReadOnlyGraph.asset");
|
|
AssetDatabase.SaveAssets();
|
|
|
|
var preview = FrameAnimationImportService.PreviewAll(readOnlyGraph);
|
|
Assert.That(preview.HasErrors, Is.False, JoinIssues(preview));
|
|
Assert.That(preview.HasSpriteChanges, Is.False);
|
|
Assert.That(EditorJsonUtility.ToJson(importer), Is.EqualTo(before));
|
|
}
|
|
|
|
[Test]
|
|
public void ReadOnlyRefresh_MapsSharedSourceFramesToOneExistingSprite()
|
|
{
|
|
var writableGraph = CreateWritableGraph(SharedRectJson());
|
|
var writablePreview = FrameAnimationImportService.PreviewAll(writableGraph);
|
|
Assert.That(FrameAnimationImportService.Apply(writablePreview, true, out var applyError),
|
|
Is.True, applyError);
|
|
var importer = (TextureImporter)AssetImporter.GetAtPath(TexturePath);
|
|
var before = EditorJsonUtility.ToJson(importer);
|
|
|
|
var readOnlyGraph = ScriptableObject.CreateInstance<FrameAnimationGraph>();
|
|
readOnlyGraph.Configure("ReadOnlyShared", "Read Only Shared", Array.Empty<FrameClip>(),
|
|
Array.Empty<AnimationNode>(), Array.Empty<AnimationEdge>(), Array.Empty<AnimationFlow>(), string.Empty);
|
|
readOnlyGraph.AddImportSource(new FrameAnimationImportSource(
|
|
"Read Only Shared", AssetDatabase.LoadAssetAtPath<Texture2D>(TexturePath),
|
|
AssetDatabase.LoadAssetAtPath<TextAsset>(JsonPath), new Vector2(0.5f, 0.5f), false));
|
|
AssetDatabase.CreateAsset(readOnlyGraph, TestRoot + "/ReadOnlySharedGraph.asset");
|
|
AssetDatabase.SaveAssets();
|
|
|
|
var preview = FrameAnimationImportService.PreviewAll(readOnlyGraph);
|
|
Assert.That(preview.HasErrors, Is.False, JoinIssues(preview));
|
|
Assert.That(preview.HasSpriteChanges, Is.False);
|
|
Assert.That(preview.Sources.Single().SpritePlan.ReadOnlySpritesBySourceIndex[0],
|
|
Is.SameAs(preview.Sources.Single().SpritePlan.ReadOnlySpritesBySourceIndex[1]));
|
|
Assert.That(FrameAnimationImportService.Apply(preview, false, out var error), Is.True, error);
|
|
var clip = readOnlyGraph.Clips.Single(item => item.ImportInfo.SourceTagName == "Shared");
|
|
Assert.That(clip.Frames[0].Sprite, Is.SameAs(clip.Frames[1].Sprite));
|
|
Assert.That(EditorJsonUtility.ToJson(importer), Is.EqualTo(before));
|
|
|
|
var secondPreview = FrameAnimationImportService.PreviewAll(readOnlyGraph);
|
|
Assert.That(secondPreview.Sources.Single().ClipDiffs.All(diff =>
|
|
diff.Kind == FrameAnimationImportChangeKind.Unchanged), Is.True);
|
|
}
|
|
|
|
[Test]
|
|
public void ReadOnlyPreview_BlocksWhenSharedRectHasNoSprite()
|
|
{
|
|
CreateTexture();
|
|
WriteJson(SharedPairJson(true));
|
|
var graph = ScriptableObject.CreateInstance<FrameAnimationGraph>();
|
|
graph.Configure("MissingSprite", "Missing Sprite", Array.Empty<FrameClip>(),
|
|
Array.Empty<AnimationNode>(), Array.Empty<AnimationEdge>(), Array.Empty<AnimationFlow>(), string.Empty);
|
|
graph.AddImportSource(new FrameAnimationImportSource(
|
|
"Read Only", AssetDatabase.LoadAssetAtPath<Texture2D>(TexturePath),
|
|
AssetDatabase.LoadAssetAtPath<TextAsset>(JsonPath), new Vector2(0.5f, 0.5f), false));
|
|
AssetDatabase.CreateAsset(graph, TestRoot + "/MissingSpriteGraph.asset");
|
|
AssetDatabase.SaveAssets();
|
|
|
|
var preview = FrameAnimationImportService.PreviewAll(graph);
|
|
Assert.That(preview.HasErrors, Is.True);
|
|
Assert.That(preview.Sources.Single().Issues.Any(issue =>
|
|
issue.Code == FrameAnimationImportIssueCode.SpriteMatchFailed), Is.True);
|
|
Assert.That(FrameAnimationImportService.Apply(preview, false, out _), Is.False);
|
|
Assert.That(graph.Clips, Is.Empty);
|
|
}
|
|
|
|
[Test]
|
|
public void Preview_BlocksMultipleWritableOwnersWithoutMutation()
|
|
{
|
|
var graph = CreateWritableGraph(ObjectJson());
|
|
var source = graph.ImportSources.Single();
|
|
graph.AddImportSource(new FrameAnimationImportSource(
|
|
"Second Owner", source.Texture, source.AsepriteJson, source.Pivot, true));
|
|
EditorUtility.SetDirty(graph);
|
|
AssetDatabase.SaveAssets();
|
|
|
|
var importer = (TextureImporter)AssetImporter.GetAtPath(TexturePath);
|
|
var before = EditorJsonUtility.ToJson(importer);
|
|
var preview = FrameAnimationImportService.PreviewAll(graph);
|
|
Assert.That(preview.HasErrors, Is.True);
|
|
Assert.That(preview.Sources.SelectMany(item => item.Issues).Any(issue =>
|
|
issue.Code == FrameAnimationImportIssueCode.TextureOwnershipConflict), Is.True);
|
|
Assert.That(FrameAnimationImportService.Apply(preview, true, out _), Is.False);
|
|
Assert.That(EditorJsonUtility.ToJson(importer), Is.EqualTo(before));
|
|
Assert.That(graph.Clips, Is.Empty);
|
|
}
|
|
|
|
private static FrameAnimationGraph CreateWritableGraph(string json)
|
|
{
|
|
CreateTexture();
|
|
WriteJson(json);
|
|
var graph = ScriptableObject.CreateInstance<FrameAnimationGraph>();
|
|
graph.Configure("ImportGraph", "Import Graph", Array.Empty<FrameClip>(),
|
|
Array.Empty<AnimationNode>(), Array.Empty<AnimationEdge>(), Array.Empty<AnimationFlow>(), string.Empty);
|
|
graph.AddImportSource(new FrameAnimationImportSource(
|
|
"中文来源",
|
|
AssetDatabase.LoadAssetAtPath<Texture2D>(TexturePath),
|
|
AssetDatabase.LoadAssetAtPath<TextAsset>(JsonPath),
|
|
new Vector2(0.5f, 0.5f),
|
|
true));
|
|
AssetDatabase.CreateAsset(graph, GraphPath);
|
|
AssetDatabase.SaveAssets();
|
|
return graph;
|
|
}
|
|
|
|
private static void CreateTexture()
|
|
{
|
|
var texture = new Texture2D(4, 2, TextureFormat.RGBA32, false);
|
|
var pixels = Enumerable.Range(0, 8)
|
|
.Select(index => index < 4 ? Color.red : Color.green)
|
|
.ToArray();
|
|
texture.SetPixels(pixels);
|
|
texture.Apply();
|
|
File.WriteAllBytes(TexturePath, texture.EncodeToPNG());
|
|
UnityEngine.Object.DestroyImmediate(texture);
|
|
AssetDatabase.ImportAsset(TexturePath, ImportAssetOptions.ForceSynchronousImport);
|
|
}
|
|
|
|
private static void WriteJson(string json)
|
|
{
|
|
File.WriteAllText(JsonPath, json, new UTF8Encoding(false));
|
|
AssetDatabase.ImportAsset(JsonPath, ImportAssetOptions.ForceSynchronousImport);
|
|
}
|
|
|
|
private static string ObjectJson(int duration = 120, bool includeIdle = true)
|
|
{
|
|
var tags = includeIdle
|
|
? "{\"name\":\"待机\",\"from\":0,\"to\":0,\"direction\":\"forward\"}," +
|
|
"{\"name\":\"眨眼\",\"from\":0,\"to\":1,\"direction\":\"pingpong\"}"
|
|
: "{\"name\":\"眨眼\",\"from\":0,\"to\":1,\"direction\":\"pingpong\"}";
|
|
return "{" +
|
|
"\"frames\":{" +
|
|
$"\"第二帧.png\":{{\"frame\":{{\"x\":0,\"y\":0,\"w\":2,\"h\":2}},\"rotated\":false,\"trimmed\":false,\"duration\":{duration},\"spriteSourceSize\":{{\"x\":0,\"y\":0,\"w\":2,\"h\":2}},\"sourceSize\":{{\"w\":2,\"h\":2}}}}," +
|
|
"\"第一帧.png\":{\"frame\":{\"x\":2,\"y\":0,\"w\":2,\"h\":2},\"rotated\":false,\"trimmed\":false,\"duration\":80,\"spriteSourceSize\":{\"x\":0,\"y\":0,\"w\":2,\"h\":2},\"sourceSize\":{\"w\":2,\"h\":2}}}," +
|
|
$"\"meta\":{{\"image\":\"Atlas.png\",\"size\":{{\"w\":4,\"h\":2}},\"frameTags\":[{tags}]}}" +
|
|
"}";
|
|
}
|
|
|
|
private static string ArrayJson()
|
|
{
|
|
return "{" +
|
|
"\"frames\":[" +
|
|
"{\"filename\":\"第二帧.png\",\"frame\":{\"x\":0,\"y\":0,\"w\":2,\"h\":2},\"rotated\":false,\"trimmed\":false,\"duration\":120,\"spriteSourceSize\":{\"x\":0,\"y\":0,\"w\":2,\"h\":2},\"sourceSize\":{\"w\":2,\"h\":2}}," +
|
|
"{\"filename\":\"第一帧.png\",\"frame\":{\"x\":2,\"y\":0,\"w\":2,\"h\":2},\"rotated\":false,\"trimmed\":false,\"duration\":80,\"spriteSourceSize\":{\"x\":0,\"y\":0,\"w\":2,\"h\":2},\"sourceSize\":{\"w\":2,\"h\":2}}]," +
|
|
"\"meta\":{\"image\":\"Atlas.png\",\"size\":{\"w\":4,\"h\":2},\"frameTags\":[" +
|
|
"{\"name\":\"待机\",\"from\":0,\"to\":0,\"direction\":\"forward\"}," +
|
|
"{\"name\":\"眨眼\",\"from\":0,\"to\":1,\"direction\":\"pingpong\"}]}}";
|
|
}
|
|
|
|
private static string SharedRectJson()
|
|
{
|
|
return "{" +
|
|
"\"frames\":{" +
|
|
"\"FrameA\":{\"frame\":{\"x\":0,\"y\":0,\"w\":2,\"h\":2},\"rotated\":false,\"trimmed\":false,\"duration\":100,\"spriteSourceSize\":{\"x\":0,\"y\":0,\"w\":2,\"h\":2},\"sourceSize\":{\"w\":2,\"h\":2}}," +
|
|
"\"FrameB\":{\"frame\":{\"x\":0,\"y\":0,\"w\":2,\"h\":2},\"rotated\":false,\"trimmed\":false,\"duration\":300,\"spriteSourceSize\":{\"x\":0,\"y\":0,\"w\":2,\"h\":2},\"sourceSize\":{\"w\":2,\"h\":2}}," +
|
|
"\"FrameC\":{\"frame\":{\"x\":2,\"y\":0,\"w\":2,\"h\":2},\"rotated\":false,\"trimmed\":false,\"duration\":80,\"spriteSourceSize\":{\"x\":0,\"y\":0,\"w\":2,\"h\":2},\"sourceSize\":{\"w\":2,\"h\":2}}}," +
|
|
"\"meta\":{\"image\":\"Atlas.png\",\"size\":{\"w\":4,\"h\":2},\"frameTags\":[" +
|
|
"{\"name\":\"Shared\",\"from\":0,\"to\":2,\"direction\":\"forward\"}]}}";
|
|
}
|
|
|
|
private static string SharedPairJson(bool merged)
|
|
{
|
|
var secondX = merged ? 0 : 2;
|
|
return "{" +
|
|
"\"frames\":{" +
|
|
"\"FrameA\":{\"frame\":{\"x\":0,\"y\":0,\"w\":2,\"h\":2},\"rotated\":false,\"trimmed\":false,\"duration\":100,\"spriteSourceSize\":{\"x\":0,\"y\":0,\"w\":2,\"h\":2},\"sourceSize\":{\"w\":2,\"h\":2}}," +
|
|
$"\"FrameB\":{{\"frame\":{{\"x\":{secondX},\"y\":0,\"w\":2,\"h\":2}},\"rotated\":false,\"trimmed\":false,\"duration\":300,\"spriteSourceSize\":{{\"x\":0,\"y\":0,\"w\":2,\"h\":2}},\"sourceSize\":{{\"w\":2,\"h\":2}}}}}}," +
|
|
"\"meta\":{\"image\":\"Atlas.png\",\"size\":{\"w\":4,\"h\":2},\"frameTags\":[" +
|
|
"{\"name\":\"SharedPair\",\"from\":0,\"to\":1,\"direction\":\"forward\"}]}}";
|
|
}
|
|
|
|
private static long GetLocalFileId(Sprite sprite)
|
|
{
|
|
Assert.That(AssetDatabase.TryGetGUIDAndLocalFileIdentifier(sprite, out _, out long localId), Is.True);
|
|
return localId;
|
|
}
|
|
|
|
private static string JoinIssues(FrameAnimationImportPreview preview)
|
|
{
|
|
return string.Join("\n", preview.Sources.SelectMany(source => source.Issues).Select(issue => issue.Message));
|
|
}
|
|
|
|
private static void EnsureFolder(string path)
|
|
{
|
|
var segments = path.Split('/');
|
|
var current = segments[0];
|
|
for (var index = 1; index < segments.Length; index++)
|
|
{
|
|
var next = current + "/" + segments[index];
|
|
if (!AssetDatabase.IsValidFolder(next))
|
|
{
|
|
AssetDatabase.CreateFolder(current, segments[index]);
|
|
}
|
|
current = next;
|
|
}
|
|
}
|
|
}
|
|
}
|