diff --git a/CHANGELOG.md b/CHANGELOG.md index 5faf6c304..7814f7bc9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -176,6 +176,7 @@ 3. In the `RenderExistingMeshGraphic` component Inspector at `Reference Skeleton Graphic` assign the original `SkeletonGraphic` object. 4. At `Replacement Material` assign e.g. the included _SkeletonGraphicDefaultOutline_ material to replace all materials with this material. Alternatively, if `Multiple CanvasRenderers` is enabled at the reference SkeletonGraphic, you can add entries to the `Replacement Materials` list and at each entry assign the original SkeletonGraphic material (e.g. _SkeletonGraphicDefault_) to be replaced and the respective `Replacement Material` (e.g. _SkeletonGraphicDefaultOutline_). - Added option for unsafe direct data loading when loading skeleton binary data to avoid some allocations, enabled via build define `SPINE_ALLOW_UNSAFE`. This define can be set via Spine Preferences, setting `Unsafe Build Defines - Direct data access`. The define is disabled by default to maintain existing behaviour. Changed asmdef setting for spine-unity assembly to allow unsafe code, has no effect other than allowing setting the `SPINE_ALLOW_UNSAFE` define. + - Added option to `BoneFollower` components to follow attachment Z spacing offset. The Inspector component property can be found under `Follow Z Position` - `Attachment Z Spacing`. Defaults to `false` to maintain existing behaviour. - **Breaking changes** diff --git a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Components/BoneFollowerGraphicInspector.cs b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Components/BoneFollowerGraphicInspector.cs index 900362120..414f56208 100644 --- a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Components/BoneFollowerGraphicInspector.cs +++ b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Components/BoneFollowerGraphicInspector.cs @@ -38,8 +38,8 @@ namespace Spine.Unity.Editor { [CustomEditor(typeof(BoneFollowerGraphic)), CanEditMultipleObjects] public class BoneFollowerGraphicInspector : Editor { - SerializedProperty boneName, skeletonGraphic, followXYPosition, followZPosition, followBoneRotation, - followLocalScale, followParentWorldScale, followSkeletonFlip, maintainedAxisOrientation; + SerializedProperty boneName, skeletonGraphic, followXYPosition, followZPosition, followAttachmentZSpacing, + followBoneRotation, followLocalScale, followParentWorldScale, followSkeletonFlip, maintainedAxisOrientation; BoneFollowerGraphic targetBoneFollower; bool needsReset; @@ -75,6 +75,7 @@ namespace Spine.Unity.Editor { followBoneRotation = serializedObject.FindProperty("followBoneRotation"); followXYPosition = serializedObject.FindProperty("followXYPosition"); followZPosition = serializedObject.FindProperty("followZPosition"); + followAttachmentZSpacing = serializedObject.FindProperty("followAttachmentZSpacing"); followLocalScale = serializedObject.FindProperty("followLocalScale"); followParentWorldScale = serializedObject.FindProperty("followParentWorldScale"); followSkeletonFlip = serializedObject.FindProperty("followSkeletonFlip"); @@ -172,6 +173,10 @@ namespace Spine.Unity.Editor { EditorGUILayout.PropertyField(followBoneRotation); EditorGUILayout.PropertyField(followXYPosition); EditorGUILayout.PropertyField(followZPosition); + if (followZPosition.boolValue == true) { + using (new SpineInspectorUtility.IndentScope()) + EditorGUILayout.PropertyField(followAttachmentZSpacing, new GUIContent("Attachment Z Spacing")); + } EditorGUILayout.PropertyField(followLocalScale); EditorGUILayout.PropertyField(followParentWorldScale); EditorGUILayout.PropertyField(followSkeletonFlip); diff --git a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Components/BoneFollowerInspector.cs b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Components/BoneFollowerInspector.cs index a32844f65..35108d96c 100644 --- a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Components/BoneFollowerInspector.cs +++ b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Components/BoneFollowerInspector.cs @@ -37,8 +37,8 @@ namespace Spine.Unity.Editor { [CustomEditor(typeof(BoneFollower)), CanEditMultipleObjects] public class BoneFollowerInspector : Editor { - SerializedProperty boneName, skeletonRenderer, followXYPosition, followZPosition, followBoneRotation, - followLocalScale, followParentWorldScale, followSkeletonFlip, maintainedAxisOrientation; + SerializedProperty boneName, skeletonRenderer, followXYPosition, followZPosition, followAttachmentZSpacing, + followBoneRotation, followLocalScale, followParentWorldScale, followSkeletonFlip, maintainedAxisOrientation; BoneFollower targetBoneFollower; bool needsReset; @@ -85,6 +85,7 @@ namespace Spine.Unity.Editor { followBoneRotation = serializedObject.FindProperty("followBoneRotation"); followXYPosition = serializedObject.FindProperty("followXYPosition"); followZPosition = serializedObject.FindProperty("followZPosition"); + followAttachmentZSpacing = serializedObject.FindProperty("followAttachmentZSpacing"); followLocalScale = serializedObject.FindProperty("followLocalScale"); followParentWorldScale = serializedObject.FindProperty("followParentWorldScale"); followSkeletonFlip = serializedObject.FindProperty("followSkeletonFlip"); @@ -178,6 +179,10 @@ namespace Spine.Unity.Editor { EditorGUILayout.PropertyField(followBoneRotation); EditorGUILayout.PropertyField(followXYPosition); EditorGUILayout.PropertyField(followZPosition); + if (followZPosition.boolValue == true) { + using (new SpineInspectorUtility.IndentScope()) + EditorGUILayout.PropertyField(followAttachmentZSpacing, new GUIContent("Attachment Z Spacing")); + } EditorGUILayout.PropertyField(followLocalScale); EditorGUILayout.PropertyField(followParentWorldScale); EditorGUILayout.PropertyField(followSkeletonFlip); diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/Following/BoneFollower.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/Following/BoneFollower.cs index e842d4835..d58e30bf7 100644 --- a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/Following/BoneFollower.cs +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/Following/BoneFollower.cs @@ -62,6 +62,7 @@ namespace Spine.Unity { public bool followXYPosition = true; public bool followZPosition = true; + public bool followAttachmentZSpacing = false; public bool followBoneRotation = true; [Tooltip("Follows the skeleton's flip state by controlling this Transform's local scale.")] @@ -158,9 +159,10 @@ namespace Spine.Unity { float additionalFlipScale = 1; if (skeletonTransformIsParent) { // Recommended setup: Use local transform properties if Spine GameObject is the immediate parent - thisTransform.localPosition = new Vector3(followXYPosition ? bone.WorldX : thisTransform.localPosition.x, - followXYPosition ? bone.WorldY : thisTransform.localPosition.y, - followZPosition ? 0f : thisTransform.localPosition.z); + thisTransform.localPosition = new Vector3( + followXYPosition ? bone.WorldX : thisTransform.localPosition.x, + followXYPosition ? bone.WorldY : thisTransform.localPosition.y, + followZPosition ? (followAttachmentZSpacing ? GetAttachmentZPosition() : 0f) : thisTransform.localPosition.z); if (followBoneRotation) { float halfRotation = Mathf.Atan2(bone.C, bone.A) * 0.5f; if (followLocalScale && bone.ScaleX < 0) // Negate rotation from negative scaleX. Don't use negative determinant. local scaleY doesn't factor into used rotation. @@ -174,7 +176,8 @@ namespace Spine.Unity { } else { // For special cases: Use transform world properties if transform relationship is complicated if (!skeletonTransform) return; - Vector3 targetWorldPosition = skeletonTransform.TransformPoint(new Vector3(bone.WorldX, bone.WorldY, 0f)); + float z0Position = (followZPosition && followAttachmentZSpacing) ? GetAttachmentZPosition() : 0f; + Vector3 targetWorldPosition = skeletonTransform.TransformPoint(new Vector3(bone.WorldX, bone.WorldY, z0Position)); if (!followZPosition) targetWorldPosition.z = thisTransform.position.z; if (!followXYPosition) { targetWorldPosition.x = thisTransform.position.x; @@ -221,5 +224,11 @@ namespace Spine.Unity { thisTransform.localScale = localScale; } } + + float GetAttachmentZPosition () { + int boneIndex = skeletonRenderer.Skeleton.DrawOrder.FindIndex(slot => slot.Bone == bone); + if (boneIndex < 0) return 0f; + return skeletonRenderer.zSpacing * boneIndex; + } } } diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/Following/BoneFollowerGraphic.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/Following/BoneFollowerGraphic.cs index b33568de5..815f5f3e5 100644 --- a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/Following/BoneFollowerGraphic.cs +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/Following/BoneFollowerGraphic.cs @@ -70,6 +70,7 @@ namespace Spine.Unity { public bool followParentWorldScale = false; public bool followXYPosition = true; public bool followZPosition = true; + public bool followAttachmentZSpacing = false; [Tooltip("Applies when 'Follow Skeleton Flip' is disabled but 'Follow Bone Rotation' is enabled." + " When flipping the skeleton by scaling its Transform, this follower's rotation is adjusted" + " instead of its scale to follow the bone orientation. When one of the axes is flipped, " @@ -145,15 +146,17 @@ namespace Spine.Unity { float additionalFlipScale = 1; if (skeletonTransformIsParent) { // Recommended setup: Use local transform properties if Spine GameObject is the immediate parent - thisTransform.localPosition = new Vector3(followXYPosition ? bone.WorldX * scale + offset.x : thisTransform.localPosition.x, - followXYPosition ? bone.WorldY * scale + offset.y : thisTransform.localPosition.y, - followZPosition ? 0f : thisTransform.localPosition.z); + thisTransform.localPosition = new Vector3( + followXYPosition ? bone.WorldX * scale + offset.x : thisTransform.localPosition.x, + followXYPosition ? bone.WorldY * scale + offset.y : thisTransform.localPosition.y, + followZPosition ? (followAttachmentZSpacing ? GetAttachmentZPosition() : 0f) : thisTransform.localPosition.z); if (followBoneRotation) thisTransform.localRotation = bone.GetQuaternion(); } else { // For special cases: Use transform world properties if transform relationship is complicated if (!skeletonTransform) return; + float z0Position = (followZPosition && followAttachmentZSpacing) ? GetAttachmentZPosition() : 0f; Vector3 targetWorldPosition = skeletonTransform.TransformPoint( - new Vector3(bone.WorldX * scale + offset.x, bone.WorldY * scale + offset.y, 0f)); + new Vector3(bone.WorldX * scale + offset.x, bone.WorldY * scale + offset.y, z0Position)); if (!followZPosition) targetWorldPosition.z = thisTransform.position.z; if (!followXYPosition) { targetWorldPosition.x = thisTransform.position.x; @@ -200,5 +203,11 @@ namespace Spine.Unity { thisTransform.localScale = localScale; } } + + float GetAttachmentZPosition () { + int boneIndex = skeletonGraphic.Skeleton.DrawOrder.FindIndex(slot => slot.Bone == bone); + if (boneIndex < 0) return 0f; + return skeletonGraphic.MeshGenerator.settings.zSpacing * skeletonGraphic.MeshScale * boneIndex; + } } }