diff --git a/spine-unity/Assets/spine-unity/BoneFollower.cs b/spine-unity/Assets/spine-unity/BoneFollower.cs index e900ba8ee..1a4071762 100644 --- a/spine-unity/Assets/spine-unity/BoneFollower.cs +++ b/spine-unity/Assets/spine-unity/BoneFollower.cs @@ -57,6 +57,8 @@ public class BoneFollower : MonoBehaviour { /// If a bone isn't set, boneName is used to find the bone. + + [SpineBone(dataField: "skeletonRenderer")] public String boneName; public bool resetOnAwake = true; protected Transform cachedTransform; diff --git a/spine-unity/Assets/spine-unity/BoundingBoxFollower.cs b/spine-unity/Assets/spine-unity/BoundingBoxFollower.cs new file mode 100644 index 000000000..f1396c6d0 --- /dev/null +++ b/spine-unity/Assets/spine-unity/BoundingBoxFollower.cs @@ -0,0 +1,160 @@ +using UnityEngine; +using System.Collections; +using System.Collections.Generic; +using Spine; + +[ExecuteInEditMode] +public class BoundingBoxFollower : MonoBehaviour { + + public SkeletonRenderer skeletonRenderer; + + [SpineSlot(dataField: "skeletonRenderer", containsBoundingBoxes: true)] + public string slotName; + + //TODO: not this + [Tooltip("LOL JK, Someone else do it!")] + public bool use3DMeshCollider; + + private Slot slot; + private BoundingBoxAttachment currentAttachment; + private PolygonCollider2D currentCollider; + private string currentAttachmentName; + private bool valid = false; + private bool hasReset; + + public Dictionary colliderTable = new Dictionary(); + public Dictionary attachmentNameTable = new Dictionary(); + + public string CurrentAttachmentName { + get { + return currentAttachmentName; + } + } + + public BoundingBoxAttachment CurrentAttachment { + get { + return currentAttachment; + } + } + + public PolygonCollider2D CurrentCollider { + get { + return currentCollider; + } + } + + public Slot Slot { + get { + return slot; + } + } + + + void OnEnable () { + ClearColliders(); + + if (skeletonRenderer == null) + skeletonRenderer = GetComponentInParent(); + + if (skeletonRenderer != null) { + skeletonRenderer.OnReset -= HandleReset; + skeletonRenderer.OnReset += HandleReset; + } + } + + void OnDisable () { + skeletonRenderer.OnReset -= HandleReset; + } + + void Start () { + if (!hasReset && skeletonRenderer != null) + HandleReset(skeletonRenderer); + } + + public void HandleReset (SkeletonRenderer renderer) { + if (slotName == null || slotName == "") + return; + + hasReset = true; + + ClearColliders(); + colliderTable.Clear(); + + if (skeletonRenderer.skeleton == null) { + skeletonRenderer.OnReset -= HandleReset; + skeletonRenderer.Reset(); + skeletonRenderer.OnReset += HandleReset; + } + + + var skeleton = skeletonRenderer.skeleton; + slot = skeleton.FindSlot(slotName); + int slotIndex = skeleton.FindSlotIndex(slotName); + + foreach (var skin in skeleton.Data.Skins) { + List attachmentNames = new List(); + skin.FindNamesForSlot(slotIndex, attachmentNames); + + foreach (var name in attachmentNames) { + var attachment = skin.GetAttachment(slotIndex, name); + if (attachment is BoundingBoxAttachment) { + var collider = SkeletonUtility.AddBoundingBoxAsComponent((BoundingBoxAttachment)attachment, gameObject, true); + collider.enabled = false; + collider.hideFlags = HideFlags.HideInInspector; + colliderTable.Add((BoundingBoxAttachment)attachment, collider); + attachmentNameTable.Add((BoundingBoxAttachment)attachment, name); + } + } + } + + if (colliderTable.Count == 0) + valid = false; + else + valid = true; + + if (!valid) + Debug.LogWarning("Bounding Box Follower not valid! Slot [" + slotName + "] does not contain any Bounding Box Attachments!"); + } + + void ClearColliders () { + var colliders = GetComponents(); + if (Application.isPlaying) { + foreach (var c in colliders) { + Destroy(c); + } + } else { + foreach (var c in colliders) { + DestroyImmediate(c); + } + } + + colliderTable.Clear(); + attachmentNameTable.Clear(); + } + + void LateUpdate () { + if (!skeletonRenderer.valid) + return; + + if (slot != null) { + if (slot.Attachment != currentAttachment) + SetCurrent((BoundingBoxAttachment)slot.Attachment); + } + } + + void SetCurrent (BoundingBoxAttachment attachment) { + if (currentCollider) + currentCollider.enabled = false; + + if (attachment != null) { + currentCollider = colliderTable[attachment]; + currentCollider.enabled = true; + } else { + currentCollider = null; + } + + currentAttachment = attachment; + + currentAttachmentName = currentAttachment == null ? null : attachmentNameTable[attachment]; + } +} diff --git a/spine-unity/Assets/spine-unity/BoundingBoxFollower.cs.meta b/spine-unity/Assets/spine-unity/BoundingBoxFollower.cs.meta new file mode 100644 index 000000000..d81fd14af --- /dev/null +++ b/spine-unity/Assets/spine-unity/BoundingBoxFollower.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0317ee9ba6e1b1e49a030268e026d372 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/spine-unity/Assets/spine-unity/Editor/BoneFollowerInspector.cs b/spine-unity/Assets/spine-unity/Editor/BoneFollowerInspector.cs index 943a98715..224bc65ff 100644 --- a/spine-unity/Assets/spine-unity/Editor/BoneFollowerInspector.cs +++ b/spine-unity/Assets/spine-unity/Editor/BoneFollowerInspector.cs @@ -35,6 +35,7 @@ using UnityEngine; public class BoneFollowerInspector : Editor { private SerializedProperty boneName, skeletonRenderer, followZPosition, followBoneRotation; BoneFollower component; + bool needsReset; void OnEnable () { skeletonRenderer = serializedObject.FindProperty("skeletonRenderer"); @@ -64,6 +65,12 @@ public class BoneFollowerInspector : Editor { } override public void OnInspectorGUI () { + if (needsReset) { + component.Reset(); + component.DoUpdate(); + needsReset = false; + SceneView.RepaintAll(); + } serializedObject.Update(); FindRenderer(); @@ -71,26 +78,16 @@ public class BoneFollowerInspector : Editor { EditorGUILayout.PropertyField(skeletonRenderer); if (component.valid) { - String[] bones = new String[1]; - try { - bones = new String[component.skeletonRenderer.skeleton.Data.Bones.Count + 1]; - } catch { - + EditorGUI.BeginChangeCheck(); + EditorGUILayout.PropertyField(boneName); + if (EditorGUI.EndChangeCheck()) { + serializedObject.ApplyModifiedProperties(); + needsReset = true; + serializedObject.Update(); } + + - bones[0] = ""; - for (int i = 0; i < bones.Length - 1; i++) - bones[i + 1] = component.skeletonRenderer.skeleton.Data.Bones[i].Name; - Array.Sort(bones); - int boneIndex = Math.Max(0, Array.IndexOf(bones, boneName.stringValue)); - - EditorGUILayout.BeginHorizontal(); - EditorGUILayout.LabelField("Bone"); - EditorGUIUtility.LookLikeControls(); - boneIndex = EditorGUILayout.Popup(boneIndex, bones); - EditorGUILayout.EndHorizontal(); - - boneName.stringValue = boneIndex == 0 ? null : bones[boneIndex]; EditorGUILayout.PropertyField(followBoneRotation); EditorGUILayout.PropertyField(followZPosition); } else { diff --git a/spine-unity/Assets/spine-unity/Editor/BoundingBoxFollowerInspector.cs b/spine-unity/Assets/spine-unity/Editor/BoundingBoxFollowerInspector.cs new file mode 100644 index 000000000..fddcd6e24 --- /dev/null +++ b/spine-unity/Assets/spine-unity/Editor/BoundingBoxFollowerInspector.cs @@ -0,0 +1,53 @@ +using UnityEngine; +using UnityEditor; +using System.Collections; + +[CustomEditor(typeof(BoundingBoxFollower))] +public class BoundingBoxFollowerInspector : Editor { + SerializedProperty skeletonRenderer, slotName; + BoundingBoxFollower follower; + bool needToReset = false; + + void OnEnable () { + skeletonRenderer = serializedObject.FindProperty("skeletonRenderer"); + slotName = serializedObject.FindProperty("slotName"); + follower = (BoundingBoxFollower)target; + } + + public override void OnInspectorGUI () { + if (needToReset) { + follower.HandleReset(null); + needToReset = false; + } + EditorGUI.BeginChangeCheck(); + EditorGUILayout.PropertyField(skeletonRenderer); + EditorGUILayout.PropertyField(slotName, new GUIContent("Slot")); + + if (EditorGUI.EndChangeCheck()){ + serializedObject.ApplyModifiedProperties(); + needToReset = true; + } + + bool hasBone = follower.GetComponent() != null; + + EditorGUI.BeginDisabledGroup(hasBone || follower.Slot == null); + { + if (GUILayout.Button(new GUIContent("Add Bone Follower", SpineEditorUtilities.Icons.bone))) { + var boneFollower = follower.gameObject.AddComponent(); + boneFollower.boneName = follower.Slot.Data.BoneData.Name; + } + } + EditorGUI.EndDisabledGroup(); + + + + //GUILayout.Space(20); + GUILayout.Label("Attachment Names", EditorStyles.boldLabel); + foreach (var kp in follower.attachmentNameTable) { + string name = kp.Value; + var collider = follower.colliderTable[kp.Key]; + bool isPlaceholder = name != kp.Key.Name; + collider.enabled = EditorGUILayout.ToggleLeft(new GUIContent(!isPlaceholder ? name : name + " [" + kp.Key.Name + "]", isPlaceholder ? SpineEditorUtilities.Icons.skinPlaceholder : SpineEditorUtilities.Icons.boundingBox), collider.enabled); + } + } +} diff --git a/spine-unity/Assets/spine-unity/Editor/BoundingBoxFollowerInspector.cs.meta b/spine-unity/Assets/spine-unity/Editor/BoundingBoxFollowerInspector.cs.meta new file mode 100644 index 000000000..e42ba7739 --- /dev/null +++ b/spine-unity/Assets/spine-unity/Editor/BoundingBoxFollowerInspector.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 670a3cefa3853bd48b5da53a424fd542 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/spine-unity/Assets/spine-unity/Editor/GUI/icon-boundingBox.png.meta b/spine-unity/Assets/spine-unity/Editor/GUI/icon-boundingBox.png.meta index 49c9ab374..0e5d62848 100644 --- a/spine-unity/Assets/spine-unity/Editor/GUI/icon-boundingBox.png.meta +++ b/spine-unity/Assets/spine-unity/Editor/GUI/icon-boundingBox.png.meta @@ -20,11 +20,8 @@ TextureImporter: isReadable: 0 grayScaleToAlpha: 0 generateCubemap: 0 - cubemapConvolution: 0 - cubemapConvolutionSteps: 8 - cubemapConvolutionExponent: 1.5 seamlessCubemap: 0 - textureFormat: -1 + textureFormat: -3 maxTextureSize: 1024 textureSettings: filterMode: -1 @@ -33,7 +30,6 @@ TextureImporter: wrapMode: 1 nPOTScale: 0 lightmap: 0 - rGBM: 0 compressionQuality: 50 spriteMode: 0 spriteExtrude: 1 @@ -49,5 +45,3 @@ TextureImporter: sprites: [] spritePackingTag: userData: - assetBundleName: - assetBundleVariant: diff --git a/spine-unity/Assets/spine-unity/Editor/GUI/icon-hingeChain.png.meta b/spine-unity/Assets/spine-unity/Editor/GUI/icon-hingeChain.png.meta index 83eb12195..e61da1e82 100644 --- a/spine-unity/Assets/spine-unity/Editor/GUI/icon-hingeChain.png.meta +++ b/spine-unity/Assets/spine-unity/Editor/GUI/icon-hingeChain.png.meta @@ -5,8 +5,8 @@ TextureImporter: serializedVersion: 2 mipmaps: mipMapMode: 0 - enableMipMap: 1 - linearTexture: 0 + enableMipMap: 0 + linearTexture: 1 correctGamma: 0 fadeOut: 0 borderMipMap: 0 @@ -20,20 +20,16 @@ TextureImporter: isReadable: 0 grayScaleToAlpha: 0 generateCubemap: 0 - cubemapConvolution: 0 - cubemapConvolutionSteps: 8 - cubemapConvolutionExponent: 1.5 seamlessCubemap: 0 - textureFormat: -1 + textureFormat: -3 maxTextureSize: 1024 textureSettings: filterMode: -1 - aniso: -1 + aniso: 1 mipBias: -1 - wrapMode: -1 - nPOTScale: 1 + wrapMode: 1 + nPOTScale: 0 lightmap: 0 - rGBM: 0 compressionQuality: 50 spriteMode: 0 spriteExtrude: 1 @@ -42,12 +38,10 @@ TextureImporter: spritePivot: {x: .5, y: .5} spriteBorder: {x: 0, y: 0, z: 0, w: 0} spritePixelsToUnits: 100 - alphaIsTransparency: 0 - textureType: -1 + alphaIsTransparency: 1 + textureType: 2 buildTargetSettings: [] spriteSheet: sprites: [] spritePackingTag: userData: - assetBundleName: - assetBundleVariant: diff --git a/spine-unity/Assets/spine-unity/Editor/SpineAttributeDrawers.cs b/spine-unity/Assets/spine-unity/Editor/SpineAttributeDrawers.cs index 9d9e3b3d2..59364ac4c 100644 --- a/spine-unity/Assets/spine-unity/Editor/SpineAttributeDrawers.cs +++ b/spine-unity/Assets/spine-unity/Editor/SpineAttributeDrawers.cs @@ -114,8 +114,35 @@ public class SpineSlotDrawer : PropertyDrawer { for (int i = 0; i < data.Slots.Count; i++) { string name = data.Slots[i].Name; - if (name.StartsWith(attrib.startsWith)) - menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); + if (name.StartsWith(attrib.startsWith)) { + if (attrib.containsBoundingBoxes) { + + int slotIndex = i; + + List attachments = new List(); + foreach (var skin in data.Skins) { + skin.FindAttachmentsForSlot(slotIndex, attachments); + } + + bool hasBoundingBox = false; + foreach (var attachment in attachments) { + if (attachment is BoundingBoxAttachment) { + menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); + hasBoundingBox = true; + break; + } + } + + if (!hasBoundingBox) + menu.AddDisabledItem(new GUIContent(name)); + + + } else { + menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); + } + + } + } menu.ShowAsContext(); diff --git a/spine-unity/Assets/spine-unity/Editor/SpineEditorUtilities.cs b/spine-unity/Assets/spine-unity/Editor/SpineEditorUtilities.cs index a697c8fc5..0ba0fd164 100644 --- a/spine-unity/Assets/spine-unity/Editor/SpineEditorUtilities.cs +++ b/spine-unity/Assets/spine-unity/Editor/SpineEditorUtilities.cs @@ -149,6 +149,7 @@ public class SpineEditorUtilities : AssetPostprocessor { public static string editorGUIPath = ""; static Dictionary skeletonRendererTable; static Dictionary skeletonUtilityBoneTable; + static Dictionary boundingBoxFollowerTable; public static float defaultScale = 0.01f; public static float defaultMix = 0.2f; public static string defaultShader = "Spine/Skeleton"; @@ -172,6 +173,7 @@ public class SpineEditorUtilities : AssetPostprocessor { skeletonRendererTable = new Dictionary(); skeletonUtilityBoneTable = new Dictionary(); + boundingBoxFollowerTable = new Dictionary(); EditorApplication.hierarchyWindowChanged += HierarchyWindowChanged; EditorApplication.hierarchyWindowItemOnGUI += HierarchyWindowItemOnGUI; @@ -188,15 +190,19 @@ public class SpineEditorUtilities : AssetPostprocessor { static void HierarchyWindowChanged () { skeletonRendererTable.Clear(); skeletonUtilityBoneTable.Clear(); + boundingBoxFollowerTable.Clear(); SkeletonRenderer[] arr = Object.FindObjectsOfType(); - foreach (SkeletonRenderer r in arr) skeletonRendererTable.Add(r.gameObject.GetInstanceID(), r.gameObject); SkeletonUtilityBone[] boneArr = Object.FindObjectsOfType(); foreach (SkeletonUtilityBone b in boneArr) skeletonUtilityBoneTable.Add(b.gameObject.GetInstanceID(), b); + + BoundingBoxFollower[] bbfArr = Object.FindObjectsOfType(); + foreach (BoundingBoxFollower bbf in bbfArr) + boundingBoxFollowerTable.Add(bbf.gameObject.GetInstanceID(), bbf); } static void HierarchyWindowItemOnGUI (int instanceId, Rect selectionRect) { @@ -226,6 +232,21 @@ public class SpineEditorUtilities : AssetPostprocessor { } } + } else if (boundingBoxFollowerTable.ContainsKey(instanceId)) { + Rect r = new Rect(selectionRect); + r.x -= 26; + + if (boundingBoxFollowerTable[instanceId] != null) { + if (boundingBoxFollowerTable[instanceId].transform.childCount == 0) + r.x += 13; + + r.y += 2; + + r.width = 13; + r.height = 13; + + GUI.DrawTexture(r, Icons.boundingBox); + } } } diff --git a/spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtility.cs b/spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtility.cs index 091c0139d..5eec07700 100644 --- a/spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtility.cs +++ b/spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtility.cs @@ -101,6 +101,28 @@ public class SkeletonUtility : MonoBehaviour { return null; } + public static PolygonCollider2D AddBoundingBoxAsComponent (BoundingBoxAttachment boundingBox, GameObject gameObject, bool isTrigger = true) { + if (boundingBox == null) + return null; + + var collider = gameObject.AddComponent(); + collider.isTrigger = isTrigger; + float[] floats = boundingBox.Vertices; + int floatCount = floats.Length; + int vertCount = floatCount / 2; + + Vector2[] verts = new Vector2[vertCount]; + int v = 0; + for (int i = 0; i < floatCount; i += 2, v++) { + verts[v].x = floats[i]; + verts[v].y = floats[i + 1]; + } + + collider.SetPath(0, verts); + + return collider; + } + public delegate void SkeletonUtilityDelegate (); diff --git a/spine-unity/Assets/spine-unity/SpineAttributes.cs b/spine-unity/Assets/spine-unity/SpineAttributes.cs index 1bf15bca2..e7a2256d5 100644 --- a/spine-unity/Assets/spine-unity/SpineAttributes.cs +++ b/spine-unity/Assets/spine-unity/SpineAttributes.cs @@ -38,6 +38,7 @@ using System.Collections; public class SpineSlot : PropertyAttribute { public string startsWith = ""; public string dataField = ""; + public bool containsBoundingBoxes = false; /// /// Smart popup menu for Spine Slots @@ -47,9 +48,11 @@ public class SpineSlot : PropertyAttribute { /// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives). /// If left empty and the script the attribute is applied to is derived from Component, GetComponent() will be called as a fallback. /// - public SpineSlot(string startsWith = "", string dataField = "") { + /// Disables popup results that don't contain bounding box attachments when true. + public SpineSlot(string startsWith = "", string dataField = "", bool containsBoundingBoxes = false) { this.startsWith = startsWith; this.dataField = dataField; + this.containsBoundingBoxes = containsBoundingBoxes; } }