Merge branch '3.8' into 4.0-beta

This commit is contained in:
Harald Csaszar 2020-11-11 16:06:57 +01:00
commit 97cf4a9294
8 changed files with 602 additions and 6 deletions

View File

@ -373,6 +373,8 @@
* Spine Timeline Extensions: Added `Hold Previous` parameter at `SpineAnimationStateClip`.
* Added more warning messages at incompatible SkeletonRenderer/SkeletonGraphic Component vs Material settings. They appear both as an info box in the Inspector as well as upon initialization in the Console log window. The Inspector box warnings can be disabled via `Edit - Preferences - Spine`.
* Now providing `BeforeApply` update callbacks at all skeleton animation components (`SkeletonAnimation`, `SkeletonMecanim` and `SkeletonGraphic`).
* Added `BoundingBoxFollowerGraphic` component. This class is a counterpart of `BoundingBoxFollower` that can be used with `SkeletonGraphic`.
* Added Inspector context menu functions `SkeletonRenderer - Add all BoundingBoxFollower GameObjects` and `SkeletonGraphic - Add all BoundingBoxFollowerGraphic GameObjects` that automatically generate bounding box follower GameObjects for every `BoundingBoxAttachment` for all skins of a skeleton.
* **Changes of default values**
* `SkeletonMecanim`'s `Layer Mix Mode` now defaults to `MixMode.MixNext` instead of `MixMode.MixAlways`.

View File

