diff --git a/spine-unity/Assets/spine-unity/BoneFollower.cs b/spine-unity/Assets/spine-unity/BoneFollower.cs index ed3b60441..2b88a12c2 100644 --- a/spine-unity/Assets/spine-unity/BoneFollower.cs +++ b/spine-unity/Assets/spine-unity/BoneFollower.cs @@ -125,10 +125,7 @@ namespace Spine.Unity { if (skeletonTransformIsParent) { // Recommended setup: Use local transform properties if Spine GameObject is the immediate parent thisTransform.localPosition = new Vector3(bone.worldX, bone.worldY, followZPosition ? 0f : thisTransform.localPosition.z); - if (followBoneRotation) { - var halfRotation = Mathf.Atan2(bone.c, bone.a) * 0.5f; - thisTransform.localRotation = new Quaternion(0, 0, Mathf.Sin(halfRotation), Mathf.Cos(halfRotation)); //thisTransform.localRotation = Quaternion.Euler(0f, 0f, bone.WorldRotationX); - } + if (followBoneRotation) thisTransform.localRotation = bone.GetQuaternion(); } else { // For special cases: Use transform world properties if transform relationship is complicated Vector3 targetWorldPosition = skeletonTransform.TransformPoint(new Vector3(bone.worldX, bone.worldY, 0f)); diff --git a/spine-unity/Assets/spine-unity/SkeletonExtensions.cs b/spine-unity/Assets/spine-unity/SkeletonExtensions.cs index 0e6e43987..cfba986e4 100644 --- a/spine-unity/Assets/spine-unity/SkeletonExtensions.cs +++ b/spine-unity/Assets/spine-unity/SkeletonExtensions.cs @@ -134,6 +134,28 @@ namespace Spine.Unity { return spineGameObjectTransform.TransformPoint(new Vector3(bone.worldX, bone.worldY)); } + /// Gets a skeleton space UnityEngine.Quaternion representation of bone.WorldRotationX. + public static Quaternion GetQuaternion (this Bone bone) { + var halfRotation = Mathf.Atan2(bone.c, bone.a) * 0.5f; + return new Quaternion(0, 0, Mathf.Sin(halfRotation), Mathf.Cos(halfRotation)); + } + + /// Gets the PointAttachment's Unity World position using its Spine GameObject Transform. + public static Vector3 GetWorldPosition (this PointAttachment attachment, Slot slot, Transform spineGameObjectTransform) { + Vector3 skeletonSpacePosition; + skeletonSpacePosition.z = 0; + attachment.ComputeWorldPosition(slot.bone, out skeletonSpacePosition.x, out skeletonSpacePosition.y); + return spineGameObjectTransform.TransformPoint(skeletonSpacePosition); + } + + /// Gets the PointAttachment's Unity World position using its Spine GameObject Transform. + public static Vector3 GetWorldPosition (this PointAttachment attachment, Bone bone, Transform spineGameObjectTransform) { + Vector3 skeletonSpacePosition; + skeletonSpacePosition.z = 0; + attachment.ComputeWorldPosition(bone, out skeletonSpacePosition.x, out skeletonSpacePosition.y); + return spineGameObjectTransform.TransformPoint(skeletonSpacePosition); + } + /// Gets the internal bone matrix as a Unity bonespace-to-skeletonspace transformation matrix. public static Matrix4x4 GetMatrix4x4 (this Bone bone) { return new Matrix4x4 { diff --git a/spine-unity/Assets/spine-unity/SpineAttributes.cs b/spine-unity/Assets/spine-unity/SpineAttributes.cs index 1876c55df..8836c0a43 100644 --- a/spine-unity/Assets/spine-unity/SpineAttributes.cs +++ b/spine-unity/Assets/spine-unity/SpineAttributes.cs @@ -50,11 +50,12 @@ namespace Spine.Unity { /// Smart popup menu for Spine Slots /// /// Filters popup results to elements that begin with supplied string. + /// Disables popup results that don't contain bounding box attachments when true. + /// If true, the dropdown list will include a "none" option which stored as an empty string. /// If specified, a locally scoped field with the name supplied by in dataField will be used to fill the popup results. /// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives). /// If left empty and the script the attribute is applied to is derived from Component, GetComponent() will be called as a fallback. /// - /// Disables popup results that don't contain bounding box attachments when true. public SpineSlot(string startsWith = "", string dataField = "", bool containsBoundingBoxes = false, bool includeNone = true) { this.startsWith = startsWith; this.dataField = dataField; @@ -68,6 +69,7 @@ namespace Spine.Unity { /// Smart popup menu for Spine Events (Spine.EventData) /// /// Filters popup results to elements that begin with supplied string. + /// If true, the dropdown list will include a "none" option which stored as an empty string. /// If specified, a locally scoped field with the name supplied by in dataField will be used to fill the popup results. /// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives). /// If left empty and the script the attribute is applied to is derived from Component, GetComponent() will be called as a fallback. @@ -79,11 +81,63 @@ namespace Spine.Unity { } } + public class SpineIkConstraint : SpineAttributeBase { + /// + /// Smart popup menu for Spine IK Constraints (Spine.IkConstraint) + /// + /// Filters popup results to elements that begin with supplied string. + /// If true, the dropdown list will include a "none" option which stored as an empty string. + /// If specified, a locally scoped field with the name supplied by in dataField will be used to fill the popup results. + /// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives). + /// If left empty and the script the attribute is applied to is derived from Component, GetComponent() will be called as a fallback. + /// + public SpineIkConstraint(string startsWith = "", string dataField = "", bool includeNone = true) { + this.startsWith = startsWith; + this.dataField = dataField; + this.includeNone = includeNone; + } + } + + public class SpinePathConstraint : SpineAttributeBase { + /// + /// Smart popup menu for Spine Events (Spine.PathConstraint) + /// + /// Filters popup results to elements that begin with supplied string. + /// If true, the dropdown list will include a "none" option which stored as an empty string. + /// If specified, a locally scoped field with the name supplied by in dataField will be used to fill the popup results. + /// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives). + /// If left empty and the script the attribute is applied to is derived from Component, GetComponent() will be called as a fallback. + /// + public SpinePathConstraint(string startsWith = "", string dataField = "", bool includeNone = true) { + this.startsWith = startsWith; + this.dataField = dataField; + this.includeNone = includeNone; + } + } + + public class SpineTransformConstraint : SpineAttributeBase { + /// + /// Smart popup menu for Spine Transform Constraints (Spine.TransformConstraint) + /// + /// Filters popup results to elements that begin with supplied string. + /// If true, the dropdown list will include a "none" option which stored as an empty string. + /// If specified, a locally scoped field with the name supplied by in dataField will be used to fill the popup results. + /// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives). + /// If left empty and the script the attribute is applied to is derived from Component, GetComponent() will be called as a fallback. + /// + public SpineTransformConstraint(string startsWith = "", string dataField = "", bool includeNone = true) { + this.startsWith = startsWith; + this.dataField = dataField; + this.includeNone = includeNone; + } + } + public class SpineSkin : SpineAttributeBase { /// /// Smart popup menu for Spine Skins /// /// Filters popup results to elements that begin with supplied string. + /// If true, the dropdown list will include a "none" option which stored as an empty string. /// If specified, a locally scoped field with the name supplied by in dataField will be used to fill the popup results. /// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives) /// If left empty and the script the attribute is applied to is derived from Component, GetComponent() will be called as a fallback. @@ -100,6 +154,7 @@ namespace Spine.Unity { /// /// Filters popup results to elements that begin with supplied string. /// If specified, a locally scoped field with the name supplied by in dataField will be used to fill the popup results. + /// If true, the dropdown list will include a "none" option which stored as an empty string. /// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives) /// If left empty and the script the attribute is applied to is derived from Component, GetComponent() will be called as a fallback. /// @@ -124,6 +179,8 @@ namespace Spine.Unity { /// Returns a fully qualified path for an Attachment in the format "Skin/Slot/AttachmentName". This path format is only used by the SpineAttachment helper methods like SpineAttachment.GetAttachment and .GetHierarchy. Do not use full path anywhere else in Spine's system. /// Filters popup results to exclude attachments that are not children of Skin Placeholders /// If specified, a locally scoped field with the name supplied by in slotField will be used to limit the popup results to children of a named slot + /// If specified, a locally scoped field with the name supplied by in skinField will be used to limit the popup results to entries of the named skin + /// If true, the dropdown list will include a "none" option which stored as an empty string. /// If specified, a locally scoped field with the name supplied by in dataField will be used to fill the popup results. /// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives) /// If left empty and the script the attribute is applied to is derived from Component, GetComponent() will be called as a fallback. @@ -184,6 +241,7 @@ namespace Spine.Unity { /// Smart popup menu for Spine Bones /// /// Filters popup results to elements that begin with supplied string. + /// /// If true, the dropdown list will include a "none" option which stored as an empty string. /// If specified, a locally scoped field with the name supplied by in dataField will be used to fill the popup results. /// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives) /// If left empty and the script the attribute is applied to is derived from Component, GetComponent() will be called as a fallback.