Эх сурвалжийг харах

Merge remote-tracking branch 'origin/master'

NathanSweet 9 жил өмнө
parent
commit
230542fb7f

+ 19 - 5
spine-unity/Assets/spine-unity/CustomSkin.cs

@@ -35,33 +35,47 @@ using Spine;
 
 public class CustomSkin : MonoBehaviour {
 
-
 	[System.Serializable]
 	public class SkinPair {
+		/// <summary>SpineAttachment attachment path to help find the attachment.</summary>
+		/// <remarks>This use of SpineAttachment generates an attachment path string that can only be used by SpineAttachment.GetAttachment.</remarks>
 		[SpineAttachment(currentSkinOnly: false, returnAttachmentPath: true, dataField: "skinSource")]
-		public string sourceAttachment;
+		[UnityEngine.Serialization.FormerlySerializedAs("sourceAttachment")]
+		public string sourceAttachmentPath;
+
 		[SpineSlot]
 		public string targetSlot;
+
+		/// <summary>The name of the skin placeholder/skin dictionary entry this attachment should be associated with.</summary>
+		/// <remarks>This name is used by the skin dictionary, used in the method Skin.AddAttachment as well as setting a slot attachment</remarks>
 		[SpineAttachment(currentSkinOnly: true, placeholdersOnly: true)]
 		public string targetAttachment;
 	}
 
+	#region Inspector
 	public SkeletonDataAsset skinSource;
-	public SkinPair[] skinning;
+
+	[UnityEngine.Serialization.FormerlySerializedAs("skinning")]
+	public SkinPair[] skinItems;
+
 	public Skin customSkin;
+	#endregion
 
 	SkeletonRenderer skeletonRenderer;
+
 	void Start () {
 		skeletonRenderer = GetComponent<SkeletonRenderer>();
 		Skeleton skeleton = skeletonRenderer.skeleton;
 
 		customSkin = new Skin("CustomSkin");
 
-		foreach (var pair in skinning) {
-			var attachment = SpineAttachment.GetAttachment(pair.sourceAttachment, skinSource);
+		foreach (var pair in skinItems) {
+			var attachment = SpineAttachment.GetAttachment(pair.sourceAttachmentPath, skinSource);
 			customSkin.AddAttachment(skeleton.FindSlotIndex(pair.targetSlot), pair.targetAttachment, attachment);
 		}
 
+		// The custom skin does not need to be added to the skeleton data for it to work.
+		// But it's useful for your script to keep a reference to it.
 		skeleton.SetSkin(customSkin);
 	}
 }

+ 32 - 30
spine-unity/Assets/spine-unity/Editor/SkeletonAnimationInspector.cs

@@ -38,7 +38,7 @@ using Spine;
 public class SkeletonAnimationInspector : SkeletonRendererInspector {
 	protected SerializedProperty animationName, loop, timeScale, autoReset;
 	protected bool m_isPrefab;
-	protected GUIContent autoResetLabel;
+	protected bool wasAnimationNameChanged;
 
 	protected override void OnEnable () {
 		base.OnEnable();
@@ -56,43 +56,45 @@ public class SkeletonAnimationInspector : SkeletonRendererInspector {
 		SkeletonAnimation component = (SkeletonAnimation)target;
 		if (!component.valid)
 			return;
-
-		//catch case where SetAnimation was used to set track 0 without using AnimationName
-		if (Application.isPlaying) {
-			TrackEntry currentState = component.state.GetCurrent(0);
-			if (currentState != null) {
-				if (component.AnimationName != animationName.stringValue) {
-					animationName.stringValue = currentState.Animation.Name;
-				}
+		
+		if (wasAnimationNameChanged) {
+			if (!Application.isPlaying) {
+				component.state.ClearTrack(0);
+				component.skeleton.SetToSetupPose();
 			}
-		}
-
-		EditorGUILayout.Space();
 
-		//TODO:  Refactor this to use GenericMenu and callbacks to avoid interfering with control by other behaviours.
-		// Animation name.
-		{
-			String[] animations = new String[component.skeleton.Data.Animations.Count + 1];
-			animations[0] = "<None>";
-			int animationIndex = 0;
-			for (int i = 0; i < animations.Length - 1; i++) {
-				String name = component.skeleton.Data.Animations.Items[i].Name;
-				animations[i + 1] = name;
-				if (name == animationName.stringValue)
-					animationIndex = i + 1;
-			}
-		
-			animationIndex = EditorGUILayout.Popup("Animation", animationIndex, animations);
+			Spine.Animation animationToUse = component.skeleton.Data.FindAnimation(animationName.stringValue);
 
-			String selectedAnimationName = animationIndex == 0 ? null : animations[animationIndex];
-			if (component.AnimationName != selectedAnimationName) {
-				component.AnimationName = selectedAnimationName;
-				animationName.stringValue = selectedAnimationName;
+			if (!Application.isPlaying) {
+				if (animationToUse != null) animationToUse.Apply(component.skeleton, 0f, 0f, false, null);
+				component.Update();
+				component.LateUpdate();
+				SceneView.RepaintAll();
+			} else {
+				if (animationToUse != null)
+					component.state.SetAnimation(0, animationToUse, loop.boolValue);
+				else
+					component.state.ClearTrack(0);
 			}
 
+			wasAnimationNameChanged = false;
+		}
 
+		// Reflect animationName serialized property in the inspector even if SetAnimation API was used.
+		if (Application.isPlaying) {
+			TrackEntry current = component.state.GetCurrent(0);
+			if (current != null) {
+				if (component.AnimationName != animationName.stringValue) {
+					animationName.stringValue = current.Animation.Name;
+				}
+			}
 		}
 
+		EditorGUILayout.Space();
+		EditorGUI.BeginChangeCheck();
+		EditorGUILayout.PropertyField(animationName);
+		wasAnimationNameChanged |= EditorGUI.EndChangeCheck();
+			
 		EditorGUILayout.PropertyField(loop);
 		EditorGUILayout.PropertyField(timeScale);
 		component.timeScale = Math.Max(component.timeScale, 0);

+ 18 - 11
spine-unity/Assets/spine-unity/Editor/SpineAttributeDrawers.cs

@@ -5,17 +5,19 @@
 *****************************************************************************/
 using UnityEngine;
 using UnityEditor;
+using System;
 using System.Collections;
 using System.Collections.Generic;
 using System.Reflection;
 using Spine;
 
 
+
 public struct SpineDrawerValuePair {
 	public string str;
 	public SerializedProperty property;
 
-	public SpineDrawerValuePair(string val, SerializedProperty property) {
+	public SpineDrawerValuePair (string val, SerializedProperty property) {
 		this.str = val;
 		this.property = property;
 	}
@@ -23,6 +25,7 @@ public struct SpineDrawerValuePair {
 
 public abstract class SpineTreeItemDrawerBase<T> : PropertyDrawer where T:SpineAttributeBase {
 	protected SkeletonDataAsset skeletonDataAsset;
+
 	protected T TargetAttribute { get { return (T)attribute; } }
 
 	public override void OnGUI (Rect position, SerializedProperty property, GUIContent label) {
@@ -72,7 +75,7 @@ public abstract class SpineTreeItemDrawerBase<T> : PropertyDrawer where T:SpineA
 			return;
 		
 		GenericMenu menu = new GenericMenu();
-		PopulateMenu (menu, property, this.TargetAttribute, data);
+		PopulateMenu(menu, property, this.TargetAttribute, data);
 		menu.ShowAsContext();
 	}
 
@@ -84,7 +87,7 @@ public abstract class SpineTreeItemDrawerBase<T> : PropertyDrawer where T:SpineA
 		pair.property.serializedObject.ApplyModifiedProperties();
 	}
 
-	public override float GetPropertyHeight(SerializedProperty property, GUIContent label) {
+	public override float GetPropertyHeight (SerializedProperty property, GUIContent label) {
 		return 18;
 	}
 
@@ -96,7 +99,7 @@ public class SpineSlotDrawer : SpineTreeItemDrawerBase<SpineSlot> {
 	protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineSlot targetAttribute, SkeletonData data) {
 		for (int i = 0; i < data.Slots.Count; i++) {
 			string name = data.Slots.Items[i].Name;
-			if (name.StartsWith(targetAttribute.startsWith)) {
+			if (name.StartsWith(targetAttribute.startsWith, StringComparison.Ordinal)) {
 				if (targetAttribute.containsBoundingBoxes) {
 					
 					int slotIndex = i;
@@ -139,7 +142,7 @@ public class SpineSkinDrawer : SpineTreeItemDrawerBase<SpineSkin> {
 		
 		for (int i = 0; i < data.Skins.Count; i++) {
 			string name = data.Skins.Items[i].Name;
-			if (name.StartsWith(targetAttribute.startsWith))
+			if (name.StartsWith(targetAttribute.startsWith, StringComparison.Ordinal))
 				menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property));
 		}
 	}
@@ -150,9 +153,13 @@ public class SpineSkinDrawer : SpineTreeItemDrawerBase<SpineSkin> {
 public class SpineAnimationDrawer : SpineTreeItemDrawerBase<SpineAnimation> {
 	protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineAnimation targetAttribute, SkeletonData data) {
 		var animations = skeletonDataAsset.GetAnimationStateData().SkeletonData.Animations;
+
+		// <None> item
+		menu.AddItem(new GUIContent("<None>"), property.stringValue == "", HandleSelect, new SpineDrawerValuePair("", property));
+
 		for (int i = 0; i < animations.Count; i++) {
 			string name = animations.Items[i].Name;
-			if (name.StartsWith(targetAttribute.startsWith))
+			if (name.StartsWith(targetAttribute.startsWith, StringComparison.Ordinal))
 				menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property));
 		}
 	}
@@ -165,7 +172,7 @@ public class SpineEventNameDrawer : SpineTreeItemDrawerBase<SpineEvent> {
 		var events = skeletonDataAsset.GetSkeletonData(false).Events;
 		for (int i = 0; i < events.Count; i++) {
 			string name = events.Items[i].Name;
-			if (name.StartsWith(targetAttribute.startsWith))
+			if (name.StartsWith(targetAttribute.startsWith, StringComparison.Ordinal))
 				menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property));
 		}
 	}
@@ -289,8 +296,8 @@ public class SpineBoneDrawer : SpineTreeItemDrawerBase<SpineBone> {
 public class SpineAtlasRegionDrawer : PropertyDrawer {
 	Component component;
 	SerializedProperty atlasProp;
-	
-	public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
+
+	public override void OnGUI (Rect position, SerializedProperty property, GUIContent label) {
 		if (property.propertyType != SerializedPropertyType.String) {
 			EditorGUI.LabelField(position, "ERROR:", "May only apply to type string");
 			return;
@@ -321,7 +328,7 @@ public class SpineAtlasRegionDrawer : PropertyDrawer {
 		}
 		
 	}
-	
+
 	void Selector (SerializedProperty property) {
 		GenericMenu menu = new GenericMenu();
 		AtlasAsset atlasAsset = (AtlasAsset)atlasProp.objectReferenceValue;
@@ -337,7 +344,7 @@ public class SpineAtlasRegionDrawer : PropertyDrawer {
 		
 		menu.ShowAsContext();
 	}
-	
+
 	static void HandleSelect (object val) {
 		var pair = (SpineDrawerValuePair)val;
 		pair.property.stringValue = pair.str;

+ 27 - 31
spine-unity/Assets/spine-unity/SkeletonAnimation.cs

@@ -68,6 +68,7 @@ public class SkeletonAnimation : SkeletonRenderer, ISkeletonAnimation {
 	}
 
 	[SerializeField]
+	[SpineAnimation]
 	private String _animationName;
 
 	public String AnimationName {
@@ -104,39 +105,25 @@ public class SkeletonAnimation : SkeletonRenderer, ISkeletonAnimation {
 	public bool loop;
 
 	/// <summary>
-	/// The rate at which animations progress over time. 1 means 100%. 0.5 means 50%.
-	/// AnimationState and TrackEntry also have their own timeScale. These are combined multiplicatively.</summary>
+	/// The rate at which animations progress over time. 1 means 100%. 0.5 means 50%.</summary>
+	/// <remarks>AnimationState and TrackEntry also have their own timeScale. These are combined multiplicatively.</remarks>
 	#if UNITY_5
 	[Tooltip("The rate at which animations progress over time. 1 means 100%. 0.5 means 50%.")]
 	#endif
 	public float timeScale = 1;
 
-	#region AutoReset
-	/**
-	[Tooltip("Setting this to true makes the SkeletonAnimation behave similar to Spine editor. New animations will not inherit the pose from a previous animation. If you need to intermittently and programmatically pose your skeleton, leave this false.")]
-	[SerializeField]
-	protected bool autoReset = false;
-
-	/// <summary>
-	/// Setting this to true makes the SkeletonAnimation behave similar to Spine editor. 
-	/// New animations will not inherit the pose from a previous animation. 
-	/// If you need to intermittently and programmatically pose your skeleton, leave this false.</summary>
-	public bool AutoReset {
-		get { return this.autoReset; }
-		set {
-			if (!autoReset && value) {
-				state.Start -= HandleNewAnimationAutoreset;	// make sure there isn't a double-subscription.
-				state.Start += HandleNewAnimationAutoreset;
-			}
-			autoReset = value;
-		}
+	#region Runtime Instantiation
+	/// <summary>Adds and prepares a SkeletonAnimation component to a GameObject at runtime.</summary>
+	/// <returns>The newly instantiated SkeletonAnimation</returns>
+	public static SkeletonAnimation AddToGameObject (GameObject gameObject, SkeletonDataAsset skeletonDataAsset) {
+		return SkeletonRenderer.AddSpineComponent<SkeletonAnimation>(gameObject, skeletonDataAsset);
 	}
 
-	protected virtual void HandleNewAnimationAutoreset (Spine.AnimationState state, int trackIndex) {
-		if (!autoReset) return;
-		if (skeleton != null) skeleton.SetToSetupPose();
+	/// <summary>Instantiates a new UnityEngine.GameObject and adds a prepared SkeletonAnimation component to it.</summary>
+	/// <returns>The newly instantiated SkeletonAnimation component.</returns>
+	public static SkeletonAnimation NewSkeletonAnimationGameObject (SkeletonDataAsset skeletonDataAsset) {
+		return SkeletonRenderer.NewSpineGameObject<SkeletonAnimation>(skeletonDataAsset);
 	}
-	*/
 	#endregion
 
 	public override void Reset () {
@@ -146,16 +133,24 @@ public class SkeletonAnimation : SkeletonRenderer, ISkeletonAnimation {
 
 		state = new Spine.AnimationState(skeletonDataAsset.GetAnimationStateData());
 
-		/*
-		if (autoReset) {
-			state.Start += HandleNewAnimationAutoreset;
+		#if UNITY_EDITOR
+		if (!string.IsNullOrEmpty(_animationName)) {
+			if (Application.isPlaying) {
+				state.SetAnimation(0, _animationName, loop);
+			} else {
+				// Assume SkeletonAnimation is valid for skeletonData and skeleton. Checked above.
+				var animationObject = skeletonDataAsset.GetSkeletonData(false).FindAnimation(_animationName);
+				if (animationObject != null)
+					animationObject.Apply(skeleton, 0f, 0f, false, null);
+			}
+			Update(0);
 		}
-		*/
-
-		if (_animationName != null && _animationName.Length > 0) {
+		#else
+		if (!string.IsNullOrEmpty(_animationName)) {
 			state.SetAnimation(0, _animationName, loop);
 			Update(0);
 		}
+		#endif
 	}
 
 	public virtual void Update () {
@@ -185,4 +180,5 @@ public class SkeletonAnimation : SkeletonRenderer, ISkeletonAnimation {
 			_UpdateComplete(this);
 		}
 	}
+		
 }

+ 60 - 22
spine-unity/Assets/spine-unity/SkeletonExtensions.cs

@@ -1,7 +1,7 @@
 
 
 /*****************************************************************************
- * Spine Extensions created by Mitch Thompson
+ * Spine Extensions by Mitch Thompson and John Dy
  * Full irrevocable rights and permissions granted to Esoteric Software
 *****************************************************************************/
 
@@ -11,6 +11,14 @@ using Spine;
 
 public static class SkeletonExtensions {
 
+	const float ByteToFloat = 1f / 255f;
+
+	#region Colors
+	public static Color GetColor (this Skeleton s) { return new Color(s.r, s.g, s.b, s.a); }
+	public static Color GetColor (this RegionAttachment a) { return new Color(a.r, a.g, a.b, a.a); }
+	public static Color GetColor (this MeshAttachment a) { return new Color(a.r, a.g, a.b, a.a); }
+	public static Color GetColor (this SkinnedMeshAttachment a) { return new Color(a.r, a.g, a.b, a.a);	}
+
 	public static void SetColor (this Skeleton skeleton, Color color) {
 		skeleton.A = color.a;
 		skeleton.R = color.r;
@@ -19,10 +27,10 @@ public static class SkeletonExtensions {
 	}
 
 	public static void SetColor (this Skeleton skeleton, Color32 color) {
-		skeleton.A = color.a / 255f;
-		skeleton.R = color.r / 255f;
-		skeleton.G = color.g / 255f;
-		skeleton.B = color.b / 255f;
+		skeleton.A = color.a * ByteToFloat;
+		skeleton.R = color.r * ByteToFloat;
+		skeleton.G = color.g * ByteToFloat;
+		skeleton.B = color.b * ByteToFloat;
 	}
 
 	public static void SetColor (this Slot slot, Color color) {
@@ -33,10 +41,10 @@ public static class SkeletonExtensions {
 	}
 
 	public static void SetColor (this Slot slot, Color32 color) {
-		slot.A = color.a / 255f;
-		slot.R = color.r / 255f;
-		slot.G = color.g / 255f;
-		slot.B = color.b / 255f;
+		slot.A = color.a * ByteToFloat;
+		slot.R = color.r * ByteToFloat;
+		slot.G = color.g * ByteToFloat;
+		slot.B = color.b * ByteToFloat;
 	}
 
 	public static void SetColor (this RegionAttachment attachment, Color color) {
@@ -47,10 +55,10 @@ public static class SkeletonExtensions {
 	}
 
 	public static void SetColor (this RegionAttachment attachment, Color32 color) {
-		attachment.A = color.a / 255f;
-		attachment.R = color.r / 255f;
-		attachment.G = color.g / 255f;
-		attachment.B = color.b / 255f;
+		attachment.A = color.a * ByteToFloat;
+		attachment.R = color.r * ByteToFloat;
+		attachment.G = color.g * ByteToFloat;
+		attachment.B = color.b * ByteToFloat;
 	}
 
 	public static void SetColor (this MeshAttachment attachment, Color color) {
@@ -61,10 +69,10 @@ public static class SkeletonExtensions {
 	}
 
 	public static void SetColor (this MeshAttachment attachment, Color32 color) {
-		attachment.A = color.a / 255f;
-		attachment.R = color.r / 255f;
-		attachment.G = color.g / 255f;
-		attachment.B = color.b / 255f;
+		attachment.A = color.a * ByteToFloat;
+		attachment.R = color.r * ByteToFloat;
+		attachment.G = color.g * ByteToFloat;
+		attachment.B = color.b * ByteToFloat;
 	}
 
 	public static void SetColor (this SkinnedMeshAttachment attachment, Color color) {
@@ -75,12 +83,14 @@ public static class SkeletonExtensions {
 	}
 
 	public static void SetColor (this SkinnedMeshAttachment attachment, Color32 color) {
-		attachment.A = color.a / 255f;
-		attachment.R = color.r / 255f;
-		attachment.G = color.g / 255f;
-		attachment.B = color.b / 255f;
+		attachment.A = color.a * ByteToFloat;
+		attachment.R = color.r * ByteToFloat;
+		attachment.G = color.g * ByteToFloat;
+		attachment.B = color.b * ByteToFloat;
 	}
+	#endregion
 
+	#region Bone Position
 	public static void SetPosition (this Bone bone, Vector2 position) {
 		bone.X = position.x;
 		bone.Y = position.y;
@@ -91,10 +101,36 @@ public static class SkeletonExtensions {
 		bone.Y = position.y;
 	}
 
+	public static Vector2 GetSkeletonSpacePosition (this Bone bone) {
+		// TODO: This changes in v3.0
+		return new Vector2(bone.worldX, bone.worldY);
+	}
+
+	public static Vector3 GetWorldPosition (this Bone bone, UnityEngine.Transform parentTransform) {		
+		return parentTransform.TransformPoint(new Vector3(bone.worldX, bone.worldY));
+	}
+	#endregion
+
+	#region Posing
+	/// <summary>
+	/// Shortcut for posing a skeleton at a specific time. Time is in seconds. (frameNumber / 30f) will give you seconds.
+	/// If you need to do this often, you should get the Animation object yourself using skeleton.data.FindAnimation. and call Apply on that.</summary>
+	/// <param name = "skeleton">The skeleton to pose.</param>
+	/// <param name="animationName">The name of the animation to use.</param>
+	/// <param name = "time">The time of the pose within the animation.</param>
+	/// <param name = "loop">Wraps the time around if it is longer than the duration of the animation.</param>
+	public static void PoseWithAnimation (this Skeleton skeleton, string animationName, float time, bool loop) {
+		// Fail loud when skeleton.data is null.
+		Spine.Animation animation = skeleton.data.FindAnimation(animationName);
+		if (animation == null) return;
+		animation.Apply(skeleton, 0, time, loop, null);
+	}
+	#endregion
+
+	#region Unity Sprite To Attachments
 	public static Attachment AttachUnitySprite (this Skeleton skeleton, string slotName, Sprite sprite, string shaderName = "Spine/Skeleton") {
 		var att = sprite.ToRegionAttachment(shaderName);
 		skeleton.FindSlot(slotName).Attachment = att;
-
 		return att;
 	}
 
@@ -117,4 +153,6 @@ public static class SkeletonExtensions {
 		loader = null;
 		return att;
 	}
+	#endregion
+
 }

+ 20 - 0
spine-unity/Assets/spine-unity/SkeletonRenderer.cs

@@ -76,6 +76,26 @@ public class SkeletonRenderer : MonoBehaviour {
 	private readonly ExposedList<Submesh> submeshes = new ExposedList<Submesh>();
 	private SkeletonUtilitySubmeshRenderer[] submeshRenderers;
 
+	#region Runtime Instantiation
+	/// <summary>Add and prepare a Spine component that derives from SkeletonRenderer to a GameObject at runtime.</summary>
+	/// <typeparam name="T">T should be SkeletonRenderer or any of its derived classes.</typeparam>
+	public static T AddSpineComponent<T> (GameObject gameObject, SkeletonDataAsset skeletonDataAsset) where T : SkeletonRenderer {
+		
+		var c = gameObject.AddComponent<T>();
+
+		if (skeletonDataAsset != null) {
+			c.skeletonDataAsset = skeletonDataAsset;
+			c.Reset(); // TODO: Method name will change.
+		}
+
+		return c;
+	}
+
+	public static T NewSpineGameObject<T> (SkeletonDataAsset skeletonDataAsset) where T : SkeletonRenderer {
+		return SkeletonRenderer.AddSpineComponent<T>(new GameObject("New Spine GameObject"), skeletonDataAsset);
+	}
+	#endregion
+
 	public virtual void Awake () {
 		Reset();
 	}

+ 10 - 12
spine-unity/Assets/spine-unity/SpineAttributes.cs

@@ -85,14 +85,14 @@ public class SpineAttachment : SpineAttributeBase {
 	/// Smart popup menu for Spine Attachments
 	/// </summary>
 	/// <param name="currentSkinOnly">Filters popup results to only include the current Skin.  Only valid when a SkeletonRenderer is the data source.</param>
-	/// <param name="returnAttachmentPath">Returns a fully qualified path for an Attachment in the format "Skin/Slot/AttachmentName"</param>
+	/// <param name="returnAttachmentPath">Returns a fully qualified path for an Attachment in the format "Skin/Slot/AttachmentName". This path format is only used by the SpineAttachment helper methods like SpineAttachment.GetAttachment and .GetHierarchy. Do not use full path anywhere else in Spine's system.</param>
 	/// <param name="placeholdersOnly">Filters popup results to exclude attachments that are not children of Skin Placeholders</param>
 	/// <param name="slotField">If specified, a locally scoped field with the name supplied by in slotField will be used to limit the popup results to children of a named slot</param>
 	/// <param name="dataField">If specified, a locally scoped field with the name supplied by in dataField will be used to fill the popup results.
 	/// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives)
 	/// If left empty and the script the attribute is applied to is derived from Component, GetComponent<SkeletonRenderer>() will be called as a fallback.
 	/// </param>
-	public SpineAttachment(bool currentSkinOnly = true, bool returnAttachmentPath = false, bool placeholdersOnly = false, string slotField = "", string dataField = "") {
+	public SpineAttachment (bool currentSkinOnly = true, bool returnAttachmentPath = false, bool placeholdersOnly = false, string slotField = "", string dataField = "") {
 		this.currentSkinOnly = currentSkinOnly;
 		this.returnAttachmentPath = returnAttachmentPath;
 		this.placeholdersOnly = placeholdersOnly;
@@ -100,11 +100,11 @@ public class SpineAttachment : SpineAttributeBase {
 		this.dataField = dataField;		
 	}
 
-	public static Hierarchy GetHierarchy(string fullPath) {
-		return new Hierarchy(fullPath);
+	public static SpineAttachment.Hierarchy GetHierarchy (string fullPath) {
+		return new SpineAttachment.Hierarchy(fullPath);
 	}
 
-	public static Spine.Attachment GetAttachment(string attachmentPath, Spine.SkeletonData skeletonData) {
+	public static Spine.Attachment GetAttachment (string attachmentPath, Spine.SkeletonData skeletonData) {
 		var hierarchy = SpineAttachment.GetHierarchy(attachmentPath);
 		if (hierarchy.name == "")
 			return null;
@@ -112,16 +112,18 @@ public class SpineAttachment : SpineAttributeBase {
 		return skeletonData.FindSkin(hierarchy.skin).GetAttachment(skeletonData.FindSlotIndex(hierarchy.slot), hierarchy.name);
 	}
 
-	public static Spine.Attachment GetAttachment(string attachmentPath, SkeletonDataAsset skeletonDataAsset) {
+	public static Spine.Attachment GetAttachment (string attachmentPath, SkeletonDataAsset skeletonDataAsset) {
 		return GetAttachment(attachmentPath, skeletonDataAsset.GetSkeletonData(true));
 	}
 
+	/// <summary>
+	/// A struct that represents 3 strings that help identify and locate an attachment in a skeleton.</summary>
 	public struct Hierarchy {
 		public string skin;
 		public string slot;
 		public string name;
 
-		public Hierarchy(string fullPath) {
+		public Hierarchy (string fullPath) {
 			string[] chunks = fullPath.Split(new char[]{'/'}, System.StringSplitOptions.RemoveEmptyEntries);
 			if (chunks.Length == 0) {
 				skin = "";
@@ -157,15 +159,11 @@ public class SpineBone : SpineAttributeBase {
 	}
 
 	public static Spine.Bone GetBone(string boneName, SkeletonRenderer renderer) {
-		if (renderer.skeleton == null)
-			return null;
-
-		return renderer.skeleton.FindBone(boneName);
+		return renderer.skeleton == null ? null : renderer.skeleton.FindBone(boneName);
 	}
 
 	public static Spine.BoneData GetBoneData(string boneName, SkeletonDataAsset skeletonDataAsset) {
 		var data = skeletonDataAsset.GetSkeletonData(true);
-
 		return data.FindBone(boneName);
 	}
 }