From 570f8df178ad467551d7b25ede6d3ec8b54f8df8 Mon Sep 17 00:00:00 2001 From: Harald Csaszar Date: Thu, 26 Sep 2019 17:04:33 +0200 Subject: [PATCH 1/6] [csharp] Store timeline ids inside set in Animation for O(1) lookup. See #1462. --- spine-csharp/src/Animation.cs | 9 +++++++++ spine-csharp/src/AnimationState.cs | 11 ++--------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/spine-csharp/src/Animation.cs b/spine-csharp/src/Animation.cs index 583a300fe..ce30bd8d3 100644 --- a/spine-csharp/src/Animation.cs +++ b/spine-csharp/src/Animation.cs @@ -37,11 +37,15 @@ namespace Spine { public class Animation { internal String name; internal ExposedList timelines; + internal HashSet timelineIds; internal float duration; public Animation (string name, ExposedList timelines, float duration) { if (name == null) throw new ArgumentNullException("name", "name cannot be null."); if (timelines == null) throw new ArgumentNullException("timelines", "timelines cannot be null."); + this.timelineIds = new HashSet(); + foreach (Timeline timeline in timelines) + timelineIds.Add(timeline.PropertyId); this.name = name; this.timelines = timelines; this.duration = duration; @@ -55,6 +59,11 @@ namespace Spine { /// The animation's name, which is unique across all animations in the skeleton. public string Name { get { return name; } } + /// Whether the timeline with the property id is contained in this animation. + public bool HasTimeline (int id) { + return timelineIds.Contains(id); + } + /// Applies all the animation's timelines to the specified skeleton. /// public void Apply (Skeleton skeleton, float lastTime, float time, bool loop, ExposedList events, float alpha, MixBlend blend, diff --git a/spine-csharp/src/AnimationState.cs b/spine-csharp/src/AnimationState.cs index b0359ecf8..3dfed92ae 100644 --- a/spine-csharp/src/AnimationState.cs +++ b/spine-csharp/src/AnimationState.cs @@ -777,11 +777,11 @@ namespace Spine { if (!propertyIDs.Add(id)) timelineMode[i] = AnimationState.Subsequent; else if (to == null || timeline is AttachmentTimeline || timeline is DrawOrderTimeline - || timeline is EventTimeline || !HasTimeline(to, id)) { + || timeline is EventTimeline || !to.animation.HasTimeline(id)) { timelineMode[i] = AnimationState.First; } else { for (TrackEntry next = to.mixingTo; next != null; next = next.mixingTo) { - if (HasTimeline(next, id)) continue; + if (next.animation.HasTimeline(id)) continue; if (next.mixDuration > 0) { timelineMode[i] = AnimationState.HoldMix; timelineHoldMix[i] = next; @@ -809,13 +809,6 @@ namespace Spine { } } - static bool HasTimeline (TrackEntry entry, int id) { - var timelines = entry.animation.timelines.Items; - for (int i = 0, n = entry.animation.timelines.Count; i < n; i++) - if (timelines[i].PropertyId == id) return true; - return false; - } - /// The track entry for the animation currently playing on the track, or null if no animation is currently playing. public TrackEntry GetCurrent (int trackIndex) { if (trackIndex >= tracks.Count) return null; From b1c1dac17a8b862dc30a43c2c7bd1e1d7732b0d2 Mon Sep 17 00:00:00 2001 From: Harald Csaszar Date: Thu, 26 Sep 2019 18:43:17 +0200 Subject: [PATCH 2/6] [unity] Fixed ignored SkeletonAnimation looping change when AnimationName is the same. Closes #1396. --- .../Runtime/spine-unity/Components/SkeletonAnimation.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonAnimation.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonAnimation.cs index 73d981778..955e57d74 100644 --- a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonAnimation.cs +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonAnimation.cs @@ -96,8 +96,11 @@ namespace Spine.Unity { } } set { - if (_animationName == value) - return; + if (_animationName == value) { + TrackEntry entry = state.GetCurrent(0); + if (entry != null && entry.loop == loop) + return; + } _animationName = value; if (string.IsNullOrEmpty(value)) { From 19bbd5519fdf1f6d1612e1a9677a2c283ebc24ff Mon Sep 17 00:00:00 2001 From: Harald Csaszar Date: Fri, 27 Sep 2019 12:03:50 +0200 Subject: [PATCH 3/6] [untiy] Fixed a compile error when building executable (Texture2D.alphaIsTransparency exists only in editor). --- .../Assets/Spine/Runtime/spine-unity/Utility/AtlasUtilities.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AtlasUtilities.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AtlasUtilities.cs index 02c882a3d..0a241356b 100644 --- a/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AtlasUtilities.cs +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AtlasUtilities.cs @@ -580,7 +580,9 @@ namespace Spine.Unity.AttachmentTools { static void CopyTextureAttributesFrom(this Texture2D destination, Texture2D source) { destination.filterMode = source.filterMode; destination.anisoLevel = source.anisoLevel; + #if UNITY_EDITOR destination.alphaIsTransparency = source.alphaIsTransparency; + #endif destination.wrapModeU = source.wrapModeU; destination.wrapModeV = source.wrapModeV; destination.wrapModeW = source.wrapModeW; From 9c1143198852b754dba539cdbdd8668b7252fc53 Mon Sep 17 00:00:00 2001 From: Harald Csaszar Date: Fri, 27 Sep 2019 17:22:19 +0200 Subject: [PATCH 4/6] [unity] Adapted skeleton baking window to Unity 2018.3 prefab API and added it to context menu again. Closes #1471. --- .../Editor/Windows/SkeletonBaker.cs | 20 ++++++++++++++----- .../Editor/Windows/SkeletonBakingWindow.cs | 6 ------ 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Windows/SkeletonBaker.cs b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Windows/SkeletonBaker.cs index f41ac733a..094e75e4b 100644 --- a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Windows/SkeletonBaker.cs +++ b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Windows/SkeletonBaker.cs @@ -184,9 +184,7 @@ namespace Spine.Unity.Editor { Debug.LogError("Could not export Spine Skeleton because SkeletonDataAsset is null or invalid!"); return; } - - #if !NEW_PREFAB_SYSTEM - + if (outputPath == "") { outputPath = System.IO.Path.GetDirectoryName(AssetDatabase.GetAssetPath(skeletonDataAsset)) + "/Baked"; System.IO.Directory.CreateDirectory(outputPath); @@ -281,7 +279,13 @@ namespace Spine.Unity.Editor { Object prefab = AssetDatabase.LoadAssetAtPath(prefabPath, typeof(GameObject)); if (prefab == null) { + #if NEW_PREFAB_SYSTEM + GameObject emptyGameObject = new GameObject(); + prefab = PrefabUtility.SaveAsPrefabAssetAndConnect(emptyGameObject, prefabPath, InteractionMode.AutomatedAction); + GameObject.DestroyImmediate(emptyGameObject); + #else prefab = PrefabUtility.CreateEmptyPrefab(prefabPath); + #endif newPrefab = true; } @@ -428,14 +432,22 @@ namespace Spine.Unity.Editor { } if (newPrefab) { + #if NEW_PREFAB_SYSTEM + PrefabUtility.SaveAsPrefabAssetAndConnect(prefabRoot, prefabPath, InteractionMode.AutomatedAction); + #else PrefabUtility.ReplacePrefab(prefabRoot, prefab, ReplacePrefabOptions.ConnectToPrefab); + #endif } else { foreach (string str in unusedMeshNames) { Mesh.DestroyImmediate(meshTable[str], true); } + #if NEW_PREFAB_SYSTEM + PrefabUtility.SaveAsPrefabAssetAndConnect(prefabRoot, prefabPath, InteractionMode.AutomatedAction); + #else PrefabUtility.ReplacePrefab(prefabRoot, prefab, ReplacePrefabOptions.ReplaceNameBased); + #endif } @@ -447,8 +459,6 @@ namespace Spine.Unity.Editor { GameObject.DestroyImmediate(prefabRoot); } - #endif - } #region Attachment Baking diff --git a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Windows/SkeletonBakingWindow.cs b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Windows/SkeletonBakingWindow.cs index 42de92cf6..08cb58713 100644 --- a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Windows/SkeletonBakingWindow.cs +++ b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Windows/SkeletonBakingWindow.cs @@ -27,10 +27,6 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ -#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER -#define NEW_PREFAB_SYSTEM -#endif - using System.Collections; using System.Collections.Generic; using UnityEngine; @@ -44,7 +40,6 @@ namespace Spine.Unity.Editor { public class SkeletonBakingWindow : EditorWindow { const bool IsUtilityWindow = true; - #if !NEW_PREFAB_SYSTEM [MenuItem("CONTEXT/SkeletonDataAsset/Skeleton Baking", false, 5000)] public static void Init (MenuCommand command) { var window = EditorWindow.GetWindow(IsUtilityWindow); @@ -54,7 +49,6 @@ namespace Spine.Unity.Editor { window.skeletonDataAsset = command.context as SkeletonDataAsset; window.Show(); } - #endif public SkeletonDataAsset skeletonDataAsset; [SpineSkin(dataField:"skeletonDataAsset")] From f0b25c8a86c739e7d502362e50d71182f1b615e5 Mon Sep 17 00:00:00 2001 From: Harald Csaszar Date: Thu, 3 Oct 2019 15:24:10 +0200 Subject: [PATCH 5/6] [unity] Added multiple features to Timeline SpineAnimationStateClip such as playback speed, blending by overlap and synchronisation of MixDuration with blend duration. Adds preferences parameter "Use Blend Duration" which can be disabled to enable old behaviour. Fixes #1262. --- .../spine-unity/Editor/Utility/Preferences.cs | 12 ++ .../Editor/Windows/SpinePreferences.cs | 10 ++ .../SpineAnimationStateClipInspector.cs | 116 ++++++++++++++++++ .../SpineAnimationStateClipInspector.cs.meta | 12 ++ .../Editor/SpineAnimationStateDrawer.cs | 37 ++++-- .../Editor/SpineSkeletonFlipClipInspector.cs | 51 ++++++++ .../SpineSkeletonFlipClipInspector.cs.meta | 12 ++ .../SpineAnimationStateBehaviour.cs | 5 +- .../SpineAnimationStateClip.cs | 10 +- .../SpineAnimationStateMixerBehaviour.cs | 21 ++-- 10 files changed, 266 insertions(+), 20 deletions(-) create mode 100644 spine-unity/Modules/com.esotericsoftware.spine.timeline/Editor/SpineAnimationStateClipInspector.cs create mode 100644 spine-unity/Modules/com.esotericsoftware.spine.timeline/Editor/SpineAnimationStateClipInspector.cs.meta create mode 100644 spine-unity/Modules/com.esotericsoftware.spine.timeline/Editor/SpineSkeletonFlipClipInspector.cs create mode 100644 spine-unity/Modules/com.esotericsoftware.spine.timeline/Editor/SpineSkeletonFlipClipInspector.cs.meta 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) { From e769b848da259819be94d5595530d0e15a09cd75 Mon Sep 17 00:00:00 2001 From: Harald Csaszar Date: Thu, 3 Oct 2019 16:03:44 +0200 Subject: [PATCH 6/6] [unity] Fixed SkeletonGraphic displaying setup pose instead of first frame on freeze. Closes #1511. --- .../Spine/Runtime/spine-unity/Components/SkeletonGraphic.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonGraphic.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonGraphic.cs index dde7a7a5e..a6e05b8f5 100644 --- a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonGraphic.cs +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonGraphic.cs @@ -288,6 +288,8 @@ namespace Spine.Unity { if (!Application.isPlaying) Update(0f); #endif + if (freeze) + Update(0f); } } }