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_TakesControlSupportsSeekAndReleasesPlayer() { var first = CreateSprite(Color.red); var second = CreateSprite(Color.blue); var clip = CreateClip(first, second); var playerObject = Track(new GameObject("Timeline 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.Result.Reason, Is.EqualTo(FrameAnimationCompletionReason.Replaced)); Assert.That(renderer.sprite, Is.SameAs(second)); setup.Director.time = 0.01d; setup.Director.Evaluate(); Assert.That(renderer.sprite, Is.SameAs(first)); setup.Director.Stop(); var nextHandle = player.Play(); Assert.That(nextHandle.IsCompleted, Is.False); Assert.That(renderer.sprite, Is.SameAs(first)); } [UnityTest] public IEnumerator Director_RespectsClipInSpeedMultiplierAndBackwardSeek() { var first = CreateSprite(Color.red); var second = CreateSprite(Color.green); var third = CreateSprite(Color.blue); var clip = CreateClip(first, second, third); var playerObject = Track(new GameObject("Timeline Time Mapping")); var renderer = playerObject.AddComponent(); var player = playerObject.AddComponent(); var setup = CreateTimeline(player, clip); setup.Clip.clipIn = 0.1d; setup.Clip.timeScale = 2d; setup.Director.Play(); setup.Director.time = 0.025d; setup.Director.Evaluate(); yield return null; Assert.That(renderer.sprite, Is.SameAs(second)); setup.Director.time = 0.075d; setup.Director.Evaluate(); Assert.That(renderer.sprite, Is.SameAs(third)); setup.Director.time = 0d; setup.Director.Evaluate(); Assert.That(renderer.sprite, Is.SameAs(second)); } [UnityTest] public IEnumerator Director_ExtendedLoopClipWrapsAtSourceDuration() { var first = CreateSprite(Color.yellow); var second = CreateSprite(Color.cyan); var clip = CreateClip(FrameClipEndBehavior.Loop, first, second); var playerObject = Track(new GameObject("Timeline 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.25d; setup.Director.Evaluate(); yield return null; Assert.That(renderer.sprite, Is.SameAs(first)); setup.Director.time = 0.35d; setup.Director.Evaluate(); Assert.That(renderer.sprite, Is.SameAs(second)); } [UnityTest] public IEnumerator Director_AppliesClearAndHideTerminalBehaviors() { var clearObject = Track(new GameObject("Timeline Clear")); var clearRenderer = clearObject.AddComponent(); var clearPlayer = clearObject.AddComponent(); var clearSetup = CreateTimeline( clearPlayer, CreateClip( FrameClipEndBehavior.Clear, CreateSprite(Color.magenta))); clearSetup.Director.Play(); clearSetup.Director.time = clearSetup.Clip.duration; clearSetup.Director.Evaluate(); yield return null; Assert.That(clearRenderer.enabled, Is.True); Assert.That(clearRenderer.sprite, Is.Null); var hideObject = Track(new GameObject("Timeline Hide")); var hideRenderer = hideObject.AddComponent(); var hidePlayer = hideObject.AddComponent(); var hideSetup = CreateTimeline( hidePlayer, CreateClip( FrameClipEndBehavior.HideTarget, CreateSprite(Color.white))); hideSetup.Director.Play(); hideSetup.Director.time = hideSetup.Clip.duration; hideSetup.Director.Evaluate(); yield return null; Assert.That(hideRenderer.enabled, Is.False); } 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.duration = asset.duration; var directorObject = Track(new GameObject("Timeline Director")); var director = directorObject.AddComponent(); director.playableAsset = timeline; director.SetGenericBinding(track, player); return new TimelineSetup(director, timelineClip); } private FrameClip CreateClip(params Sprite[] sprites) { return CreateClip(FrameClipEndBehavior.HoldLastFrame, sprites); } private FrameClip CreateClip( 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( "Timeline", "Timeline", 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 TimelineSetup(PlayableDirector director, TimelineClip clip) { Director = director; Clip = clip; } internal PlayableDirector Director { get; } internal TimelineClip Clip { get; } } } }