From 6c40624d5e99d8b147d00bb23c6192ebf1c18c83 Mon Sep 17 00:00:00 2001 From: Harald Csaszar Date: Thu, 8 Apr 2021 19:17:46 +0200 Subject: [PATCH 01/10] [unity] Set Mipmap Bias was not persisted on recent Unity versions. Closes #1877. --- .../Editor/Asset Types/SpineAtlasAssetInspector.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Asset Types/SpineAtlasAssetInspector.cs b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Asset Types/SpineAtlasAssetInspector.cs index 6614da270..ef9b437e4 100644 --- a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Asset Types/SpineAtlasAssetInspector.cs +++ b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Asset Types/SpineAtlasAssetInspector.cs @@ -136,7 +136,10 @@ namespace Spine.Unity.Editor { if (SpineInspectorUtility.LargeCenteredButton(SpineInspectorUtility.TempContent("Set Mipmap Bias to " + SpinePreferences.DEFAULT_MIPMAPBIAS, tooltip: "This may help textures with mipmaps be less blurry when used for 2D sprites."))) { foreach (var m in atlasAsset.materials) { var texture = m.mainTexture; - texture.mipMapBias = SpinePreferences.DEFAULT_MIPMAPBIAS; + string texturePath = AssetDatabase.GetAssetPath(texture.GetInstanceID()); + var importer = (TextureImporter)TextureImporter.GetAtPath(texturePath); + importer.mipMapBias = SpinePreferences.DEFAULT_MIPMAPBIAS; + EditorUtility.SetDirty(texture); } Debug.Log("Texture mipmap bias set to " + SpinePreferences.DEFAULT_MIPMAPBIAS); } From 758a83ac9666c79f81d385e3933e199b8bd7f9c0 Mon Sep 17 00:00:00 2001 From: Harald Csaszar Date: Fri, 9 Apr 2021 17:15:45 +0200 Subject: [PATCH 02/10] [unity] `SkeletonRootMotion` components now support arbitrary bones in the hierarchy as `Root Motion Bone`. Now supporting arbitrary `Skeleton.ScaleX/Y` and parent bone scale. Closes #1876. --- CHANGELOG.md | 1 + .../RootMotion/SkeletonRootMotionBase.cs | 46 +++++++++++++------ 2 files changed, 34 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9578d901d..09638ce64 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -163,6 +163,7 @@ b) to an arbitrary directory outside the Assets directory and then open Package Manager in Unity, select the `+` icon, choose `Add package from disk..` and point it to the package.json file. The Project panel should now show an entry `Spine Timeline Extensions` under `Packages`. If the directory is not yet listed, you will need to close and re-open Unity to have it display the directory and its contents. * `SkeletonMecanim`'s `Layer Mix Mode` enum name `MixMode.SpineStyle` has been renamed to `MixMode.Hard`. This is most likely not set via code and thus unlikely to be a problem. Serialized scenes and prefabs are unaffected. + * `SkeletonRootMotion` and `SkeletonMecanimRootMotion` components now support arbitrary bones in the hierarchy as `Root Motion Bone`. Previously there were problems when selecting a non-root bone as `Root Motion Bone`. `Skeleton.ScaleX` and `.ScaleY` and parent bone scale is now respected as well. * **Additions** * **Spine Preferences stored in Assets/Editor/SpineSettings.asset** Now Spine uses the new `SettingsProvider` API, storing settings in a SpineSettings.asset file which can be shared with team members. Your old preferences are automatically migrated to the new system. diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/RootMotion/SkeletonRootMotionBase.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/RootMotion/SkeletonRootMotionBase.cs index 9f7defcd0..32ecaac06 100644 --- a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/RootMotion/SkeletonRootMotionBase.cs +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/RootMotion/SkeletonRootMotionBase.cs @@ -61,6 +61,7 @@ namespace Spine.Unity { protected Bone rootMotionBone; protected int rootMotionBoneIndex; protected List topLevelBones = new List(); + protected Vector2 initialOffset = Vector2.zero; protected Vector2 rigidbodyDisplacement; protected virtual void Reset () { @@ -71,10 +72,14 @@ namespace Spine.Unity { skeletonComponent = GetComponent(); GatherTopLevelBones(); SetRootMotionBone(rootMotionBoneName); + if (rootMotionBone != null) + initialOffset = new Vector2(rootMotionBone.x, rootMotionBone.y); var skeletonAnimation = skeletonComponent as ISkeletonAnimation; - if (skeletonAnimation != null) + if (skeletonAnimation != null) { + skeletonAnimation.UpdateLocal -= HandleUpdateLocal; skeletonAnimation.UpdateLocal += HandleUpdateLocal; + } } protected virtual void FixedUpdate () { @@ -178,36 +183,51 @@ namespace Spine.Unity { return; // Root motion is only applied when component is enabled. var movementDelta = CalculateAnimationsMovementDelta(); - AdjustMovementDeltaToConfiguration(ref movementDelta, animatedSkeletonComponent.Skeleton); - ApplyRootMotion(movementDelta); + Vector2 parentBoneScale; + AdjustMovementDeltaToConfiguration(ref movementDelta, out parentBoneScale, animatedSkeletonComponent.Skeleton); + ApplyRootMotion(movementDelta, parentBoneScale); } - void AdjustMovementDeltaToConfiguration (ref Vector2 localDelta, Skeleton skeleton) { - if (skeleton.ScaleX < 0) localDelta.x = -localDelta.x; - if (skeleton.ScaleY < 0) localDelta.y = -localDelta.y; - if (!transformPositionX) localDelta.x = 0f; - if (!transformPositionY) localDelta.y = 0f; - } + void AdjustMovementDeltaToConfiguration (ref Vector2 localDelta, out Vector2 parentBoneScale, Skeleton skeleton) { + localDelta.x *= skeleton.ScaleX; + localDelta.y *= skeleton.ScaleY; + + parentBoneScale = Vector2.one; + Bone scaleBone = rootMotionBone; + while ((scaleBone = scaleBone.parent) != null) { + parentBoneScale.x *= scaleBone.ScaleX; + parentBoneScale.y *= scaleBone.ScaleY; + } + localDelta *= parentBoneScale; - void ApplyRootMotion (Vector2 localDelta) { localDelta *= AdditionalScale; localDelta.x *= rootMotionScaleX; localDelta.y *= rootMotionScaleY; + if (!transformPositionX) localDelta.x = 0f; + if (!transformPositionY) localDelta.y = 0f; + } + + void ApplyRootMotion (Vector2 localDelta, Vector2 parentBoneScale) { // Apply root motion to Transform or RigidBody; if (UsesRigidbody) { rigidbodyDisplacement += (Vector2)transform.TransformVector(localDelta); // Accumulated displacement is applied on the next Physics update (FixedUpdate) } else { - transform.position += transform.TransformVector(localDelta); } // Move top level bones in opposite direction of the root motion bone foreach (var topLevelBone in topLevelBones) { - if (transformPositionX) topLevelBone.x -= rootMotionBone.x; - if (transformPositionY) topLevelBone.y -= rootMotionBone.y; + if (topLevelBone == rootMotionBone) { + if (transformPositionX) topLevelBone.x = 0; + if (transformPositionY) topLevelBone.y = 0; + } + else { + if (transformPositionX) topLevelBone.x = -(rootMotionBone.x - initialOffset.x) * parentBoneScale.x; + if (transformPositionY) topLevelBone.y = -(rootMotionBone.y - initialOffset.y) * parentBoneScale.y; + } } } } From 7782b01cdf42f1b27df1fe567dc33ab6a3c9e5cb Mon Sep 17 00:00:00 2001 From: Harald Csaszar Date: Fri, 9 Apr 2021 17:30:55 +0200 Subject: [PATCH 03/10] [unity] Fixed a compile error occurring only on Unity 2017, Vector2 not supporting operator* (introduced by commit 758a83a). See #1876. --- .../spine-unity/Components/RootMotion/SkeletonRootMotionBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/RootMotion/SkeletonRootMotionBase.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/RootMotion/SkeletonRootMotionBase.cs index 32ecaac06..699cfb3da 100644 --- a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/RootMotion/SkeletonRootMotionBase.cs +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/RootMotion/SkeletonRootMotionBase.cs @@ -198,7 +198,7 @@ namespace Spine.Unity { parentBoneScale.x *= scaleBone.ScaleX; parentBoneScale.y *= scaleBone.ScaleY; } - localDelta *= parentBoneScale; + localDelta = Vector2.Scale(localDelta, parentBoneScale); localDelta *= AdditionalScale; localDelta.x *= rootMotionScaleX; From dd7c3b1bccc17ae5b7270b5c2c84ed19199df76c Mon Sep 17 00:00:00 2001 From: Harald Csaszar Date: Mon, 12 Apr 2021 17:31:00 +0200 Subject: [PATCH 04/10] [unity] Root motion delta compensation now allows to only adjust X or Y components instead of both. See #1876. --- CHANGELOG.md | 1 + .../Sample Components/RootMotionDeltaCompensation.cs | 4 +++- .../Components/RootMotion/SkeletonRootMotionBase.cs | 8 +++++--- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 09638ce64..49c41a2ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -256,6 +256,7 @@ * `BoneFollower` and `BoneFollowerGraphic` components now provide better support for following bones when the skeleton's Transform is not the parent of the follower's Transform. Previously e.g. rotating a common parent Transform did not lead to the desired result, as well as negatively scaling a skeleton's Transform when it is not a parent of the follower's Transform. * URP and LWRP `Sprite` and `SkeletonLit` shaders no longer require `Advanced - Add Normals` enabled to properly cast and receive shadows. It is recommended to disable `Add Normals` if normals are otherwise not needed. * Added an example component `RootMotionDeltaCompensation` located in `Spine Examples/Scripts/Sample Components` which can be used for applying simple delta compensation. You can enable and disable the component to toggle delta compensation of the currently playing animation on and off. + * Root motion delta compensation now allows to only adjust X or Y components instead of both. Adds two parameters to `SkeletonRootMotionBase.AdjustRootMotionToDistance()` which default to adjusting both X and Y as before. The `RootMotionDeltaCompensation` example component exposes these parameters as public attributes. * **Changes of default values** * `SkeletonMecanim`'s `Layer Mix Mode` now defaults to `MixMode.MixNext` instead of `MixMode.MixAlways`. diff --git a/spine-unity/Assets/Spine Examples/Scripts/Sample Components/RootMotionDeltaCompensation.cs b/spine-unity/Assets/Spine Examples/Scripts/Sample Components/RootMotionDeltaCompensation.cs index 7731374ab..c1d9f3530 100644 --- a/spine-unity/Assets/Spine Examples/Scripts/Sample Components/RootMotionDeltaCompensation.cs +++ b/spine-unity/Assets/Spine Examples/Scripts/Sample Components/RootMotionDeltaCompensation.cs @@ -8,6 +8,8 @@ namespace Spine.Unity.Examples { protected SkeletonRootMotionBase rootMotion; public Transform targetPosition; public int trackIndex = 0; + public bool adjustX = true; + public bool adjustY = true; void Start () { rootMotion = this.GetComponent(); @@ -23,7 +25,7 @@ namespace Spine.Unity.Examples { void AdjustDelta() { Vector3 toTarget = targetPosition.position - this.transform.position; - rootMotion.AdjustRootMotionToDistance(toTarget, trackIndex); + rootMotion.AdjustRootMotionToDistance(toTarget, trackIndex, adjustX, adjustY); } } } diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/RootMotion/SkeletonRootMotionBase.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/RootMotion/SkeletonRootMotionBase.cs index 699cfb3da..a0b259d78 100644 --- a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/RootMotion/SkeletonRootMotionBase.cs +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/RootMotion/SkeletonRootMotionBase.cs @@ -131,14 +131,16 @@ namespace Spine.Unity { } } - public void AdjustRootMotionToDistance (Vector2 distanceToTarget, int trackIndex = 0) { + public void AdjustRootMotionToDistance (Vector2 distanceToTarget, int trackIndex = 0, bool adjustX = true, bool adjustY = true) { Vector2 remainingRootMotion = GetRemainingRootMotion(trackIndex); if (remainingRootMotion.x == 0) remainingRootMotion.x = 0.0001f; if (remainingRootMotion.y == 0) remainingRootMotion.y = 0.0001f; - rootMotionScaleX = distanceToTarget.x / remainingRootMotion.x; - rootMotionScaleY = distanceToTarget.y / remainingRootMotion.y; + if (adjustX) + rootMotionScaleX = distanceToTarget.x / remainingRootMotion.x; + if (adjustY) + rootMotionScaleY = distanceToTarget.y / remainingRootMotion.y; } public Vector2 GetAnimationRootMotion (Animation animation) { From 692eed3a758f079943417b173058ae8884abff74 Mon Sep 17 00:00:00 2001 From: Harald Csaszar Date: Wed, 14 Apr 2021 17:56:59 +0200 Subject: [PATCH 05/10] [unity] Root motion delta compensation now allows to add translation root motion to e.g. adjust a horizontal jump upwards or downwards. Fixed delta compensation to also respect any Skeleton-, Transform- or parent Bone scale. See #1876. --- CHANGELOG.md | 1 + .../RootMotionDeltaCompensation.cs | 25 +++++- .../SkeletonRootMotionBaseInspector.cs | 12 ++- .../RootMotion/SkeletonMecanimRootMotion.cs | 9 +++ .../RootMotion/SkeletonRootMotion.cs | 10 +++ .../RootMotion/SkeletonRootMotionBase.cs | 76 +++++++++++++++++-- 6 files changed, 120 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 49c41a2ae..59d655eca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -257,6 +257,7 @@ * URP and LWRP `Sprite` and `SkeletonLit` shaders no longer require `Advanced - Add Normals` enabled to properly cast and receive shadows. It is recommended to disable `Add Normals` if normals are otherwise not needed. * Added an example component `RootMotionDeltaCompensation` located in `Spine Examples/Scripts/Sample Components` which can be used for applying simple delta compensation. You can enable and disable the component to toggle delta compensation of the currently playing animation on and off. * Root motion delta compensation now allows to only adjust X or Y components instead of both. Adds two parameters to `SkeletonRootMotionBase.AdjustRootMotionToDistance()` which default to adjusting both X and Y as before. The `RootMotionDeltaCompensation` example component exposes these parameters as public attributes. + * Root motion delta compensation now allows to also add translation root motion to e.g. adjust a horizontal jump upwards or downwards over time. This is necessary because a Y root motion of zero cannot be scaled to become non-zero. * **Changes of default values** * `SkeletonMecanim`'s `Layer Mix Mode` now defaults to `MixMode.MixNext` instead of `MixMode.MixAlways`. diff --git a/spine-unity/Assets/Spine Examples/Scripts/Sample Components/RootMotionDeltaCompensation.cs b/spine-unity/Assets/Spine Examples/Scripts/Sample Components/RootMotionDeltaCompensation.cs index c1d9f3530..52f5f306e 100644 --- a/spine-unity/Assets/Spine Examples/Scripts/Sample Components/RootMotionDeltaCompensation.cs +++ b/spine-unity/Assets/Spine Examples/Scripts/Sample Components/RootMotionDeltaCompensation.cs @@ -5,14 +5,22 @@ namespace Spine.Unity.Examples { public class RootMotionDeltaCompensation : MonoBehaviour { - protected SkeletonRootMotionBase rootMotion; + [SerializeField] protected SkeletonRootMotionBase rootMotion; public Transform targetPosition; public int trackIndex = 0; public bool adjustX = true; public bool adjustY = true; + public float minScaleX = -999; + public float minScaleY = -999; + public float maxScaleX = 999; + public float maxScaleY = 999; + + public bool allowXTranslation = false; + public bool allowYTranslation = true; void Start () { - rootMotion = this.GetComponent(); + if (rootMotion == null) + rootMotion = this.GetComponent(); } void Update () { @@ -20,12 +28,21 @@ namespace Spine.Unity.Examples { } void OnDisable () { - rootMotion.rootMotionScaleX = rootMotion.rootMotionScaleY = 1; + if (adjustX) + rootMotion.rootMotionScaleX = 1; + if (adjustY) + rootMotion.rootMotionScaleY = 1; + if (allowXTranslation) + rootMotion.rootMotionTranslateXPerY = 0; + if (allowYTranslation) + rootMotion.rootMotionTranslateYPerX = 0; } void AdjustDelta() { Vector3 toTarget = targetPosition.position - this.transform.position; - rootMotion.AdjustRootMotionToDistance(toTarget, trackIndex, adjustX, adjustY); + rootMotion.AdjustRootMotionToDistance(toTarget, trackIndex, adjustX, adjustY, + minScaleX, maxScaleX, minScaleY, maxScaleY, + allowXTranslation, allowYTranslation); } } } diff --git a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Components/SkeletonRootMotionBaseInspector.cs b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Components/SkeletonRootMotionBaseInspector.cs index 4f1af9540..8314eb092 100644 --- a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Components/SkeletonRootMotionBaseInspector.cs +++ b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Components/SkeletonRootMotionBaseInspector.cs @@ -39,6 +39,8 @@ namespace Spine.Unity.Editor { protected SerializedProperty transformPositionY; protected SerializedProperty rootMotionScaleX; protected SerializedProperty rootMotionScaleY; + protected SerializedProperty rootMotionTranslateXPerY; + protected SerializedProperty rootMotionTranslateYPerX; protected SerializedProperty rigidBody2D; protected SerializedProperty rigidBody; @@ -47,6 +49,8 @@ namespace Spine.Unity.Editor { protected GUIContent transformPositionYLabel; protected GUIContent rootMotionScaleXLabel; protected GUIContent rootMotionScaleYLabel; + protected GUIContent rootMotionTranslateXPerYLabel; + protected GUIContent rootMotionTranslateYPerXLabel; protected GUIContent rigidBody2DLabel; protected GUIContent rigidBodyLabel; @@ -57,6 +61,8 @@ namespace Spine.Unity.Editor { transformPositionY = serializedObject.FindProperty("transformPositionY"); rootMotionScaleX = serializedObject.FindProperty("rootMotionScaleX"); rootMotionScaleY = serializedObject.FindProperty("rootMotionScaleY"); + rootMotionTranslateXPerY = serializedObject.FindProperty("rootMotionTranslateXPerY"); + rootMotionTranslateYPerX = serializedObject.FindProperty("rootMotionTranslateYPerX"); rigidBody2D = serializedObject.FindProperty("rigidBody2D"); rigidBody = serializedObject.FindProperty("rigidBody"); @@ -65,6 +71,8 @@ namespace Spine.Unity.Editor { transformPositionYLabel = new UnityEngine.GUIContent("Y", "Use the Y-movement of the bone."); rootMotionScaleXLabel = new UnityEngine.GUIContent("Root Motion Scale (X)", "Scale applied to the horizontal root motion delta. Can be used for delta compensation to e.g. stretch a jump to the desired distance."); rootMotionScaleYLabel = new UnityEngine.GUIContent("Root Motion Scale (Y)", "Scale applied to the vertical root motion delta. Can be used for delta compensation to e.g. stretch a jump to the desired distance."); + rootMotionTranslateXPerYLabel = new UnityEngine.GUIContent("Root Motion Translate (X)", "Added X translation per root motion Y delta. Can be used for delta compensation when scaling is not enough, to e.g. offset a horizontal jump to a vertically different goal."); + rootMotionTranslateYPerXLabel = new UnityEngine.GUIContent("Root Motion Translate (Y)", "Added Y translation per root motion X delta. Can be used for delta compensation when scaling is not enough, to e.g. offset a horizontal jump to a vertically different goal."); rigidBody2DLabel = new UnityEngine.GUIContent("Rigidbody2D", "Optional Rigidbody2D: Assign a Rigidbody2D here if you want " + " to apply the root motion to the rigidbody instead of the Transform." + @@ -92,10 +100,12 @@ namespace Spine.Unity.Editor { EditorGUILayout.PropertyField(rootMotionScaleX, rootMotionScaleXLabel); EditorGUILayout.PropertyField(rootMotionScaleY, rootMotionScaleYLabel); + + EditorGUILayout.PropertyField(rootMotionTranslateXPerY, rootMotionTranslateXPerYLabel); + EditorGUILayout.PropertyField(rootMotionTranslateYPerX, rootMotionTranslateYPerXLabel); } protected virtual void OptionalPropertyFields () { - //EditorGUILayout.LabelField("Optional", EditorStyles.boldLabel); EditorGUILayout.PropertyField(rigidBody2D, rigidBody2DLabel); EditorGUILayout.PropertyField(rigidBody, rigidBodyLabel); } diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/RootMotion/SkeletonMecanimRootMotion.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/RootMotion/SkeletonMecanimRootMotion.cs index e42e8ec27..4a90bdb00 100644 --- a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/RootMotion/SkeletonMecanimRootMotion.cs +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/RootMotion/SkeletonMecanimRootMotion.cs @@ -73,6 +73,15 @@ namespace Spine.Unity { return GetAnimationRootMotion(start, end, animation); } + public override RootMotionInfo GetRootMotionInfo (int layerIndex) { + var pair = skeletonMecanim.Translator.GetActiveAnimationAndTime(layerIndex); + var animation = pair.Key; + var time = pair.Value; + if (animation == null) + return new RootMotionInfo(); + return GetAnimationRootMotionInfo(animation, time); + } + protected override void Reset () { base.Reset(); mecanimLayerFlags = DefaultMecanimLayerFlags; diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/RootMotion/SkeletonRootMotion.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/RootMotion/SkeletonRootMotion.cs index b493af45f..b2293ba60 100644 --- a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/RootMotion/SkeletonRootMotion.cs +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/RootMotion/SkeletonRootMotion.cs @@ -67,6 +67,16 @@ namespace Spine.Unity { return GetAnimationRootMotion(start, end, animation); } + public override RootMotionInfo GetRootMotionInfo (int trackIndex) { + TrackEntry track = animationState.GetCurrent(trackIndex); + if (track == null) + return new RootMotionInfo(); + + var animation = track.Animation; + float time = track.AnimationTime; + return GetAnimationRootMotionInfo(track.Animation, time); + } + protected override float AdditionalScale { get { return canvas ? canvas.referencePixelsPerUnit: 1.0f; diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/RootMotion/SkeletonRootMotionBase.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/RootMotion/SkeletonRootMotionBase.cs index a0b259d78..00c222e2b 100644 --- a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/RootMotion/SkeletonRootMotionBase.cs +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/RootMotion/SkeletonRootMotionBase.cs @@ -30,6 +30,7 @@ using UnityEngine; using System.Collections.Generic; using Spine.Unity.AnimationTools; +using System; namespace Spine.Unity { @@ -47,6 +48,8 @@ namespace Spine.Unity { public float rootMotionScaleX = 1; public float rootMotionScaleY = 1; + public float rootMotionTranslateXPerY = 0; + public float rootMotionTranslateYPerX = 0; [Header("Optional")] public Rigidbody2D rigidBody2D; @@ -117,6 +120,15 @@ namespace Spine.Unity { abstract protected Vector2 CalculateAnimationsMovementDelta (); abstract public Vector2 GetRemainingRootMotion (int trackIndex = 0); + public struct RootMotionInfo { + public Vector2 start; + public Vector2 current; + public Vector2 mid; + public Vector2 end; + public bool timeIsPastMid; + }; + abstract public RootMotionInfo GetRootMotionInfo (int trackIndex = 0); + public void SetRootMotionBone (string name) { var skeleton = skeletonComponent.Skeleton; int index = skeleton.FindBoneIndex(name); @@ -131,16 +143,30 @@ namespace Spine.Unity { } } - public void AdjustRootMotionToDistance (Vector2 distanceToTarget, int trackIndex = 0, bool adjustX = true, bool adjustY = true) { + public void AdjustRootMotionToDistance (Vector2 distanceToTarget, int trackIndex = 0, bool adjustX = true, bool adjustY = true, + float minX = 0, float maxX = float.MaxValue, float minY = 0, float maxY = float.MaxValue, + bool allowXTranslation = false, bool allowYTranslation = false) { + + distanceToTarget = (Vector2)transform.InverseTransformVector(distanceToTarget); + + Vector2 scaleAffectingRootMotion = GetScaleAffectingRootMotion(); + distanceToTarget.Scale(new Vector2(1f / scaleAffectingRootMotion.x, 1f / scaleAffectingRootMotion.y)); + Vector2 remainingRootMotion = GetRemainingRootMotion(trackIndex); if (remainingRootMotion.x == 0) remainingRootMotion.x = 0.0001f; if (remainingRootMotion.y == 0) remainingRootMotion.y = 0.0001f; + + if (allowXTranslation) + rootMotionTranslateXPerY = (distanceToTarget.x - remainingRootMotion.x) / Math.Abs(remainingRootMotion.y); + if (allowYTranslation) + rootMotionTranslateYPerX = (distanceToTarget.y - remainingRootMotion.y) / Math.Abs(remainingRootMotion.x); + if (adjustX) - rootMotionScaleX = distanceToTarget.x / remainingRootMotion.x; + rootMotionScaleX = Math.Min(maxX, Math.Max(minX, distanceToTarget.x / remainingRootMotion.x)); if (adjustY) - rootMotionScaleY = distanceToTarget.y / remainingRootMotion.y; + rootMotionScaleY = Math.Min(maxY, Math.Max(minY, distanceToTarget.y / remainingRootMotion.y)); } public Vector2 GetAnimationRootMotion (Animation animation) { @@ -157,6 +183,21 @@ namespace Spine.Unity { return Vector2.zero; } + public RootMotionInfo GetAnimationRootMotionInfo (Animation animation, float currentTime) { + RootMotionInfo rootMotion = new RootMotionInfo(); + var timeline = animation.FindTranslateTimelineForBone(rootMotionBoneIndex); + if (timeline != null) { + float duration = animation.duration; + float mid = duration * 0.5f; + rootMotion.start = timeline.Evaluate(0); + rootMotion.current = timeline.Evaluate(currentTime); + rootMotion.mid = timeline.Evaluate(mid); + rootMotion.end = timeline.Evaluate(duration); + rootMotion.timeIsPastMid = currentTime > mid; + } + return rootMotion; + } + Vector2 GetTimelineMovementDelta (float startTime, float endTime, TranslateTimeline timeline, Animation animation) { @@ -190,9 +231,16 @@ namespace Spine.Unity { ApplyRootMotion(movementDelta, parentBoneScale); } - void AdjustMovementDeltaToConfiguration (ref Vector2 localDelta, out Vector2 parentBoneScale, Skeleton skeleton) { - localDelta.x *= skeleton.ScaleX; - localDelta.y *= skeleton.ScaleY; + Vector2 GetScaleAffectingRootMotion () { + Vector2 parentBoneScale; + return GetScaleAffectingRootMotion(out parentBoneScale); + } + + Vector2 GetScaleAffectingRootMotion (out Vector2 parentBoneScale) { + var skeleton = skeletonComponent.Skeleton; + Vector2 totalScale = Vector2.one; + totalScale.x *= skeleton.ScaleX; + totalScale.y *= skeleton.ScaleY; parentBoneScale = Vector2.one; Bone scaleBone = rootMotionBone; @@ -200,11 +248,23 @@ namespace Spine.Unity { parentBoneScale.x *= scaleBone.ScaleX; parentBoneScale.y *= scaleBone.ScaleY; } - localDelta = Vector2.Scale(localDelta, parentBoneScale); + totalScale = Vector2.Scale(totalScale, parentBoneScale); + totalScale *= AdditionalScale; + return totalScale; + } + + void AdjustMovementDeltaToConfiguration (ref Vector2 localDelta, out Vector2 parentBoneScale, Skeleton skeleton) { + Vector2 totalScale = GetScaleAffectingRootMotion(out parentBoneScale); + localDelta.Scale(totalScale); + + Vector2 rootMotionTranslation = new Vector2( + rootMotionTranslateXPerY * Math.Abs(localDelta.y), + rootMotionTranslateYPerX * Math.Abs(localDelta.x)); - localDelta *= AdditionalScale; localDelta.x *= rootMotionScaleX; localDelta.y *= rootMotionScaleY; + localDelta.x += rootMotionTranslation.x; + localDelta.y += rootMotionTranslation.y; if (!transformPositionX) localDelta.x = 0f; if (!transformPositionY) localDelta.y = 0f; From e425d64d2c81a055203cee6c2b5a2883f574c6ef Mon Sep 17 00:00:00 2001 From: Harald Csaszar Date: Wed, 21 Apr 2021 20:27:52 +0200 Subject: [PATCH 06/10] [unity] Fixed RootMotion scripts in combination with Rigidbody components. Now works smoothly. Closes #1880. --- .../RootMotion/SkeletonRootMotionBase.cs | 110 +++++++++++------- 1 file changed, 68 insertions(+), 42 deletions(-) diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/RootMotion/SkeletonRootMotionBase.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/RootMotion/SkeletonRootMotionBase.cs index 00c222e2b..a6222fb23 100644 --- a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/RootMotion/SkeletonRootMotionBase.cs +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/RootMotion/SkeletonRootMotionBase.cs @@ -48,7 +48,9 @@ namespace Spine.Unity { public float rootMotionScaleX = 1; public float rootMotionScaleY = 1; + /// Skeleton space X translation per skeleton space Y translation root motion. public float rootMotionTranslateXPerY = 0; + /// Skeleton space Y translation per skeleton space X translation root motion. public float rootMotionTranslateYPerX = 0; [Header("Optional")] @@ -65,6 +67,7 @@ namespace Spine.Unity { protected int rootMotionBoneIndex; protected List topLevelBones = new List(); protected Vector2 initialOffset = Vector2.zero; + protected Vector2 tempSkeletonDisplacement; protected Vector2 rigidbodyDisplacement; protected virtual void Reset () { @@ -97,11 +100,16 @@ namespace Spine.Unity { rigidBody.MovePosition(transform.position + new Vector3(rigidbodyDisplacement.x, rigidbodyDisplacement.y, 0)); } + Vector2 parentBoneScale; + GetScaleAffectingRootMotion(out parentBoneScale); + ClearEffectiveBoneOffsets(parentBoneScale); rigidbodyDisplacement = Vector2.zero; + tempSkeletonDisplacement = Vector2.zero; } protected virtual void OnDisable () { rigidbodyDisplacement = Vector2.zero; + tempSkeletonDisplacement = Vector2.zero; } protected void FindRigidbodyComponent () { @@ -147,26 +155,27 @@ namespace Spine.Unity { float minX = 0, float maxX = float.MaxValue, float minY = 0, float maxY = float.MaxValue, bool allowXTranslation = false, bool allowYTranslation = false) { - distanceToTarget = (Vector2)transform.InverseTransformVector(distanceToTarget); - + Vector2 distanceToTargetSkeletonSpace = (Vector2)transform.InverseTransformVector(distanceToTarget); Vector2 scaleAffectingRootMotion = GetScaleAffectingRootMotion(); - distanceToTarget.Scale(new Vector2(1f / scaleAffectingRootMotion.x, 1f / scaleAffectingRootMotion.y)); + if (UsesRigidbody) + distanceToTargetSkeletonSpace -= tempSkeletonDisplacement; - Vector2 remainingRootMotion = GetRemainingRootMotion(trackIndex); - if (remainingRootMotion.x == 0) - remainingRootMotion.x = 0.0001f; - if (remainingRootMotion.y == 0) - remainingRootMotion.y = 0.0001f; - - if (allowXTranslation) - rootMotionTranslateXPerY = (distanceToTarget.x - remainingRootMotion.x) / Math.Abs(remainingRootMotion.y); - if (allowYTranslation) - rootMotionTranslateYPerX = (distanceToTarget.y - remainingRootMotion.y) / Math.Abs(remainingRootMotion.x); + Vector2 remainingRootMotionSkeletonSpace = GetRemainingRootMotion(trackIndex); + remainingRootMotionSkeletonSpace.Scale(scaleAffectingRootMotion); + if (remainingRootMotionSkeletonSpace.x == 0) + remainingRootMotionSkeletonSpace.x = 0.0001f; + if (remainingRootMotionSkeletonSpace.y == 0) + remainingRootMotionSkeletonSpace.y = 0.0001f; if (adjustX) - rootMotionScaleX = Math.Min(maxX, Math.Max(minX, distanceToTarget.x / remainingRootMotion.x)); + rootMotionScaleX = Math.Min(maxX, Math.Max(minX, distanceToTargetSkeletonSpace.x / remainingRootMotionSkeletonSpace.x)); if (adjustY) - rootMotionScaleY = Math.Min(maxY, Math.Max(minY, distanceToTarget.y / remainingRootMotion.y)); + rootMotionScaleY = Math.Min(maxY, Math.Max(minY, distanceToTargetSkeletonSpace.y / remainingRootMotionSkeletonSpace.y)); + + if (allowXTranslation) + rootMotionTranslateXPerY = (distanceToTargetSkeletonSpace.x - remainingRootMotionSkeletonSpace.x * rootMotionScaleX) / remainingRootMotionSkeletonSpace.y; + if (allowYTranslation) + rootMotionTranslateYPerX = (distanceToTargetSkeletonSpace.y - remainingRootMotionSkeletonSpace.y * rootMotionScaleY) / remainingRootMotionSkeletonSpace.x; } public Vector2 GetAnimationRootMotion (Animation animation) { @@ -225,10 +234,27 @@ namespace Spine.Unity { if (!this.isActiveAndEnabled) return; // Root motion is only applied when component is enabled. - var movementDelta = CalculateAnimationsMovementDelta(); + var boneLocalDelta = CalculateAnimationsMovementDelta(); Vector2 parentBoneScale; - AdjustMovementDeltaToConfiguration(ref movementDelta, out parentBoneScale, animatedSkeletonComponent.Skeleton); - ApplyRootMotion(movementDelta, parentBoneScale); + Vector2 skeletonDelta = GetSkeletonSpaceMovementDelta(boneLocalDelta, out parentBoneScale); + ApplyRootMotion(skeletonDelta, parentBoneScale); + } + + void ApplyRootMotion (Vector2 skeletonDelta, Vector2 parentBoneScale) { + // Apply root motion to Transform or RigidBody; + if (UsesRigidbody) { + rigidbodyDisplacement += (Vector2)transform.TransformVector(skeletonDelta); + + // Accumulated displacement is applied on the next Physics update in FixedUpdate. + // Until the next Physics update, tempBoneDisplacement is offsetting bone locations + // to prevent stutter which would otherwise occur if we don't move every Update. + tempSkeletonDisplacement += skeletonDelta; + SetEffectiveBoneOffsetsTo(tempSkeletonDisplacement, parentBoneScale); + } + else { + transform.position += transform.TransformVector(skeletonDelta); + ClearEffectiveBoneOffsets(parentBoneScale); + } } Vector2 GetScaleAffectingRootMotion () { @@ -253,44 +279,44 @@ namespace Spine.Unity { return totalScale; } - void AdjustMovementDeltaToConfiguration (ref Vector2 localDelta, out Vector2 parentBoneScale, Skeleton skeleton) { + Vector2 GetSkeletonSpaceMovementDelta (Vector2 boneLocalDelta, out Vector2 parentBoneScale) { + Vector2 skeletonDelta = boneLocalDelta; Vector2 totalScale = GetScaleAffectingRootMotion(out parentBoneScale); - localDelta.Scale(totalScale); + skeletonDelta.Scale(totalScale); Vector2 rootMotionTranslation = new Vector2( - rootMotionTranslateXPerY * Math.Abs(localDelta.y), - rootMotionTranslateYPerX * Math.Abs(localDelta.x)); + rootMotionTranslateXPerY * skeletonDelta.y, + rootMotionTranslateYPerX * skeletonDelta.x); - localDelta.x *= rootMotionScaleX; - localDelta.y *= rootMotionScaleY; - localDelta.x += rootMotionTranslation.x; - localDelta.y += rootMotionTranslation.y; + skeletonDelta.x *= rootMotionScaleX; + skeletonDelta.y *= rootMotionScaleY; + skeletonDelta.x += rootMotionTranslation.x; + skeletonDelta.y += rootMotionTranslation.y; - if (!transformPositionX) localDelta.x = 0f; - if (!transformPositionY) localDelta.y = 0f; + if (!transformPositionX) skeletonDelta.x = 0f; + if (!transformPositionY) skeletonDelta.y = 0f; + return skeletonDelta; } - void ApplyRootMotion (Vector2 localDelta, Vector2 parentBoneScale) { - // Apply root motion to Transform or RigidBody; - if (UsesRigidbody) { - rigidbodyDisplacement += (Vector2)transform.TransformVector(localDelta); - // Accumulated displacement is applied on the next Physics update (FixedUpdate) - } - else { - transform.position += transform.TransformVector(localDelta); - } - + void SetEffectiveBoneOffsetsTo (Vector2 displacementSkeletonSpace, Vector2 parentBoneScale) { // Move top level bones in opposite direction of the root motion bone + var skeleton = skeletonComponent.Skeleton; foreach (var topLevelBone in topLevelBones) { if (topLevelBone == rootMotionBone) { - if (transformPositionX) topLevelBone.x = 0; - if (transformPositionY) topLevelBone.y = 0; + if (transformPositionX) topLevelBone.x = displacementSkeletonSpace.x / skeleton.ScaleX; + if (transformPositionY) topLevelBone.y = displacementSkeletonSpace.y / skeleton.ScaleY; } else { - if (transformPositionX) topLevelBone.x = -(rootMotionBone.x - initialOffset.x) * parentBoneScale.x; - if (transformPositionY) topLevelBone.y = -(rootMotionBone.y - initialOffset.y) * parentBoneScale.y; + float offsetX = (initialOffset.x - rootMotionBone.x) * parentBoneScale.x; + float offsetY = (initialOffset.y - rootMotionBone.y) * parentBoneScale.y; + if (transformPositionX) topLevelBone.x = (displacementSkeletonSpace.x / skeleton.ScaleX) + offsetX; + if (transformPositionY) topLevelBone.y = (displacementSkeletonSpace.y / skeleton.ScaleY) + offsetY; } } } + + void ClearEffectiveBoneOffsets (Vector2 parentBoneScale) { + SetEffectiveBoneOffsetsTo(Vector2.zero, parentBoneScale); + } } } From 1e888db53a1b5f7716a8443818e18d93a329f0c3 Mon Sep 17 00:00:00 2001 From: Harald Csaszar Date: Thu, 22 Apr 2021 11:45:58 +0200 Subject: [PATCH 07/10] [unity] Prevent null reference exception when using `Advanced - Fix Draw Order` and disabling the renderer and/or GameObject. --- .../Spine/Runtime/spine-unity/Components/SkeletonRenderer.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonRenderer.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonRenderer.cs index fc29fab9e..424f78212 100644 --- a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonRenderer.cs +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonRenderer.cs @@ -690,6 +690,9 @@ namespace Spine.Unity { } for (int i = 0; i < meshRenderer.sharedMaterials.Length; ++i) { + if (!meshRenderer.sharedMaterials[i]) + continue; + if (!hasPerRendererBlock) meshRenderer.GetPropertyBlock(reusedPropertyBlock, i); // Note: this parameter shall not exist at any shader, then Unity will create separate // material instances (not in terms of memory cost or leakage). From 25d7f8266f976a3ae6bdcf23afca2af4b7f23a6e Mon Sep 17 00:00:00 2001 From: Harald Csaszar Date: Wed, 28 Apr 2021 19:54:48 +0200 Subject: [PATCH 08/10] [unity] Fixed UpdateMode when invisible not being used when instantiated off-screen. Closes #1883. --- .../Spine/Runtime/spine-unity/Components/SkeletonGraphic.cs | 1 + .../Spine/Runtime/spine-unity/Components/SkeletonRenderer.cs | 1 + 2 files 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 d01fb7523..38ef41147 100644 --- a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonGraphic.cs +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonGraphic.cs @@ -202,6 +202,7 @@ namespace Spine.Unity { protected override void Awake () { base.Awake (); + updateMode = updateWhenInvisible; SyncRawImagesWithCanvasRenderers(); if (!this.IsValid) { #if UNITY_EDITOR diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonRenderer.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonRenderer.cs index 424f78212..332ece243 100644 --- a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonRenderer.cs +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonRenderer.cs @@ -280,6 +280,7 @@ namespace Spine.Unity { public virtual void Awake () { Initialize(false); + updateMode = updateWhenInvisible; } #if UNITY_EDITOR && CONFIGURABLE_ENTER_PLAY_MODE From 63ef1938b9661dd44a966ec66b4d29edea2ffd1f Mon Sep 17 00:00:00 2001 From: RH Date: Thu, 29 Apr 2021 22:07:16 +1000 Subject: [PATCH 09/10] Allow SkeletonBinary and SkeletonJson to own externally created AttachmentLoader (#1846) --- spine-cpp/spine-cpp/include/spine/SkeletonBinary.h | 2 +- spine-cpp/spine-cpp/include/spine/SkeletonJson.h | 2 +- spine-cpp/spine-cpp/src/spine/SkeletonBinary.cpp | 4 ++-- spine-cpp/spine-cpp/src/spine/SkeletonJson.cpp | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/spine-cpp/spine-cpp/include/spine/SkeletonBinary.h b/spine-cpp/spine-cpp/include/spine/SkeletonBinary.h index 88af8bd77..0a59f3085 100644 --- a/spine-cpp/spine-cpp/include/spine/SkeletonBinary.h +++ b/spine-cpp/spine-cpp/include/spine/SkeletonBinary.h @@ -68,7 +68,7 @@ namespace spine { explicit SkeletonBinary(Atlas* atlasArray); - explicit SkeletonBinary(AttachmentLoader* attachmentLoader); + explicit SkeletonBinary(AttachmentLoader* attachmentLoader, bool ownsLoader = false); ~SkeletonBinary(); diff --git a/spine-cpp/spine-cpp/include/spine/SkeletonJson.h b/spine-cpp/spine-cpp/include/spine/SkeletonJson.h index b719855a4..aeb60f5ba 100644 --- a/spine-cpp/spine-cpp/include/spine/SkeletonJson.h +++ b/spine-cpp/spine-cpp/include/spine/SkeletonJson.h @@ -57,7 +57,7 @@ class SP_API SkeletonJson : public SpineObject { public: explicit SkeletonJson(Atlas *atlas); - explicit SkeletonJson(AttachmentLoader *attachmentLoader); + explicit SkeletonJson(AttachmentLoader *attachmentLoader, bool ownsLoader = false); ~SkeletonJson(); diff --git a/spine-cpp/spine-cpp/src/spine/SkeletonBinary.cpp b/spine-cpp/spine-cpp/src/spine/SkeletonBinary.cpp index f26398a77..ac61ab83d 100644 --- a/spine-cpp/spine-cpp/src/spine/SkeletonBinary.cpp +++ b/spine-cpp/spine-cpp/src/spine/SkeletonBinary.cpp @@ -98,8 +98,8 @@ SkeletonBinary::SkeletonBinary(Atlas *atlasArray) : _attachmentLoader( } -SkeletonBinary::SkeletonBinary(AttachmentLoader *attachmentLoader) : _attachmentLoader(attachmentLoader), _error(), - _scale(1), _ownsLoader(false) +SkeletonBinary::SkeletonBinary(AttachmentLoader* attachmentLoader, bool ownsLoader) : _attachmentLoader(attachmentLoader), _error(), +_scale(1), _ownsLoader(ownsLoader) { assert(_attachmentLoader != NULL); } diff --git a/spine-cpp/spine-cpp/src/spine/SkeletonJson.cpp b/spine-cpp/spine-cpp/src/spine/SkeletonJson.cpp index 18be34c90..54f94c6a9 100644 --- a/spine-cpp/spine-cpp/src/spine/SkeletonJson.cpp +++ b/spine-cpp/spine-cpp/src/spine/SkeletonJson.cpp @@ -84,8 +84,8 @@ SkeletonJson::SkeletonJson(Atlas *atlas) : _attachmentLoader(new(__FILE__, __LIN _scale(1), _ownsLoader(true) {} -SkeletonJson::SkeletonJson(AttachmentLoader *attachmentLoader) : _attachmentLoader(attachmentLoader), _scale(1), - _ownsLoader(false) +SkeletonJson::SkeletonJson(AttachmentLoader *attachmentLoader, bool ownsLoader) : _attachmentLoader(attachmentLoader), _scale(1), + _ownsLoader(ownsLoader) { assert(_attachmentLoader != NULL); } From 700e316e571aa1ec1f8c910de290df9135f774ec Mon Sep 17 00:00:00 2001 From: Harald Csaszar Date: Fri, 30 Apr 2021 18:01:04 +0200 Subject: [PATCH 10/10] [unity] `Attachment.GetRemappedClone(Sprite)` method now provides an additional optional parameter `useOriginalRegionScale`. --- CHANGELOG.md | 1 + .../Utility/AttachmentCloneExtensions.cs | 13 +++++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 59d655eca..9e6e33b52 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -258,6 +258,7 @@ * Added an example component `RootMotionDeltaCompensation` located in `Spine Examples/Scripts/Sample Components` which can be used for applying simple delta compensation. You can enable and disable the component to toggle delta compensation of the currently playing animation on and off. * Root motion delta compensation now allows to only adjust X or Y components instead of both. Adds two parameters to `SkeletonRootMotionBase.AdjustRootMotionToDistance()` which default to adjusting both X and Y as before. The `RootMotionDeltaCompensation` example component exposes these parameters as public attributes. * Root motion delta compensation now allows to also add translation root motion to e.g. adjust a horizontal jump upwards or downwards over time. This is necessary because a Y root motion of zero cannot be scaled to become non-zero. + * `Attachment.GetRemappedClone(Sprite)` method now provides an additional optional parameter `useOriginalRegionScale`. When set to `true`, the replaced attachment's scale is used instead of the Sprite's `Pixel per Unity` setting, allowing for more consistent scaling. *Note:* When remapping Sprites, be sure to set the Sprite's `Mesh Type` to `Full Rect` and not `Tight`, otherwise the scale will be wrong. * **Changes of default values** * `SkeletonMecanim`'s `Layer Mix Mode` now defaults to `MixMode.MixNext` instead of `MixMode.MixAlways`. diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AttachmentCloneExtensions.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AttachmentCloneExtensions.cs index 52c4ba1ad..2629c41f9 100644 --- a/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AttachmentCloneExtensions.cs +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AttachmentCloneExtensions.cs @@ -86,16 +86,25 @@ namespace Spine.Unity.AttachmentTools { /// If true and the original Attachment is a MeshAttachment, then /// a non-central sprite pivot will shift uv coords in the opposite direction. Vertices will not be offset in /// any case when the original Attachment is a MeshAttachment. + /// If true and the original Attachment is a RegionAttachment, then + /// the original region's scale value is used instead of the Sprite's pixels per unit property. Since uniform scale is used, + /// x scale of the original attachment (width scale) is used, scale in y direction (height scale) is ignored. public static Attachment GetRemappedClone (this Attachment o, Sprite sprite, Material sourceMaterial, bool premultiplyAlpha = true, bool cloneMeshAsLinked = true, bool useOriginalRegionSize = false, - bool pivotShiftsMeshUVCoords = true) { + bool pivotShiftsMeshUVCoords = true, bool useOriginalRegionScale = false) { var atlasRegion = premultiplyAlpha ? sprite.ToAtlasRegionPMAClone(sourceMaterial) : sprite.ToAtlasRegion(new Material(sourceMaterial) { mainTexture = sprite.texture } ); if (!pivotShiftsMeshUVCoords && o is MeshAttachment) { // prevent non-central sprite pivot setting offsetX/Y and shifting uv coords out of mesh bounds atlasRegion.offsetX = 0; atlasRegion.offsetY = 0; } - return o.GetRemappedClone(atlasRegion, cloneMeshAsLinked, useOriginalRegionSize, 1f/sprite.pixelsPerUnit); + float scale = 1f / sprite.pixelsPerUnit; + if (useOriginalRegionScale) { + var regionAttachment = o as RegionAttachment; + if (regionAttachment != null) + scale = regionAttachment.width / regionAttachment.regionOriginalWidth; + } + return o.GetRemappedClone(atlasRegion, cloneMeshAsLinked, useOriginalRegionSize, scale); } ///