diff --git a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Utility/Preferences.cs b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Utility/Preferences.cs index ba20dfa98..16d6257ba 100644 --- a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Utility/Preferences.cs +++ b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Utility/Preferences.cs @@ -138,6 +138,9 @@ namespace Spine.Unity.Editor { const string MECANIM_EVENT_INCLUDE_FOLDERNAME_KEY = "SPINE_MECANIM_EVENT_INCLUDE_FOLDERNAME"; public static bool mecanimEventIncludeFolderName = SpinePreferences.DEFAULT_MECANIM_EVENT_INCLUDE_FOLDERNAME; + const string TIMELINE_USE_BLEND_DURATION_KEY = "SPINE_TIMELINE_USE_BLEND_DURATION_KEY"; + public static bool timelineUseBlendDuration = SpinePreferences.DEFAULT_TIMELINE_USE_BLEND_DURATION; + static bool preferencesLoaded = false; public static void Load () { @@ -154,6 +157,7 @@ namespace Spine.Unity.Editor { mecanimEventIncludeFolderName = EditorPrefs.GetBool(MECANIM_EVENT_INCLUDE_FOLDERNAME_KEY, SpinePreferences.DEFAULT_MECANIM_EVENT_INCLUDE_FOLDERNAME); atlasTxtImportWarning = EditorPrefs.GetBool(ATLASTXT_WARNING_KEY, SpinePreferences.DEFAULT_ATLASTXT_WARNING); textureImporterWarning = EditorPrefs.GetBool(TEXTUREIMPORTER_WARNING_KEY, SpinePreferences.DEFAULT_TEXTUREIMPORTER_WARNING); + timelineUseBlendDuration = EditorPrefs.GetBool(TIMELINE_USE_BLEND_DURATION_KEY, SpinePreferences.DEFAULT_TIMELINE_USE_BLEND_DURATION); SpineHandles.handleScale = EditorPrefs.GetFloat(SCENE_ICONS_SCALE_KEY, DEFAULT_SCENE_ICONS_SCALE); preferencesLoaded = true; @@ -171,6 +175,7 @@ namespace Spine.Unity.Editor { newPreferences.mecanimEventIncludeFolderName = EditorPrefs.GetBool(MECANIM_EVENT_INCLUDE_FOLDERNAME_KEY, SpinePreferences.DEFAULT_MECANIM_EVENT_INCLUDE_FOLDERNAME); newPreferences.atlasTxtImportWarning = EditorPrefs.GetBool(ATLASTXT_WARNING_KEY, SpinePreferences.DEFAULT_ATLASTXT_WARNING); newPreferences.textureImporterWarning = EditorPrefs.GetBool(TEXTUREIMPORTER_WARNING_KEY, SpinePreferences.DEFAULT_TEXTUREIMPORTER_WARNING); + newPreferences.timelineUseBlendDuration = EditorPrefs.GetBool(TIMELINE_USE_BLEND_DURATION_KEY, SpinePreferences.DEFAULT_TIMELINE_USE_BLEND_DURATION); } public static void SaveToEditorPrefs(SpinePreferences preferences) { @@ -184,6 +189,7 @@ namespace Spine.Unity.Editor { EditorPrefs.SetBool(MECANIM_EVENT_INCLUDE_FOLDERNAME_KEY, preferences.mecanimEventIncludeFolderName); EditorPrefs.SetBool(ATLASTXT_WARNING_KEY, preferences.atlasTxtImportWarning); EditorPrefs.SetBool(TEXTUREIMPORTER_WARNING_KEY, preferences.textureImporterWarning); + EditorPrefs.SetBool(TIMELINE_USE_BLEND_DURATION_KEY, preferences.timelineUseBlendDuration); } #endif @@ -265,6 +271,12 @@ namespace Spine.Unity.Editor { if (GUILayout.Button("Disable", GUILayout.Width(64))) SpineTK2DEditorUtility.DisableTK2D(); } + + GUILayout.Space(20); + EditorGUILayout.LabelField("Timeline Extension", EditorStyles.boldLabel); + { + SpineEditorUtilities.BoolPrefsField(ref timelineUseBlendDuration, TIMELINE_USE_BLEND_DURATION_KEY, new GUIContent("Use Blend Duration", "When enabled, MixDuration will be synced with timeline clip transition duration 'Ease In Duration'.")); + } } #endif // !NEW_PREFERENCES_SETTINGS_PROVIDER } diff --git a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Windows/SpinePreferences.cs b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Windows/SpinePreferences.cs index 6311fb559..4343311b9 100644 --- a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Windows/SpinePreferences.cs +++ b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Windows/SpinePreferences.cs @@ -92,6 +92,10 @@ namespace Spine.Unity.Editor { public const bool DEFAULT_MECANIM_EVENT_INCLUDE_FOLDERNAME = true; public bool mecanimEventIncludeFolderName = DEFAULT_MECANIM_EVENT_INCLUDE_FOLDERNAME; + // Timeline extension module + public const bool DEFAULT_TIMELINE_USE_BLEND_DURATION = true; + public bool timelineUseBlendDuration = DEFAULT_TIMELINE_USE_BLEND_DURATION; + #if NEW_PREFERENCES_SETTINGS_PROVIDER public static void Load () { SpineHandles.handleScale = EditorPrefs.GetFloat(SCENE_ICONS_SCALE_KEY, DEFAULT_SCENE_ICONS_SCALE); @@ -185,6 +189,12 @@ namespace Spine.Unity.Editor { if (GUILayout.Button("Disable", GUILayout.Width(64))) SpineEditorUtilities.SpineTK2DEditorUtility.DisableTK2D(); } + + GUILayout.Space(20); + EditorGUILayout.LabelField("Timeline Extension", EditorStyles.boldLabel); + { + EditorGUILayout.PropertyField(settings.FindProperty("timelineUseBlendDuration"), new GUIContent("Use Blend Duration", "When enabled, MixDuration will be synced with timeline clip transition duration 'Ease In Duration'.")); + } } EditorGUIUtility.labelWidth = prevLabelWidth; } diff --git a/spine-unity/Modules/com.esotericsoftware.spine.timeline/Editor/SpineAnimationStateClipInspector.cs b/spine-unity/Modules/com.esotericsoftware.spine.timeline/Editor/SpineAnimationStateClipInspector.cs new file mode 100644 index 000000000..3146633c7 --- /dev/null +++ b/spine-unity/Modules/com.esotericsoftware.spine.timeline/Editor/SpineAnimationStateClipInspector.cs @@ -0,0 +1,116 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using UnityEditor; +using Spine.Unity.Playables; +using UnityEngine.Timeline; + +namespace Spine.Unity.Editor { + + [CustomEditor(typeof(SpineAnimationStateClip))] + [CanEditMultipleObjects] + public class SpineAnimationStateClipInspector : UnityEditor.Editor { + + protected SerializedProperty templateProp = null; + + protected class ClipInfo { + public TimelineClip timelineClip; + public float previousBlendInDuration = -1.0f; + public float unblendedMixDuration = 0.2f; + } + + protected ClipInfo[] clipInfo = null; + + public void OnEnable () { + templateProp = serializedObject.FindProperty("template"); + System.Array.Resize(ref clipInfo, targets.Length); + for (int i = 0; i < targets.Length; ++i) { + var clip = (SpineAnimationStateClip)targets[i]; + clipInfo[i] = new ClipInfo(); + clipInfo[i].timelineClip = FindTimelineClip(clip); + } + } + + public override void OnInspectorGUI () { + serializedObject.Update(); + EditorGUILayout.PropertyField(templateProp); + + for (int i = 0; i < targets.Length; ++i) { + var targetClip = (SpineAnimationStateClip)targets[i]; + if (targetClip.template.useBlendDuration) + AdjustMixDuration(targetClip, clipInfo[i]); + } + + serializedObject.ApplyModifiedProperties(); + } + + protected void AdjustMixDuration(SpineAnimationStateClip targetClip, ClipInfo timelineClipInfo) { + + if (timelineClipInfo == null) + return; + + var timelineClip = timelineClipInfo.timelineClip; + if (timelineClip == null) + return; + + float blendInDur = (float)timelineClip.blendInDuration; + bool isBlendingNow = blendInDur > 0; + bool wasBlendingBefore = timelineClipInfo.previousBlendInDuration > 0; + + if (isBlendingNow) { + if (!wasBlendingBefore) { + timelineClipInfo.unblendedMixDuration = targetClip.template.mixDuration; + } + targetClip.template.mixDuration = blendInDur; + EditorUtility.SetDirty(targetClip); + } + else if (wasBlendingBefore) { + targetClip.template.mixDuration = timelineClipInfo.unblendedMixDuration; + EditorUtility.SetDirty(targetClip); + } + timelineClipInfo.previousBlendInDuration = blendInDur; + } + + protected TimelineClip FindTimelineClip(SpineAnimationStateClip targetClip) { + string[] guids = AssetDatabase.FindAssets("t:TimelineAsset"); + foreach (string guid in guids) { + TimelineAsset timeline = (TimelineAsset)AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(guid), typeof(TimelineAsset)); + foreach (var track in timeline.GetOutputTracks()) { + foreach (var clip in track.GetClips()) { + if (clip.asset.GetType() == typeof(SpineAnimationStateClip) && object.ReferenceEquals(clip.asset, targetClip)) { + return clip; + } + } + } + } + return null; + } + + } +} diff --git a/spine-unity/Modules/com.esotericsoftware.spine.timeline/Editor/SpineAnimationStateClipInspector.cs.meta b/spine-unity/Modules/com.esotericsoftware.spine.timeline/Editor/SpineAnimationStateClipInspector.cs.meta new file mode 100644 index 000000000..2bf0bf181 --- /dev/null +++ b/spine-unity/Modules/com.esotericsoftware.spine.timeline/Editor/SpineAnimationStateClipInspector.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 95642d062fbcfda4e8d9262fa715b098 +timeCreated: 1570044805 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/spine-unity/Modules/com.esotericsoftware.spine.timeline/Editor/SpineAnimationStateDrawer.cs b/spine-unity/Modules/com.esotericsoftware.spine.timeline/Editor/SpineAnimationStateDrawer.cs index 7b8ce4453..dcd472ea7 100644 --- a/spine-unity/Modules/com.esotericsoftware.spine.timeline/Editor/SpineAnimationStateDrawer.cs +++ b/spine-unity/Modules/com.esotericsoftware.spine.timeline/Editor/SpineAnimationStateDrawer.cs @@ -32,30 +32,39 @@ using UnityEngine; using Spine; using Spine.Unity; using Spine.Unity.Playables; +using Spine.Unity.Editor; -//[CustomPropertyDrawer(typeof(SpineAnimationStateBehaviour))] +[CustomPropertyDrawer(typeof(SpineAnimationStateBehaviour))] public class SpineAnimationStateDrawer : PropertyDrawer { - /* + public override float GetPropertyHeight (SerializedProperty property, GUIContent label) { const int fieldCount = 8; return fieldCount * EditorGUIUtility.singleLineHeight; } public override void OnGUI (Rect position, SerializedProperty property, GUIContent label) { - SerializedProperty skeletonDataAssetProp = property.FindPropertyRelative("skeletonDataAsset"); - SerializedProperty animationNameProp = property.FindPropertyRelative("animationName"); + SerializedProperty animationReferenceProp = property.FindPropertyRelative("animationReference"); SerializedProperty loopProp = property.FindPropertyRelative("loop"); + + SerializedProperty customDurationProp = property.FindPropertyRelative("customDuration"); + SerializedProperty useBlendDurationProp = property.FindPropertyRelative("useBlendDuration"); + SerializedProperty mixDurationProp = property.FindPropertyRelative("mixDuration"); SerializedProperty eventProp = property.FindPropertyRelative("eventThreshold"); SerializedProperty attachmentProp = property.FindPropertyRelative("attachmentThreshold"); SerializedProperty drawOrderProp = property.FindPropertyRelative("drawOrderThreshold"); + // initialize useBlendDuration parameter according to preferences + SerializedProperty isInitializedProp = property.FindPropertyRelative("isInitialized"); + if (!isInitializedProp.hasMultipleDifferentValues && isInitializedProp.boolValue == false) { + useBlendDurationProp.boolValue = SpineEditorUtilities.Preferences.timelineUseBlendDuration; + isInitializedProp.boolValue = true; + } + Rect singleFieldRect = new Rect(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight); - EditorGUI.PropertyField(singleFieldRect, skeletonDataAssetProp); float lineHeightWithSpacing = EditorGUIUtility.singleLineHeight + 2f; - singleFieldRect.y += lineHeightWithSpacing; - EditorGUI.PropertyField(singleFieldRect, animationNameProp); + EditorGUI.PropertyField(singleFieldRect, animationReferenceProp); singleFieldRect.y += lineHeightWithSpacing; EditorGUI.PropertyField(singleFieldRect, loopProp); @@ -65,6 +74,19 @@ public class SpineAnimationStateDrawer : PropertyDrawer { singleFieldRect.y += lineHeightWithSpacing; EditorGUI.LabelField(singleFieldRect, "Mixing Settings", EditorStyles.boldLabel); + singleFieldRect.y += lineHeightWithSpacing; + EditorGUI.PropertyField(singleFieldRect, customDurationProp); + + bool greyOutCustomDurations = (!customDurationProp.hasMultipleDifferentValues && + customDurationProp.boolValue == false); + using (new EditorGUI.DisabledGroupScope(greyOutCustomDurations)) { + singleFieldRect.y += lineHeightWithSpacing; + EditorGUI.PropertyField(singleFieldRect, useBlendDurationProp); + + singleFieldRect.y += lineHeightWithSpacing; + EditorGUI.PropertyField(singleFieldRect, mixDurationProp); + } + singleFieldRect.y += lineHeightWithSpacing; EditorGUI.PropertyField(singleFieldRect, eventProp); @@ -74,5 +96,4 @@ public class SpineAnimationStateDrawer : PropertyDrawer { singleFieldRect.y += lineHeightWithSpacing; EditorGUI.PropertyField(singleFieldRect, drawOrderProp); } - */ } diff --git a/spine-unity/Modules/com.esotericsoftware.spine.timeline/Editor/SpineSkeletonFlipClipInspector.cs b/spine-unity/Modules/com.esotericsoftware.spine.timeline/Editor/SpineSkeletonFlipClipInspector.cs new file mode 100644 index 000000000..d309d2af5 --- /dev/null +++ b/spine-unity/Modules/com.esotericsoftware.spine.timeline/Editor/SpineSkeletonFlipClipInspector.cs @@ -0,0 +1,51 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using UnityEditor; +using Spine.Unity.Playables; + +namespace Spine.Unity.Editor { + + [CustomEditor(typeof(SpineSkeletonFlipClip))] + [CanEditMultipleObjects] + public class SpineSkeletonFlipClipInspector : UnityEditor.Editor { + + protected SerializedProperty templateProp = null; + + public void OnEnable () { + templateProp = serializedObject.FindProperty("template"); + } + + public override void OnInspectorGUI () { + serializedObject.Update(); + EditorGUILayout.PropertyField(templateProp); + serializedObject.ApplyModifiedProperties(); + } + } +} diff --git a/spine-unity/Modules/com.esotericsoftware.spine.timeline/Editor/SpineSkeletonFlipClipInspector.cs.meta b/spine-unity/Modules/com.esotericsoftware.spine.timeline/Editor/SpineSkeletonFlipClipInspector.cs.meta new file mode 100644 index 000000000..8d76bb6f9 --- /dev/null +++ b/spine-unity/Modules/com.esotericsoftware.spine.timeline/Editor/SpineSkeletonFlipClipInspector.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: c6d7cdfbf1ccc0042b92586542f085a4 +timeCreated: 1570045635 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/spine-unity/Modules/com.esotericsoftware.spine.timeline/Runtime/SpineAnimationState/SpineAnimationStateBehaviour.cs b/spine-unity/Modules/com.esotericsoftware.spine.timeline/Runtime/SpineAnimationState/SpineAnimationStateBehaviour.cs index f9827e47f..1a1d028de 100644 --- a/spine-unity/Modules/com.esotericsoftware.spine.timeline/Runtime/SpineAnimationState/SpineAnimationStateBehaviour.cs +++ b/spine-unity/Modules/com.esotericsoftware.spine.timeline/Runtime/SpineAnimationState/SpineAnimationStateBehaviour.cs @@ -44,8 +44,11 @@ namespace Spine.Unity.Playables { public AnimationReferenceAsset animationReference; public bool loop; - [Header("Mix Properties")] + // Mix Properties public bool customDuration = false; + public bool useBlendDuration = true; + [SerializeField] + private bool isInitialized = false; // required to read preferences values from editor side. public float mixDuration = 0.1f; [Range(0, 1f)] diff --git a/spine-unity/Modules/com.esotericsoftware.spine.timeline/Runtime/SpineAnimationState/SpineAnimationStateClip.cs b/spine-unity/Modules/com.esotericsoftware.spine.timeline/Runtime/SpineAnimationState/SpineAnimationStateClip.cs index ed62715e1..21a0a9034 100644 --- a/spine-unity/Modules/com.esotericsoftware.spine.timeline/Runtime/SpineAnimationState/SpineAnimationStateClip.cs +++ b/spine-unity/Modules/com.esotericsoftware.spine.timeline/Runtime/SpineAnimationState/SpineAnimationStateClip.cs @@ -37,13 +37,21 @@ namespace Spine.Unity.Playables { public class SpineAnimationStateClip : PlayableAsset, ITimelineClipAsset { public SpineAnimationStateBehaviour template = new SpineAnimationStateBehaviour(); - public ClipCaps clipCaps { get { return ClipCaps.None; } } + public ClipCaps clipCaps { get { return ClipCaps.Blending | ClipCaps.ClipIn | ClipCaps.SpeedMultiplier | (template.loop ? ClipCaps.Looping : 0); } } public override Playable CreatePlayable (PlayableGraph graph, GameObject owner) { var playable = ScriptPlayable.Create(graph, template); playable.GetBehaviour(); return playable; } + + public override double duration { + get { + if (template.animationReference == null) + return 0; + return template.animationReference.Animation.Duration; + } + } } } 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 b92da4a52..3d03d275c 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 @@ -70,7 +70,7 @@ namespace Spine.Unity.Playables { for (int i = 0; i < inputCount; i++) { float lastInputWeight = lastInputWeights[i]; float inputWeight = playable.GetInputWeight(i); - bool trackStarted = inputWeight > lastInputWeight; + bool trackStarted = lastInputWeight == 0 && inputWeight > 0; lastInputWeights[i] = inputWeight; if (trackStarted) { @@ -84,9 +84,10 @@ namespace Spine.Unity.Playables { if (clipData.animationReference.Animation != null) { Spine.TrackEntry trackEntry = state.SetAnimation(trackIndex, clipData.animationReference.Animation, clipData.loop); - //trackEntry.TrackTime = (float)inputPlayable.GetTime(); // More accurate time-start? trackEntry.EventThreshold = clipData.eventThreshold; trackEntry.DrawOrderThreshold = clipData.drawOrderThreshold; + trackEntry.TrackTime = (float)inputPlayable.GetTime() * (float)inputPlayable.GetSpeed(); + trackEntry.TimeScale = (float)inputPlayable.GetSpeed(); trackEntry.AttachmentThreshold = clipData.attachmentThreshold; if (clipData.customDuration) @@ -111,15 +112,15 @@ namespace Spine.Unity.Playables { if (spineComponent == null) return; int inputCount = playable.GetInputCount(); - int lastOneWeight = -1; + int lastNonZeroWeightTrack = -1; for (int i = 0; i < inputCount; i++) { float inputWeight = playable.GetInputWeight(i); - if (inputWeight >= 1) lastOneWeight = i; + if (inputWeight > 0) lastNonZeroWeightTrack = i; } - if (lastOneWeight != -1) { - ScriptPlayable inputPlayableClip = (ScriptPlayable)playable.GetInput(lastOneWeight); + if (lastNonZeroWeightTrack != -1) { + ScriptPlayable inputPlayableClip = (ScriptPlayable)playable.GetInput(lastNonZeroWeightTrack); SpineAnimationStateBehaviour clipData = inputPlayableClip.GetBehaviour(); var skeleton = spineComponent.Skeleton; @@ -133,16 +134,16 @@ namespace Spine.Unity.Playables { Animation fromAnimation = null; float fromClipTime = 0; bool fromClipLoop = false; - if (lastOneWeight != 0 && inputCount > 1) { - var fromClip = (ScriptPlayable)playable.GetInput(lastOneWeight - 1); + if (lastNonZeroWeightTrack != 0 && inputCount > 1) { + var fromClip = (ScriptPlayable)playable.GetInput(lastNonZeroWeightTrack - 1); var fromClipData = fromClip.GetBehaviour(); fromAnimation = fromClipData.animationReference != null ? fromClipData.animationReference.Animation : null; - fromClipTime = (float)fromClip.GetTime(); + fromClipTime = (float)fromClip.GetTime() * (float)fromClip.GetSpeed(); fromClipLoop = fromClipData.loop; } Animation toAnimation = clipData.animationReference != null ? clipData.animationReference.Animation : null; - float toClipTime = (float)inputPlayableClip.GetTime(); + float toClipTime = (float)inputPlayableClip.GetTime() * (float)inputPlayableClip.GetSpeed(); float mixDuration = clipData.mixDuration; if (!clipData.customDuration && fromAnimation != null && toAnimation != null) {