[unity] Added option to BoneFollower components to follow attachment Z spacing offset. Defaults to false to maintain existing behaviour.

This commit is contained in:
Harald Csaszar 2025-08-13 21:57:35 +02:00
parent e103d4494f
commit 57844ebe15
5 changed files with 41 additions and 12 deletions

View File

@ -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**

View File

@ -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);

View File

@ -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);

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}