浏览代码

[unity] Add PointFollower editor conveniences.

pharan 7 年之前
父节点
当前提交
3be056c422

+ 188 - 0
spine-unity/Assets/spine-unity/Editor/PointFollowerEditor.cs

@@ -0,0 +1,188 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes or (b) remove,
+ * delete, alter, or obscure any trademarks or any copyright, trademark, patent,
+ * or other intellectual property or proprietary rights notices on or in the
+ * Software, including any copy thereof. Redistributions in binary or source
+ * form must include this license and terms.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "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 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 THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+using UnityEditor;
+using UnityEngine;
+
+namespace Spine.Unity.Editor {
+
+	using Editor = UnityEditor.Editor;
+	using Event = UnityEngine.Event;
+
+	[CustomEditor(typeof(PointFollower)), CanEditMultipleObjects]
+	public class PointFollowerEditor : Editor {
+		SerializedProperty slotName, pointAttachmentName, skeletonRenderer, followZPosition, followBoneRotation, followSkeletonFlip;
+		PointFollower targetPointFollower;
+		bool needsReset;
+
+		#region Context Menu Item
+		[MenuItem("CONTEXT/SkeletonRenderer/Add PointFollower GameObject")]
+		static void AddBoneFollowerGameObject (MenuCommand cmd) {
+			var skeletonRenderer = cmd.context as SkeletonRenderer;
+			var go = new GameObject("PointFollower");
+			var t = go.transform;
+			t.SetParent(skeletonRenderer.transform);
+			t.localPosition = Vector3.zero;
+
+			var f = go.AddComponent<PointFollower>();
+			f.skeletonRenderer = skeletonRenderer;
+
+			EditorGUIUtility.PingObject(t);
+
+			Undo.RegisterCreatedObjectUndo(go, "Add PointFollower");
+		}
+
+		// Validate
+		[MenuItem("CONTEXT/SkeletonRenderer/Add PointFollower GameObject", true)]
+		static bool ValidateAddBoneFollowerGameObject (MenuCommand cmd) {
+			var skeletonRenderer = cmd.context as SkeletonRenderer;
+			return skeletonRenderer.valid;
+		}
+		#endregion
+
+		void OnEnable () {
+			skeletonRenderer = serializedObject.FindProperty("skeletonRenderer");
+			slotName = serializedObject.FindProperty("slotName");
+			pointAttachmentName = serializedObject.FindProperty("pointAttachmentName");
+
+			targetPointFollower = (PointFollower)target;
+			if (targetPointFollower.skeletonRenderer != null)
+				targetPointFollower.skeletonRenderer.Initialize(false);
+
+			if (!targetPointFollower.IsValid || needsReset) {
+				targetPointFollower.Initialize();
+				targetPointFollower.LateUpdate();
+				needsReset = false;
+				SceneView.RepaintAll();
+			}
+		}
+
+		public void OnSceneGUI () {
+			var tbf = target as PointFollower;
+			var skeletonRendererComponent = tbf.skeletonRenderer;
+			if (skeletonRendererComponent == null)
+				return;
+
+			var skeleton = skeletonRendererComponent.skeleton;
+			var skeletonTransform = skeletonRendererComponent.transform;
+
+			if (string.IsNullOrEmpty(pointAttachmentName.stringValue)) {
+				// Draw all active PointAttachments in the current skin
+				var currentSkin = skeleton.Skin;
+				if (currentSkin != skeleton.Data.DefaultSkin) DrawPointsInSkin(skeleton.Data.DefaultSkin, skeleton, skeletonTransform);
+				if (currentSkin != null) DrawPointsInSkin(currentSkin, skeleton, skeletonTransform);
+			} else {
+				int slotIndex = skeleton.FindSlotIndex(slotName.stringValue);
+				if (slotIndex >= 0) {
+					var slot = skeleton.Slots.Items[slotIndex];
+					var point = skeleton.GetAttachment(slotIndex, pointAttachmentName.stringValue) as PointAttachment;
+					if (point != null) {
+						DrawPointAttachmentWithLabel(point, slot.Bone, skeletonTransform);
+					}
+				}
+			}
+		}
+
+		static void DrawPointsInSkin (Skin skin, Skeleton skeleton, Transform transform) {
+			foreach (var skinEntry in skin.Attachments) {
+				var attachment = skinEntry.Value as PointAttachment;
+				if (attachment != null) {
+					var skinKey = skinEntry.Key;
+					var slot = skeleton.Slots.Items[skinKey.slotIndex];
+					DrawPointAttachmentWithLabel(attachment, slot.Bone, transform);
+				}
+			}
+		}
+
+		static void DrawPointAttachmentWithLabel (PointAttachment point, Bone bone, Transform transform) {
+			Vector3 labelOffset = new Vector3(0f, -0.2f, 0f);
+			SpineHandles.DrawPointAttachment(bone, point, transform);
+			Handles.Label(labelOffset + point.GetWorldPosition(bone, transform), point.Name, SpineHandles.PointNameStyle);
+		}
+
+		override public void OnInspectorGUI () {
+			if (serializedObject.isEditingMultipleObjects) {
+				if (needsReset) {
+					needsReset = false;
+					foreach (var o in targets) {
+						var bf = (BoneFollower)o;
+						bf.Initialize();
+						bf.LateUpdate();
+					}
+					SceneView.RepaintAll();
+				}
+
+				EditorGUI.BeginChangeCheck();
+				DrawDefaultInspector();
+				needsReset |= EditorGUI.EndChangeCheck();
+				return;
+			}
+
+			if (needsReset && Event.current.type == EventType.Layout) {
+				targetPointFollower.Initialize();
+				targetPointFollower.LateUpdate();
+				needsReset = false;
+				SceneView.RepaintAll();
+			}
+			serializedObject.Update();
+
+			DrawDefaultInspector();
+
+			// Find Renderer
+			if (skeletonRenderer.objectReferenceValue == null) {
+				SkeletonRenderer parentRenderer = targetPointFollower.GetComponentInParent<SkeletonRenderer>();
+				if (parentRenderer != null && parentRenderer.gameObject != targetPointFollower.gameObject) {
+					skeletonRenderer.objectReferenceValue = parentRenderer;
+					Debug.Log("Inspector automatically assigned PointFollower.SkeletonRenderer");
+				}
+			}
+
+			var skeletonRendererReference = skeletonRenderer.objectReferenceValue as SkeletonRenderer;
+			if (skeletonRendererReference != null) {
+				if (skeletonRendererReference.gameObject == targetPointFollower.gameObject) {
+					skeletonRenderer.objectReferenceValue = null;
+					EditorUtility.DisplayDialog("Invalid assignment.", "PointFollower can only follow a skeleton on a separate GameObject.\n\nCreate a new GameObject for your PointFollower, or choose a SkeletonRenderer from a different GameObject.", "Ok");
+				}
+			}
+
+			if (!targetPointFollower.IsValid) {
+				needsReset = true;
+			}
+
+			var current = Event.current;
+			bool wasUndo = (current.type == EventType.ValidateCommand && current.commandName == "UndoRedoPerformed");
+			if (wasUndo)
+				targetPointFollower.Initialize();
+
+			serializedObject.ApplyModifiedProperties();
+		}
+	}
+
+}

