[spine-unity] Clarify BoundingBoxFollower behavior in editor. (#630)

This commit is contained in:
John 2016-07-11 09:40:38 +08:00 committed by GitHub
parent e0c2db276f
commit d6dc425817
2 changed files with 103 additions and 64 deletions

View File

@ -32,28 +32,30 @@ using UnityEngine;
using System.Collections.Generic; using System.Collections.Generic;
namespace Spine.Unity { namespace Spine.Unity {
[ExecuteInEditMode] [ExecuteInEditMode]
public class BoundingBoxFollower : MonoBehaviour { public class BoundingBoxFollower : MonoBehaviour {
#region Inspector
public SkeletonRenderer skeletonRenderer; public SkeletonRenderer skeletonRenderer;
[SpineSlot(dataField: "skeletonRenderer", containsBoundingBoxes: true)] [SpineSlot(dataField: "skeletonRenderer", containsBoundingBoxes: true)]
public string slotName; public string slotName;
#endregion
Slot slot; Slot slot;
BoundingBoxAttachment currentAttachment; BoundingBoxAttachment currentAttachment;
PolygonCollider2D currentCollider;
string currentAttachmentName; string currentAttachmentName;
PolygonCollider2D currentCollider;
bool valid = false; bool valid = false;
bool hasReset; bool hasReset;
public Dictionary<BoundingBoxAttachment, PolygonCollider2D> colliderTable = new Dictionary<BoundingBoxAttachment, PolygonCollider2D>(); public readonly Dictionary<BoundingBoxAttachment, PolygonCollider2D> colliderTable = new Dictionary<BoundingBoxAttachment, PolygonCollider2D>();
public Dictionary<BoundingBoxAttachment, string> attachmentNameTable = new Dictionary<BoundingBoxAttachment, string>(); public readonly Dictionary<BoundingBoxAttachment, string> attachmentNameTable = new Dictionary<BoundingBoxAttachment, string>();
public string CurrentAttachmentName { get { return currentAttachmentName; } }
public BoundingBoxAttachment CurrentAttachment { get { return currentAttachment; } }
public PolygonCollider2D CurrentCollider { get { return currentCollider; } }
public Slot Slot { get { return slot; } } public Slot Slot { get { return slot; } }
public BoundingBoxAttachment CurrentAttachment { get { return currentAttachment; } }
public string CurrentAttachmentName { get { return currentAttachmentName; } }
public PolygonCollider2D CurrentCollider { get { return currentCollider; } }
void OnEnable () { void OnEnable () {
ClearColliders(); ClearColliders();
@ -62,74 +64,91 @@ namespace Spine.Unity {
skeletonRenderer = GetComponentInParent<SkeletonRenderer>(); skeletonRenderer = GetComponentInParent<SkeletonRenderer>();
if (skeletonRenderer != null) { if (skeletonRenderer != null) {
skeletonRenderer.OnRebuild -= HandleReset; skeletonRenderer.OnRebuild -= HandleRebuild;
skeletonRenderer.OnRebuild += HandleReset; skeletonRenderer.OnRebuild += HandleRebuild;
if (hasReset) if (hasReset)
HandleReset(skeletonRenderer); HandleRebuild(skeletonRenderer);
} }
} }
void OnDisable () { void OnDisable () {
skeletonRenderer.OnRebuild -= HandleReset; skeletonRenderer.OnRebuild -= HandleRebuild;
} }
void Start () { void Start () {
if (!hasReset && skeletonRenderer != null) if (!hasReset && skeletonRenderer != null)
HandleReset(skeletonRenderer); HandleRebuild(skeletonRenderer);
} }
public void HandleReset (SkeletonRenderer renderer) { public void HandleRebuild (SkeletonRenderer renderer) {
if (string.IsNullOrEmpty(slotName)) if (string.IsNullOrEmpty(slotName))
return; return;
hasReset = true; hasReset = true;
ClearColliders(); ClearColliders();
colliderTable.Clear(); colliderTable.Clear();
if (skeletonRenderer.skeleton == null) { if (skeletonRenderer.skeleton == null) {
skeletonRenderer.OnRebuild -= HandleReset; skeletonRenderer.OnRebuild -= HandleRebuild;
skeletonRenderer.Initialize(false); skeletonRenderer.Initialize(false);
skeletonRenderer.OnRebuild += HandleReset; skeletonRenderer.OnRebuild += HandleRebuild;
} }
var skeleton = skeletonRenderer.skeleton; var skeleton = skeletonRenderer.skeleton;
slot = skeleton.FindSlot(slotName); slot = skeleton.FindSlot(slotName);
int slotIndex = skeleton.FindSlotIndex(slotName); int slotIndex = skeleton.FindSlotIndex(slotName);
foreach (var skin in skeleton.Data.Skins) { if (this.gameObject.activeInHierarchy) {
var attachmentNames = new List<string>(); foreach (var skin in skeleton.Data.Skins) {
skin.FindNamesForSlot(slotIndex, attachmentNames); var attachmentNames = new List<string>();
skin.FindNamesForSlot(slotIndex, attachmentNames);
foreach (var attachmentName in attachmentNames) { foreach (var attachmentName in attachmentNames) {
var attachment = skin.GetAttachment(slotIndex, attachmentName); var attachment = skin.GetAttachment(slotIndex, attachmentName);
var boundingBoxAttachment = attachment as BoundingBoxAttachment; var boundingBoxAttachment = attachment as BoundingBoxAttachment;
if (boundingBoxAttachment != null) { if (boundingBoxAttachment != null) {
var bbCollider = SkeletonUtility.AddBoundingBoxAsComponent(boundingBoxAttachment, gameObject, true); var bbCollider = SkeletonUtility.AddBoundingBoxAsComponent(boundingBoxAttachment, gameObject, true);
bbCollider.enabled = false; bbCollider.enabled = false;
bbCollider.hideFlags = HideFlags.HideInInspector; bbCollider.hideFlags = HideFlags.NotEditable;
colliderTable.Add(boundingBoxAttachment, bbCollider); colliderTable.Add(boundingBoxAttachment, bbCollider);
attachmentNameTable.Add(boundingBoxAttachment, attachmentName); attachmentNameTable.Add(boundingBoxAttachment, attachmentName);
}
} }
} }
} }
#if UNITY_EDITOR
valid = colliderTable.Count != 0; 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.");
}
#endif
if (!valid)
Debug.LogWarning("Bounding Box Follower not valid! Slot [" + slotName + "] does not contain any Bounding Box Attachments!");
} }
void ClearColliders () { void ClearColliders () {
var colliders = GetComponents<PolygonCollider2D>(); var colliders = GetComponents<PolygonCollider2D>();
if (colliders.Length == 0) return;
#if UNITY_EDITOR
if (Application.isPlaying) { if (Application.isPlaying) {
foreach (var c in colliders) foreach (var c in colliders) {
Destroy(c); if (c != null)
Destroy(c);
}
} else { } else {
foreach (var c in colliders) foreach (var c in colliders)
DestroyImmediate(c); DestroyImmediate(c);
} }
#else
foreach (var c in colliders)
if (c != null)
Destroy(c);
#endif
colliderTable.Clear(); colliderTable.Clear();
attachmentNameTable.Clear(); attachmentNameTable.Clear();
@ -139,21 +158,19 @@ namespace Spine.Unity {
if (!skeletonRenderer.valid) if (!skeletonRenderer.valid)
return; return;
if (slot != null) { if (slot != null && slot.Attachment != currentAttachment)
if (slot.Attachment != currentAttachment) SetCurrent((BoundingBoxAttachment)slot.Attachment);
SetCurrent((BoundingBoxAttachment)slot.Attachment);
}
} }
void SetCurrent (BoundingBoxAttachment attachment) { void SetCurrent (BoundingBoxAttachment attachment) {
if (currentCollider) if (currentCollider != null)
currentCollider.enabled = false; currentCollider.enabled = false;
if (attachment != null) { if (attachment == null) {
currentCollider = null;
} else {
currentCollider = colliderTable[attachment]; currentCollider = colliderTable[attachment];
currentCollider.enabled = true; currentCollider.enabled = true;
} else {
currentCollider = null;
} }
currentAttachment = attachment; currentAttachment = attachment;

View File

@ -31,7 +31,6 @@
using UnityEngine; using UnityEngine;
using UnityEditor; using UnityEditor;
using System.Collections;
namespace Spine.Unity.Editor { namespace Spine.Unity.Editor {
@ -39,7 +38,8 @@ namespace Spine.Unity.Editor {
public class BoundingBoxFollowerInspector : UnityEditor.Editor { public class BoundingBoxFollowerInspector : UnityEditor.Editor {
SerializedProperty skeletonRenderer, slotName; SerializedProperty skeletonRenderer, slotName;
BoundingBoxFollower follower; BoundingBoxFollower follower;
bool needToReset = false; bool rebuildRequired = false;
bool addBoneFollower = false;
void OnEnable () { void OnEnable () {
skeletonRenderer = serializedObject.FindProperty("skeletonRenderer"); skeletonRenderer = serializedObject.FindProperty("skeletonRenderer");
@ -48,41 +48,63 @@ namespace Spine.Unity.Editor {
} }
public override void OnInspectorGUI () { public override void OnInspectorGUI () {
if (needToReset) { bool isInspectingPrefab = (PrefabUtility.GetPrefabType(target) == PrefabType.Prefab);
follower.HandleReset(null); bool repaintEvent = UnityEngine.Event.current.type == EventType.Repaint;
needToReset = false;
if (rebuildRequired) {
follower.HandleRebuild(null);
rebuildRequired = false;
} }
EditorGUI.BeginChangeCheck(); EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(skeletonRenderer); EditorGUILayout.PropertyField(skeletonRenderer);
EditorGUILayout.PropertyField(slotName, new GUIContent("Slot")); EditorGUILayout.PropertyField(slotName, new GUIContent("Slot"));
if (EditorGUI.EndChangeCheck()) {
if (EditorGUI.EndChangeCheck()){
serializedObject.ApplyModifiedProperties(); serializedObject.ApplyModifiedProperties();
needToReset = true; if (!isInspectingPrefab)
rebuildRequired = true;
} }
bool hasBone = follower.GetComponent<BoneFollower>() != null; bool hasBoneFollower = follower.GetComponent<BoneFollower>() != null;
using (new EditorGUI.DisabledGroupScope(hasBoneFollower || follower.Slot == null)) {
EditorGUI.BeginDisabledGroup(hasBone || follower.Slot == null);
{
if (GUILayout.Button(new GUIContent("Add Bone Follower", SpineEditorUtilities.Icons.bone))) { if (GUILayout.Button(new GUIContent("Add Bone Follower", SpineEditorUtilities.Icons.bone))) {
var boneFollower = follower.gameObject.AddComponent<BoneFollower>(); addBoneFollower = true;
boneFollower.boneName = follower.Slot.Data.BoneData.Name;
} }
} }
EditorGUI.EndDisabledGroup();
if (isInspectingPrefab) {
follower.colliderTable.Clear();
follower.attachmentNameTable.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 BoundingBoxFollower collider components in prefab. These are disposed and regenerated at runtime.");
//GUILayout.Space(20); } else {
GUILayout.Label("Attachment Names", EditorStyles.boldLabel); EditorGUILayout.LabelField(string.Format("Attachment Names ({0} PolygonCollider2D)", follower.colliderTable.Count), EditorStyles.boldLabel);
foreach (var kp in follower.attachmentNameTable) { EditorGUI.BeginChangeCheck();
string name = kp.Value; foreach (var kp in follower.attachmentNameTable) {
var collider = follower.colliderTable[kp.Key]; string attachmentName = kp.Value;
bool isPlaceholder = name != kp.Key.Name; var collider = follower.colliderTable[kp.Key];
collider.enabled = EditorGUILayout.ToggleLeft(new GUIContent(!isPlaceholder ? name : name + " [" + kp.Key.Name + "]", isPlaceholder ? SpineEditorUtilities.Icons.skinPlaceholder : SpineEditorUtilities.Icons.boundingBox), collider.enabled); bool isPlaceholder = attachmentName != kp.Key.Name;
collider.enabled = EditorGUILayout.ToggleLeft(new GUIContent(!isPlaceholder ? attachmentName : attachmentName + " [" + kp.Key.Name + "]", isPlaceholder ? SpineEditorUtilities.Icons.skinPlaceholder : SpineEditorUtilities.Icons.boundingBox), collider.enabled);
}
if (EditorGUI.EndChangeCheck()) {
SceneView.RepaintAll();
}
if (!Application.isPlaying)
EditorGUILayout.HelpBox("\nAt runtime, BoundingBoxFollower enables and disables PolygonCollider2Ds based on the currently active attachment in the slot.\n\nCheckboxes in Edit Mode are only for preview. Checkbox states are not saved.\n", MessageType.Info);
}
if (addBoneFollower && repaintEvent) {
var boneFollower = follower.gameObject.AddComponent<BoneFollower>();
boneFollower.boneName = follower.Slot.Data.BoneData.Name;
addBoneFollower = false;
} }
} }
} }
} }