764 lines
34 KiB
C#
764 lines
34 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using NUnit.Framework;
|
|
using UnityEngine;
|
|
using UnityEngine.TestTools;
|
|
using UnityEngine.UI;
|
|
|
|
namespace AibisDream.FrameAnimation.Tests.PlayMode
|
|
{
|
|
public sealed class FrameAnimationPlayerTests
|
|
{
|
|
private readonly List<UnityEngine.Object> createdObjects = new List<UnityEngine.Object>();
|
|
|
|
[TearDown]
|
|
public void TearDown()
|
|
{
|
|
foreach (var createdObject in createdObjects)
|
|
{
|
|
if (createdObject != null)
|
|
{
|
|
UnityEngine.Object.DestroyImmediate(createdObject);
|
|
}
|
|
}
|
|
createdObjects.Clear();
|
|
}
|
|
|
|
[Test]
|
|
public void SpriteRenderer_PlayerShowsFirstFrameAndCompletesWithClear()
|
|
{
|
|
var first = CreateSprite(Color.red);
|
|
var second = CreateSprite(Color.blue);
|
|
var clip = CreateClip("Clip", FrameClipEndBehavior.Clear, first, second);
|
|
var graph = CreateDirectGraph(clip);
|
|
var gameObject = Track(new GameObject("Sprite Player"));
|
|
var renderer = gameObject.AddComponent<SpriteRenderer>();
|
|
var player = gameObject.AddComponent<FrameAnimationPlayer>();
|
|
player.ConfigureForAuthoring(graph, false, 1f);
|
|
|
|
var handle = player.Play(clip.Id);
|
|
Assert.That(renderer.sprite, Is.EqualTo(first));
|
|
player.EvaluateForTests(0.2d);
|
|
|
|
Assert.That(handle.IsCompleted, Is.True);
|
|
Assert.That(handle.Result.Reason, Is.EqualTo(FrameAnimationCompletionReason.Completed));
|
|
Assert.That(renderer.sprite, Is.Null);
|
|
Assert.That(renderer.enabled, Is.True);
|
|
}
|
|
|
|
[Test]
|
|
public void FrameClipPlayer_PlaysDefaultClipWithoutGraph()
|
|
{
|
|
var first = CreateSprite(Color.magenta);
|
|
var second = CreateSprite(Color.cyan);
|
|
var clip = CreateClip("Standalone", FrameClipEndBehavior.HoldLastFrame, first, second);
|
|
var gameObject = Track(new GameObject("Standalone Clip Player"));
|
|
var renderer = gameObject.AddComponent<SpriteRenderer>();
|
|
var player = gameObject.AddComponent<FrameClipPlayer>();
|
|
player.ConfigureForAuthoring(clip, false, 1f);
|
|
|
|
var handle = player.Play();
|
|
|
|
Assert.That(renderer.sprite, Is.SameAs(first));
|
|
Assert.That(player.Clip, Is.SameAs(clip));
|
|
Assert.That(player.CurrentClip, Is.SameAs(clip));
|
|
player.EvaluateForTests(0.2d);
|
|
Assert.That(handle.Result.Reason, Is.EqualTo(FrameAnimationCompletionReason.Completed));
|
|
Assert.That(renderer.sprite, Is.SameAs(second));
|
|
}
|
|
|
|
[Test]
|
|
public void FrameClipPlayer_TemporaryClipDoesNotReplaceDefaultAndFailedPlayKeepsActive()
|
|
{
|
|
var defaultClip = CreateClip("Default", FrameClipEndBehavior.Loop, CreateSprite(Color.red));
|
|
var temporary = CreateClip("Temporary", FrameClipEndBehavior.Loop, CreateSprite(Color.blue));
|
|
var invalid = Track(ScriptableObject.CreateInstance<FrameClip>());
|
|
var gameObject = Track(new GameObject("Temporary Clip Player"));
|
|
gameObject.AddComponent<SpriteRenderer>();
|
|
var player = gameObject.AddComponent<FrameClipPlayer>();
|
|
player.ConfigureForAuthoring(defaultClip, false, 1f);
|
|
|
|
var active = player.Play(temporary);
|
|
var failed = player.Play(invalid);
|
|
|
|
Assert.That(failed.Result.Reason, Is.EqualTo(FrameAnimationCompletionReason.Failed));
|
|
Assert.That(active.IsCompleted, Is.False);
|
|
Assert.That(player.Clip, Is.SameAs(defaultClip));
|
|
Assert.That(player.CurrentClip, Is.SameAs(temporary));
|
|
}
|
|
|
|
[Test]
|
|
public void FrameClipPlayer_RestoreTerminalStateAppliesLastFrame()
|
|
{
|
|
var first = CreateSprite(Color.red);
|
|
var last = CreateSprite(Color.green);
|
|
var clip = CreateClip("RestoreStandalone", FrameClipEndBehavior.HoldLastFrame, first, last);
|
|
var gameObject = Track(new GameObject("Restore Standalone"));
|
|
var renderer = gameObject.AddComponent<SpriteRenderer>();
|
|
var player = gameObject.AddComponent<FrameClipPlayer>();
|
|
player.ConfigureForAuthoring(clip, false, 1f);
|
|
|
|
var handle = player.RestoreTerminalState();
|
|
|
|
Assert.That(handle.Result.Reason, Is.EqualTo(FrameAnimationCompletionReason.Completed));
|
|
Assert.That(renderer.sprite, Is.SameAs(last));
|
|
}
|
|
|
|
[Test]
|
|
public void FrameClipPlayer_ImageLoopWaitsForFirstPassAndKeepsPlaying()
|
|
{
|
|
var first = CreateSprite(Color.yellow);
|
|
var second = CreateSprite(Color.black);
|
|
var clip = CreateClip("StandaloneLoop", FrameClipEndBehavior.Loop, first, second);
|
|
var gameObject = Track(new GameObject("Standalone Image", typeof(RectTransform), typeof(CanvasRenderer)));
|
|
var image = gameObject.AddComponent<Image>();
|
|
var player = gameObject.AddComponent<FrameClipPlayer>();
|
|
player.ConfigureForAuthoring(clip, false, 1f);
|
|
|
|
var handle = player.Play();
|
|
var wait = player.WaitForFirstPass(handle);
|
|
player.EvaluateForTests(0.2d);
|
|
|
|
Assert.That(wait.keepWaiting, Is.False);
|
|
Assert.That(handle.IsCompleted, Is.False);
|
|
Assert.That(player.State, Is.EqualTo(FrameAnimationPlaybackState.Playing));
|
|
Assert.That(image.sprite, Is.SameAs(first));
|
|
}
|
|
|
|
[Test]
|
|
public void GraphAndClipPlayersOnSameObjectReturnConflict()
|
|
{
|
|
var clip = CreateClip("ConflictClip", FrameClipEndBehavior.Loop, CreateSprite(Color.white));
|
|
var graph = CreateDirectGraph(clip);
|
|
var gameObject = Track(new GameObject("Player Conflict"));
|
|
gameObject.AddComponent<SpriteRenderer>();
|
|
var graphPlayer = gameObject.AddComponent<FrameAnimationPlayer>();
|
|
graphPlayer.ConfigureForAuthoring(graph, false, 1f);
|
|
var clipPlayer = gameObject.AddComponent<FrameClipPlayer>();
|
|
clipPlayer.ConfigureForAuthoring(clip, false, 1f);
|
|
|
|
Assert.That(graphPlayer.Play().Result.Error.Code,
|
|
Is.EqualTo(FrameAnimationPlaybackErrorCode.TargetConflict));
|
|
Assert.That(clipPlayer.Play().Result.Error.Code,
|
|
Is.EqualTo(FrameAnimationPlaybackErrorCode.TargetConflict));
|
|
}
|
|
|
|
[Test]
|
|
public void Image_PlayerSupportsHideAndNextPlayReEnablesTarget()
|
|
{
|
|
var sprite = CreateSprite(Color.green);
|
|
var clip = CreateClip("Clip", FrameClipEndBehavior.HideTarget, sprite);
|
|
var graph = CreateDirectGraph(clip);
|
|
var gameObject = Track(new GameObject("Image Player", typeof(RectTransform), typeof(CanvasRenderer)));
|
|
var image = gameObject.AddComponent<Image>();
|
|
var player = gameObject.AddComponent<FrameAnimationPlayer>();
|
|
player.ConfigureForAuthoring(graph, false, 1f);
|
|
|
|
player.Play(clip.Id);
|
|
player.EvaluateForTests(0.1d);
|
|
Assert.That(image.enabled, Is.False);
|
|
|
|
player.Play(clip.Id, new FrameAnimationPlayOptions(FrameClipEndBehavior.HoldLastFrame));
|
|
Assert.That(image.enabled, Is.True);
|
|
Assert.That(image.sprite, Is.EqualTo(sprite));
|
|
}
|
|
|
|
[Test]
|
|
public void FailedPlayDoesNotReplaceActiveRequest()
|
|
{
|
|
var clip = CreateClip("Loop", FrameClipEndBehavior.Loop, CreateSprite(Color.white));
|
|
var graph = CreateDirectGraph(clip);
|
|
var gameObject = Track(new GameObject("Player"));
|
|
gameObject.AddComponent<SpriteRenderer>();
|
|
var player = gameObject.AddComponent<FrameAnimationPlayer>();
|
|
player.ConfigureForAuthoring(graph, false, 1f);
|
|
|
|
var active = player.Play(clip.Id);
|
|
var failed = player.Play("Missing");
|
|
|
|
Assert.That(failed.Result.Reason, Is.EqualTo(FrameAnimationCompletionReason.Failed));
|
|
Assert.That(failed.Result.Error.Code, Is.EqualTo(FrameAnimationPlaybackErrorCode.PlayableNotFound));
|
|
Assert.That(active.IsCompleted, Is.False);
|
|
Assert.That(player.State, Is.EqualTo(FrameAnimationPlaybackState.Playing));
|
|
Assert.That(player.CurrentPlayableId, Is.EqualTo(clip.Id));
|
|
}
|
|
|
|
[Test]
|
|
public void SuccessfulPlayReplacesPreviousRequestExactlyOnce()
|
|
{
|
|
var first = CreateClip("First", FrameClipEndBehavior.Loop, CreateSprite(Color.white));
|
|
var second = CreateClip("Second", FrameClipEndBehavior.Loop, CreateSprite(Color.black));
|
|
var graph = Track(ScriptableObject.CreateInstance<FrameAnimationGraph>());
|
|
graph.Configure(
|
|
"Graph",
|
|
"Graph",
|
|
new[] { first, second },
|
|
Array.Empty<AnimationNode>(),
|
|
Array.Empty<AnimationEdge>(),
|
|
Array.Empty<AnimationFlow>(),
|
|
first.Id);
|
|
var gameObject = Track(new GameObject("Replace"));
|
|
gameObject.AddComponent<SpriteRenderer>();
|
|
var player = gameObject.AddComponent<FrameAnimationPlayer>();
|
|
player.ConfigureForAuthoring(graph, false, 1f);
|
|
|
|
var firstHandle = player.Play(first.Id);
|
|
var secondHandle = player.Play(second.Id);
|
|
|
|
Assert.That(firstHandle.Result.Reason, Is.EqualTo(FrameAnimationCompletionReason.Replaced));
|
|
Assert.That(secondHandle.IsCompleted, Is.False);
|
|
Assert.That(player.CurrentPlayableId, Is.EqualTo(second.Id));
|
|
}
|
|
|
|
[Test]
|
|
public void WaitForFirstPass_DirectLoopReleasesAfterFirstWrapAndKeepsPlaying()
|
|
{
|
|
var first = CreateSprite(Color.red);
|
|
var second = CreateSprite(Color.blue);
|
|
var clip = CreateClip("Loop", FrameClipEndBehavior.Loop, first, second);
|
|
var graph = CreateDirectGraph(clip);
|
|
var gameObject = Track(new GameObject("Wait Direct Loop"));
|
|
var renderer = gameObject.AddComponent<SpriteRenderer>();
|
|
var player = gameObject.AddComponent<FrameAnimationPlayer>();
|
|
player.ConfigureForAuthoring(graph, false, 1f);
|
|
|
|
var handle = player.Play(clip.Id);
|
|
var wait = player.WaitForFirstPass(handle);
|
|
|
|
Assert.That(wait.keepWaiting, Is.True);
|
|
player.EvaluateForTests(0.1d);
|
|
Assert.That(wait.keepWaiting, Is.True);
|
|
|
|
player.EvaluateForTests(0.1d);
|
|
|
|
Assert.That(wait.keepWaiting, Is.False);
|
|
Assert.That(handle.IsCompleted, Is.False);
|
|
Assert.That(player.State, Is.EqualTo(FrameAnimationPlaybackState.Playing));
|
|
Assert.That(player.CurrentFrameIndex, Is.EqualTo(0));
|
|
Assert.That(renderer.sprite, Is.SameAs(first));
|
|
}
|
|
|
|
[Test]
|
|
public void WaitForFirstPass_FlowWaitsForIntroAndOneTerminalLoopCycle()
|
|
{
|
|
var introClip = CreateClip(
|
|
"Intro",
|
|
FrameClipEndBehavior.HoldLastFrame,
|
|
CreateSprite(Color.red));
|
|
var loopFirst = CreateSprite(Color.green);
|
|
var loopClip = CreateClip(
|
|
"Loop",
|
|
FrameClipEndBehavior.Loop,
|
|
loopFirst,
|
|
CreateSprite(Color.blue));
|
|
var introNode = new AnimationNode(introClip.Id);
|
|
var loopNode = new AnimationNode(loopClip.Id);
|
|
var edge = new AnimationEdge(introNode.InternalId, loopNode.InternalId);
|
|
var flow = new AnimationFlow("Flow", "Flow", introNode.InternalId);
|
|
var graph = Track(ScriptableObject.CreateInstance<FrameAnimationGraph>());
|
|
graph.Configure(
|
|
"Graph",
|
|
"Graph",
|
|
new[] { introClip, loopClip },
|
|
new[] { introNode, loopNode },
|
|
new[] { edge },
|
|
new[] { flow },
|
|
flow.Id);
|
|
var gameObject = Track(new GameObject("Wait Flow Loop"));
|
|
var renderer = gameObject.AddComponent<SpriteRenderer>();
|
|
var player = gameObject.AddComponent<FrameAnimationPlayer>();
|
|
player.ConfigureForAuthoring(graph, false, 1f);
|
|
|
|
var handle = player.Play(flow.Id);
|
|
var wait = player.WaitForFirstPass(handle);
|
|
|
|
player.EvaluateForTests(0.1d);
|
|
Assert.That(player.CurrentClipId, Is.EqualTo(loopClip.Id));
|
|
Assert.That(wait.keepWaiting, Is.True);
|
|
|
|
player.EvaluateForTests(0.2d);
|
|
|
|
Assert.That(wait.keepWaiting, Is.False);
|
|
Assert.That(handle.IsCompleted, Is.False);
|
|
Assert.That(player.State, Is.EqualTo(FrameAnimationPlaybackState.Playing));
|
|
Assert.That(player.CurrentFrameIndex, Is.EqualTo(0));
|
|
Assert.That(renderer.sprite, Is.SameAs(loopFirst));
|
|
}
|
|
|
|
[Test]
|
|
public void WaitForFirstPass_LargeDeltaCrossesIntroAndTerminalLoopCycle()
|
|
{
|
|
var introClip = CreateClip(
|
|
"Intro",
|
|
FrameClipEndBehavior.HoldLastFrame,
|
|
CreateSprite(Color.red));
|
|
var loopClip = CreateClip(
|
|
"Loop",
|
|
FrameClipEndBehavior.Loop,
|
|
CreateSprite(Color.green),
|
|
CreateSprite(Color.blue));
|
|
var introNode = new AnimationNode(introClip.Id);
|
|
var loopNode = new AnimationNode(loopClip.Id);
|
|
var graph = Track(ScriptableObject.CreateInstance<FrameAnimationGraph>());
|
|
graph.Configure(
|
|
"Graph",
|
|
"Graph",
|
|
new[] { introClip, loopClip },
|
|
new[] { introNode, loopNode },
|
|
new[] { new AnimationEdge(introNode.InternalId, loopNode.InternalId) },
|
|
new[] { new AnimationFlow("Flow", "Flow", introNode.InternalId) },
|
|
"Flow");
|
|
var gameObject = Track(new GameObject("Wait Large Delta"));
|
|
gameObject.AddComponent<SpriteRenderer>();
|
|
var player = gameObject.AddComponent<FrameAnimationPlayer>();
|
|
player.ConfigureForAuthoring(graph, false, 1f);
|
|
|
|
var handle = player.Play("Flow");
|
|
var wait = player.WaitForFirstPass(handle);
|
|
player.EvaluateForTests(1.35d);
|
|
|
|
Assert.That(wait.keepWaiting, Is.False);
|
|
Assert.That(handle.IsCompleted, Is.False);
|
|
Assert.That(player.State, Is.EqualTo(FrameAnimationPlaybackState.Playing));
|
|
}
|
|
|
|
[Test]
|
|
public void WaitForFirstPass_NonLoopMirrorsNaturalCompletion()
|
|
{
|
|
var clip = CreateClip(
|
|
"Once",
|
|
FrameClipEndBehavior.HoldLastFrame,
|
|
CreateSprite(Color.red),
|
|
CreateSprite(Color.blue));
|
|
var graph = CreateDirectGraph(clip);
|
|
var gameObject = Track(new GameObject("Wait Once"));
|
|
gameObject.AddComponent<SpriteRenderer>();
|
|
var player = gameObject.AddComponent<FrameAnimationPlayer>();
|
|
player.ConfigureForAuthoring(graph, false, 1f);
|
|
|
|
var handle = player.Play(clip.Id);
|
|
var wait = player.WaitForFirstPass(handle);
|
|
player.EvaluateForTests(0.2d);
|
|
|
|
Assert.That(wait.keepWaiting, Is.False);
|
|
Assert.That(handle.Result.Reason, Is.EqualTo(FrameAnimationCompletionReason.Completed));
|
|
Assert.That(player.State, Is.EqualTo(FrameAnimationPlaybackState.Stopped));
|
|
}
|
|
|
|
[Test]
|
|
public void WaitForFirstPass_ReleasesWhenRequestIsReplacedOrStopped()
|
|
{
|
|
var first = CreateClip("First", FrameClipEndBehavior.Loop, CreateSprite(Color.red));
|
|
var second = CreateClip("Second", FrameClipEndBehavior.Loop, CreateSprite(Color.blue));
|
|
var graph = Track(ScriptableObject.CreateInstance<FrameAnimationGraph>());
|
|
graph.Configure(
|
|
"Graph",
|
|
"Graph",
|
|
new[] { first, second },
|
|
Array.Empty<AnimationNode>(),
|
|
Array.Empty<AnimationEdge>(),
|
|
Array.Empty<AnimationFlow>(),
|
|
first.Id);
|
|
var gameObject = Track(new GameObject("Wait Termination"));
|
|
gameObject.AddComponent<SpriteRenderer>();
|
|
var player = gameObject.AddComponent<FrameAnimationPlayer>();
|
|
player.ConfigureForAuthoring(graph, false, 1f);
|
|
|
|
var replacedHandle = player.Play(first.Id);
|
|
var replacedWait = player.WaitForFirstPass(replacedHandle);
|
|
var stoppedHandle = player.Play(second.Id);
|
|
var stoppedWait = player.WaitForFirstPass(stoppedHandle);
|
|
|
|
Assert.That(replacedWait.keepWaiting, Is.False);
|
|
Assert.That(replacedHandle.Result.Reason, Is.EqualTo(FrameAnimationCompletionReason.Replaced));
|
|
Assert.That(stoppedWait.keepWaiting, Is.True);
|
|
|
|
player.Stop();
|
|
|
|
Assert.That(stoppedWait.keepWaiting, Is.False);
|
|
Assert.That(stoppedHandle.Result.Reason, Is.EqualTo(FrameAnimationCompletionReason.Stopped));
|
|
}
|
|
|
|
[Test]
|
|
public void WaitForFirstPass_PauseAndZeroSpeedDoNotReleaseEarly()
|
|
{
|
|
var clip = CreateClip(
|
|
"Loop",
|
|
FrameClipEndBehavior.Loop,
|
|
CreateSprite(Color.red),
|
|
CreateSprite(Color.blue));
|
|
var graph = CreateDirectGraph(clip);
|
|
var gameObject = Track(new GameObject("Wait Paused"));
|
|
gameObject.AddComponent<SpriteRenderer>();
|
|
var player = gameObject.AddComponent<FrameAnimationPlayer>();
|
|
player.ConfigureForAuthoring(graph, false, 0f);
|
|
|
|
var handle = player.Play(clip.Id);
|
|
var wait = player.WaitForFirstPass(handle);
|
|
player.EvaluateForTests(1d);
|
|
Assert.That(wait.keepWaiting, Is.True);
|
|
|
|
player.SetSpeed(1f);
|
|
player.Pause();
|
|
player.EvaluateForTests(1d);
|
|
Assert.That(wait.keepWaiting, Is.True);
|
|
|
|
player.Resume();
|
|
player.EvaluateForTests(0.2d);
|
|
Assert.That(wait.keepWaiting, Is.False);
|
|
Assert.That(handle.IsCompleted, Is.False);
|
|
}
|
|
|
|
[Test]
|
|
public void WaitForFirstPass_FailedRequestDoesNotWait()
|
|
{
|
|
var clip = CreateClip("Loop", FrameClipEndBehavior.Loop, CreateSprite(Color.white));
|
|
var graph = CreateDirectGraph(clip);
|
|
var gameObject = Track(new GameObject("Wait Failed"));
|
|
gameObject.AddComponent<SpriteRenderer>();
|
|
var player = gameObject.AddComponent<FrameAnimationPlayer>();
|
|
player.ConfigureForAuthoring(graph, false, 1f);
|
|
|
|
var handle = player.Play("Missing");
|
|
var wait = player.WaitForFirstPass(handle);
|
|
|
|
Assert.That(wait.keepWaiting, Is.False);
|
|
Assert.That(handle.Result.Reason, Is.EqualTo(FrameAnimationCompletionReason.Failed));
|
|
Assert.That(handle.Result.Error.Code, Is.EqualTo(FrameAnimationPlaybackErrorCode.PlayableNotFound));
|
|
}
|
|
|
|
[Test]
|
|
public void FailedTargetProbeDoesNotDiscardCurrentTargetOrRequest()
|
|
{
|
|
var clip = CreateClip("Loop", FrameClipEndBehavior.Loop, CreateSprite(Color.white));
|
|
var graph = CreateDirectGraph(clip);
|
|
var gameObject = Track(new GameObject("Player", typeof(RectTransform), typeof(CanvasRenderer)));
|
|
gameObject.AddComponent<SpriteRenderer>();
|
|
var player = gameObject.AddComponent<FrameAnimationPlayer>();
|
|
player.ConfigureForAuthoring(graph, false, 1f);
|
|
var active = player.Play(clip.Id);
|
|
gameObject.AddComponent<Image>();
|
|
|
|
var failed = player.Play(clip.Id);
|
|
|
|
Assert.That(failed.Result.Error.Code, Is.EqualTo(FrameAnimationPlaybackErrorCode.TargetConflict));
|
|
Assert.That(active.IsCompleted, Is.False);
|
|
Assert.That(player.State, Is.EqualTo(FrameAnimationPlaybackState.Playing));
|
|
player.EvaluateForTests(0.05d);
|
|
Assert.That(active.IsCompleted, Is.False);
|
|
}
|
|
|
|
[Test]
|
|
public void ZeroSpeedKeepsRequestPlayingWithoutAdvancing()
|
|
{
|
|
var first = CreateSprite(Color.red);
|
|
var second = CreateSprite(Color.blue);
|
|
var clip = CreateClip("Clip", FrameClipEndBehavior.HoldLastFrame, first, second);
|
|
var graph = CreateDirectGraph(clip);
|
|
var gameObject = Track(new GameObject("Zero Speed"));
|
|
var renderer = gameObject.AddComponent<SpriteRenderer>();
|
|
var player = gameObject.AddComponent<FrameAnimationPlayer>();
|
|
player.ConfigureForAuthoring(graph, false, 0f);
|
|
|
|
var handle = player.Play(clip.Id);
|
|
player.EvaluateForTests(10d);
|
|
|
|
Assert.That(handle.IsCompleted, Is.False);
|
|
Assert.That(player.State, Is.EqualTo(FrameAnimationPlaybackState.Playing));
|
|
Assert.That(player.CurrentFrameIndex, Is.EqualTo(0));
|
|
Assert.That(renderer.sprite, Is.EqualTo(first));
|
|
}
|
|
|
|
[Test]
|
|
public void PauseAndResumePreserveFrameProgress()
|
|
{
|
|
var clip = CreateClip(
|
|
"Clip",
|
|
FrameClipEndBehavior.HoldLastFrame,
|
|
CreateSprite(Color.red),
|
|
CreateSprite(Color.blue));
|
|
var graph = CreateDirectGraph(clip);
|
|
var gameObject = Track(new GameObject("Pause"));
|
|
gameObject.AddComponent<SpriteRenderer>();
|
|
var player = gameObject.AddComponent<FrameAnimationPlayer>();
|
|
player.ConfigureForAuthoring(graph, false, 1f);
|
|
player.Play(clip.Id);
|
|
player.EvaluateForTests(0.05d);
|
|
|
|
player.Pause();
|
|
player.EvaluateForTests(1d);
|
|
Assert.That(player.CurrentFrameIndex, Is.EqualTo(0));
|
|
player.Resume();
|
|
player.EvaluateForTests(0.05d);
|
|
|
|
Assert.That(player.CurrentFrameIndex, Is.EqualTo(1));
|
|
Assert.That(player.State, Is.EqualTo(FrameAnimationPlaybackState.Playing));
|
|
}
|
|
|
|
[Test]
|
|
public void TargetConflictReturnsStructuredFailure()
|
|
{
|
|
var clip = CreateClip("Clip", FrameClipEndBehavior.HoldLastFrame, CreateSprite(Color.white));
|
|
var graph = CreateDirectGraph(clip);
|
|
var gameObject = Track(new GameObject("Conflict", typeof(RectTransform), typeof(CanvasRenderer)));
|
|
gameObject.AddComponent<SpriteRenderer>();
|
|
gameObject.AddComponent<Image>();
|
|
var player = gameObject.AddComponent<FrameAnimationPlayer>();
|
|
player.ConfigureForAuthoring(graph, false, 1f);
|
|
|
|
var handle = player.Play(clip.Id);
|
|
|
|
Assert.That(handle.Result.Reason, Is.EqualTo(FrameAnimationCompletionReason.Failed));
|
|
Assert.That(handle.Result.Error.Code, Is.EqualTo(FrameAnimationPlaybackErrorCode.TargetConflict));
|
|
}
|
|
|
|
[Test]
|
|
public void StopAppliesVisualModeWithoutActiveRequest()
|
|
{
|
|
var gameObject = Track(new GameObject("Stop"));
|
|
var renderer = gameObject.AddComponent<SpriteRenderer>();
|
|
renderer.sprite = CreateSprite(Color.yellow);
|
|
var player = gameObject.AddComponent<FrameAnimationPlayer>();
|
|
|
|
player.Stop(FrameAnimationStopMode.Clear);
|
|
|
|
Assert.That(renderer.sprite, Is.Null);
|
|
Assert.That(renderer.enabled, Is.True);
|
|
}
|
|
|
|
[Test]
|
|
public void TryBindGraph_AllowsInitialBindAndRejectsReplacementAfterPlayback()
|
|
{
|
|
var firstGraph = CreateDirectGraph(
|
|
CreateClip("First", FrameClipEndBehavior.Loop, CreateSprite(Color.red)));
|
|
var secondGraph = CreateDirectGraph(
|
|
CreateClip("Second", FrameClipEndBehavior.Loop, CreateSprite(Color.blue)));
|
|
var gameObject = Track(new GameObject("Runtime Bind"));
|
|
gameObject.AddComponent<SpriteRenderer>();
|
|
var player = gameObject.AddComponent<FrameAnimationPlayer>();
|
|
|
|
Assert.That(player.TryBindGraph(firstGraph, out var bindError), Is.True);
|
|
Assert.That(bindError.Code, Is.EqualTo(FrameAnimationPlaybackErrorCode.None));
|
|
player.Play("First");
|
|
|
|
Assert.That(player.TryBindGraph(secondGraph, out var replaceError), Is.False);
|
|
Assert.That(
|
|
replaceError.Code,
|
|
Is.EqualTo(FrameAnimationPlaybackErrorCode.GraphBindingNotAllowed));
|
|
Assert.That(player.Graph, Is.SameAs(firstGraph));
|
|
}
|
|
|
|
[Test]
|
|
public void RestoreTerminalState_HoldLastFrameAppliesLastFrameImmediately()
|
|
{
|
|
var first = CreateSprite(Color.red);
|
|
var last = CreateSprite(Color.blue);
|
|
var clip = CreateClip("Terminal", FrameClipEndBehavior.HoldLastFrame, first, last);
|
|
var graph = CreateDirectGraph(clip);
|
|
var gameObject = Track(new GameObject("Restore Terminal"));
|
|
var renderer = gameObject.AddComponent<SpriteRenderer>();
|
|
var player = gameObject.AddComponent<FrameAnimationPlayer>();
|
|
player.ConfigureForAuthoring(graph, false, 1f);
|
|
|
|
var handle = player.RestoreTerminalState(clip.Id);
|
|
|
|
Assert.That(handle.IsCompleted, Is.True);
|
|
Assert.That(handle.Result.Reason, Is.EqualTo(FrameAnimationCompletionReason.Completed));
|
|
Assert.That(player.State, Is.EqualTo(FrameAnimationPlaybackState.Stopped));
|
|
Assert.That(renderer.sprite, Is.SameAs(last));
|
|
Assert.That(renderer.enabled, Is.True);
|
|
}
|
|
|
|
[Test]
|
|
public void RestoreTerminalState_AppliesClearAndHideTargetEndBehaviors()
|
|
{
|
|
var clearClip = CreateClip(
|
|
"Clear",
|
|
FrameClipEndBehavior.Clear,
|
|
CreateSprite(Color.red));
|
|
var hideClip = CreateClip(
|
|
"Hide",
|
|
FrameClipEndBehavior.HideTarget,
|
|
CreateSprite(Color.blue));
|
|
var graph = Track(ScriptableObject.CreateInstance<FrameAnimationGraph>());
|
|
graph.Configure(
|
|
"Graph",
|
|
"Graph",
|
|
new[] { clearClip, hideClip },
|
|
Array.Empty<AnimationNode>(),
|
|
Array.Empty<AnimationEdge>(),
|
|
Array.Empty<AnimationFlow>(),
|
|
clearClip.Id);
|
|
var gameObject = Track(new GameObject("Restore End Behaviors"));
|
|
var renderer = gameObject.AddComponent<SpriteRenderer>();
|
|
var player = gameObject.AddComponent<FrameAnimationPlayer>();
|
|
player.ConfigureForAuthoring(graph, false, 1f);
|
|
|
|
player.RestoreTerminalState(clearClip.Id);
|
|
Assert.That(renderer.sprite, Is.Null);
|
|
Assert.That(renderer.enabled, Is.True);
|
|
|
|
player.RestoreTerminalState(hideClip.Id);
|
|
Assert.That(renderer.enabled, Is.False);
|
|
}
|
|
|
|
[Test]
|
|
public void RestoreTerminalState_LoopRestartsFromFirstFrameWithoutCompleting()
|
|
{
|
|
var first = CreateSprite(Color.red);
|
|
var second = CreateSprite(Color.blue);
|
|
var clip = CreateClip("Loop", FrameClipEndBehavior.Loop, first, second);
|
|
var graph = CreateDirectGraph(clip);
|
|
var gameObject = Track(new GameObject("Restore Loop"));
|
|
var renderer = gameObject.AddComponent<SpriteRenderer>();
|
|
var player = gameObject.AddComponent<FrameAnimationPlayer>();
|
|
player.ConfigureForAuthoring(graph, false, 1f);
|
|
|
|
var handle = player.RestoreTerminalState(clip.Id);
|
|
|
|
Assert.That(handle.IsCompleted, Is.False);
|
|
Assert.That(player.State, Is.EqualTo(FrameAnimationPlaybackState.Playing));
|
|
Assert.That(player.CurrentFrameIndex, Is.EqualTo(0));
|
|
Assert.That(renderer.sprite, Is.SameAs(first));
|
|
}
|
|
|
|
[Test]
|
|
public void FailedTerminalRestoreDoesNotReplaceActiveRequest()
|
|
{
|
|
var clip = CreateClip("Loop", FrameClipEndBehavior.Loop, CreateSprite(Color.white));
|
|
var graph = CreateDirectGraph(clip);
|
|
var gameObject = Track(new GameObject("Failed Restore"));
|
|
gameObject.AddComponent<SpriteRenderer>();
|
|
var player = gameObject.AddComponent<FrameAnimationPlayer>();
|
|
player.ConfigureForAuthoring(graph, false, 1f);
|
|
var active = player.Play(clip.Id);
|
|
|
|
var failed = player.RestoreTerminalState("Missing");
|
|
|
|
Assert.That(failed.IsCompleted, Is.True);
|
|
Assert.That(failed.Result.Reason, Is.EqualTo(FrameAnimationCompletionReason.Failed));
|
|
Assert.That(
|
|
failed.Result.Error.Code,
|
|
Is.EqualTo(FrameAnimationPlaybackErrorCode.PlayableNotFound));
|
|
Assert.That(active.IsCompleted, Is.False);
|
|
Assert.That(player.State, Is.EqualTo(FrameAnimationPlaybackState.Playing));
|
|
Assert.That(player.CurrentPlayableId, Is.EqualTo(clip.Id));
|
|
}
|
|
|
|
[Test]
|
|
public void RestoreTerminalState_FlowUsesTerminalStepEndState()
|
|
{
|
|
var firstClip = CreateClip(
|
|
"First",
|
|
FrameClipEndBehavior.HoldLastFrame,
|
|
CreateSprite(Color.red));
|
|
var terminalSprite = CreateSprite(Color.green);
|
|
var terminalClip = CreateClip(
|
|
"Terminal",
|
|
FrameClipEndBehavior.HoldLastFrame,
|
|
CreateSprite(Color.blue),
|
|
terminalSprite);
|
|
var firstNode = new AnimationNode(firstClip.Id);
|
|
var terminalNode = new AnimationNode(terminalClip.Id);
|
|
var edge = new AnimationEdge(firstNode.InternalId, terminalNode.InternalId);
|
|
var flow = new AnimationFlow("Flow", "Flow", firstNode.InternalId);
|
|
var graph = Track(ScriptableObject.CreateInstance<FrameAnimationGraph>());
|
|
graph.Configure(
|
|
"Graph",
|
|
"Graph",
|
|
new[] { firstClip, terminalClip },
|
|
new[] { firstNode, terminalNode },
|
|
new[] { edge },
|
|
new[] { flow },
|
|
flow.Id);
|
|
var gameObject = Track(new GameObject("Restore Flow"));
|
|
var renderer = gameObject.AddComponent<SpriteRenderer>();
|
|
var player = gameObject.AddComponent<FrameAnimationPlayer>();
|
|
player.ConfigureForAuthoring(graph, false, 1f);
|
|
|
|
var handle = player.RestoreTerminalState(flow.Id);
|
|
|
|
Assert.That(handle.IsCompleted, Is.True);
|
|
Assert.That(renderer.sprite, Is.SameAs(terminalSprite));
|
|
}
|
|
|
|
[UnityTest]
|
|
public IEnumerator OnDisableSettlesActiveRequestAsStopped()
|
|
{
|
|
var clip = CreateClip("Loop", FrameClipEndBehavior.Loop, CreateSprite(Color.white));
|
|
var graph = CreateDirectGraph(clip);
|
|
var gameObject = Track(new GameObject("Disable"));
|
|
gameObject.AddComponent<SpriteRenderer>();
|
|
var player = gameObject.AddComponent<FrameAnimationPlayer>();
|
|
player.ConfigureForAuthoring(graph, false, 1f);
|
|
var handle = player.Play(clip.Id);
|
|
|
|
player.enabled = false;
|
|
yield return null;
|
|
|
|
Assert.That(handle.Result.Reason, Is.EqualTo(FrameAnimationCompletionReason.Stopped));
|
|
Assert.That(player.State, Is.EqualTo(FrameAnimationPlaybackState.Stopped));
|
|
}
|
|
|
|
[UnityTest]
|
|
public IEnumerator PlayOnEnableStartsDefaultPlayableFromBeginning()
|
|
{
|
|
var clip = CreateClip("Loop", FrameClipEndBehavior.Loop, CreateSprite(Color.cyan));
|
|
var graph = CreateDirectGraph(clip);
|
|
var gameObject = Track(new GameObject("Auto Play"));
|
|
gameObject.SetActive(false);
|
|
gameObject.AddComponent<SpriteRenderer>();
|
|
var player = gameObject.AddComponent<FrameAnimationPlayer>();
|
|
player.ConfigureForAuthoring(graph, true, 1f);
|
|
|
|
gameObject.SetActive(true);
|
|
yield return null;
|
|
|
|
Assert.That(player.State, Is.EqualTo(FrameAnimationPlaybackState.Playing));
|
|
Assert.That(player.CurrentPlayableId, Is.EqualTo(clip.Id));
|
|
Assert.That(player.CurrentFrameIndex, Is.EqualTo(0));
|
|
}
|
|
|
|
private FrameClip CreateClip(string id, FrameClipEndBehavior endBehavior, 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, 1f, endBehavior);
|
|
return clip;
|
|
}
|
|
|
|
private FrameAnimationGraph CreateDirectGraph(FrameClip clip)
|
|
{
|
|
var graph = Track(ScriptableObject.CreateInstance<FrameAnimationGraph>());
|
|
graph.Configure(
|
|
"Graph",
|
|
"Graph",
|
|
new[] { clip },
|
|
Array.Empty<AnimationNode>(),
|
|
Array.Empty<AnimationEdge>(),
|
|
Array.Empty<AnimationFlow>(),
|
|
clip.Id);
|
|
return graph;
|
|
}
|
|
|
|
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), new Vector2(0.5f, 0.5f)));
|
|
}
|
|
|
|
private T Track<T>(T value) where T : UnityEngine.Object
|
|
{
|
|
createdObjects.Add(value);
|
|
return value;
|
|
}
|
|
}
|
|
}
|