@ -0,0 +1,261 @@
/******************************************************************************
* 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 UnityEditor;
using Boo.Lang;
namespace Spine.Unity.Editor {
using Event = UnityEngine.Event;
using Icons = SpineEditorUtilities.Icons;
[CustomEditor(typeof(BoundingBoxFollowerGraphic))]
public class BoundingBoxFollowerGraphicInspector : UnityEditor.Editor {
SerializedProperty skeletonGraphic, slotName, isTrigger, clearStateOnDisable;
BoundingBoxFollowerGraphic follower;
bool rebuildRequired = false;
bool addBoneFollower = false;
bool sceneRepaintRequired = false;
bool debugIsExpanded;
GUIContent addBoneFollowerLabel;
GUIContent AddBoneFollowerLabel {
get {
if (addBoneFollowerLabel == null) addBoneFollowerLabel = new GUIContent("Add Bone Follower", Icons.bone);
return addBoneFollowerLabel;
}
}
void InitializeEditor () {
skeletonGraphic = serializedObject.FindProperty("skeletonGraphic");
slotName = serializedObject.FindProperty("slotName");
isTrigger = serializedObject.FindProperty("isTrigger");
clearStateOnDisable = serializedObject.FindProperty("clearStateOnDisable");
follower = (BoundingBoxFollowerGraphic)target;
}
public override void OnInspectorGUI () {
#if !NEW_PREFAB_SYSTEM
bool isInspectingPrefab = (PrefabUtility.GetPrefabType(target) == PrefabType.Prefab);
#else
bool isInspectingPrefab = false;
#endif
// Note: when calling InitializeEditor() in OnEnable, it throws exception
// "SerializedObjectNotCreatableException: Object at index 0 is null".
InitializeEditor();
// Try to auto-assign SkeletonGraphic field.
if (skeletonGraphic.objectReferenceValue == null) {
var foundSkeletonGraphic = follower.GetComponentInParent<SkeletonGraphic>();
if (foundSkeletonGraphic != null)
Debug.Log("BoundingBoxFollowerGraphic automatically assigned: " + foundSkeletonGraphic.gameObject.name);
else if (Event.current.type == EventType.Repaint)
Debug.Log("No Spine GameObject detected. Make sure to set this GameObject as a child of the Spine GameObject; or set BoundingBoxFollowerGraphic's 'Skeleton Graphic' field in the inspector.");
skeletonGraphic.objectReferenceValue = foundSkeletonGraphic;
serializedObject.ApplyModifiedProperties();
InitializeEditor();
}
var skeletonGraphicValue = skeletonGraphic.objectReferenceValue as SkeletonGraphic;
if (skeletonGraphicValue != null && skeletonGraphicValue.gameObject == follower.gameObject) {
using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox)) {
EditorGUILayout.HelpBox("It's ideal to add BoundingBoxFollowerGraphic to a separate child GameObject of the Spine GameObject.", MessageType.Warning);
if (GUILayout.Button(new GUIContent("Move BoundingBoxFollowerGraphic to new GameObject", Icons.boundingBox), GUILayout.Height(30f))) {
AddBoundingBoxFollowerGraphicChild(skeletonGraphicValue, follower);
DestroyImmediate(follower);
return;
}
}
EditorGUILayout.Space();
}
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(skeletonGraphic);
EditorGUILayout.PropertyField(slotName, new GUIContent("Slot"));
if (EditorGUI.EndChangeCheck()) {
serializedObject.ApplyModifiedProperties();
InitializeEditor();
#if !NEW_PREFAB_SYSTEM
if (!isInspectingPrefab)
rebuildRequired = true;
#endif
}
using (new SpineInspectorUtility.LabelWidthScope(150f)) {
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(isTrigger);
bool triggerChanged = EditorGUI.EndChangeCheck();
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(clearStateOnDisable, new GUIContent(clearStateOnDisable.displayName, "Enable this if you are pooling your Spine GameObject"));
bool clearStateChanged = EditorGUI.EndChangeCheck();
if (clearStateChanged || triggerChanged) {
serializedObject.ApplyModifiedProperties();
InitializeEditor();
if (triggerChanged)
foreach (var col in follower.colliderTable.Values)
col.isTrigger = isTrigger.boolValue;
}
}
if (isInspectingPrefab) {
follower.colliderTable.Clear();
follower.nameTable.Clear();
EditorGUILayout.HelpBox("BoundingBoxAttachments cannot be previewed in prefabs.", MessageType.Info);
// How do you prevent components from being saved into the prefab? No such HideFlag. DontSaveInEditor | DontSaveInBuild does not work. DestroyImmediate does not work.
var collider = follower.GetComponent<PolygonCollider2D>();
if (collider != null) Debug.LogWarning("Found BoundingBoxFollowerGraphic collider components in prefab. These are disposed and regenerated at runtime.");
} else {
using (new SpineInspectorUtility.BoxScope()) {
if (debugIsExpanded = EditorGUILayout.Foldout(debugIsExpanded, "Debug Colliders")) {
EditorGUI.indentLevel++;
EditorGUILayout.LabelField(string.Format("Attachment Names ({0} PolygonCollider2D)", follower.colliderTable.Count));
EditorGUI.BeginChangeCheck();
foreach (var kp in follower.nameTable) {
string attachmentName = kp.Value;
var collider = follower.colliderTable[kp.Key];
bool isPlaceholder = attachmentName != kp.Key.Name;
collider.enabled = EditorGUILayout.ToggleLeft(new GUIContent(!isPlaceholder ? attachmentName : string.Format("{0} [{1}]", attachmentName, kp.Key.Name), isPlaceholder ? Icons.skinPlaceholder : Icons.boundingBox), collider.enabled);
}
sceneRepaintRequired |= EditorGUI.EndChangeCheck();
EditorGUI.indentLevel--;
}
}
}
if (follower.Slot == null)
follower.Initialize(false);
bool hasBoneFollower = follower.GetComponent<BoneFollowerGraphic>() != null;
if (!hasBoneFollower) {
bool buttonDisabled = follower.Slot == null;
using (new EditorGUI.DisabledGroupScope(buttonDisabled)) {
addBoneFollower |= SpineInspectorUtility.LargeCenteredButton(AddBoneFollowerLabel, true);
EditorGUILayout.Space();
}
}
if (Event.current.type == EventType.Repaint) {
if (addBoneFollower) {
var boneFollower = follower.gameObject.AddComponent<BoneFollowerGraphic>();
boneFollower.skeletonGraphic = skeletonGraphicValue;
boneFollower.SetBone(follower.Slot.Data.BoneData.Name);
addBoneFollower = false;
}
if (sceneRepaintRequired) {
SceneView.RepaintAll();
sceneRepaintRequired = false;
}
if (rebuildRequired) {
follower.Initialize();
rebuildRequired = false;
}
}
}
#region Menus
[MenuItem("CONTEXT/SkeletonGraphic/Add BoundingBoxFollowerGraphic GameObject")]
static void AddBoundingBoxFollowerGraphicChild (MenuCommand command) {
var go = AddBoundingBoxFollowerGraphicChild((SkeletonGraphic)command.context);
Undo.RegisterCreatedObjectUndo(go, "Add BoundingBoxFollowerGraphic");
}
[MenuItem("CONTEXT/SkeletonGraphic/Add all BoundingBoxFollowerGraphic GameObjects")]
static void AddAllBoundingBoxFollowerGraphicChildren (MenuCommand command) {
var objects = AddAllBoundingBoxFollowerGraphicChildren((SkeletonGraphic)command.context);
foreach (var go in objects)
Undo.RegisterCreatedObjectUndo(go, "Add BoundingBoxFollowerGraphic");
}
#endregion
public static GameObject AddBoundingBoxFollowerGraphicChild (SkeletonGraphic skeletonGraphic,
BoundingBoxFollowerGraphic original = null, string name = "BoundingBoxFollowerGraphic",
string slotName = null) {
var go = EditorInstantiation.NewGameObject(name, true);
go.transform.SetParent(skeletonGraphic.transform, false);
go.AddComponent<RectTransform>();
var newFollower = go.AddComponent<BoundingBoxFollowerGraphic>();
if (original != null) {
newFollower.slotName = original.slotName;
newFollower.isTrigger = original.isTrigger;
newFollower.clearStateOnDisable = original.clearStateOnDisable;
}
if (slotName != null)
newFollower.slotName = slotName;
newFollower.skeletonGraphic = skeletonGraphic;
newFollower.Initialize();
Selection.activeGameObject = go;
EditorGUIUtility.PingObject(go);
return go;
}
public static List<GameObject> AddAllBoundingBoxFollowerGraphicChildren (
SkeletonGraphic skeletonGraphic, BoundingBoxFollowerGraphic original = null) {
List<GameObject> createdGameObjects = new List<GameObject>();
foreach (var skin in skeletonGraphic.Skeleton.Data.Skins) {
var attachments = skin.Attachments;
foreach (var entry in attachments) {
var boundingBoxAttachment = entry.Value as BoundingBoxAttachment;
if (boundingBoxAttachment == null)
continue;
int slotIndex = entry.Key.SlotIndex;
var slot = skeletonGraphic.Skeleton.Slots.Items[slotIndex];
string slotName = slot.Data.Name;
GameObject go = AddBoundingBoxFollowerGraphicChild(skeletonGraphic,
original, boundingBoxAttachment.Name, slotName);
var boneFollower = go.AddComponent<BoneFollowerGraphic>();
boneFollower.skeletonGraphic = skeletonGraphic;
boneFollower.SetBone(slot.Data.BoneData.Name);
createdGameObjects.Add(go);
}
}
return createdGameObjects;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7c4f5b276299bc048ad00f3cd2d1ea09
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -33,6 +33,7 @@
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
namespace Spine.Unity.Editor {
using Event = UnityEngine.Event;
@ -198,11 +199,21 @@ namespace Spine.Unity.Editor {
var go = AddBoundingBoxFollowerChild((SkeletonRenderer)command.context);
Undo.RegisterCreatedObjectUndo(go, "Add BoundingBoxFollower");
}
[MenuItem("CONTEXT/SkeletonRenderer/Add all BoundingBoxFollower GameObjects")]
static void AddAllBoundingBoxFollowerChildren (MenuCommand command) {
var objects = AddAllBoundingBoxFollowerChildren((SkeletonRenderer)command.context);
foreach (var go in objects)
Undo.RegisterCreatedObjectUndo(go, "Add BoundingBoxFollower");
}
#endregion
static GameObject AddBoundingBoxFollowerChild (SkeletonRenderer sr, BoundingBoxFollower original = null) {
var go = EditorInstantiation.NewGameObject("BoundingBoxFollower", true);
go.transform.SetParent(sr.transform, false);
public static GameObject AddBoundingBoxFollowerChild (SkeletonRenderer skeletonRenderer,
BoundingBoxFollower original = null, string name = "BoundingBoxFollower",
string slotName = null) {
var go = EditorInstantiation.NewGameObject(name, true);
go.transform.SetParent(skeletonRenderer.transform, false);
var newFollower = go.AddComponent<BoundingBoxFollower>();
if (original != null) {
@ -210,16 +221,40 @@ namespace Spine.Unity.Editor {
newFollower.isTrigger = original.isTrigger;
newFollower.clearStateOnDisable = original.clearStateOnDisable;
}
if (slotName != null)
newFollower.slotName = slotName;
newFollower.skeletonRenderer = sr;
newFollower.skeletonRenderer = skeletonRenderer;
newFollower.Initialize();
Selection.activeGameObject = go;
EditorGUIUtility.PingObject(go);
return go;
}
public static List<GameObject> AddAllBoundingBoxFollowerChildren (
SkeletonRenderer skeletonRenderer, BoundingBoxFollower original = null) {
List<GameObject> createdGameObjects = new List<GameObject>();
foreach (var skin in skeletonRenderer.Skeleton.Data.Skins) {
var attachments = skin.Attachments;
foreach (var entry in attachments) {
var boundingBoxAttachment = entry.Value as BoundingBoxAttachment;
if (boundingBoxAttachment == null)
continue;
int slotIndex = entry.Key.SlotIndex;
var slot = skeletonRenderer.Skeleton.Slots.Items[slotIndex];
string slotName = slot.Data.Name;
GameObject go = AddBoundingBoxFollowerChild(skeletonRenderer,
original, boundingBoxAttachment.Name, slotName);
var boneFollower = go.AddComponent<BoneFollower>();
boneFollower.skeletonRenderer = skeletonRenderer;
boneFollower.SetBone(slot.Data.BoneData.Name);
createdGameObjects.Add(go);
}
}
return createdGameObjects;
}
}
}

View File

@ -277,6 +277,7 @@ namespace Spine.Unity.Editor {
static Dictionary<int, GameObject> skeletonRendererTable = new Dictionary<int, GameObject>();
static Dictionary<int, SkeletonUtilityBone> skeletonUtilityBoneTable = new Dictionary<int, SkeletonUtilityBone>();
static Dictionary<int, BoundingBoxFollower> boundingBoxFollowerTable = new Dictionary<int, BoundingBoxFollower>();
static Dictionary<int, BoundingBoxFollowerGraphic> boundingBoxFollowerGraphicTable = new Dictionary<int, BoundingBoxFollowerGraphic>();
#if NEWPLAYMODECALLBACKS
internal static void IconsOnPlaymodeStateChanged (PlayModeStateChange stateChange) {
@ -286,6 +287,7 @@ namespace Spine.Unity.Editor {
skeletonRendererTable.Clear();
skeletonUtilityBoneTable.Clear();
boundingBoxFollowerTable.Clear();
boundingBoxFollowerGraphicTable.Clear();
#if NEWHIERARCHYWINDOWCALLBACKS
EditorApplication.hierarchyChanged -= IconsOnChanged;
@ -309,6 +311,7 @@ namespace Spine.Unity.Editor {
skeletonRendererTable.Clear();
skeletonUtilityBoneTable.Clear();
boundingBoxFollowerTable.Clear();
boundingBoxFollowerGraphicTable.Clear();
SkeletonRenderer[] arr = Object.FindObjectsOfType<SkeletonRenderer>();
foreach (SkeletonRenderer r in arr)
@ -321,6 +324,10 @@ namespace Spine.Unity.Editor {
BoundingBoxFollower[] bbfArr = Object.FindObjectsOfType<BoundingBoxFollower>();
foreach (BoundingBoxFollower bbf in bbfArr)
boundingBoxFollowerTable[bbf.gameObject.GetInstanceID()] = bbf;
BoundingBoxFollowerGraphic[] bbfgArr = Object.FindObjectsOfType<BoundingBoxFollowerGraphic>();
foreach (BoundingBoxFollowerGraphic bbf in bbfgArr)
boundingBoxFollowerGraphicTable[bbf.gameObject.GetInstanceID()] = bbf;
}
internal static void IconsOnGUI (int instanceId, Rect selectionRect) {
@ -352,6 +359,16 @@ namespace Spine.Unity.Editor {
r.height = 13;
GUI.DrawTexture(r, Icons.boundingBox);
}
} else if (boundingBoxFollowerGraphicTable.ContainsKey(instanceId)) {
r.x -= 26;
if (boundingBoxFollowerGraphicTable[instanceId] != null) {
if (boundingBoxFollowerGraphicTable[instanceId].transform.childCount == 0)
r.x += 13;
r.y += 2;
r.width = 13;
r.height = 13;
GUI.DrawTexture(r, Icons.boundingBox);
}
}
}

View File

@ -0,0 +1,255 @@
/******************************************************************************
* 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;
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);
}
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1c0bf7b497af9f74280040d96cdf88da
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -91,10 +91,14 @@ namespace Spine.Unity {
return collider;
}
public static void SetColliderPointsLocal (PolygonCollider2D collider, Slot slot, BoundingBoxAttachment box) {
public static void SetColliderPointsLocal (PolygonCollider2D collider, Slot slot, BoundingBoxAttachment box, float scale = 1.0f) {
if (box == null) return;
if (box.IsWeighted()) Debug.LogWarning("UnityEngine.PolygonCollider2D does not support weighted or animated points. Collider points will not be animated and may have incorrect orientation. If you want to use it as a collider, please remove weights and animations from the bounding box in Spine editor.");
var verts = box.GetLocalVertices(slot, null);
if (scale != 1.0f) {
for (int i = 0, n = verts.Length; i < n; ++i)
verts[i] *= scale;
}
collider.SetPath(0, verts);
}