/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, 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.
*
* THE SPINE RUNTIMES ARE 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
* THE SPINE RUNTIMES, 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 UnityEngine;
namespace Spine.Unity {
/// Sets a GameObject's transform to match a bone on a Spine skeleton.
#if NEW_PREFAB_SYSTEM
[ExecuteAlways]
#else
[ExecuteInEditMode]
#endif
[AddComponentMenu("Spine/SkeletonUtilityBone")]
[HelpURL("http://esotericsoftware.com/spine-unity#SkeletonUtilityBone")]
public class SkeletonUtilityBone : MonoBehaviour {
public enum Mode {
Follow,
Override
}
public enum UpdatePhase {
Local,
World,
Complete
}
#region Inspector
/// If a bone isn't set, boneName is used to find the bone.
public string boneName;
public Transform parentReference;
public Mode mode;
public bool position, rotation, scale, zPosition = true;
[Range(0f, 1f)]
public float overrideAlpha = 1;
#endregion
public SkeletonUtility hierarchy;
[System.NonSerialized] public Bone bone;
[System.NonSerialized] public bool transformLerpComplete;
[System.NonSerialized] public bool valid;
Transform cachedTransform;
Transform skeletonTransform;
bool incompatibleTransformMode;
public bool IncompatibleTransformMode { get { return incompatibleTransformMode; } }
public void Reset () {
bone = null;
cachedTransform = transform;
valid = hierarchy != null && hierarchy.IsValid;
if (!valid)
return;
skeletonTransform = hierarchy.transform;
hierarchy.OnReset -= HandleOnReset;
hierarchy.OnReset += HandleOnReset;
DoUpdate(UpdatePhase.Local);
}
void OnEnable () {
if (hierarchy == null) hierarchy = transform.GetComponentInParent();
if (hierarchy == null) return;
hierarchy.RegisterBone(this);
hierarchy.OnReset += HandleOnReset;
}
void HandleOnReset () {
Reset();
}
void OnDisable () {
if (hierarchy != null) {
hierarchy.OnReset -= HandleOnReset;
hierarchy.UnregisterBone(this);
}
}
public void DoUpdate (UpdatePhase phase) {
if (!valid) {
Reset();
return;
}
var skeleton = hierarchy.Skeleton;
if (bone == null) {
if (string.IsNullOrEmpty(boneName)) return;
bone = skeleton.FindBone(boneName);
if (bone == null) {
Debug.LogError("Bone not found: " + boneName, this);
return;
}
}
if (!bone.Active) return;
float positionScale = hierarchy.PositionScale;
var thisTransform = cachedTransform;
float skeletonFlipRotation = Mathf.Sign(skeleton.ScaleX * skeleton.ScaleY);
if (mode == Mode.Follow) {
switch (phase) {
case UpdatePhase.Local:
if (position)
thisTransform.localPosition = new Vector3(bone.x * positionScale, bone.y * positionScale, 0);
if (rotation) {
if (bone.data.transformMode.InheritsRotation()) {
thisTransform.localRotation = Quaternion.Euler(0, 0, bone.rotation);
} else {
Vector3 euler = skeletonTransform.rotation.eulerAngles;
thisTransform.rotation = Quaternion.Euler(euler.x, euler.y, euler.z + (bone.WorldRotationX * skeletonFlipRotation));
}
}
if (scale) {
thisTransform.localScale = new Vector3(bone.scaleX, bone.scaleY, 1f);
incompatibleTransformMode = BoneTransformModeIncompatible(bone);
}
break;
case UpdatePhase.World:
case UpdatePhase.Complete:
// Use Applied transform values (ax, ay, AppliedRotation, ascale) if world values were modified by constraints.
if (!bone.appliedValid) {
bone.UpdateAppliedTransform();
}
if (position)
thisTransform.localPosition = new Vector3(bone.ax * positionScale, bone.ay * positionScale, 0);
if (rotation) {
if (bone.data.transformMode.InheritsRotation()) {
thisTransform.localRotation = Quaternion.Euler(0, 0, bone.AppliedRotation);
} else {
Vector3 euler = skeletonTransform.rotation.eulerAngles;
thisTransform.rotation = Quaternion.Euler(euler.x, euler.y, euler.z + (bone.WorldRotationX * skeletonFlipRotation));
}
}
if (scale) {
thisTransform.localScale = new Vector3(bone.ascaleX, bone.ascaleY, 1f);
incompatibleTransformMode = BoneTransformModeIncompatible(bone);
}
break;
}
} else if (mode == Mode.Override) {
if (transformLerpComplete)
return;
if (parentReference == null) {
if (position) {
Vector3 clp = thisTransform.localPosition / positionScale;
bone.x = Mathf.Lerp(bone.x, clp.x, overrideAlpha);
bone.y = Mathf.Lerp(bone.y, clp.y, overrideAlpha);
}
if (rotation) {
float angle = Mathf.LerpAngle(bone.Rotation, thisTransform.localRotation.eulerAngles.z, overrideAlpha);
bone.Rotation = angle;
bone.AppliedRotation = angle;
}
if (scale) {
Vector3 cls = thisTransform.localScale;
bone.scaleX = Mathf.Lerp(bone.scaleX, cls.x, overrideAlpha);
bone.scaleY = Mathf.Lerp(bone.scaleY, cls.y, overrideAlpha);
}
} else {
if (transformLerpComplete)
return;
if (position) {
Vector3 pos = parentReference.InverseTransformPoint(thisTransform.position) / positionScale;
bone.x = Mathf.Lerp(bone.x, pos.x, overrideAlpha);
bone.y = Mathf.Lerp(bone.y, pos.y, overrideAlpha);
}
if (rotation) {
float angle = Mathf.LerpAngle(bone.Rotation, Quaternion.LookRotation(Vector3.forward, parentReference.InverseTransformDirection(thisTransform.up)).eulerAngles.z, overrideAlpha);
bone.Rotation = angle;
bone.AppliedRotation = angle;
}
if (scale) {
Vector3 cls = thisTransform.localScale;
bone.scaleX = Mathf.Lerp(bone.scaleX, cls.x, overrideAlpha);
bone.scaleY = Mathf.Lerp(bone.scaleY, cls.y, overrideAlpha);
}
incompatibleTransformMode = BoneTransformModeIncompatible(bone);
}
transformLerpComplete = true;
}
}
public static bool BoneTransformModeIncompatible (Bone bone) {
return !bone.data.transformMode.InheritsScale();
}
public void AddBoundingBox (string skinName, string slotName, string attachmentName) {
SkeletonUtility.AddBoneRigidbody2D(transform.gameObject);
SkeletonUtility.AddBoundingBoxGameObject(bone.skeleton, skinName, slotName, attachmentName, transform);
}
#if UNITY_EDITOR
void OnDrawGizmos () {
if (IncompatibleTransformMode)
Gizmos.DrawIcon(transform.position + new Vector3(0, 0.128f, 0), "icon-warning");
}
#endif
}
}