You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
253 lines
9.2 KiB
253 lines
9.2 KiB
/****************************************************************************** |
|
* 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); |
|
} |
|
} |
|
} |
|
} |
|
|
|
}
|
|
|