+ 12 - 0
spine-unity/Assets/spine-unity/Editor/PointFollowerEditor.cs.meta

@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 7c7e838a8ec295a4e9c53602f690f42f
+timeCreated: 1518163038
+licenseType: Free
+MonoImporter:
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 29 - 0
spine-unity/Assets/spine-unity/Editor/SpineEditorUtilities.cs

@@ -1577,6 +1577,7 @@ namespace Spine.Unity.Editor {
 		public static Color PathColor { get { return new Color(254/255f, 127/255f, 0); } }
 		public static Color TransformContraintColor { get { return new Color(170/255f, 226/255f, 35/255f); } }
 		public static Color IkColor { get { return new Color(228/255f,90/255f,43/255f); } }
+		public static Color PointColor { get { return new Color(1f, 1f, 0f, 1f);  } }
 
 		static Vector3[] _boneMeshVerts = {
 			new Vector3(0, 0, 0),
@@ -1677,6 +1678,17 @@ namespace Spine.Unity.Editor {
 			}
 		}
 
+		static GUIStyle _pointNameStyle;
+		public static GUIStyle PointNameStyle {
+			get {
+				if (_pointNameStyle == null) {
+					_pointNameStyle = new GUIStyle(SpineHandles.BoneNameStyle);
+					_pointNameStyle.normal.textColor = SpineHandles.PointColor;
+				}
+				return _pointNameStyle;
+			}
+		}
+
 		public static void DrawBoneNames (Transform transform, Skeleton skeleton, float positionScale = 1f) {
 			GUIStyle style = BoneNameStyle;
 			foreach (Bone b in skeleton.Bones) {
@@ -1850,6 +1862,19 @@ namespace Spine.Unity.Editor {
 			Handles.DrawLine(lastVert, firstVert);
 		}
 
+		public static void DrawPointAttachment (Bone bone, PointAttachment pointAttachment, Transform skeletonTransform) {
+			if (bone == null) return;
+			if (pointAttachment == null) return;
+
+			Vector2 localPos;
+			pointAttachment.ComputeWorldPosition(bone, out localPos.x, out localPos.y);
+			float localRotation = pointAttachment.ComputeWorldRotation(bone);
+			Matrix4x4 m = Matrix4x4.TRS(localPos, Quaternion.Euler(0, 0, localRotation), Vector3.one) * Matrix4x4.TRS(Vector3.right * 0.25f, Quaternion.identity, Vector3.one);
+
+			DrawBoneCircle(skeletonTransform.TransformPoint(localPos), SpineHandles.PointColor, Vector3.back, 1.3f);
+			DrawArrowhead(skeletonTransform.localToWorldMatrix * m);
+		}
+
 		public static void DrawConstraints (Transform transform, Skeleton skeleton, float skeletonRenderScale = 1f) {
 			Vector3 targetPos;
 			Vector3 pos;
@@ -1953,6 +1978,10 @@ namespace Spine.Unity.Editor {
 			Graphics.DrawMeshNow(SpineHandles.ArrowheadMesh, Matrix4x4.TRS(pos, Quaternion.Euler(0, 0, localRotation), new Vector3(scale, scale, scale)));
 		}
 
+		static void DrawArrowhead (Vector3 pos, Quaternion worldQuaternion) {
+			Graphics.DrawMeshNow(SpineHandles.ArrowheadMesh, pos, worldQuaternion, 0);
+		}
+
 		static void DrawArrowhead (Matrix4x4 m) {
 			var s = SpineHandles.handleScale;
 			m.m00 *= s;

+ 12 - 1
spine-unity/Assets/spine-unity/ISkeletonAnimation.cs

@@ -46,10 +46,11 @@ namespace Spine.Unity {
 		/// <summary>Gets the SkeletonDataAsset of the Spine Component.</summary>
 		SkeletonDataAsset SkeletonDataAsset { get; }
 	}
-
+	
 	/// <summary>A Spine-Unity Component that manages a Spine.Skeleton instance, instantiated from a SkeletonDataAsset.</summary>
 	public interface ISkeletonComponent {
 		/// <summary>Gets the SkeletonDataAsset of the Spine Component.</summary>
+		//[System.Obsolete]
 		SkeletonDataAsset SkeletonDataAsset { get; }
 
 		/// <summary>Gets the Spine.Skeleton instance of the Spine Component. This is equivalent to SkeletonRenderer's .skeleton.</summary>
@@ -61,4 +62,14 @@ namespace Spine.Unity {
 		/// <summary>Gets the Spine.AnimationState of the animated Spine Component. This is equivalent to SkeletonAnimation.state.</summary>
 		AnimationState AnimationState { get; }
 	}
+
+	/// <summary>A Spine-Unity Component that holds a reference to a SkeletonRenderer.</summary>
+	public interface IHasSkeletonRenderer {
+		SkeletonRenderer SkeletonRenderer { get; }
+	}
+
+	/// <summary>A Spine-Unity Component that holds a reference to an ISkeletonComponent.</summary>
+	public interface IHasSkeletonComponent {
+		ISkeletonComponent SkeletonComponent { get; }
+	}
 }

+ 6 - 12
spine-unity/Assets/spine-unity/PointFollower.cs

@@ -36,9 +36,11 @@ namespace Spine.Unity {
 
 	[ExecuteInEditMode]
 	[AddComponentMenu("Spine/Point Follower")]
-	public class PointFollower : MonoBehaviour {
+	public class PointFollower : MonoBehaviour, IHasSkeletonRenderer, IHasSkeletonComponent {
 
-		public SkeletonRenderer skeletonRenderer;
+		[SerializeField] public SkeletonRenderer skeletonRenderer;
+		public SkeletonRenderer SkeletonRenderer { get { return this.skeletonRenderer; } }
+		public ISkeletonComponent SkeletonComponent { get { return skeletonRenderer as ISkeletonComponent; } }
 
 		[SpineSlot(dataField:"skeletonRenderer", includeNone: true)]
 		public string slotName;
@@ -55,16 +57,7 @@ namespace Spine.Unity {
 		PointAttachment point;
 		Bone bone;
 		bool valid;
-
-		#if UNITY_EDITOR
-		void OnValidate () {
-			if (skeletonRenderer == null) {
-				skeletonRenderer = GetComponent<SkeletonRenderer>();
-				if (skeletonRenderer == null)
-					skeletonRenderer = GetComponentInParent<SkeletonRenderer>();
-			}
-		}
-		#endif
+		public bool IsValid { get { return valid; } }
 
 		public void Initialize () {
 			valid = skeletonRenderer != null && skeletonRenderer.valid;
@@ -92,6 +85,7 @@ namespace Spine.Unity {
 			point = null;
 			if (!string.IsNullOrEmpty(pointAttachmentName)) {
 				var skeleton = skeletonRenderer.skeleton;
+
 				int slotIndex = skeleton.FindSlotIndex(slotName);
 				if (slotIndex >= 0) {
 					var slot = skeleton.slots.Items[slotIndex];

+ 16 - 16
spine-unity/Assets/spine-unity/SkeletonExtensions.cs

@@ -152,22 +152,6 @@ namespace Spine.Unity {
 			return new Quaternion(0, 0, Mathf.Sin(halfRotation), Mathf.Cos(halfRotation));
 		}
 
-		/// <summary>Gets the PointAttachment's Unity World position using its Spine GameObject Transform.</summary>
-		public static Vector3 GetWorldPosition (this PointAttachment attachment, Slot slot, Transform spineGameObjectTransform) {
-			Vector3 skeletonSpacePosition;
-			skeletonSpacePosition.z = 0;
-			attachment.ComputeWorldPosition(slot.bone, out skeletonSpacePosition.x, out skeletonSpacePosition.y);
-			return spineGameObjectTransform.TransformPoint(skeletonSpacePosition);
-		}
-
-		/// <summary>Gets the PointAttachment's Unity World position using its Spine GameObject Transform.</summary>
-		public static Vector3 GetWorldPosition (this PointAttachment attachment, Bone bone, Transform spineGameObjectTransform) {
-			Vector3 skeletonSpacePosition;
-			skeletonSpacePosition.z = 0;
-			attachment.ComputeWorldPosition(bone, out skeletonSpacePosition.x, out skeletonSpacePosition.y);
-			return spineGameObjectTransform.TransformPoint(skeletonSpacePosition);
-		}
-
 		/// <summary>Gets the internal bone matrix as a Unity bonespace-to-skeletonspace transformation matrix.</summary>
 		public static Matrix4x4 GetMatrix4x4 (this Bone bone) {
 			return new Matrix4x4 {
@@ -281,6 +265,22 @@ namespace Spine.Unity {
 
 			return buffer;
 		}
+
+		/// <summary>Gets the PointAttachment's Unity World position using its Spine GameObject Transform.</summary>
+		public static Vector3 GetWorldPosition (this PointAttachment attachment, Slot slot, Transform spineGameObjectTransform) {
+			Vector3 skeletonSpacePosition;
+			skeletonSpacePosition.z = 0;
+			attachment.ComputeWorldPosition(slot.bone, out skeletonSpacePosition.x, out skeletonSpacePosition.y);
+			return spineGameObjectTransform.TransformPoint(skeletonSpacePosition);
+		}
+
+		/// <summary>Gets the PointAttachment's Unity World position using its Spine GameObject Transform.</summary>
+		public static Vector3 GetWorldPosition (this PointAttachment attachment, Bone bone, Transform spineGameObjectTransform) {
+			Vector3 skeletonSpacePosition;
+			skeletonSpacePosition.z = 0;
+			attachment.ComputeWorldPosition(bone, out skeletonSpacePosition.x, out skeletonSpacePosition.y);
+			return spineGameObjectTransform.TransformPoint(skeletonSpacePosition);
+		}
 		#endregion
 	}
 }