diff --git a/CHANGELOG.MD b/CHANGELOG.MD index 9f0f84a..e64d50f 100644 --- a/CHANGELOG.MD +++ b/CHANGELOG.MD @@ -7,12 +7,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased ### Changed + +## [0.5.3] +### Changed +- Exposed the `AnimationSteps` from the `AnimationSequencerController` + +### Added +- Add `ReplaceTarget` that allows you to replace the target of a step by a new one, this is useful when you want to replace a step target by a prefab, or a new instance of the same object. This will replace the target of the step and all the actions that are using the same target of that specific type. +- Add `ReplaceTargets(params (GameObject original, GameObject target)[] replacements)` that replaces all the original targets to the newTarget +- Add `ReplaceTargets(GameObject originalTarget, GameObject newTarget)` to replace all the targets of the original target to the new target + + ## [0.5.2] ### Changed - Fixed Animation not saving progress changes - The last renaming caused a lot of unwanted issues, I'm reverting this until I figure out exactly how to handle this - ## [0.5.1] ### Changed - Fixed issue when previewing animation and having the `AutoHideWhenPreviewing` enabled would cause the Steps not expand on the second play @@ -204,6 +214,7 @@ Thanks for all the [suggestions](https://github.com/brunomikoski/Animation-Seque ### Added - First initial working version +[0.5.3]: https://github.com/brunomikoski/Animation-Sequencer/releases/tag/v0.5.3 [0.5.2]: https://github.com/brunomikoski/Animation-Sequencer/releases/tag/v0.5.2 [0.5.1]: https://github.com/brunomikoski/Animation-Sequencer/releases/tag/v0.5.1 [0.5.0]: https://github.com/brunomikoski/Animation-Sequencer/releases/tag/v0.5.0 diff --git a/Scripts/Runtime/Core/AnimationSequencerController.cs b/Scripts/Runtime/Core/AnimationSequencerController.cs index fd61158..da0a340 100644 --- a/Scripts/Runtime/Core/AnimationSequencerController.cs +++ b/Scripts/Runtime/Core/AnimationSequencerController.cs @@ -1,6 +1,7 @@ #if DOTWEEN_ENABLED using System; using System.Collections; +using System.Collections.Generic; #if UNITASK_ENABLED using System.Threading; using Cysharp.Threading.Tasks; @@ -19,7 +20,7 @@ public enum PlayType Forward, Backward } - + public enum AutoplayType { Awake, @@ -27,35 +28,49 @@ public enum AutoplayType Nothing } - [SerializeReference] + [SerializeReference] private AnimationStepBase[] animationSteps = Array.Empty(); - [SerializeField] + public AnimationStepBase[] AnimationSteps => animationSteps; + + [SerializeField] private UpdateType updateType = UpdateType.Normal; - [SerializeField] + [SerializeField] private bool timeScaleIndependent = false; - [SerializeField] + [SerializeField] private AutoplayType autoplayMode = AutoplayType.Awake; - [SerializeField] + [SerializeField] protected bool startPaused; - [SerializeField] + [SerializeField] private float playbackSpeed = 1f; public float PlaybackSpeed => playbackSpeed; - [SerializeField] + [SerializeField] protected PlayType playType = PlayType.Forward; - [SerializeField] + [SerializeField] private int loops = 0; - [SerializeField] + [SerializeField] private LoopType loopType = LoopType.Restart; - [SerializeField] + [SerializeField] private bool autoKill = true; - - [SerializeField] + + [SerializeField] private UnityEvent onStartEvent = new UnityEvent(); - public UnityEvent OnStartEvent { get { return onStartEvent;} protected set {onStartEvent = value;}} - [SerializeField] + + public UnityEvent OnStartEvent + { + get => onStartEvent; + protected set => onStartEvent = value; + } + + [SerializeField] private UnityEvent onFinishedEvent = new UnityEvent(); - public UnityEvent OnFinishedEvent { get { return onFinishedEvent;} protected set {onFinishedEvent = value;}} - [SerializeField] + + public UnityEvent OnFinishedEvent + { + get => onFinishedEvent; + protected set => onFinishedEvent = value; + } + + [SerializeField] private UnityEvent onProgressEvent = new UnityEvent(); public UnityEvent OnProgressEvent => onProgressEvent; @@ -77,10 +92,10 @@ protected virtual void Awake() progress = -1; if (autoplayMode != AutoplayType.Awake) return; - + Autoplay(); } - + protected virtual void OnEnable() { if (autoplayMode != AutoplayType.OnEnable) @@ -95,12 +110,12 @@ private void Autoplay() if (startPaused) playingSequence.Pause(); } - + protected virtual void OnDisable() { if (autoplayMode != AutoplayType.OnEnable) return; - + if (playingSequence == null) return; @@ -114,7 +129,7 @@ protected virtual void OnDestroy() { ClearPlayingSequence(); } - + public virtual void Play() { Play(null); @@ -123,11 +138,11 @@ public virtual void Play() public virtual void Play(Action onCompleteCallback) { playTypeInternal = playType; - + ClearPlayingSequence(); - + onFinishedEvent.RemoveAllListeners(); - + if (onCompleteCallback != null) onFinishedEvent.AddListener(onCompleteCallback.Invoke); @@ -153,16 +168,16 @@ public virtual void PlayForward(bool resetFirst = true, Action onCompleteCallbac { if (playingSequence == null) Play(); - + playTypeInternal = PlayType.Forward; onFinishedEvent.RemoveAllListeners(); if (onCompleteCallback != null) onFinishedEvent.AddListener(onCompleteCallback.Invoke); - + if (resetFirst) SetProgress(0); - + playingSequence.PlayForward(); } @@ -170,16 +185,16 @@ public virtual void PlayBackwards(bool completeFirst = true, Action onCompleteCa { if (playingSequence == null) Play(); - + playTypeInternal = PlayType.Backward; onFinishedEvent.RemoveAllListeners(); if (onCompleteCallback != null) onFinishedEvent.AddListener(onCompleteCallback.Invoke); - + if (completeFirst) SetProgress(1); - + playingSequence.PlayBackwards(); } @@ -192,11 +207,11 @@ public virtual void SetTime(float seconds, bool andPlay = true) float finalProgress = Mathf.Clamp01(seconds / duration); SetProgress(finalProgress, andPlay); } - + public virtual void SetProgress(float targetProgress, bool andPlay = true) { targetProgress = Mathf.Clamp01(targetProgress); - + if (playingSequence == null) Play(); @@ -261,7 +276,7 @@ public virtual IEnumerator PlayEnumerator() public virtual Sequence GenerateSequence() { Sequence sequence = DOTween.Sequence(); - + // Various edge cases exists with OnStart() and OnComplete(), some of which can be solved with OnRewind(), // but it still leaves callbacks unfired when reversing direction after natural completion of the animation. // Rather than using the in-built callbacks, we simply bookend the Sequence with AppendCallback to ensure @@ -277,7 +292,7 @@ public virtual Sequence GenerateSequence() onFinishedEvent.Invoke(); } }); - + for (int i = 0; i < animationSteps.Length; i++) { animationSteps[i].AddTweenToSequence(sequence); @@ -286,10 +301,7 @@ public virtual Sequence GenerateSequence() sequence.SetTarget(this); sequence.SetAutoKill(autoKill); sequence.SetUpdate(updateType, timeScaleIndependent); - sequence.OnUpdate(() => - { - onProgressEvent.Invoke(); - }); + sequence.OnUpdate(() => { onProgressEvent.Invoke(); }); // See comment above regarding bookending via AppendCallback. sequence.AppendCallback(() => { @@ -334,41 +346,41 @@ public void ClearPlayingSequence() DOTween.Kill(playingSequence); playingSequence = null; } - + public void SetAutoplayMode(AutoplayType autoplayType) { autoplayMode = autoplayType; } - + public void SetPlayOnAwake(bool targetPlayOnAwake) { } - + public void SetPauseOnAwake(bool targetPauseOnAwake) { startPaused = targetPauseOnAwake; } - + public void SetTimeScaleIndependent(bool targetTimeScaleIndependent) { timeScaleIndependent = targetTimeScaleIndependent; } - + public void SetPlayType(PlayType targetPlayType) { playType = targetPlayType; } - + public void SetUpdateType(UpdateType targetUpdateType) { updateType = targetUpdateType; } - + public void SetAutoKill(bool targetAutoKill) { autoKill = targetAutoKill; } - + public void SetLoops(int targetLoops) { loops = targetLoops; @@ -378,7 +390,7 @@ private void Update() { if (progress == -1.0f) return; - + SetProgress(progress); } @@ -413,6 +425,46 @@ public bool TryGetStepAtIndex(int index, out T result) where T : AnimationSte return result != null; } + public void ReplaceTarget(GameObject targetGameObject) where T : GameObjectAnimationStep + { + for (int i = animationSteps.Length - 1; i >= 0; i--) + { + AnimationStepBase animationStepBase = animationSteps[i]; + if (animationStepBase == null) + continue; + + if (animationStepBase is not T gameObjectAnimationStep) + continue; + + gameObjectAnimationStep.SetTarget(targetGameObject); + } + } + + public void ReplaceTargets(params (GameObject original, GameObject target)[] replacements) + { + for (int i = 0; i < replacements.Length; i++) + { + (GameObject original, GameObject target) replacement = replacements[i]; + ReplaceTargets(replacement.original, replacement.target); + } + } + + public void ReplaceTargets(GameObject originalTarget, GameObject newTarget) + { + for (int i = animationSteps.Length - 1; i >= 0; i--) + { + AnimationStepBase animationStepBase = animationSteps[i]; + if (animationStepBase == null) + continue; + + if(animationStepBase is not GameObjectAnimationStep gameObjectAnimationStep) + continue; + + if (gameObjectAnimationStep.Target == originalTarget) + gameObjectAnimationStep.SetTarget(newTarget); + } + } + #if UNITASK_ENABLED public async UniTask PlayAsync(CancellationToken cancellationTokenSource = default) { diff --git a/package.json b/package.json index 8785961..3deba6b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "com.brunomikoski.animationsequencer", "displayName": "Animation Sequencer", - "version": "0.5.2", + "version": "0.5.3", "unity": "2021.3", "description": "Animation sequencer is a way to create complex animations of sequence of events by a friendly user interface. Requires DOTween v1.2.632", "keywords": [