From d7961316681228767ae934e3b515f3417afbe13e Mon Sep 17 00:00:00 2001 From: Harald Csaszar Date: Wed, 1 Jun 2022 19:26:55 +0200 Subject: [PATCH] [unity] Fixed Timeline SkeletonGraphic clip exception when DefaultMixDuration is disabled. Closes #2090. Added `Unscaled Time` property at Spine Timeline tracks. --- CHANGELOG.md | 3 ++- .../SpineAnimationStateGraphicTrack.cs | 16 ++++++++++++++++ .../SpineAnimationStateMixerBehaviour.cs | 3 +++ .../SpineAnimationStateTrack.cs | 8 ++++++++ 4 files changed, 29 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8289bf037..72e4e19d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -85,7 +85,8 @@ * Spine property Inspector fields (`Animation Name`, `Bone Name`, `Slot` and similar) now display the name in red when the respective animation/bone/etc no longer exists at the skeleton data. This may be helpful when such items have been renamed or deleted. * Added `UnscaledTime` property at `SkeletonAnimation` as well, behaving like `SkeletonGraphic.UnscaledTime`. If enabled, AnimationState uses unscaled game time (`Time.unscaledDeltaTime`), running animations independent of e.g. game pause (`Time.timeScale`). * `SkeletonAnimation`, `SkeletonMecanim` and `SkeletonGraphic` now provide an additional `OnAnimationRebuild` callback delegate which is issued after both the skeleton and the animation state have been initialized. - + * Timeline `SkeletonAnimation Track` and `SkeletonGraphic Track` now provide an `Unscaled Time` property. Whenever starting a new animation clip of this track, `SkeletonAnimation.UnscaledTime` or `SkeletonGraphic.UnscaledTime` will be set to this value. This allows you to play back Timeline clips either in normal game time or unscaled game time. Note that `PlayableDirector.UpdateMethod` is ignored and replaced by this property, which allows more fine-granular control per Timeline track. + * **Breaking changes** * Made `SkeletonGraphic.unscaledTime` parameter protected, use the new property `UnscaledTime` instead. * `SkeletonGraphic` `OnRebuild` callback delegate is now issued after the skeleton has been initialized, before the `AnimationState` component is initialized. This makes behaviour consistent with `SkeletonAnimation` and `SkeletonMecanim` component behaviour. Use the new callback `OnAnimationRebuild` if you want to receive a callback after the `SkeletonGraphic` `AnimationState` has been initialized. diff --git a/spine-unity/Modules/com.esotericsoftware.spine.timeline/Runtime/SpineAnimationState/SpineAnimationStateGraphicTrack.cs b/spine-unity/Modules/com.esotericsoftware.spine.timeline/Runtime/SpineAnimationState/SpineAnimationStateGraphicTrack.cs index 1c16f2f94..d8f48d241 100644 --- a/spine-unity/Modules/com.esotericsoftware.spine.timeline/Runtime/SpineAnimationState/SpineAnimationStateGraphicTrack.cs +++ b/spine-unity/Modules/com.esotericsoftware.spine.timeline/Runtime/SpineAnimationState/SpineAnimationStateGraphicTrack.cs @@ -30,6 +30,7 @@ #if UNITY_EDITOR using System.ComponentModel; #endif +using System.Collections.Generic; using UnityEngine; using UnityEngine.Playables; using UnityEngine.Timeline; @@ -43,11 +44,26 @@ namespace Spine.Unity.Playables { #endif public class SpineAnimationStateGraphicTrack : TrackAsset { public int trackIndex = 0; + [Tooltip("Whenever starting a new animation clip of this track, " + + "SkeletonGraphic.UnscaledTime will be set to this value. " + + "This allows you to play back Timeline clips either in normal game time " + + "or unscaled game time. Note that PlayableDirector.UpdateMethod " + + "is ignored and replaced by this property, which allows more fine-granular " + + "control per Timeline track.")] + public bool unscaledTime = false; public override Playable CreateTrackMixer (PlayableGraph graph, GameObject go, int inputCount) { + IEnumerable clips = this.GetClips(); + foreach (TimelineClip clip in clips) { + var animationStateClip = clip.asset as SpineAnimationStateClip; + if (animationStateClip != null) + animationStateClip.timelineClip = clip; + } + var scriptPlayable = ScriptPlayable.Create(graph, inputCount); var mixerBehaviour = scriptPlayable.GetBehaviour(); mixerBehaviour.trackIndex = this.trackIndex; + mixerBehaviour.unscaledTime = this.unscaledTime; return scriptPlayable; } } diff --git a/spine-unity/Modules/com.esotericsoftware.spine.timeline/Runtime/SpineAnimationState/SpineAnimationStateMixerBehaviour.cs b/spine-unity/Modules/com.esotericsoftware.spine.timeline/Runtime/SpineAnimationState/SpineAnimationStateMixerBehaviour.cs index 77dd3b983..5b4f5e342 100644 --- a/spine-unity/Modules/com.esotericsoftware.spine.timeline/Runtime/SpineAnimationState/SpineAnimationStateMixerBehaviour.cs +++ b/spine-unity/Modules/com.esotericsoftware.spine.timeline/Runtime/SpineAnimationState/SpineAnimationStateMixerBehaviour.cs @@ -40,6 +40,7 @@ namespace Spine.Unity.Playables { float[] lastInputWeights; bool lastAnyClipPlaying = false; public int trackIndex; + public bool unscaledTime; ScriptPlayable[] startingClips = new ScriptPlayable[2]; @@ -177,6 +178,8 @@ namespace Spine.Unity.Playables { state.SetEmptyAnimation(trackIndex, mixDuration); } else { if (clipData.animationReference.Animation != null) { + animationStateComponent.UnscaledTime = this.unscaledTime; + TrackEntry currentEntry = state.GetCurrent(trackIndex); Spine.TrackEntry trackEntry; float customMixDuration = clipData.customDuration ? GetCustomMixDuration(clipData) : 0.0f; diff --git a/spine-unity/Modules/com.esotericsoftware.spine.timeline/Runtime/SpineAnimationState/SpineAnimationStateTrack.cs b/spine-unity/Modules/com.esotericsoftware.spine.timeline/Runtime/SpineAnimationState/SpineAnimationStateTrack.cs index f62f4f88a..7a0f58f9d 100644 --- a/spine-unity/Modules/com.esotericsoftware.spine.timeline/Runtime/SpineAnimationState/SpineAnimationStateTrack.cs +++ b/spine-unity/Modules/com.esotericsoftware.spine.timeline/Runtime/SpineAnimationState/SpineAnimationStateTrack.cs @@ -44,6 +44,13 @@ namespace Spine.Unity.Playables { #endif public class SpineAnimationStateTrack : TrackAsset { public int trackIndex = 0; + [Tooltip("Whenever starting a new animation clip of this track, " + + "SkeletonAnimation.UnscaledTime will be set to this value. " + + "This allows you to play back Timeline clips either in normal game time " + + "or unscaled game time. Note that PlayableDirector.UpdateMethod " + + "is ignored and replaced by this property, which allows more fine-granular " + + "control per Timeline track.")] + public bool unscaledTime = false; public override Playable CreateTrackMixer (PlayableGraph graph, GameObject go, int inputCount) { IEnumerable clips = this.GetClips(); @@ -56,6 +63,7 @@ namespace Spine.Unity.Playables { var scriptPlayable = ScriptPlayable.Create(graph, inputCount); var mixerBehaviour = scriptPlayable.GetBehaviour(); mixerBehaviour.trackIndex = this.trackIndex; + mixerBehaviour.unscaledTime = this.unscaledTime; return scriptPlayable; } }