fix:1、添加项目
This commit is contained in:
+217
@@ -0,0 +1,217 @@
|
||||
/******************************************************************************
|
||||
* 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 System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Spine.Unity {
|
||||
|
||||
/// <summary>Sets a GameObject's transform to match a bone on a Spine skeleton.</summary>
|
||||
#if NEW_PREFAB_SYSTEM
|
||||
[ExecuteAlways]
|
||||
#else
|
||||
[ExecuteInEditMode]
|
||||
#endif
|
||||
[AddComponentMenu("Spine/BoneFollower")]
|
||||
[HelpURL("http://esotericsoftware.com/spine-unity#BoneFollower")]
|
||||
public class BoneFollower : MonoBehaviour {
|
||||
|
||||
#region Inspector
|
||||
public SkeletonRenderer skeletonRenderer;
|
||||
public SkeletonRenderer SkeletonRenderer {
|
||||
get { return skeletonRenderer; }
|
||||
set {
|
||||
skeletonRenderer = value;
|
||||
Initialize();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>If a bone isn't set in code, boneName is used to find the bone at the beginning. For runtime switching by name, use SetBoneByName. You can also set the BoneFollower.bone field directly.</summary>
|
||||
[SpineBone(dataField: "skeletonRenderer")]
|
||||
public string boneName;
|
||||
|
||||
public bool followXYPosition = true;
|
||||
public bool followZPosition = true;
|
||||
public bool followBoneRotation = true;
|
||||
|
||||
[Tooltip("Follows the skeleton's flip state by controlling this Transform's local scale.")]
|
||||
public bool followSkeletonFlip = true;
|
||||
|
||||
[Tooltip("Follows the target bone's local scale. BoneFollower cannot inherit world/skewed scale because of UnityEngine.Transform property limitations.")]
|
||||
public bool followLocalScale = false;
|
||||
|
||||
public enum AxisOrientation {
|
||||
XAxis = 1,
|
||||
YAxis
|
||||
}
|
||||
[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, "
|
||||
+ " only one axis can be followed, either the X or the Y axis, which is selected here.")]
|
||||
public AxisOrientation maintainedAxisOrientation = AxisOrientation.XAxis;
|
||||
|
||||
[UnityEngine.Serialization.FormerlySerializedAs("resetOnAwake")]
|
||||
public bool initializeOnAwake = true;
|
||||
#endregion
|
||||
|
||||
[NonSerialized] public bool valid;
|
||||
[NonSerialized] public Bone bone;
|
||||
|
||||
Transform skeletonTransform;
|
||||
bool skeletonTransformIsParent;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the target bone by its bone name. Returns false if no bone was found. To set the bone by reference, use BoneFollower.bone directly.</summary>
|
||||
public bool SetBone (string name) {
|
||||
bone = skeletonRenderer.skeleton.FindBone(name);
|
||||
if (bone == null) {
|
||||
Debug.LogError("Bone not found: " + name, this);
|
||||
return false;
|
||||
}
|
||||
boneName = name;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Awake () {
|
||||
if (initializeOnAwake) Initialize();
|
||||
}
|
||||
|
||||
public void HandleRebuildRenderer (SkeletonRenderer skeletonRenderer) {
|
||||
Initialize();
|
||||
}
|
||||
|
||||
public void Initialize () {
|
||||
bone = null;
|
||||
valid = skeletonRenderer != null && skeletonRenderer.valid;
|
||||
if (!valid) return;
|
||||
|
||||
skeletonTransform = skeletonRenderer.transform;
|
||||
skeletonRenderer.OnRebuild -= HandleRebuildRenderer;
|
||||
skeletonRenderer.OnRebuild += HandleRebuildRenderer;
|
||||
skeletonTransformIsParent = Transform.ReferenceEquals(skeletonTransform, transform.parent);
|
||||
|
||||
if (!string.IsNullOrEmpty(boneName))
|
||||
bone = skeletonRenderer.skeleton.FindBone(boneName);
|
||||
|
||||
#if UNITY_EDITOR
|
||||
if (Application.isEditor)
|
||||
LateUpdate();
|
||||
#endif
|
||||
}
|
||||
|
||||
void OnDestroy () {
|
||||
if (skeletonRenderer != null)
|
||||
skeletonRenderer.OnRebuild -= HandleRebuildRenderer;
|
||||
}
|
||||
|
||||
public void LateUpdate () {
|
||||
if (!valid) {
|
||||
Initialize();
|
||||
return;
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
if (!Application.isPlaying)
|
||||
skeletonTransformIsParent = Transform.ReferenceEquals(skeletonTransform, transform.parent);
|
||||
#endif
|
||||
|
||||
if (bone == null) {
|
||||
if (string.IsNullOrEmpty(boneName)) return;
|
||||
bone = skeletonRenderer.skeleton.FindBone(boneName);
|
||||
if (!SetBone(boneName)) return;
|
||||
}
|
||||
|
||||
Transform thisTransform = this.transform;
|
||||
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);
|
||||
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.
|
||||
halfRotation += Mathf.PI * 0.5f;
|
||||
|
||||
var q = default(Quaternion);
|
||||
q.z = Mathf.Sin(halfRotation);
|
||||
q.w = Mathf.Cos(halfRotation);
|
||||
thisTransform.localRotation = q;
|
||||
}
|
||||
} else {
|
||||
// For special cases: Use transform world properties if transform relationship is complicated
|
||||
Vector3 targetWorldPosition = skeletonTransform.TransformPoint(new Vector3(bone.worldX, bone.worldY, 0f));
|
||||
if (!followZPosition) targetWorldPosition.z = thisTransform.position.z;
|
||||
if (!followXYPosition) {
|
||||
targetWorldPosition.x = thisTransform.position.x;
|
||||
targetWorldPosition.y = thisTransform.position.y;
|
||||
}
|
||||
|
||||
Vector3 skeletonLossyScale = skeletonTransform.lossyScale;
|
||||
Transform transformParent = thisTransform.parent;
|
||||
Vector3 parentLossyScale = transformParent != null ? transformParent.lossyScale : Vector3.one;
|
||||
if (followBoneRotation) {
|
||||
float boneWorldRotation = bone.WorldRotationX;
|
||||
|
||||
if ((skeletonLossyScale.x * skeletonLossyScale.y) < 0)
|
||||
boneWorldRotation = -boneWorldRotation;
|
||||
|
||||
if (followSkeletonFlip || maintainedAxisOrientation == AxisOrientation.XAxis) {
|
||||
if ((skeletonLossyScale.x * parentLossyScale.x < 0))
|
||||
boneWorldRotation += 180f;
|
||||
}
|
||||
else {
|
||||
if ((skeletonLossyScale.y * parentLossyScale.y < 0))
|
||||
boneWorldRotation += 180f;
|
||||
}
|
||||
|
||||
Vector3 worldRotation = skeletonTransform.rotation.eulerAngles;
|
||||
if (followLocalScale && bone.scaleX < 0) boneWorldRotation += 180f;
|
||||
thisTransform.SetPositionAndRotation(targetWorldPosition, Quaternion.Euler(worldRotation.x, worldRotation.y, worldRotation.z + boneWorldRotation));
|
||||
} else {
|
||||
thisTransform.position = targetWorldPosition;
|
||||
}
|
||||
|
||||
additionalFlipScale = Mathf.Sign(skeletonLossyScale.x * parentLossyScale.x
|
||||
* skeletonLossyScale.y * parentLossyScale.y);
|
||||
}
|
||||
|
||||
Vector3 localScale = followLocalScale ? new Vector3(bone.scaleX, bone.scaleY, 1f) : new Vector3(1f, 1f, 1f);
|
||||
if (followSkeletonFlip)
|
||||
localScale.y *= Mathf.Sign(bone.skeleton.ScaleX * bone.skeleton.ScaleY) * additionalFlipScale;
|
||||
|
||||
thisTransform.localScale = localScale;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a1fd8daaed7b64148a34acb96ba14ce1
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+196
@@ -0,0 +1,196 @@
|
||||
/******************************************************************************
|
||||
* 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 {
|
||||
using AxisOrientation = BoneFollower.AxisOrientation;
|
||||
|
||||
#if NEW_PREFAB_SYSTEM
|
||||
[ExecuteAlways]
|
||||
#else
|
||||
[ExecuteInEditMode]
|
||||
#endif
|
||||
[RequireComponent(typeof(RectTransform)), DisallowMultipleComponent]
|
||||
[AddComponentMenu("Spine/UI/BoneFollowerGraphic")]
|
||||
[HelpURL("http://esotericsoftware.com/spine-unity#BoneFollowerGraphic")]
|
||||
public class BoneFollowerGraphic : MonoBehaviour {
|
||||
public SkeletonGraphic skeletonGraphic;
|
||||
public SkeletonGraphic SkeletonGraphic {
|
||||
get { return skeletonGraphic; }
|
||||
set {
|
||||
skeletonGraphic = value;
|
||||
Initialize();
|
||||
}
|
||||
}
|
||||
|
||||
public bool initializeOnAwake = true;
|
||||
|
||||
/// <summary>If a bone isn't set in code, boneName is used to find the bone at the beginning. For runtime switching by name, use SetBoneByName. You can also set the BoneFollower.bone field directly.</summary>
|
||||
[SpineBone(dataField: "skeletonGraphic")]
|
||||
public string boneName;
|
||||
|
||||
public bool followBoneRotation = true;
|
||||
[Tooltip("Follows the skeleton's flip state by controlling this Transform's local scale.")]
|
||||
public bool followSkeletonFlip = true;
|
||||
[Tooltip("Follows the target bone's local scale. BoneFollower cannot inherit world/skewed scale because of UnityEngine.Transform property limitations.")]
|
||||
public bool followLocalScale = false;
|
||||
public bool followXYPosition = true;
|
||||
public bool followZPosition = true;
|
||||
[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, "
|
||||
+ " only one axis can be followed, either the X or the Y axis, which is selected here.")]
|
||||
public AxisOrientation maintainedAxisOrientation = AxisOrientation.XAxis;
|
||||
|
||||
[System.NonSerialized] public Bone bone;
|
||||
|
||||
Transform skeletonTransform;
|
||||
bool skeletonTransformIsParent;
|
||||
|
||||
[System.NonSerialized] public bool valid;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the target bone by its bone name. Returns false if no bone was found.</summary>
|
||||
public bool SetBone (string name) {
|
||||
bone = skeletonGraphic.Skeleton.FindBone(name);
|
||||
if (bone == null) {
|
||||
Debug.LogError("Bone not found: " + name, this);
|
||||
return false;
|
||||
}
|
||||
boneName = name;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Awake () {
|
||||
if (initializeOnAwake) Initialize();
|
||||
}
|
||||
|
||||
public void Initialize () {
|
||||
bone = null;
|
||||
valid = skeletonGraphic != null && skeletonGraphic.IsValid;
|
||||
if (!valid) return;
|
||||
|
||||
skeletonTransform = skeletonGraphic.transform;
|
||||
// skeletonGraphic.OnRebuild -= HandleRebuildRenderer;
|
||||
// skeletonGraphic.OnRebuild += HandleRebuildRenderer;
|
||||
skeletonTransformIsParent = Transform.ReferenceEquals(skeletonTransform, transform.parent);
|
||||
|
||||
if (!string.IsNullOrEmpty(boneName))
|
||||
bone = skeletonGraphic.Skeleton.FindBone(boneName);
|
||||
|
||||
#if UNITY_EDITOR
|
||||
if (Application.isEditor) {
|
||||
LateUpdate();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public void LateUpdate () {
|
||||
if (!valid) {
|
||||
Initialize();
|
||||
return;
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
if (!Application.isPlaying)
|
||||
skeletonTransformIsParent = Transform.ReferenceEquals(skeletonTransform, transform.parent);
|
||||
#endif
|
||||
|
||||
if (bone == null) {
|
||||
if (string.IsNullOrEmpty(boneName)) return;
|
||||
bone = skeletonGraphic.Skeleton.FindBone(boneName);
|
||||
if (!SetBone(boneName)) return;
|
||||
}
|
||||
|
||||
var thisTransform = this.transform as RectTransform;
|
||||
if (thisTransform == null) return;
|
||||
|
||||
var canvas = skeletonGraphic.canvas;
|
||||
if (canvas == null) canvas = skeletonGraphic.GetComponentInParent<Canvas>();
|
||||
float scale = canvas != null ? canvas.referencePixelsPerUnit : 100.0f;
|
||||
|
||||
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 : thisTransform.localPosition.x,
|
||||
followXYPosition ? bone.worldY * scale : thisTransform.localPosition.y,
|
||||
followZPosition ? 0f : thisTransform.localPosition.z);
|
||||
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 * scale, bone.worldY * scale, 0f));
|
||||
if (!followZPosition) targetWorldPosition.z = thisTransform.position.z;
|
||||
if (!followXYPosition) {
|
||||
targetWorldPosition.x = thisTransform.position.x;
|
||||
targetWorldPosition.y = thisTransform.position.y;
|
||||
}
|
||||
|
||||
Vector3 skeletonLossyScale = skeletonTransform.lossyScale;
|
||||
Transform transformParent = thisTransform.parent;
|
||||
Vector3 parentLossyScale = transformParent != null ? transformParent.lossyScale : Vector3.one;
|
||||
if (followBoneRotation) {
|
||||
float boneWorldRotation = bone.WorldRotationX;
|
||||
|
||||
if ((skeletonLossyScale.x * skeletonLossyScale.y) < 0)
|
||||
boneWorldRotation = -boneWorldRotation;
|
||||
|
||||
if (followSkeletonFlip || maintainedAxisOrientation == AxisOrientation.XAxis) {
|
||||
if ((skeletonLossyScale.x * parentLossyScale.x < 0))
|
||||
boneWorldRotation += 180f;
|
||||
}
|
||||
else {
|
||||
if ((skeletonLossyScale.y * parentLossyScale.y < 0))
|
||||
boneWorldRotation += 180f;
|
||||
}
|
||||
|
||||
Vector3 worldRotation = skeletonTransform.rotation.eulerAngles;
|
||||
if (followLocalScale && bone.scaleX < 0) boneWorldRotation += 180f;
|
||||
thisTransform.SetPositionAndRotation(targetWorldPosition, Quaternion.Euler(worldRotation.x, worldRotation.y, worldRotation.z + boneWorldRotation));
|
||||
} else {
|
||||
thisTransform.position = targetWorldPosition;
|
||||
}
|
||||
|
||||
additionalFlipScale = Mathf.Sign(skeletonLossyScale.x * parentLossyScale.x
|
||||
* skeletonLossyScale.y * parentLossyScale.y);
|
||||
}
|
||||
|
||||
Vector3 localScale = followLocalScale ? new Vector3(bone.scaleX, bone.scaleY, 1f) : new Vector3(1f, 1f, 1f);
|
||||
if (followSkeletonFlip)
|
||||
localScale.y *= Mathf.Sign(bone.skeleton.ScaleX * bone.skeleton.ScaleY) * additionalFlipScale;
|
||||
thisTransform.localScale = localScale;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b42a195b47491d34b9bcbc40898bcb29
|
||||
timeCreated: 1499211965
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+253
@@ -0,0 +1,253 @@
|
||||
/******************************************************************************
|
||||
* 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;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Spine.Unity {
|
||||
|
||||
#if NEW_PREFAB_SYSTEM
|
||||
[ExecuteAlways]
|
||||
#else
|
||||
[ExecuteInEditMode]
|
||||
#endif
|
||||
[HelpURL("http://esotericsoftware.com/spine-unity#BoundingBoxFollower")]
|
||||
public class BoundingBoxFollower : MonoBehaviour {
|
||||
internal static bool DebugMessages = true;
|
||||
|
||||
#region Inspector
|
||||
public SkeletonRenderer skeletonRenderer;
|
||||
[SpineSlot(dataField: "skeletonRenderer", containsBoundingBoxes: true)]
|
||||
public string slotName;
|
||||
public bool isTrigger;
|
||||
public bool clearStateOnDisable = true;
|
||||
#endregion
|
||||
|
||||
Slot slot;
|
||||
BoundingBoxAttachment currentAttachment;
|
||||
string currentAttachmentName;
|
||||
PolygonCollider2D currentCollider;
|
||||
|
||||
public readonly Dictionary<BoundingBoxAttachment, PolygonCollider2D> colliderTable = new Dictionary<BoundingBoxAttachment, PolygonCollider2D>();
|
||||
public readonly Dictionary<BoundingBoxAttachment, string> nameTable = new Dictionary<BoundingBoxAttachment, string>();
|
||||
|
||||
public Slot Slot { get { return slot; } }
|
||||
public BoundingBoxAttachment CurrentAttachment { get { return currentAttachment; } }
|
||||
public string CurrentAttachmentName { get { return currentAttachmentName; } }
|
||||
public PolygonCollider2D CurrentCollider { get { return currentCollider; } }
|
||||
public bool IsTrigger { get { return isTrigger; } }
|
||||
|
||||
void Start () {
|
||||
Initialize();
|
||||
}
|
||||
|
||||
void OnEnable () {
|
||||
if (skeletonRenderer != null) {
|
||||
skeletonRenderer.OnRebuild -= HandleRebuild;
|
||||
skeletonRenderer.OnRebuild += HandleRebuild;
|
||||
}
|
||||
|
||||
Initialize();
|
||||
}
|
||||
|
||||
void HandleRebuild (SkeletonRenderer sr) {
|
||||
//if (BoundingBoxFollower.DebugMessages) Debug.Log("Skeleton was rebuilt. Repopulating BoundingBoxFollower.");
|
||||
Initialize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize and instantiate the BoundingBoxFollower colliders. This is method checks if the BoundingBoxFollower has already been initialized for the skeleton instance and slotName and prevents overwriting unless it detects a new setup.</summary>
|
||||
public void Initialize (bool overwrite = false) {
|
||||
if (skeletonRenderer == null)
|
||||
return;
|
||||
|
||||
skeletonRenderer.Initialize(false);
|
||||
|
||||
if (string.IsNullOrEmpty(slotName))
|
||||
return;
|
||||
|
||||
// Don't reinitialize if the setup did not change.
|
||||
if (!overwrite
|
||||
&&
|
||||
colliderTable.Count > 0 && slot != null // Slot is set and colliders already populated.
|
||||
&&
|
||||
skeletonRenderer.skeleton == slot.Skeleton // Skeleton object did not change.
|
||||
&&
|
||||
slotName == slot.data.name // Slot object did not change.
|
||||
)
|
||||
return;
|
||||
|
||||
slot = null;
|
||||
currentAttachment = null;
|
||||
currentAttachmentName = null;
|
||||
currentCollider = null;
|
||||
colliderTable.Clear();
|
||||
nameTable.Clear();
|
||||
|
||||
var skeleton = skeletonRenderer.skeleton;
|
||||
if (skeleton == null)
|
||||
return;
|
||||
slot = skeleton.FindSlot(slotName);
|
||||
int slotIndex = skeleton.FindSlotIndex(slotName);
|
||||
|
||||
if (slot == null) {
|
||||
if (BoundingBoxFollower.DebugMessages)
|
||||
Debug.LogWarning(string.Format("Slot '{0}' not found for BoundingBoxFollower on '{1}'. (Previous colliders were disposed.)", slotName, this.gameObject.name));
|
||||
return;
|
||||
}
|
||||
|
||||
int requiredCollidersCount = 0;
|
||||
var colliders = GetComponents<PolygonCollider2D>();
|
||||
if (this.gameObject.activeInHierarchy) {
|
||||
foreach (var skin in skeleton.Data.Skins)
|
||||
AddCollidersForSkin(skin, slotIndex, colliders, ref requiredCollidersCount);
|
||||
|
||||
if (skeleton.skin != null)
|
||||
AddCollidersForSkin(skeleton.skin, slotIndex, colliders, ref requiredCollidersCount);
|
||||
}
|
||||
DisposeExcessCollidersAfter(requiredCollidersCount);
|
||||
|
||||
if (BoundingBoxFollower.DebugMessages) {
|
||||
bool valid = colliderTable.Count != 0;
|
||||
if (!valid) {
|
||||
if (this.gameObject.activeInHierarchy)
|
||||
Debug.LogWarning("Bounding Box Follower not valid! Slot [" + slotName + "] does not contain any Bounding Box Attachments!");
|
||||
else
|
||||
Debug.LogWarning("Bounding Box Follower tried to rebuild as a prefab.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AddCollidersForSkin (Skin skin, int slotIndex, PolygonCollider2D[] previousColliders, ref int collidersCount) {
|
||||
if (skin == null) return;
|
||||
var skinEntries = new List<Skin.SkinEntry>();
|
||||
skin.GetAttachments(slotIndex, skinEntries);
|
||||
|
||||
foreach (var entry in skinEntries) {
|
||||
var attachment = skin.GetAttachment(slotIndex, entry.Name);
|
||||
var boundingBoxAttachment = attachment as BoundingBoxAttachment;
|
||||
|
||||
if (BoundingBoxFollower.DebugMessages && attachment != null && boundingBoxAttachment == null)
|
||||
Debug.Log("BoundingBoxFollower tried to follow a slot that contains non-boundingbox attachments: " + slotName);
|
||||
|
||||
if (boundingBoxAttachment != null) {
|
||||
if (!colliderTable.ContainsKey(boundingBoxAttachment)) {
|
||||
var bbCollider = collidersCount < previousColliders.Length ?
|
||||
previousColliders[collidersCount] : gameObject.AddComponent<PolygonCollider2D>();
|
||||
++collidersCount;
|
||||
SkeletonUtility.SetColliderPointsLocal(bbCollider, slot, boundingBoxAttachment);
|
||||
bbCollider.isTrigger = isTrigger;
|
||||
bbCollider.enabled = false;
|
||||
bbCollider.hideFlags = HideFlags.NotEditable;
|
||||
bbCollider.isTrigger = IsTrigger;
|
||||
colliderTable.Add(boundingBoxAttachment, bbCollider);
|
||||
nameTable.Add(boundingBoxAttachment, entry.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnDisable () {
|
||||
if (clearStateOnDisable)
|
||||
ClearState();
|
||||
|
||||
if (skeletonRenderer != null)
|
||||
skeletonRenderer.OnRebuild -= HandleRebuild;
|
||||
}
|
||||
|
||||
public void ClearState () {
|
||||
if (colliderTable != null)
|
||||
foreach (var col in colliderTable.Values)
|
||||
col.enabled = false;
|
||||
|
||||
currentAttachment = null;
|
||||
currentAttachmentName = null;
|
||||
currentCollider = null;
|
||||
}
|
||||
|
||||
void DisposeExcessCollidersAfter (int requiredCount) {
|
||||
var colliders = GetComponents<PolygonCollider2D>();
|
||||
if (colliders.Length == 0) return;
|
||||
|
||||
for (int i = requiredCount; i < colliders.Length; ++i) {
|
||||
var collider = colliders[i];
|
||||
if (collider != null) {
|
||||
#if UNITY_EDITOR
|
||||
if (Application.isEditor && !Application.isPlaying)
|
||||
DestroyImmediate(collider);
|
||||
else
|
||||
#endif
|
||||
Destroy(collider);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LateUpdate () {
|
||||
if (slot != null && slot.Attachment != currentAttachment)
|
||||
MatchAttachment(slot.Attachment);
|
||||
}
|
||||
|
||||
/// <summary>Sets the current collider to match attachment.</summary>
|
||||
/// <param name="attachment">If the attachment is not a bounding box, it will be treated as null.</param>
|
||||
void MatchAttachment (Attachment attachment) {
|
||||
var bbAttachment = attachment as BoundingBoxAttachment;
|
||||
|
||||
if (BoundingBoxFollower.DebugMessages && attachment != null && bbAttachment == null)
|
||||
Debug.LogWarning("BoundingBoxFollower tried to match a non-boundingbox attachment. It will treat it as null.");
|
||||
|
||||
if (currentCollider != null)
|
||||
currentCollider.enabled = false;
|
||||
|
||||
if (bbAttachment == null) {
|
||||
currentCollider = null;
|
||||
currentAttachment = null;
|
||||
currentAttachmentName = null;
|
||||
} else {
|
||||
PolygonCollider2D foundCollider;
|
||||
colliderTable.TryGetValue(bbAttachment, out foundCollider);
|
||||
if (foundCollider != null) {
|
||||
currentCollider = foundCollider;
|
||||
currentCollider.enabled = true;
|
||||
currentAttachment = bbAttachment;
|
||||
currentAttachmentName = nameTable[bbAttachment];
|
||||
} else {
|
||||
currentCollider = null;
|
||||
currentAttachment = bbAttachment;
|
||||
currentAttachmentName = null;
|
||||
if (BoundingBoxFollower.DebugMessages) Debug.LogFormat("Collider for BoundingBoxAttachment named '{0}' was not initialized. It is possibly from a new skin. currentAttachmentName will be null. You may need to call BoundingBoxFollower.Initialize(overwrite: true);", bbAttachment.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0317ee9ba6e1b1e49a030268e026d372
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
+257
@@ -0,0 +1,257 @@
|
||||
/******************************************************************************
|
||||
* 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;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Spine.Unity {
|
||||
|
||||
#if NEW_PREFAB_SYSTEM
|
||||
[ExecuteAlways]
|
||||
#else
|
||||
[ExecuteInEditMode]
|
||||
#endif
|
||||
[HelpURL("http://esotericsoftware.com/spine-unity#BoundingBoxFollowerGraphic")]
|
||||
public class BoundingBoxFollowerGraphic : MonoBehaviour {
|
||||
internal static bool DebugMessages = true;
|
||||
|
||||
#region Inspector
|
||||
public SkeletonGraphic skeletonGraphic;
|
||||
[SpineSlot(dataField: "skeletonGraphic", containsBoundingBoxes: true)]
|
||||
public string slotName;
|
||||
public bool isTrigger;
|
||||
public bool clearStateOnDisable = true;
|
||||
#endregion
|
||||
|
||||
Slot slot;
|
||||
BoundingBoxAttachment currentAttachment;
|
||||
string currentAttachmentName;
|
||||
PolygonCollider2D currentCollider;
|
||||
|
||||
public readonly Dictionary<BoundingBoxAttachment, PolygonCollider2D> colliderTable = new Dictionary<BoundingBoxAttachment, PolygonCollider2D>();
|
||||
public readonly Dictionary<BoundingBoxAttachment, string> nameTable = new Dictionary<BoundingBoxAttachment, string>();
|
||||
|
||||
public Slot Slot { get { return slot; } }
|
||||
public BoundingBoxAttachment CurrentAttachment { get { return currentAttachment; } }
|
||||
public string CurrentAttachmentName { get { return currentAttachmentName; } }
|
||||
public PolygonCollider2D CurrentCollider { get { return currentCollider; } }
|
||||
public bool IsTrigger { get { return isTrigger; } }
|
||||
|
||||
void Start () {
|
||||
Initialize();
|
||||
}
|
||||
|
||||
void OnEnable () {
|
||||
if (skeletonGraphic != null) {
|
||||
skeletonGraphic.OnRebuild -= HandleRebuild;
|
||||
skeletonGraphic.OnRebuild += HandleRebuild;
|
||||
}
|
||||
|
||||
Initialize();
|
||||
}
|
||||
|
||||
void HandleRebuild (SkeletonGraphic sr) {
|
||||
//if (BoundingBoxFollowerGraphic.DebugMessages) Debug.Log("Skeleton was rebuilt. Repopulating BoundingBoxFollowerGraphic.");
|
||||
Initialize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize and instantiate the BoundingBoxFollowerGraphic colliders. This is method checks if the BoundingBoxFollowerGraphic has already been initialized for the skeleton instance and slotName and prevents overwriting unless it detects a new setup.</summary>
|
||||
public void Initialize (bool overwrite = false) {
|
||||
if (skeletonGraphic == null)
|
||||
return;
|
||||
|
||||
skeletonGraphic.Initialize(false);
|
||||
|
||||
if (string.IsNullOrEmpty(slotName))
|
||||
return;
|
||||
|
||||
// Don't reinitialize if the setup did not change.
|
||||
if (!overwrite
|
||||
&&
|
||||
colliderTable.Count > 0 && slot != null // Slot is set and colliders already populated.
|
||||
&&
|
||||
skeletonGraphic.Skeleton == slot.Skeleton // Skeleton object did not change.
|
||||
&&
|
||||
slotName == slot.data.name // Slot object did not change.
|
||||
)
|
||||
return;
|
||||
|
||||
slot = null;
|
||||
currentAttachment = null;
|
||||
currentAttachmentName = null;
|
||||
currentCollider = null;
|
||||
colliderTable.Clear();
|
||||
nameTable.Clear();
|
||||
|
||||
var skeleton = skeletonGraphic.Skeleton;
|
||||
if (skeleton == null)
|
||||
return;
|
||||
slot = skeleton.FindSlot(slotName);
|
||||
int slotIndex = skeleton.FindSlotIndex(slotName);
|
||||
|
||||
if (slot == null) {
|
||||
if (BoundingBoxFollowerGraphic.DebugMessages)
|
||||
Debug.LogWarning(string.Format("Slot '{0}' not found for BoundingBoxFollowerGraphic on '{1}'. (Previous colliders were disposed.)", slotName, this.gameObject.name));
|
||||
return;
|
||||
}
|
||||
|
||||
int requiredCollidersCount = 0;
|
||||
var colliders = GetComponents<PolygonCollider2D>();
|
||||
if (this.gameObject.activeInHierarchy) {
|
||||
var canvas = skeletonGraphic.canvas;
|
||||
if (canvas == null) canvas = skeletonGraphic.GetComponentInParent<Canvas>();
|
||||
float scale = canvas != null ? canvas.referencePixelsPerUnit : 100.0f;
|
||||
|
||||
foreach (var skin in skeleton.Data.Skins)
|
||||
AddCollidersForSkin(skin, slotIndex, colliders, scale, ref requiredCollidersCount);
|
||||
|
||||
if (skeleton.skin != null)
|
||||
AddCollidersForSkin(skeleton.skin, slotIndex, colliders, scale, ref requiredCollidersCount);
|
||||
}
|
||||
DisposeExcessCollidersAfter(requiredCollidersCount);
|
||||
|
||||
if (BoundingBoxFollowerGraphic.DebugMessages) {
|
||||
bool valid = colliderTable.Count != 0;
|
||||
if (!valid) {
|
||||
if (this.gameObject.activeInHierarchy)
|
||||
Debug.LogWarning("Bounding Box Follower not valid! Slot [" + slotName + "] does not contain any Bounding Box Attachments!");
|
||||
else
|
||||
Debug.LogWarning("Bounding Box Follower tried to rebuild as a prefab.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AddCollidersForSkin (Skin skin, int slotIndex, PolygonCollider2D[] previousColliders, float scale, ref int collidersCount) {
|
||||
if (skin == null) return;
|
||||
var skinEntries = new List<Skin.SkinEntry>();
|
||||
skin.GetAttachments(slotIndex, skinEntries);
|
||||
|
||||
foreach (var entry in skinEntries) {
|
||||
var attachment = skin.GetAttachment(slotIndex, entry.Name);
|
||||
var boundingBoxAttachment = attachment as BoundingBoxAttachment;
|
||||
|
||||
if (BoundingBoxFollowerGraphic.DebugMessages && attachment != null && boundingBoxAttachment == null)
|
||||
Debug.Log("BoundingBoxFollowerGraphic tried to follow a slot that contains non-boundingbox attachments: " + slotName);
|
||||
|
||||
if (boundingBoxAttachment != null) {
|
||||
if (!colliderTable.ContainsKey(boundingBoxAttachment)) {
|
||||
var bbCollider = collidersCount < previousColliders.Length ?
|
||||
previousColliders[collidersCount] : gameObject.AddComponent<PolygonCollider2D>();
|
||||
++collidersCount;
|
||||
SkeletonUtility.SetColliderPointsLocal(bbCollider, slot, boundingBoxAttachment, scale);
|
||||
bbCollider.isTrigger = isTrigger;
|
||||
bbCollider.enabled = false;
|
||||
bbCollider.hideFlags = HideFlags.NotEditable;
|
||||
bbCollider.isTrigger = IsTrigger;
|
||||
colliderTable.Add(boundingBoxAttachment, bbCollider);
|
||||
nameTable.Add(boundingBoxAttachment, entry.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnDisable () {
|
||||
if (clearStateOnDisable)
|
||||
ClearState();
|
||||
|
||||
if (skeletonGraphic != null)
|
||||
skeletonGraphic.OnRebuild -= HandleRebuild;
|
||||
}
|
||||
|
||||
public void ClearState () {
|
||||
if (colliderTable != null)
|
||||
foreach (var col in colliderTable.Values)
|
||||
col.enabled = false;
|
||||
|
||||
currentAttachment = null;
|
||||
currentAttachmentName = null;
|
||||
currentCollider = null;
|
||||
}
|
||||
|
||||
void DisposeExcessCollidersAfter (int requiredCount) {
|
||||
var colliders = GetComponents<PolygonCollider2D>();
|
||||
if (colliders.Length == 0) return;
|
||||
|
||||
for (int i = requiredCount; i < colliders.Length; ++i) {
|
||||
var collider = colliders[i];
|
||||
if (collider != null) {
|
||||
#if UNITY_EDITOR
|
||||
if (Application.isEditor && !Application.isPlaying)
|
||||
DestroyImmediate(collider);
|
||||
else
|
||||
#endif
|
||||
Destroy(collider);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LateUpdate () {
|
||||
if (slot != null && slot.Attachment != currentAttachment)
|
||||
MatchAttachment(slot.Attachment);
|
||||
}
|
||||
|
||||
/// <summary>Sets the current collider to match attachment.</summary>
|
||||
/// <param name="attachment">If the attachment is not a bounding box, it will be treated as null.</param>
|
||||
void MatchAttachment (Attachment attachment) {
|
||||
var bbAttachment = attachment as BoundingBoxAttachment;
|
||||
|
||||
if (BoundingBoxFollowerGraphic.DebugMessages && attachment != null && bbAttachment == null)
|
||||
Debug.LogWarning("BoundingBoxFollowerGraphic tried to match a non-boundingbox attachment. It will treat it as null.");
|
||||
|
||||
if (currentCollider != null)
|
||||
currentCollider.enabled = false;
|
||||
|
||||
if (bbAttachment == null) {
|
||||
currentCollider = null;
|
||||
currentAttachment = null;
|
||||
currentAttachmentName = null;
|
||||
} else {
|
||||
PolygonCollider2D foundCollider;
|
||||
colliderTable.TryGetValue(bbAttachment, out foundCollider);
|
||||
if (foundCollider != null) {
|
||||
currentCollider = foundCollider;
|
||||
currentCollider.enabled = true;
|
||||
currentAttachment = bbAttachment;
|
||||
currentAttachmentName = nameTable[bbAttachment];
|
||||
} else {
|
||||
currentCollider = null;
|
||||
currentAttachment = bbAttachment;
|
||||
currentAttachmentName = null;
|
||||
if (BoundingBoxFollowerGraphic.DebugMessages) Debug.LogFormat("Collider for BoundingBoxAttachment named '{0}' was not initialized. It is possibly from a new skin. currentAttachmentName will be null. You may need to call BoundingBoxFollowerGraphic.Initialize(overwrite: true);", bbAttachment.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1c0bf7b497af9f74280040d96cdf88da
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+164
@@ -0,0 +1,164 @@
|
||||
/******************************************************************************
|
||||
* 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 {
|
||||
|
||||
#if NEW_PREFAB_SYSTEM
|
||||
[ExecuteAlways]
|
||||
#else
|
||||
[ExecuteInEditMode]
|
||||
#endif
|
||||
[AddComponentMenu("Spine/Point Follower")]
|
||||
[HelpURL("http://esotericsoftware.com/spine-unity#PointFollower")]
|
||||
public class PointFollower : MonoBehaviour, IHasSkeletonRenderer, IHasSkeletonComponent {
|
||||
|
||||
public SkeletonRenderer skeletonRenderer;
|
||||
public SkeletonRenderer SkeletonRenderer { get { return this.skeletonRenderer; } }
|
||||
public ISkeletonComponent SkeletonComponent { get { return skeletonRenderer as ISkeletonComponent; } }
|
||||
|
||||
[SpineSlot(dataField:"skeletonRenderer", includeNone: true)]
|
||||
public string slotName;
|
||||
|
||||
[SpineAttachment(slotField:"slotName", dataField: "skeletonRenderer", fallbackToTextField:true, includeNone: true)]
|
||||
public string pointAttachmentName;
|
||||
|
||||
public bool followRotation = true;
|
||||
public bool followSkeletonFlip = true;
|
||||
public bool followSkeletonZPosition = false;
|
||||
|
||||
Transform skeletonTransform;
|
||||
bool skeletonTransformIsParent;
|
||||
PointAttachment point;
|
||||
Bone bone;
|
||||
bool valid;
|
||||
public bool IsValid { get { return valid; } }
|
||||
|
||||
public void Initialize () {
|
||||
valid = skeletonRenderer != null && skeletonRenderer.valid;
|
||||
if (!valid)
|
||||
return;
|
||||
|
||||
UpdateReferences();
|
||||
|
||||
#if UNITY_EDITOR
|
||||
if (Application.isEditor) LateUpdate();
|
||||
#endif
|
||||
}
|
||||
|
||||
private void HandleRebuildRenderer (SkeletonRenderer skeletonRenderer) {
|
||||
Initialize();
|
||||
}
|
||||
|
||||
void UpdateReferences () {
|
||||
skeletonTransform = skeletonRenderer.transform;
|
||||
skeletonRenderer.OnRebuild -= HandleRebuildRenderer;
|
||||
skeletonRenderer.OnRebuild += HandleRebuildRenderer;
|
||||
skeletonTransformIsParent = Transform.ReferenceEquals(skeletonTransform, transform.parent);
|
||||
|
||||
bone = null;
|
||||
point = null;
|
||||
if (!string.IsNullOrEmpty(pointAttachmentName)) {
|
||||
var skeleton = skeletonRenderer.Skeleton;
|
||||
|
||||
int slotIndex = skeleton.FindSlotIndex(slotName);
|
||||
if (slotIndex >= 0) {
|
||||
var slot = skeleton.slots.Items[slotIndex];
|
||||
bone = slot.bone;
|
||||
point = skeleton.GetAttachment(slotIndex, pointAttachmentName) as PointAttachment;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnDestroy () {
|
||||
if (skeletonRenderer != null)
|
||||
skeletonRenderer.OnRebuild -= HandleRebuildRenderer;
|
||||
}
|
||||
|
||||
public void LateUpdate () {
|
||||
#if UNITY_EDITOR
|
||||
if (!Application.isPlaying) skeletonTransformIsParent = Transform.ReferenceEquals(skeletonTransform, transform.parent);
|
||||
#endif
|
||||
|
||||
if (point == null) {
|
||||
if (string.IsNullOrEmpty(pointAttachmentName)) return;
|
||||
UpdateReferences();
|
||||
if (point == null) return;
|
||||
}
|
||||
|
||||
Vector2 worldPos;
|
||||
point.ComputeWorldPosition(bone, out worldPos.x, out worldPos.y);
|
||||
float rotation = point.ComputeWorldRotation(bone);
|
||||
|
||||
Transform thisTransform = this.transform;
|
||||
if (skeletonTransformIsParent) {
|
||||
// Recommended setup: Use local transform properties if Spine GameObject is the immediate parent
|
||||
thisTransform.localPosition = new Vector3(worldPos.x, worldPos.y, followSkeletonZPosition ? 0f : thisTransform.localPosition.z);
|
||||
if (followRotation) {
|
||||
float halfRotation = rotation * 0.5f * Mathf.Deg2Rad;
|
||||
|
||||
var q = default(Quaternion);
|
||||
q.z = Mathf.Sin(halfRotation);
|
||||
q.w = Mathf.Cos(halfRotation);
|
||||
thisTransform.localRotation = q;
|
||||
}
|
||||
} else {
|
||||
// For special cases: Use transform world properties if transform relationship is complicated
|
||||
Vector3 targetWorldPosition = skeletonTransform.TransformPoint(new Vector3(worldPos.x, worldPos.y, 0f));
|
||||
if (!followSkeletonZPosition)
|
||||
targetWorldPosition.z = thisTransform.position.z;
|
||||
|
||||
Transform transformParent = thisTransform.parent;
|
||||
if (transformParent != null) {
|
||||
Matrix4x4 m = transformParent.localToWorldMatrix;
|
||||
if (m.m00 * m.m11 - m.m01 * m.m10 < 0) // Determinant2D is negative
|
||||
rotation = -rotation;
|
||||
}
|
||||
|
||||
if (followRotation) {
|
||||
Vector3 transformWorldRotation = skeletonTransform.rotation.eulerAngles;
|
||||
thisTransform.SetPositionAndRotation(targetWorldPosition, Quaternion.Euler(transformWorldRotation.x, transformWorldRotation.y, transformWorldRotation.z + rotation));
|
||||
} else {
|
||||
thisTransform.position = targetWorldPosition;
|
||||
}
|
||||
}
|
||||
|
||||
if (followSkeletonFlip) {
|
||||
Vector3 localScale = thisTransform.localScale;
|
||||
localScale.y = Mathf.Abs(localScale.y) * Mathf.Sign(bone.skeleton.ScaleX * bone.skeleton.ScaleY);
|
||||
thisTransform.localScale = localScale;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: daf461e4341180341a648c07e1899528
|
||||
timeCreated: 1518094986
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user