using System.Collections; using System.Collections.Generic; using AibisDream.FrameAnimation.Timeline; using NUnit.Framework; using UnityEngine; using UnityEngine.Playables; using UnityEngine.TestTools; using UnityEngine.Timeline; namespace AibisDream.FrameAnimation.Tests.PlayMode { public sealed class FrameAnimationTimelinePlayModeTests { private readonly List createdObjects = new List(); [TearDown] public void TearDown() { for (var index = createdObjects.Count - 1; index >= 0; index--) { if (createdObjects[index] != null) { UnityEngine.Object.DestroyImmediate(createdObjects[index]); } } createdObjects.Clear(); } [UnityTest] public IEnumerator Director_TakesControlStopsAtTerminalAndPlayerCanPlayAgain() { var first = CreateSprite(Color.red); var second = CreateSprite(Color.blue); var clip = CreateClip("Timeline", FrameClipEndBehavior.HoldLastFrame, first, second); var playerObject = Track(new GameObject("Timeline Runtime Player")); var renderer = playerObject.AddComponent(); var player = playerObject.AddComponent(); player.ConfigureForAuthoring(clip, false, 1f); var ordinaryHandle = player.Play(); var setup = CreateTimeline(player, clip); setup.Director.Play(); setup.Director.time = 0.15d; setup.Director.Evaluate(); yield return null; Assert.That(ordinaryHandle.IsCompleted, Is.True); Assert.That(ordinaryHandle.Result.Reason, Is.EqualTo(FrameAnimationCompletionReason.Replaced)); Assert.That(renderer.sprite, Is.SameAs(second)); setup.Director.Stop(); Assert.That(renderer.sprite, Is.SameAs(second)); var nextHandle = player.Play(); Assert.That(nextHandle.IsCompleted, Is.False); Assert.That(renderer.sprite, Is.SameAs(first)); } [UnityTest] public IEnumerator Director_CanPauseSeekBackwardAndReplayLoop() { var first = CreateSprite(Color.green); var second = CreateSprite(Color.yellow); var clip = CreateClip("Loop", FrameClipEndBehavior.Loop, first, second); var playerObject = Track(new GameObject("Timeline Runtime Loop")); var renderer = playerObject.AddComponent(); var player = playerObject.AddComponent(); var setup = CreateTimeline(player, clip); setup.Clip.duration = 0.6d; setup.Director.Play(); setup.Director.time = 0.15d; setup.Director.Evaluate(); setup.Director.Pause(); Assert.That(renderer.sprite, Is.SameAs(second)); setup.Director.time = 0.01d; setup.Director.Evaluate(); Assert.That(renderer.sprite, Is.SameAs(first)); setup.Director.Play(); setup.Director.time = 0.25d; setup.Director.Evaluate(); yield return null; Assert.That(renderer.sprite, Is.SameAs(first)); setup.Director.Stop(); Assert.That(renderer.sprite, Is.Not.Null); } private TimelineSetup CreateTimeline(FrameClipPlayer player, FrameClip clip) { var timeline = Track(ScriptableObject.CreateInstance()); var track = Track(timeline.CreateTrack()); var timelineClip = track.CreateClip(); var asset = Track((FrameAnimationTimelineClip)timelineClip.asset); asset.SetFrameClipForTests(clip); timelineClip.start = 0d; timelineClip.duration = asset.duration; var directorObject = Track(new GameObject("Frame Animation Runtime Director")); var director = directorObject.AddComponent(); director.playableAsset = timeline; director.SetGenericBinding(track, player); return new TimelineSetup(director, timelineClip); } private FrameClip CreateClip(string id, FrameClipEndBehavior endBehavior, params Sprite[] sprites) { var clip = Track(ScriptableObject.CreateInstance()); var frames = new List(); 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 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 value) where T : UnityEngine.Object { createdObjects.Add(value); return value; } private readonly struct TimelineSetup { internal PlayableDirector Director { get; } internal TimelineClip Clip { get; } internal TimelineSetup(PlayableDirector director, TimelineClip clip) { Director = director; Clip = clip; } } } }