feat: 帧动画系统Timeline兼容
This commit is contained in:
+4
-1
@@ -3,7 +3,10 @@
|
||||
"rootNamespace": "AibisDream.FrameAnimation.Tests.EditMode",
|
||||
"references": [
|
||||
"AibisDream.FrameAnimation.Runtime",
|
||||
"AibisDream.FrameAnimation.Editor"
|
||||
"AibisDream.FrameAnimation.Editor",
|
||||
"AibisDream.FrameAnimation.Timeline",
|
||||
"Unity.Timeline",
|
||||
"UnityEngine.UI"
|
||||
],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
|
||||
@@ -0,0 +1,307 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using AibisDream.FrameAnimation.Timeline;
|
||||
using NUnit.Framework;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Playables;
|
||||
using UnityEngine.TestTools;
|
||||
using UnityEngine.Timeline;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace AibisDream.FrameAnimation.Tests.EditMode
|
||||
{
|
||||
public sealed class FrameAnimationTimelineTests
|
||||
{
|
||||
private readonly List<UnityEngine.Object> createdObjects = new List<UnityEngine.Object>();
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
for (var index = createdObjects.Count - 1; index >= 0; index--)
|
||||
{
|
||||
if (createdObjects[index] != null)
|
||||
{
|
||||
UnityEngine.Object.DestroyImmediate(createdObjects[index]);
|
||||
}
|
||||
}
|
||||
createdObjects.Clear();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TimelineClip_ReportsDurationAndSupportedCapabilities()
|
||||
{
|
||||
var holdClip = CreateClip("Hold", FrameClipEndBehavior.HoldLastFrame, 2f, null, null);
|
||||
var loopClip = CreateClip("Loop", FrameClipEndBehavior.Loop, 1f, null, null);
|
||||
var asset = Track(ScriptableObject.CreateInstance<FrameAnimationTimelineClip>());
|
||||
|
||||
asset.SetFrameClipForTests(holdClip);
|
||||
Assert.That(asset.duration, Is.EqualTo(0.1d).Within(0.000001d));
|
||||
Assert.That(asset.TrySynchronizeSourceDuration(out var synchronizedDuration), Is.True);
|
||||
Assert.That(synchronizedDuration, Is.EqualTo(0.1d).Within(0.000001d));
|
||||
Assert.That(asset.TrySynchronizeSourceDuration(out _), Is.False);
|
||||
Assert.That(asset.clipCaps.HasFlag(ClipCaps.ClipIn), Is.True);
|
||||
Assert.That(asset.clipCaps.HasFlag(ClipCaps.SpeedMultiplier), Is.True);
|
||||
Assert.That(asset.clipCaps.HasFlag(ClipCaps.Blending), Is.False);
|
||||
Assert.That(asset.clipCaps.HasFlag(ClipCaps.Looping), Is.False);
|
||||
|
||||
asset.SetFrameClipForTests(loopClip);
|
||||
Assert.That(asset.TrySynchronizeSourceDuration(out _), Is.True);
|
||||
Assert.That(asset.clipCaps.HasFlag(ClipCaps.Looping), Is.True);
|
||||
|
||||
asset.SetFrameClipForTests(null);
|
||||
Assert.That(asset.duration, Is.EqualTo(1d));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Track_ScrubsBothDirectionsAndHonorsClipInAndSpeedMultiplier()
|
||||
{
|
||||
var first = CreateSprite(Color.red);
|
||||
var second = CreateSprite(Color.blue);
|
||||
var clip = CreateClip("Scrub", FrameClipEndBehavior.HoldLastFrame, 1f, first, second);
|
||||
var playerObject = Track(new GameObject("Timeline Sprite Player"));
|
||||
var renderer = playerObject.AddComponent<SpriteRenderer>();
|
||||
var player = playerObject.AddComponent<FrameClipPlayer>();
|
||||
var setup = CreateTimeline(player, clip, 0d, 0.2d);
|
||||
setup.Clip.clipIn = 0.05d;
|
||||
setup.Clip.timeScale = 2d;
|
||||
|
||||
Evaluate(setup.Director, 0.03d);
|
||||
Assert.That(renderer.sprite, Is.SameAs(second));
|
||||
|
||||
Evaluate(setup.Director, 0d);
|
||||
Assert.That(renderer.sprite, Is.SameAs(first));
|
||||
|
||||
Evaluate(setup.Director, 0.03d);
|
||||
Assert.That(renderer.sprite, Is.SameAs(second));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Track_GapUsesPreviousTerminalStateAndOverlapPrefersLaterClip()
|
||||
{
|
||||
var baseline = CreateSprite(Color.white);
|
||||
var firstSprite = CreateSprite(Color.red);
|
||||
var secondSprite = CreateSprite(Color.blue);
|
||||
var hold = CreateClip("Hold", FrameClipEndBehavior.HoldLastFrame, 1f, firstSprite);
|
||||
var later = CreateClip("Later", FrameClipEndBehavior.HoldLastFrame, 1f, secondSprite);
|
||||
var playerObject = Track(new GameObject("Timeline Overlap Player"));
|
||||
var renderer = playerObject.AddComponent<SpriteRenderer>();
|
||||
renderer.sprite = baseline;
|
||||
var player = playerObject.AddComponent<FrameClipPlayer>();
|
||||
var setup = CreateTimeline(player, hold, 0.1d, 0.3d);
|
||||
AddClip(setup.Track, later, 0.2d, 0.1d);
|
||||
|
||||
Evaluate(setup.Director, 0.25d);
|
||||
Assert.That(renderer.sprite, Is.SameAs(secondSprite));
|
||||
|
||||
Evaluate(setup.Director, 0.35d);
|
||||
Assert.That(renderer.sprite, Is.SameAs(firstSprite));
|
||||
|
||||
Evaluate(setup.Director, 0.45d);
|
||||
Assert.That(renderer.sprite, Is.SameAs(firstSprite));
|
||||
|
||||
Evaluate(setup.Director, -0.01d);
|
||||
Assert.That(renderer.sprite, Is.SameAs(baseline));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Track_AppliesClearAndHideTerminalStates()
|
||||
{
|
||||
var sprite = CreateSprite(Color.green);
|
||||
var clear = CreateClip("Clear", FrameClipEndBehavior.Clear, 1f, sprite);
|
||||
var hide = CreateClip("Hide", FrameClipEndBehavior.HideTarget, 1f, sprite);
|
||||
var playerObject = Track(new GameObject("Timeline End States"));
|
||||
var renderer = playerObject.AddComponent<SpriteRenderer>();
|
||||
var player = playerObject.AddComponent<FrameClipPlayer>();
|
||||
var setup = CreateTimeline(player, clear, 0d, 0.1d);
|
||||
AddClip(setup.Track, hide, 0.2d, 0.1d);
|
||||
|
||||
Evaluate(setup.Director, 0.15d);
|
||||
Assert.That(renderer.enabled, Is.True);
|
||||
Assert.That(renderer.sprite, Is.Null);
|
||||
|
||||
Evaluate(setup.Director, 0.35d);
|
||||
Assert.That(renderer.enabled, Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Track_SamplesImageAndZeroSpeedClipStaysOnFirstFrame()
|
||||
{
|
||||
var first = CreateSprite(Color.yellow);
|
||||
var second = CreateSprite(Color.black);
|
||||
var clip = CreateClip("Zero", FrameClipEndBehavior.HoldLastFrame, 0f, first, second);
|
||||
var playerObject = Track(new GameObject(
|
||||
"Timeline Image Player",
|
||||
typeof(RectTransform),
|
||||
typeof(CanvasRenderer)));
|
||||
var image = playerObject.AddComponent<Image>();
|
||||
var player = playerObject.AddComponent<FrameClipPlayer>();
|
||||
var setup = CreateTimeline(player, clip, 0d, 1d);
|
||||
|
||||
Evaluate(setup.Director, 0.75d);
|
||||
|
||||
Assert.That(image.sprite, Is.SameAs(first));
|
||||
Assert.That(setup.Asset.duration, Is.EqualTo(1d));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Track_InvalidClipLogsStructuredErrorWithoutThrowing()
|
||||
{
|
||||
var invalid = CreateClip("Empty", FrameClipEndBehavior.HoldLastFrame, 1f);
|
||||
var playerObject = Track(new GameObject("Timeline Invalid Clip"));
|
||||
playerObject.AddComponent<SpriteRenderer>();
|
||||
var player = playerObject.AddComponent<FrameClipPlayer>();
|
||||
var setup = CreateTimeline(player, invalid, 0d, 1d);
|
||||
LogAssert.Expect(
|
||||
LogType.Error,
|
||||
new Regex("Frame Animation Timeline 播放失败.*InvalidPlayableData"));
|
||||
|
||||
Assert.DoesNotThrow(() => Evaluate(setup.Director, 0.25d));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Track_GathersSpriteAndEnabledPropertiesForPreview()
|
||||
{
|
||||
var clip = CreateClip(
|
||||
"Preview",
|
||||
FrameClipEndBehavior.HoldLastFrame,
|
||||
1f,
|
||||
CreateSprite(Color.white));
|
||||
var spriteObject = Track(new GameObject("Timeline Sprite Preview"));
|
||||
spriteObject.AddComponent<SpriteRenderer>();
|
||||
var spritePlayer = spriteObject.AddComponent<FrameClipPlayer>();
|
||||
var spriteSetup = CreateTimeline(spritePlayer, clip, 0d, 0.1d);
|
||||
var spriteCollector = new RecordingPropertyCollector();
|
||||
|
||||
spriteSetup.Track.GatherProperties(spriteSetup.Director, spriteCollector);
|
||||
|
||||
Assert.That(spriteCollector.Properties, Does.Contain("SpriteRenderer:m_Sprite"));
|
||||
Assert.That(spriteCollector.Properties, Does.Contain("SpriteRenderer:m_Enabled"));
|
||||
|
||||
var imageObject = Track(new GameObject(
|
||||
"Timeline Image Preview",
|
||||
typeof(RectTransform),
|
||||
typeof(CanvasRenderer)));
|
||||
imageObject.AddComponent<Image>();
|
||||
var imagePlayer = imageObject.AddComponent<FrameClipPlayer>();
|
||||
var imageSetup = CreateTimeline(imagePlayer, clip, 0d, 0.1d);
|
||||
var imageCollector = new RecordingPropertyCollector();
|
||||
|
||||
imageSetup.Track.GatherProperties(imageSetup.Director, imageCollector);
|
||||
|
||||
Assert.That(imageCollector.Properties, Does.Contain("Image:m_Sprite"));
|
||||
Assert.That(imageCollector.Properties, Does.Contain("Image:m_Enabled"));
|
||||
}
|
||||
|
||||
private TimelineSetup CreateTimeline(
|
||||
FrameClipPlayer player,
|
||||
FrameClip clip,
|
||||
double start,
|
||||
double duration)
|
||||
{
|
||||
var timeline = Track(ScriptableObject.CreateInstance<TimelineAsset>());
|
||||
var track = Track(timeline.CreateTrack<FrameAnimationTrack>());
|
||||
var timelineClip = AddClip(track, clip, start, duration);
|
||||
var directorObject = Track(new GameObject("Frame Animation Director"));
|
||||
var director = directorObject.AddComponent<PlayableDirector>();
|
||||
director.playableAsset = timeline;
|
||||
director.SetGenericBinding(track, player);
|
||||
return new TimelineSetup(
|
||||
director,
|
||||
track,
|
||||
timelineClip,
|
||||
(FrameAnimationTimelineClip)timelineClip.asset);
|
||||
}
|
||||
|
||||
private TimelineClip AddClip(
|
||||
FrameAnimationTrack track,
|
||||
FrameClip clip,
|
||||
double start,
|
||||
double duration)
|
||||
{
|
||||
var timelineClip = track.CreateClip<FrameAnimationTimelineClip>();
|
||||
var asset = Track((FrameAnimationTimelineClip)timelineClip.asset);
|
||||
asset.SetFrameClipForTests(clip);
|
||||
timelineClip.start = start;
|
||||
timelineClip.duration = duration;
|
||||
return timelineClip;
|
||||
}
|
||||
|
||||
private static void Evaluate(PlayableDirector director, double time)
|
||||
{
|
||||
director.time = Math.Max(0d, time);
|
||||
director.Evaluate();
|
||||
}
|
||||
|
||||
private FrameClip CreateClip(
|
||||
string id,
|
||||
FrameClipEndBehavior endBehavior,
|
||||
float speed,
|
||||
params Sprite[] sprites)
|
||||
{
|
||||
var clip = Track(ScriptableObject.CreateInstance<FrameClip>());
|
||||
var frames = new List<FrameAnimationFrame>();
|
||||
for (var index = 0; index < sprites.Length; index++)
|
||||
{
|
||||
frames.Add(new FrameAnimationFrame(sprites[index], 100, $"frame_{index}", index));
|
||||
}
|
||||
clip.Configure(id, id, frames, speed, endBehavior);
|
||||
return clip;
|
||||
}
|
||||
|
||||
private Sprite CreateSprite(Color color)
|
||||
{
|
||||
var texture = Track(new Texture2D(2, 2));
|
||||
texture.SetPixels(new[] { color, color, color, color });
|
||||
texture.Apply();
|
||||
return Track(Sprite.Create(texture, new Rect(0, 0, 2, 2), Vector2.one * 0.5f));
|
||||
}
|
||||
|
||||
private T Track<T>(T value) where T : UnityEngine.Object
|
||||
{
|
||||
createdObjects.Add(value);
|
||||
return value;
|
||||
}
|
||||
|
||||
private readonly struct TimelineSetup
|
||||
{
|
||||
internal PlayableDirector Director { get; }
|
||||
internal FrameAnimationTrack Track { get; }
|
||||
internal TimelineClip Clip { get; }
|
||||
internal FrameAnimationTimelineClip Asset { get; }
|
||||
|
||||
internal TimelineSetup(
|
||||
PlayableDirector director,
|
||||
FrameAnimationTrack track,
|
||||
TimelineClip clip,
|
||||
FrameAnimationTimelineClip asset)
|
||||
{
|
||||
Director = director;
|
||||
Track = track;
|
||||
Clip = clip;
|
||||
Asset = asset;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class RecordingPropertyCollector : IPropertyCollector
|
||||
{
|
||||
internal List<string> Properties { get; } = new List<string>();
|
||||
|
||||
public void PushActiveGameObject(GameObject gameObject) { }
|
||||
public void PopActiveGameObject() { }
|
||||
public void AddFromClip(AnimationClip clip) { }
|
||||
public void AddFromClips(IEnumerable<AnimationClip> clips) { }
|
||||
public void AddFromName<T>(string name) where T : Component =>
|
||||
Properties.Add($"{typeof(T).Name}:{name}");
|
||||
public void AddFromName(string name) { }
|
||||
public void AddFromClip(GameObject obj, AnimationClip clip) { }
|
||||
public void AddFromClips(GameObject obj, IEnumerable<AnimationClip> clips) { }
|
||||
public void AddFromName<T>(GameObject obj, string name) where T : Component =>
|
||||
Properties.Add($"{typeof(T).Name}:{name}");
|
||||
public void AddFromName(GameObject obj, string name) { }
|
||||
public void AddFromName(Component component, string name) { }
|
||||
public void AddFromComponent(GameObject obj, Component component) { }
|
||||
public void AddObjectProperties(UnityEngine.Object obj, AnimationClip clip) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0886b53af79f4406ba432ad67cd822be
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,196 @@
|
||||
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 FrameClipStandaloneImportTests
|
||||
{
|
||||
private const string Root = "Assets/Tests/FrameAnimation/GeneratedStandaloneClipTests";
|
||||
private const string TexturePath = Root + "/Atlas.png";
|
||||
private const string JsonPath = Root + "/Atlas.json";
|
||||
private const string ClipPath = Root + "/Standalone.asset";
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
AssetDatabase.DeleteAsset(Root);
|
||||
EnsureFolder(Root);
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
AssetDatabase.DeleteAsset(Root);
|
||||
AssetDatabase.Refresh();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Preview_UsesOnlyFirstTagAndRespectsDirection()
|
||||
{
|
||||
var clip = CreateSourceClip(string.Empty, JsonWithTags("reverse"));
|
||||
|
||||
var preview = FrameClipImportService.Preview(clip);
|
||||
|
||||
Assert.That(preview.HasErrors, Is.False, JoinIssues(preview));
|
||||
Assert.That(preview.SourceTagName, Is.EqualTo("First"));
|
||||
CollectionAssert.AreEqual(new[] { 1, 0 }, preview.SourceIndices);
|
||||
Assert.That(preview.SpriteDiffs.Count(diff => diff.Kind == FrameAnimationSpriteChangeKind.Added),
|
||||
Is.EqualTo(2));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Preview_WithoutTagsUsesAllFrames()
|
||||
{
|
||||
var clip = CreateSourceClip("Existing", JsonWithoutTags());
|
||||
|
||||
var preview = FrameClipImportService.Preview(clip);
|
||||
|
||||
Assert.That(preview.HasErrors, Is.False, JoinIssues(preview));
|
||||
Assert.That(preview.SourceTagName, Is.Empty);
|
||||
CollectionAssert.AreEqual(new[] { 0, 1 }, preview.SourceIndices);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Apply_InitializesEmptyIdAndRefreshPreservesExistingId()
|
||||
{
|
||||
var clip = CreateSourceClip(string.Empty, JsonWithTags("forward"));
|
||||
var first = FrameClipImportService.Preview(clip);
|
||||
Assert.That(FrameClipImportService.Apply(first, true, out var error), Is.True, error);
|
||||
|
||||
Assert.That(clip.Id, Is.EqualTo("First"));
|
||||
Assert.That(clip.DisplayName, Is.EqualTo("First"));
|
||||
Assert.That(clip.FrameCount, Is.EqualTo(2));
|
||||
Assert.That(clip.StandaloneImportSource.LastImportedTagName, Is.EqualTo("First"));
|
||||
|
||||
var serialized = new SerializedObject(clip);
|
||||
serialized.FindProperty("id").stringValue = "CustomId";
|
||||
serialized.ApplyModifiedPropertiesWithoutUndo();
|
||||
WriteJson(JsonWithTags("reverse"));
|
||||
var second = FrameClipImportService.Preview(clip);
|
||||
Assert.That(FrameClipImportService.Apply(second, true, out error), Is.True, error);
|
||||
Assert.That(clip.Id, Is.EqualTo("CustomId"));
|
||||
CollectionAssert.AreEqual(new[] { 1, 0 }, clip.Frames.Select(frame => frame.SourceIndex));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Detach_PreservesFramesAndAllowsGraphReference()
|
||||
{
|
||||
var clip = CreateSourceClip("External", JsonWithoutTags());
|
||||
var preview = FrameClipImportService.Preview(clip);
|
||||
Assert.That(FrameClipImportService.Apply(preview, true, out var error), Is.True, error);
|
||||
var frames = clip.Frames.ToArray();
|
||||
|
||||
var graph = ScriptableObject.CreateInstance<FrameAnimationGraph>();
|
||||
graph.Configure("Graph", "Graph", Array.Empty<FrameClip>(), Array.Empty<AnimationNode>(),
|
||||
Array.Empty<AnimationEdge>(), Array.Empty<AnimationFlow>(), clip.Id);
|
||||
AssetDatabase.CreateAsset(graph, Root + "/Graph.asset");
|
||||
Assert.That(FrameAnimationAssetOperations.AddExistingManualClip(graph, clip, out error), Is.True, error);
|
||||
Assert.That(FrameAnimationGraphValidator.Validate(graph).HasErrors, Is.False);
|
||||
|
||||
FrameClipImportService.DetachSource(clip);
|
||||
|
||||
Assert.That(clip.HasStandaloneImportSource, Is.False);
|
||||
CollectionAssert.AreEqual(frames, clip.Frames);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Preview_RejectsSecondWritableStandaloneOwnerForSameTexture()
|
||||
{
|
||||
CreateTexture();
|
||||
WriteJson(JsonWithoutTags());
|
||||
var first = CreateLinkedClipAt(ClipPath, "FirstOwner");
|
||||
var second = CreateLinkedClipAt(Root + "/Second.asset", "SecondOwner");
|
||||
|
||||
var preview = FrameClipImportService.Preview(second);
|
||||
|
||||
Assert.That(first.StandaloneImportSource.Texture, Is.SameAs(second.StandaloneImportSource.Texture));
|
||||
Assert.That(preview.HasErrors, Is.True);
|
||||
Assert.That(preview.Issues.Any(issue =>
|
||||
issue.Code == FrameAnimationImportIssueCode.TextureOwnershipConflict), Is.True);
|
||||
}
|
||||
|
||||
private static FrameClip CreateSourceClip(string id, string json)
|
||||
{
|
||||
CreateTexture();
|
||||
WriteJson(json);
|
||||
return CreateLinkedClipAt(ClipPath, id);
|
||||
}
|
||||
|
||||
private static FrameClip CreateLinkedClipAt(string path, string id)
|
||||
{
|
||||
var clip = ScriptableObject.CreateInstance<FrameClip>();
|
||||
clip.Configure(id, string.Empty, Array.Empty<FrameAnimationFrame>());
|
||||
AssetDatabase.CreateAsset(clip, path);
|
||||
Assert.That(FrameClipImportService.EnableSource(clip, out var error), Is.True, error);
|
||||
var serialized = new SerializedObject(clip);
|
||||
var source = serialized.FindProperty("standaloneImportSource");
|
||||
source.FindPropertyRelative("texture").objectReferenceValue =
|
||||
AssetDatabase.LoadAssetAtPath<Texture2D>(TexturePath);
|
||||
source.FindPropertyRelative("asepriteJson").objectReferenceValue =
|
||||
AssetDatabase.LoadAssetAtPath<TextAsset>(JsonPath);
|
||||
source.FindPropertyRelative("manageSpriteSlicing").boolValue = true;
|
||||
serialized.ApplyModifiedPropertiesWithoutUndo();
|
||||
AssetDatabase.SaveAssets();
|
||||
return clip;
|
||||
}
|
||||
|
||||
private static void CreateTexture()
|
||||
{
|
||||
var texture = new Texture2D(4, 2, TextureFormat.RGBA32, false);
|
||||
texture.SetPixels(Enumerable.Range(0, 8).Select(index => index < 4 ? Color.red : Color.green).ToArray());
|
||||
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 JsonWithTags(string direction)
|
||||
{
|
||||
return Json("[{\"name\":\"First\",\"from\":0,\"to\":1,\"direction\":\"" + direction +
|
||||
"\"},{\"name\":\"Ignored\",\"from\":1,\"to\":1,\"direction\":\"invalid\"}]");
|
||||
}
|
||||
|
||||
private static string JsonWithoutTags()
|
||||
{
|
||||
return Json("[]");
|
||||
}
|
||||
|
||||
private static string Json(string tags)
|
||||
{
|
||||
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\":2,\"y\":0,\"w\":2,\"h\":2},\"rotated\":false,\"trimmed\":false,\"duration\":200,\"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 JoinIssues(FrameClipImportPreview preview)
|
||||
{
|
||||
return string.Join("\n", preview.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fd9f1e89abcc595428c137e42258e008
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user