Initial commit: Client Doc docs Server Tools
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,291 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated April 5, 2025. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2026, 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
|
||||
|
||||
#if UNITY_2023_1_OR_NEWER
|
||||
#define USE_COLLIDER_COMPOSITE_OPERATION
|
||||
#endif
|
||||
|
||||
#if !SPINE_AUTO_UPGRADE_COMPONENTS_OFF
|
||||
#define AUTO_UPGRADE_TO_43_COMPONENTS
|
||||
#endif
|
||||
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace Spine.Unity {
|
||||
|
||||
#if NEW_PREFAB_SYSTEM
|
||||
[ExecuteAlways]
|
||||
#else
|
||||
[ExecuteInEditMode]
|
||||
#endif
|
||||
[HelpURL("http://esotericsoftware.com/spine-unity-utility-components#BoundingBoxFollower")]
|
||||
public class BoundingBoxFollower : MonoBehaviour, IUpgradable {
|
||||
internal static bool DebugMessages = true;
|
||||
|
||||
#region Inspector
|
||||
public SkeletonRenderer skeletonRenderer;
|
||||
[SpineSlot(dataField: "skeletonRenderer", containsBoundingBoxes: true)]
|
||||
public string slotName;
|
||||
public bool isTrigger, usedByEffector, usedByComposite;
|
||||
public bool clearStateOnDisable = true;
|
||||
#endregion
|
||||
|
||||
Slot slot;
|
||||
BoundingBoxAttachment currentAttachment;
|
||||
string currentAttachmentName;
|
||||
PolygonCollider2D currentCollider;
|
||||
bool skinBoneEnabled = true;
|
||||
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; } }
|
||||
|
||||
#if UNITY_EDITOR && AUTO_UPGRADE_TO_43_COMPONENTS
|
||||
protected void Awake () {
|
||||
if (!Application.isPlaying && !wasUpgradedTo43) {
|
||||
UpgradeTo43();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void Start () {
|
||||
Initialize();
|
||||
}
|
||||
|
||||
void OnEnable () {
|
||||
if (skeletonRenderer != null) {
|
||||
skeletonRenderer.OnRebuild -= HandleRebuild;
|
||||
skeletonRenderer.OnRebuild += HandleRebuild;
|
||||
}
|
||||
|
||||
Initialize();
|
||||
}
|
||||
|
||||
void HandleRebuild (ISkeletonRenderer 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.
|
||||
slotName == slot.Data.Name // Slot object did not change.
|
||||
)
|
||||
return;
|
||||
|
||||
slot = null;
|
||||
currentAttachment = null;
|
||||
currentAttachmentName = null;
|
||||
currentCollider = null;
|
||||
colliderTable.Clear();
|
||||
nameTable.Clear();
|
||||
|
||||
Skeleton skeleton = skeletonRenderer.skeleton;
|
||||
if (skeleton == null)
|
||||
return;
|
||||
slot = skeleton.FindSlot(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 slotIndex = slot.Data.Index;
|
||||
|
||||
int requiredCollidersCount = 0;
|
||||
PolygonCollider2D[] colliders = GetComponents<PolygonCollider2D>();
|
||||
if (this.gameObject.activeInHierarchy) {
|
||||
foreach (Skin skin in skeleton.Data.Skins)
|
||||
AddCollidersForSkin(skin, skeleton, slotIndex, colliders, ref requiredCollidersCount);
|
||||
|
||||
if (skeleton.Skin != null)
|
||||
AddCollidersForSkin(skeleton.Skin, skeleton, slotIndex, colliders, ref requiredCollidersCount);
|
||||
}
|
||||
DisposeExcessCollidersAfter(requiredCollidersCount);
|
||||
skinBoneEnabled = slot.Bone.Active;
|
||||
|
||||
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, Skeleton skeleton, int slotIndex, PolygonCollider2D[] previousColliders, ref int collidersCount) {
|
||||
if (skin == null) return;
|
||||
List<Skin.SkinEntry> skinEntries = new List<Skin.SkinEntry>();
|
||||
skin.GetAttachments(slotIndex, skinEntries);
|
||||
|
||||
foreach (Skin.SkinEntry entry in skinEntries) {
|
||||
Attachment attachment = skin.GetAttachment(slotIndex, entry.Placeholder);
|
||||
BoundingBoxAttachment 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)) {
|
||||
PolygonCollider2D bbCollider = collidersCount < previousColliders.Length ?
|
||||
previousColliders[collidersCount] : gameObject.AddComponent<PolygonCollider2D>();
|
||||
++collidersCount;
|
||||
SkeletonUtility.SetColliderPointsLocal(bbCollider, skeleton, slot, boundingBoxAttachment);
|
||||
bbCollider.isTrigger = isTrigger;
|
||||
bbCollider.usedByEffector = usedByEffector;
|
||||
#if USE_COLLIDER_COMPOSITE_OPERATION
|
||||
bbCollider.compositeOperation = usedByComposite ?
|
||||
Collider2D.CompositeOperation.Merge : Collider2D.CompositeOperation.None;
|
||||
#else
|
||||
bbCollider.usedByComposite = usedByComposite;
|
||||
#endif
|
||||
bbCollider.enabled = false;
|
||||
bbCollider.hideFlags = HideFlags.NotEditable;
|
||||
colliderTable.Add(boundingBoxAttachment, bbCollider);
|
||||
nameTable.Add(boundingBoxAttachment, entry.Placeholder);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnDisable () {
|
||||
if (clearStateOnDisable)
|
||||
ClearState();
|
||||
|
||||
if (skeletonRenderer != null)
|
||||
skeletonRenderer.OnRebuild -= HandleRebuild;
|
||||
}
|
||||
|
||||
public void ClearState () {
|
||||
if (colliderTable != null)
|
||||
foreach (PolygonCollider2D col in colliderTable.Values)
|
||||
col.enabled = false;
|
||||
|
||||
currentAttachment = null;
|
||||
currentAttachmentName = null;
|
||||
currentCollider = null;
|
||||
}
|
||||
|
||||
void DisposeExcessCollidersAfter (int requiredCount) {
|
||||
PolygonCollider2D[] colliders = GetComponents<PolygonCollider2D>();
|
||||
if (colliders.Length == 0) return;
|
||||
|
||||
for (int i = requiredCount; i < colliders.Length; ++i) {
|
||||
PolygonCollider2D collider = colliders[i];
|
||||
if (collider != null) {
|
||||
#if UNITY_EDITOR
|
||||
if (Application.isEditor && !Application.isPlaying)
|
||||
DestroyImmediate(collider);
|
||||
else
|
||||
#endif
|
||||
Destroy(collider);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LateUpdate () {
|
||||
var slotPose = slot.AppliedPose;
|
||||
if (slot != null && (slotPose.Attachment != currentAttachment || skinBoneEnabled != slot.Bone.Active)) {
|
||||
skinBoneEnabled = slot.Bone.Active;
|
||||
MatchAttachment(slotPose.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) {
|
||||
BoundingBoxAttachment 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 || !skinBoneEnabled) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region Transfer of Deprecated Fields
|
||||
#if UNITY_EDITOR && AUTO_UPGRADE_TO_43_COMPONENTS
|
||||
public virtual void UpgradeTo43 () {
|
||||
wasUpgradedTo43 = true;
|
||||
if (skeletonRenderer == null) {
|
||||
Component previousReference = previousSkeletonRenderer != null ? previousSkeletonRenderer : this;
|
||||
skeletonRenderer = previousReference.GetComponent<SkeletonRenderer>();
|
||||
if (skeletonRenderer == null)
|
||||
Debug.LogError("Please manually re-assign SkeletonRenderer at BoundingBoxFollower, " +
|
||||
"automatic upgrade failed.", this);
|
||||
}
|
||||
}
|
||||
[SerializeField, HideInInspector, FormerlySerializedAs("skeletonRenderer")] Component previousSkeletonRenderer;
|
||||
[SerializeField] protected bool wasUpgradedTo43 = false;
|
||||
#endif
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user