// Copyright (c) 2015 - 2023 Doozy Entertainment. All Rights Reserved. // This code can only be used under the standard Unity Asset Store End User License Agreement // A Copy of the EULA APPENDIX 1 is available at http://unity3d.com/company/legal/as_terms using System; using System.Collections.Generic; using System.Linq; using Doozy.Runtime.Reactor.Internal; using Doozy.Runtime.Reactor.Reactions; using Doozy.Runtime.Reactor.Targets; using UnityEngine; namespace Doozy.Runtime.Reactor.Animations { /// /// Reactor animation that works with a list of sprites. /// It works like a frame by frame animation, each sprite counting as a frame, for a sprite target. /// [Serializable] public class SpriteAnimation : ReactorAnimation { /// Reference to a sprite target component public ReactorSpriteTarget spriteTarget { get; private set; } [SerializeField] private List Sprites = new List(); /// List of sprites, each sprite counted as a frame for the animation public List sprites => Sprites; /// Check if a sprite target is referenced or not public override bool hasTarget => spriteTarget != null; [SerializeField] private SpriteTargetReaction Animation; /// Sprite reaction designed to change (via a frame by frame animation) the sprite of a sprite target public SpriteTargetReaction animation => Animation ?? (Animation = Reaction.Get()); /// Animation start frame public int startFrame { get => animation.startFrame; set => animation.startFrame = value; } /// Is the animation enabled public override bool isEnabled => animation.enabled; /// Is the animation in the idle state (is enabled, but not active) public override bool isIdle => animation.isIdle; /// Is the animation in the active state (is enabled and either playing or paused) public override bool isActive => animation.isActive; /// Is the animation in the paused state (is enabled, started playing and then paused) public override bool isPaused => animation.isPaused; /// Is the animation in the playing state (is enabled and started playing) public override bool isPlaying => animation.isPlaying; /// Is the animation in the start delay state (is enabled, started playing and waiting to start running after the start delay duration has passed) public override bool inStartDelay => animation.inStartDelay; /// Is the animation in the loop delay state (is enabled, started playing and is between loops waiting to continue running after the loop delay duration has passed) public override bool inLoopDelay => animation.inLoopDelay; /// Construct a new SpriteAnimation with the given sprite target /// Sprite target public SpriteAnimation(ReactorSpriteTarget target = null) { if (target == null) return; SetTarget(target); } /// Load a set of sprites to the animation. Each sprite is considered a frame. /// Collection of sprites public SpriteAnimation SetSprites(IEnumerable spriteEnumerable) { if (spriteEnumerable == null) return this; Sprites.Clear(); Sprites.AddRange(spriteEnumerable); return UpdateAnimationSprites(); } /// Updates the list of sprites referenced in the animation public SpriteAnimation UpdateAnimationSprites() { animation.SetSprites(Sprites, false); return this; } /// Sorts the sprites in an alphabetical order from A to Z public SpriteAnimation SortSpritesAz() { Sprites = Sprites.OrderBy(item => item.name).ToList(); return UpdateAnimationSprites(); } /// Sorts the sprites in reverse alphabetical order from Z to A public SpriteAnimation SortSpritesZa() { Sprites = Sprites.OrderByDescending(item => item.name).ToList(); return UpdateAnimationSprites(); } /// Set the sprite target /// Sprite target public void SetTarget(ReactorSpriteTarget target) { spriteTarget = null; _ = target ? target : throw new NullReferenceException(nameof(target)); spriteTarget = target; Initialize(); } /// Initialize de animation public void Initialize() { animation?.Stop(true); Animation ??= Reaction.Get(); animation?.SetTarget(spriteTarget); UpdateValues(); } /// /// Recycle the reactions controlled by this animation. /// Reactions are pooled can (and should) be recycled to improve overall performance. /// public override void Recycle() => animation?.Recycle(); /// /// Update the initial values for the reactions controlled by this animation /// public override void UpdateValues() { UpdateAnimationSprites(); animation.UpdateValues(); } /// /// Stop all the reactions controlled by this animation /// public override void StopAllReactionsOnTarget() => Reaction.StopAllReactionsByTargetObject(spriteTarget, true); /// Set the animation at the given progress value /// Target progress [0,1] public override void SetProgressAt(float targetProgress) { base.SetProgressAt(targetProgress); if (!animation.enabled) return; UpdateAnimationSprites(); animation.SetProgressAt(targetProgress); } /// Play the animation at the given progress value from the current value /// To progress [0,1] public override void PlayToProgress(float toProgress) { base.PlayToProgress(toProgress); if (!animation.enabled) return; UpdateAnimationSprites(); animation.PlayToProgress(toProgress); } /// Play the animation from the given progress value to the current value /// From progress [0,1] public override void PlayFromProgress(float fromProgress) { base.PlayFromProgress(fromProgress); if (!animation.enabled) return; UpdateAnimationSprites(); animation.PlayFromProgress(fromProgress); } /// Play the animation from the given progress value to the given progress value /// From progress [0,1] /// To progress [0,1] public override void PlayFromToProgress(float fromProgress, float toProgress) { base.PlayFromToProgress(fromProgress, toProgress); if (!animation.enabled) return; UpdateAnimationSprites(); animation.PlayFromToProgress(fromProgress, toProgress); } /// Play the animation all the way /// Play the animation in reverse? public override void Play(bool inReverse = false) { if (spriteTarget == null) return; RegisterCallbacks(); if (!isActive) { StopAllReactionsOnTarget(); // ResetToStartValues(); } if (!animation.enabled) return; UpdateAnimationSprites(); animation.Play(inReverse); } /// Reset all the reactions to their initial values (if the animation is enabled) /// If true, forced will ignore if the animation is enabled or not public override void ResetToStartValues(bool forced = false) { if(spriteTarget == null) return; if (forced || animation.enabled) { UpdateAnimationSprites(); animation.SetValue(startFrame); } } /// /// Stop the animation. /// Called every time the animation is stopped. Also called before calling Finish() /// public override void Stop() { if (animation.isActive || animation.enabled) animation.Stop(); base.Stop(); } /// /// Finish the animation. /// Called to mark that that animation completed playing. /// public override void Finish() { if (animation.isActive || animation.enabled) animation.Finish(); base.Finish(); } /// /// Reverse the animation's direction while playing. /// Works only if the animation is active (it either playing or paused) /// public override void Reverse() { if (animation.isActive) animation.Reverse(); else if (animation.enabled) animation.Play(PlayDirection.Reverse); } /// /// Rewind the animation to the start values /// public override void Rewind() { if (!animation.enabled) return; UpdateAnimationSprites(); animation.Rewind(); } /// /// Pause the animation. /// Works only if the animation is playing. /// public override void Pause() => animation.Pause(); /// /// Resume a paused animation. /// Works only if the animation is paused. /// public override void Resume() => animation.Resume(); /// Register all callbacks to the animation protected override void RegisterCallbacks() { base.RegisterCallbacks(); if (!animation.enabled) return; startedReactionsCount++; animation.OnPlayCallback += InvokeOnPlay; animation.OnStopCallback += InvokeOnStop; animation.OnFinishCallback += InvokeOnFinish; } /// Unregister OnPlayCallback protected override void UnregisterOnPlayCallbacks() { if (!animation.enabled) return; animation.OnPlayCallback -= InvokeOnPlay; } /// Unregister OnStopCallback protected override void UnregisterOnStopCallbacks() { if (!animation.enabled) return; animation.OnStopCallback -= InvokeOnStop; } /// Unregister OnFinishCallback protected override void UnregisterOnFinishCallbacks() { if (!animation.enabled) return; animation.OnFinishCallback -= InvokeOnFinish; } } }