浏览代码

Merge pull request #286 from Fenrisul/master

[Feature] Skeleton Utility
Nathan Sweet 11 年之前
父节点
当前提交
1b1e9e1a9e
共有 100 个文件被更改,包括 2795 次插入58 次删除
  1. 5 1
      spine-csharp/src/Bone.cs
  2. 5 0
      spine-csharp/src/Skeleton.cs
  3. 二进制
      spine-unity/Assets/Examples/Scenes/Eyes.unity
  4. 4 0
      spine-unity/Assets/Examples/Scenes/Eyes.unity.meta
  5. 二进制
      spine-unity/Assets/Examples/Scenes/Raptor Animated Physics.unity
  6. 4 0
      spine-unity/Assets/Examples/Scenes/Raptor Animated Physics.unity.meta
  7. 二进制
      spine-unity/Assets/Examples/Scenes/Raptor GroundConstraint.unity
  8. 4 0
      spine-unity/Assets/Examples/Scenes/Raptor GroundConstraint.unity.meta
  9. 二进制
      spine-unity/Assets/Examples/Scenes/Spineboy.unity
  10. 2 2
      spine-unity/Assets/Examples/Scripts/Goblins.cs
  11. 5 0
      spine-unity/Assets/Examples/Spine/Eyes.meta
  12. 34 0
      spine-unity/Assets/Examples/Spine/Eyes/eyes.atlas.txt
  13. 4 0
      spine-unity/Assets/Examples/Spine/Eyes/eyes.atlas.txt.meta
  14. 1 0
      spine-unity/Assets/Examples/Spine/Eyes/eyes.json
  15. 4 0
      spine-unity/Assets/Examples/Spine/Eyes/eyes.json.meta
  16. 二进制
      spine-unity/Assets/Examples/Spine/Eyes/eyes.png
  17. 47 0
      spine-unity/Assets/Examples/Spine/Eyes/eyes.png.meta
  18. 二进制
      spine-unity/Assets/Examples/Spine/Eyes/eyes_Atlas.asset
  19. 4 0
      spine-unity/Assets/Examples/Spine/Eyes/eyes_Atlas.asset.meta
  20. 二进制
      spine-unity/Assets/Examples/Spine/Eyes/eyes_Material.mat
  21. 4 0
      spine-unity/Assets/Examples/Spine/Eyes/eyes_Material.mat.meta
  22. 二进制
      spine-unity/Assets/Examples/Spine/Eyes/eyes_SkeletonData.asset
  23. 4 0
      spine-unity/Assets/Examples/Spine/Eyes/eyes_SkeletonData.asset.meta
  24. 二进制
      spine-unity/Assets/Examples/Spine/dragon.prefab
  25. 二进制
      spine-unity/Assets/Examples/Spine/eyes.prefab
  26. 4 0
      spine-unity/Assets/Examples/Spine/eyes.prefab.meta
  27. 二进制
      spine-unity/Assets/Examples/Spine/goblins-ffd.prefab
  28. 二进制
      spine-unity/Assets/Examples/Spine/raptor.prefab
  29. 二进制
      spine-unity/Assets/Examples/Spine/spineboy.prefab
  30. 36 12
      spine-unity/Assets/spine-unity/BoneFollower.cs
  31. 1 1
      spine-unity/Assets/spine-unity/BoneFollower.cs.meta
  32. 34 6
      spine-unity/Assets/spine-unity/Editor/BoneFollowerInspector.cs
  33. 1 1
      spine-unity/Assets/spine-unity/Editor/BoneFollowerInspector.cs.meta
  34. 1 0
      spine-unity/Assets/spine-unity/Editor/GUI/icon-animation.png.meta
  35. 1 0
      spine-unity/Assets/spine-unity/Editor/GUI/icon-animationRoot.png.meta
  36. 二进制
      spine-unity/Assets/spine-unity/Editor/GUI/icon-bone.png
  37. 47 0
      spine-unity/Assets/spine-unity/Editor/GUI/icon-bone.png.meta
  38. 二进制
      spine-unity/Assets/spine-unity/Editor/GUI/icon-boneNib.png
  39. 47 0
      spine-unity/Assets/spine-unity/Editor/GUI/icon-boneNib.png.meta
  40. 二进制
      spine-unity/Assets/spine-unity/Editor/GUI/icon-boundingBox.png
  41. 47 0
      spine-unity/Assets/spine-unity/Editor/GUI/icon-boundingBox.png.meta
  42. 二进制
      spine-unity/Assets/spine-unity/Editor/GUI/icon-constraintNib.png
  43. 47 0
      spine-unity/Assets/spine-unity/Editor/GUI/icon-constraintNib.png.meta
  44. 1 0
      spine-unity/Assets/spine-unity/Editor/GUI/icon-event.png.meta
  45. 二进制
      spine-unity/Assets/spine-unity/Editor/GUI/icon-hingeChain.png
  46. 47 0
      spine-unity/Assets/spine-unity/Editor/GUI/icon-hingeChain.png.meta
  47. 二进制
      spine-unity/Assets/spine-unity/Editor/GUI/icon-image.png
  48. 47 0
      spine-unity/Assets/spine-unity/Editor/GUI/icon-image.png.meta
  49. 二进制
      spine-unity/Assets/spine-unity/Editor/GUI/icon-mesh.png
  50. 47 0
      spine-unity/Assets/spine-unity/Editor/GUI/icon-mesh.png.meta
  51. 二进制
      spine-unity/Assets/spine-unity/Editor/GUI/icon-null.png
  52. 47 0
      spine-unity/Assets/spine-unity/Editor/GUI/icon-null.png.meta
  53. 二进制
      spine-unity/Assets/spine-unity/Editor/GUI/icon-poseBones.png
  54. 47 0
      spine-unity/Assets/spine-unity/Editor/GUI/icon-poseBones.png.meta
  55. 1 0
      spine-unity/Assets/spine-unity/Editor/GUI/icon-skeleton.png.meta
  56. 二进制
      spine-unity/Assets/spine-unity/Editor/GUI/icon-skeletonUtility.png
  57. 47 0
      spine-unity/Assets/spine-unity/Editor/GUI/icon-skeletonUtility.png.meta
  58. 二进制
      spine-unity/Assets/spine-unity/Editor/GUI/icon-skin.png
  59. 47 0
      spine-unity/Assets/spine-unity/Editor/GUI/icon-skin.png.meta
  60. 二进制
      spine-unity/Assets/spine-unity/Editor/GUI/icon-skinPlaceholder.png
  61. 47 0
      spine-unity/Assets/spine-unity/Editor/GUI/icon-skinPlaceholder.png.meta
  62. 1 0
      spine-unity/Assets/spine-unity/Editor/GUI/icon-skinsRoot.png.meta
  63. 二进制
      spine-unity/Assets/spine-unity/Editor/GUI/icon-slot.png
  64. 47 0
      spine-unity/Assets/spine-unity/Editor/GUI/icon-slot.png.meta
  65. 1 0
      spine-unity/Assets/spine-unity/Editor/GUI/icon-spine.png.meta
  66. 二进制
      spine-unity/Assets/spine-unity/Editor/GUI/icon-subMeshRenderer.png
  67. 47 0
      spine-unity/Assets/spine-unity/Editor/GUI/icon-subMeshRenderer.png.meta
  68. 二进制
      spine-unity/Assets/spine-unity/Editor/GUI/icon-warning.png
  69. 47 0
      spine-unity/Assets/spine-unity/Editor/GUI/icon-warning.png.meta
  70. 37 0
      spine-unity/Assets/spine-unity/Editor/SkeletonAnimationInspector.cs
  71. 25 12
      spine-unity/Assets/spine-unity/Editor/SkeletonDataAssetInspector.cs
  72. 97 17
      spine-unity/Assets/spine-unity/Editor/SpineEditorUtilities.cs
  73. 69 0
      spine-unity/Assets/spine-unity/Shaders/Bones.shader
  74. 5 0
      spine-unity/Assets/spine-unity/Shaders/Bones.shader.meta
  75. 二进制
      spine-unity/Assets/spine-unity/Shaders/HiddenPass.mat
  76. 4 0
      spine-unity/Assets/spine-unity/Shaders/HiddenPass.mat.meta
  77. 21 0
      spine-unity/Assets/spine-unity/Shaders/HiddenPass.shader
  78. 5 0
      spine-unity/Assets/spine-unity/Shaders/HiddenPass.shader.meta
  79. 16 2
      spine-unity/Assets/spine-unity/SkeletonAnimation.cs
  80. 36 1
      spine-unity/Assets/spine-unity/SkeletonExtensions.cs
  81. 8 3
      spine-unity/Assets/spine-unity/SkeletonRenderer.cs
  82. 5 0
      spine-unity/Assets/spine-unity/SkeletonUtility.meta
  83. 5 0
      spine-unity/Assets/spine-unity/SkeletonUtility/Editor.meta
  84. 287 0
      spine-unity/Assets/spine-unity/SkeletonUtility/Editor/SkeletonUtilityBoneInspector.cs
  85. 8 0
      spine-unity/Assets/spine-unity/SkeletonUtility/Editor/SkeletonUtilityBoneInspector.cs.meta
  86. 316 0
      spine-unity/Assets/spine-unity/SkeletonUtility/Editor/SkeletonUtilityInspector.cs
  87. 8 0
      spine-unity/Assets/spine-unity/SkeletonUtility/Editor/SkeletonUtilityInspector.cs.meta
  88. 335 0
      spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtility.cs
  89. 8 0
      spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtility.cs.meta
  90. 218 0
      spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtilityBone.cs
  91. 8 0
      spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtilityBone.cs.meta
  92. 22 0
      spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtilityConstraint.cs
  93. 8 0
      spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtilityConstraint.cs.meta
  94. 61 0
      spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtilityEyeConstraint.cs
  95. 8 0
      spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtilityEyeConstraint.cs.meta
  96. 58 0
      spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtilityGroundConstraint.cs
  97. 8 0
      spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtilityGroundConstraint.cs.meta
  98. 81 0
      spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtilityKinematicShadow.cs
  99. 8 0
      spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtilityKinematicShadow.cs.meta
  100. 102 0
      spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtilitySubmeshRenderer.cs

+ 5 - 1
spine-csharp/src/Bone.cs

@@ -29,14 +29,16 @@
  *****************************************************************************/
 
 using System;
+using System.Collections.Generic;
 
 namespace Spine {
-	public class Bone {
+	public class Bone{
 		static public bool yDown;
 
 		internal BoneData data;
 		internal Skeleton skeleton;
 		internal Bone parent;
+		internal List<Bone> children;
 		internal float x, y, rotation, rotationIK, scaleX, scaleY;
 		internal float m00, m01, m10, m11;
 		internal float worldX, worldY, worldRotation, worldScaleX, worldScaleY;
@@ -44,6 +46,7 @@ namespace Spine {
 		public BoneData Data { get { return data; } }
 		public Skeleton Skeleton { get { return skeleton; } }
 		public Bone Parent { get { return parent; } }
+		public List<Bone> Children { get { return children; } }
 		public float X { get { return x; } set { x = value; } }
 		public float Y { get { return y; } set { y = value; } }
 		/// <summary>The forward kinetics rotation.</summary>
@@ -70,6 +73,7 @@ namespace Spine {
 			this.data = data;
 			this.skeleton = skeleton;
 			this.parent = parent;
+			this.children = new List<Bone>();
 			SetToSetupPose();
 		}
 

+ 5 - 0
spine-csharp/src/Skeleton.cs

@@ -77,6 +77,11 @@ namespace Spine {
 				bones.Add(new Bone(boneData, this, parent));
 			}
 
+			foreach(Bone b in bones){
+				if(b.Parent != null)
+					b.Parent.children.Add(b);
+			}
+
 			slots = new List<Slot>(data.slots.Count);
 			drawOrder = new List<Slot>(data.slots.Count);
 			foreach (SlotData slotData in data.slots) {

二进制
spine-unity/Assets/Examples/Scenes/Eyes.unity


+ 4 - 0
spine-unity/Assets/Examples/Scenes/Eyes.unity.meta

@@ -0,0 +1,4 @@
+fileFormatVersion: 2
+guid: 51036c61bd222d9469b4e94cc89cceae
+DefaultImporter:
+  userData: 

二进制
spine-unity/Assets/Examples/Scenes/Raptor Animated Physics.unity


+ 4 - 0
spine-unity/Assets/Examples/Scenes/Raptor Animated Physics.unity.meta

@@ -0,0 +1,4 @@
+fileFormatVersion: 2
+guid: a0deee17ee93fb047a1b70bc302c4dd6
+DefaultImporter:
+  userData: 

二进制
spine-unity/Assets/Examples/Scenes/Raptor GroundConstraint.unity


+ 4 - 0
spine-unity/Assets/Examples/Scenes/Raptor GroundConstraint.unity.meta

@@ -0,0 +1,4 @@
+fileFormatVersion: 2
+guid: c7f661c0d47f631438d4608f740c4922
+DefaultImporter:
+  userData: 

二进制
spine-unity/Assets/Examples/Scenes/Spineboy.unity


+ 2 - 2
spine-unity/Assets/Examples/Scripts/Goblins.cs

@@ -40,11 +40,11 @@ public class Goblins : MonoBehaviour {
 	public void Start () {
 		skeletonAnimation = GetComponent<SkeletonAnimation>();
 		headBone = skeletonAnimation.skeleton.FindBone("head");
-		skeletonAnimation.UpdateBones += UpdateBones;
+		skeletonAnimation.UpdateLocal += UpdateLocal;
 	}
 
 	// This is called after the animation is applied to the skeleton and can be used to adjust the bones dynamically.
-	public void UpdateBones (SkeletonAnimation skeletonAnimation) {
+	public void UpdateLocal (SkeletonAnimation skeletonAnimation) {
 		headBone.Rotation += 15;
 	}
 	

+ 5 - 0
spine-unity/Assets/Examples/Spine/Eyes.meta

@@ -0,0 +1,5 @@
+fileFormatVersion: 2
+guid: d85018bfbaa1a484bb36432eb063bdb8
+folderAsset: yes
+DefaultImporter:
+  userData: 

+ 34 - 0
spine-unity/Assets/Examples/Spine/Eyes/eyes.atlas.txt

@@ -0,0 +1,34 @@
+
+eyes.png
+size: 1020,474
+format: RGBA8888
+filter: Linear,Linear
+repeat: none
+EyeLines
+  rotate: false
+  xy: 2, 152
+  size: 868, 322
+  orig: 868, 322
+  offset: 0, 0
+  index: -1
+EyeWhite
+  rotate: false
+  xy: 2, 2
+  size: 700, 148
+  orig: 700, 148
+  offset: 0, 0
+  index: -1
+L_Eye
+  rotate: false
+  xy: 872, 326
+  size: 148, 148
+  orig: 148, 148
+  offset: 0, 0
+  index: -1
+R_Eye
+  rotate: false
+  xy: 872, 326
+  size: 148, 148
+  orig: 148, 148
+  offset: 0, 0
+  index: -1

+ 4 - 0
spine-unity/Assets/Examples/Spine/Eyes/eyes.atlas.txt.meta

@@ -0,0 +1,4 @@
+fileFormatVersion: 2
+guid: d2899150110e7384badaef80d9f784ee
+TextScriptImporter:
+  userData: 

+ 1 - 0
spine-unity/Assets/Examples/Spine/Eyes/eyes.json

@@ -0,0 +1 @@
+{"skeleton":{"spine":"1.9.17","hash":"uXiFYVR/43EWnB5cZw4eKd9Yyx0","width":868,"height":322},"bones":[{"name":"root"},{"name":"L_Eye","parent":"root","x":-223.18,"y":2.99},{"name":"R_Eye","parent":"root","x":237.32,"y":6.4}],"slots":[{"name":"EyeWhite","bone":"root","attachment":"EyeWhite"},{"name":"R_Eye","bone":"R_Eye","attachment":"R_Eye"},{"name":"L_Eye","bone":"L_Eye","attachment":"L_Eye"},{"name":"EyeLines","bone":"root","attachment":"EyeLines"}],"skins":{"default":{"EyeLines":{"EyeLines":{"y":28,"width":868,"height":322}},"EyeWhite":{"EyeWhite":{"x":3,"y":2,"width":700,"height":148}},"L_Eye":{"L_Eye":{"x":-0.82,"y":2,"width":148,"height":148}},"R_Eye":{"R_Eye":{"x":0.67,"y":-1.4,"width":148,"height":148}}}}}

+ 4 - 0
spine-unity/Assets/Examples/Spine/Eyes/eyes.json.meta

@@ -0,0 +1,4 @@
+fileFormatVersion: 2
+guid: deb987fe02427834d9959416f01385fe
+TextScriptImporter:
+  userData: 

二进制
spine-unity/Assets/Examples/Spine/Eyes/eyes.png


+ 47 - 0
spine-unity/Assets/Examples/Spine/Eyes/eyes.png.meta

@@ -0,0 +1,47 @@
+fileFormatVersion: 2
+guid: 49441e5a1682e564694545bd9b509785
+TextureImporter:
+  fileIDToRecycleName: {}
+  serializedVersion: 2
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 0
+    linearTexture: 0
+    correctGamma: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: .25
+    normalMapFilter: 0
+  isReadable: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 0
+  seamlessCubemap: 0
+  textureFormat: -3
+  maxTextureSize: 1024
+  textureSettings:
+    filterMode: -1
+    aniso: -1
+    mipBias: -1
+    wrapMode: -1
+  nPOTScale: 1
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: .5, y: .5}
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spritePixelsToUnits: 100
+  alphaIsTransparency: 0
+  textureType: -1
+  buildTargetSettings: []
+  spriteSheet:
+    sprites: []
+  spritePackingTag: 
+  userData: 

二进制
spine-unity/Assets/Examples/Spine/Eyes/eyes_Atlas.asset


+ 4 - 0
spine-unity/Assets/Examples/Spine/Eyes/eyes_Atlas.asset.meta

@@ -0,0 +1,4 @@
+fileFormatVersion: 2
+guid: dcae7523c93690f4c80520308eced1e2
+NativeFormatImporter:
+  userData: 

二进制
spine-unity/Assets/Examples/Spine/Eyes/eyes_Material.mat


+ 4 - 0
spine-unity/Assets/Examples/Spine/Eyes/eyes_Material.mat.meta

@@ -0,0 +1,4 @@
+fileFormatVersion: 2
+guid: 4f9d106a1e4d45b468b980311947a225
+NativeFormatImporter:
+  userData: 

二进制
spine-unity/Assets/Examples/Spine/Eyes/eyes_SkeletonData.asset


+ 4 - 0
spine-unity/Assets/Examples/Spine/Eyes/eyes_SkeletonData.asset.meta

@@ -0,0 +1,4 @@
+fileFormatVersion: 2
+guid: ef2f009a37ff7ff42bc2a2f407ca9483
+NativeFormatImporter:
+  userData: 

二进制
spine-unity/Assets/Examples/Spine/dragon.prefab


二进制
spine-unity/Assets/Examples/Spine/eyes.prefab


+ 4 - 0
spine-unity/Assets/Examples/Spine/eyes.prefab.meta

@@ -0,0 +1,4 @@
+fileFormatVersion: 2
+guid: 370927d98ff6b024c96ea2935adb4efb
+NativeFormatImporter:
+  userData: 

二进制
spine-unity/Assets/Examples/Spine/goblins-ffd.prefab


二进制
spine-unity/Assets/Examples/Spine/raptor.prefab


二进制
spine-unity/Assets/Examples/Spine/spineboy.prefab


+ 36 - 12
spine-unity/Assets/spine-unity/BoneComponent.cs → spine-unity/Assets/spine-unity/BoneFollower.cs

@@ -36,8 +36,9 @@ using Spine;
 
 /// <summary>Sets a GameObject's transform to match a bone on a Spine skeleton.</summary>
 [ExecuteInEditMode]
-[AddComponentMenu("Spine/BoneComponent")]
-public class BoneComponent : MonoBehaviour {
+[AddComponentMenu("Spine/BoneFollower")]
+public class BoneFollower : MonoBehaviour {
+
 	[System.NonSerialized]
 	public bool valid;
 
@@ -55,31 +56,50 @@ public class BoneComponent : MonoBehaviour {
 		}
 	}
 
-	// TODO: Make the rotation behavior more customizable
-	// public bool followTransformRotation = false;
-
-	// TODO: Make transform follow bone scale? too specific? This is really useful for shared shadow assets.
-	//public bool followBoneScale = false;
 
 	/// <summary>If a bone isn't set, boneName is used to find the bone.</summary>
 	public String boneName;
 
+	public bool resetOnAwake = true;
+
 	protected Transform cachedTransform;
 	protected Transform skeletonTransform;
-	
+
+	public void HandleResetRenderer(SkeletonRenderer skeletonRenderer){
+		Reset();
+	}
+
 	public void Reset () {
 		bone = null;
 		cachedTransform = transform;
 		valid = skeletonRenderer != null && skeletonRenderer.valid;
 		if (!valid) return;
 		skeletonTransform = skeletonRenderer.transform;
+
+		skeletonRenderer.OnReset -= HandleResetRenderer;
+		skeletonRenderer.OnReset += HandleResetRenderer;
+
+		if(Application.isEditor)
+			DoUpdate();
+	}
+
+	void OnDestroy(){
+		//cleanup
+		if(skeletonRenderer != null)
+			skeletonRenderer.OnReset -= HandleResetRenderer;
 	}
 
 	public void Awake () {
-		Reset();
+		if(resetOnAwake)
+			Reset();
+	}
+
+	void LateUpdate(){
+		DoUpdate();
 	}
 
-	public void LateUpdate () {
+
+	public void DoUpdate () {
 		if (!valid) {
 			Reset();
 			return;
@@ -92,6 +112,9 @@ public class BoneComponent : MonoBehaviour {
 				Debug.LogError("Bone not found: " + boneName, this);
 				return;
 			}
+			else{
+
+			}
 		}
 
 		Spine.Skeleton skeleton = skeletonRenderer.skeleton;
@@ -113,9 +136,10 @@ public class BoneComponent : MonoBehaviour {
 
 			if(followBoneRotation) {
 				Vector3 rotation = skeletonTransform.rotation.eulerAngles;
-				cachedTransform.rotation = Quaternion.Euler(rotation.x, rotation.y, 
-				                                            skeletonTransform.rotation.eulerAngles.z + (bone.worldRotation * flipRotation) );
+
+				cachedTransform.rotation = Quaternion.Euler(rotation.x, rotation.y, skeletonTransform.rotation.eulerAngles.z + (bone.worldRotation * flipRotation) );
 			}
 		}
+
 	}
 }

+ 1 - 1
spine-unity/Assets/spine-unity/BoneComponent.cs.meta → spine-unity/Assets/spine-unity/BoneFollower.cs.meta

@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 487b42efe96d8cc408a757541ea3f169
+guid: a1fd8daaed7b64148a34acb96ba14ce1
 MonoImporter:
   serializedVersion: 2
   defaultReferences: []

+ 34 - 6
spine-unity/Assets/spine-unity/Editor/BoneComponentInspector.cs → spine-unity/Assets/spine-unity/Editor/BoneFollowerInspector.cs

@@ -32,25 +32,51 @@ using System;
 using UnityEditor;
 using UnityEngine;
 
-[CustomEditor(typeof(BoneComponent))]
-public class BoneComponentInspector : Editor {
+[CustomEditor(typeof(BoneFollower))]
+public class BoneFollowerInspector : Editor {
 	private SerializedProperty boneName, skeletonRenderer, followZPosition, followBoneRotation;
-
+	BoneFollower component;
 	void OnEnable () {
 		skeletonRenderer = serializedObject.FindProperty("skeletonRenderer");
 		boneName = serializedObject.FindProperty("boneName");
 		followBoneRotation = serializedObject.FindProperty("followBoneRotation");
 		followZPosition = serializedObject.FindProperty("followZPosition");
+		component = (BoneFollower)target;
+		ForceReload();
+	}
+
+	void FindRenderer(){
+		if(skeletonRenderer.objectReferenceValue == null){
+			SkeletonRenderer parentRenderer = component.GetComponentInParent<SkeletonRenderer>();
+			if(parentRenderer != null){
+				skeletonRenderer.objectReferenceValue = (UnityEngine.Object)parentRenderer;
+			}
+		}
+	}
+
+	void ForceReload(){
+		if(component.skeletonRenderer != null){
+			if(component.skeletonRenderer.valid == false)
+				component.skeletonRenderer.Reset();
+		}
 	}
 
 	override public void OnInspectorGUI () {
 		serializedObject.Update();
-		BoneComponent component = (BoneComponent)target;
+
+		FindRenderer();
 
 		EditorGUILayout.PropertyField(skeletonRenderer);
 
 		if (component.valid) {
-			String[] bones = new String[component.skeletonRenderer.skeleton.Data.Bones.Count + 1];
+			String[] bones = new String[1];
+			try{
+				bones = new String[component.skeletonRenderer.skeleton.Data.Bones.Count + 1];
+			}
+			catch{
+
+			}
+
 			bones[0] = "<None>";
 			for (int i = 0; i < bones.Length - 1; i++)
 				bones[i + 1] = component.skeletonRenderer.skeleton.Data.Bones[i].Name;
@@ -64,10 +90,12 @@ public class BoneComponentInspector : Editor {
 			EditorGUILayout.EndHorizontal();
 
 			boneName.stringValue = boneIndex == 0 ? null : bones[boneIndex];
-
 			EditorGUILayout.PropertyField(followBoneRotation);
 			EditorGUILayout.PropertyField(followZPosition);
 		}
+		else{
+			GUILayout.Label("INVALID");
+		}
 
 		if (serializedObject.ApplyModifiedProperties() ||
 	    	(Event.current.type == EventType.ValidateCommand && Event.current.commandName == "UndoRedoPerformed")

+ 1 - 1
spine-unity/Assets/spine-unity/Editor/BoneComponentInspector.cs.meta → spine-unity/Assets/spine-unity/Editor/BoneFollowerInspector.cs.meta

@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: dca21b77745bb4245a4e2e012c282ce6
+guid: c71ca35fd6241cb49a0b0756a664fcf7
 MonoImporter:
   serializedVersion: 2
   defaultReferences: []

+ 1 - 0
spine-unity/Assets/spine-unity/Editor/GUI/icon-animation.png.meta

@@ -1,6 +1,7 @@
 fileFormatVersion: 2
 guid: 52b12ec801461494185a4d3dc66f3d1d
 TextureImporter:
+  fileIDToRecycleName: {}
   serializedVersion: 2
   mipmaps:
     mipMapMode: 0

+ 1 - 0
spine-unity/Assets/spine-unity/Editor/GUI/icon-animationRoot.png.meta

@@ -1,6 +1,7 @@
 fileFormatVersion: 2
 guid: 3d1be4ea889f3a14b864352fe49a1bde
 TextureImporter:
+  fileIDToRecycleName: {}
   serializedVersion: 2
   mipmaps:
     mipMapMode: 0

二进制
spine-unity/Assets/spine-unity/Editor/GUI/icon-bone.png


+ 47 - 0
spine-unity/Assets/spine-unity/Editor/GUI/icon-bone.png.meta

@@ -0,0 +1,47 @@
+fileFormatVersion: 2
+guid: 8322793223a533a4ca8be6f430256dfc
+TextureImporter:
+  fileIDToRecycleName: {}
+  serializedVersion: 2
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 0
+    linearTexture: 1
+    correctGamma: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: .25
+    normalMapFilter: 0
+  isReadable: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 0
+  seamlessCubemap: 0
+  textureFormat: -3
+  maxTextureSize: 1024
+  textureSettings:
+    filterMode: -1
+    aniso: 1
+    mipBias: -1
+    wrapMode: 1
+  nPOTScale: 0
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: .5, y: .5}
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spritePixelsToUnits: 100
+  alphaIsTransparency: 1
+  textureType: 2
+  buildTargetSettings: []
+  spriteSheet:
+    sprites: []
+  spritePackingTag: 
+  userData: 

二进制
spine-unity/Assets/spine-unity/Editor/GUI/icon-boneNib.png


+ 47 - 0
spine-unity/Assets/spine-unity/Editor/GUI/icon-boneNib.png.meta

@@ -0,0 +1,47 @@
+fileFormatVersion: 2
+guid: 97a43f11e00735147a9dc3dff6d68191
+TextureImporter:
+  fileIDToRecycleName: {}
+  serializedVersion: 2
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 0
+    linearTexture: 1
+    correctGamma: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: .25
+    normalMapFilter: 0
+  isReadable: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 0
+  seamlessCubemap: 0
+  textureFormat: -3
+  maxTextureSize: 1024
+  textureSettings:
+    filterMode: -1
+    aniso: 1
+    mipBias: -1
+    wrapMode: 1
+  nPOTScale: 0
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: .5, y: .5}
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spritePixelsToUnits: 100
+  alphaIsTransparency: 1
+  textureType: 2
+  buildTargetSettings: []
+  spriteSheet:
+    sprites: []
+  spritePackingTag: 
+  userData: 

二进制
spine-unity/Assets/spine-unity/Editor/GUI/icon-boundingBox.png


+ 47 - 0
spine-unity/Assets/spine-unity/Editor/GUI/icon-boundingBox.png.meta

@@ -0,0 +1,47 @@
+fileFormatVersion: 2
+guid: 955aed20030d0504b8a9c6934a5cb47a
+TextureImporter:
+  fileIDToRecycleName: {}
+  serializedVersion: 2
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 0
+    linearTexture: 1
+    correctGamma: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: .25
+    normalMapFilter: 0
+  isReadable: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 0
+  seamlessCubemap: 0
+  textureFormat: -1
+  maxTextureSize: 1024
+  textureSettings:
+    filterMode: -1
+    aniso: 1
+    mipBias: -1
+    wrapMode: 1
+  nPOTScale: 0
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: .5, y: .5}
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spritePixelsToUnits: 100
+  alphaIsTransparency: 1
+  textureType: 2
+  buildTargetSettings: []
+  spriteSheet:
+    sprites: []
+  spritePackingTag: 
+  userData: 

二进制
spine-unity/Assets/spine-unity/Editor/GUI/icon-constraintNib.png


+ 47 - 0
spine-unity/Assets/spine-unity/Editor/GUI/icon-constraintNib.png.meta

@@ -0,0 +1,47 @@
+fileFormatVersion: 2
+guid: de1a4f5ad4bdf1a4ea072c4d59ba87d8
+TextureImporter:
+  fileIDToRecycleName: {}
+  serializedVersion: 2
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 0
+    linearTexture: 1
+    correctGamma: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: .25
+    normalMapFilter: 0
+  isReadable: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 0
+  seamlessCubemap: 0
+  textureFormat: -3
+  maxTextureSize: 1024
+  textureSettings:
+    filterMode: -1
+    aniso: 1
+    mipBias: -1
+    wrapMode: 1
+  nPOTScale: 0
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: .5, y: .5}
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spritePixelsToUnits: 100
+  alphaIsTransparency: 1
+  textureType: 2
+  buildTargetSettings: []
+  spriteSheet:
+    sprites: []
+  spritePackingTag: 
+  userData: 

+ 1 - 0
spine-unity/Assets/spine-unity/Editor/GUI/icon-event.png.meta

@@ -1,6 +1,7 @@
 fileFormatVersion: 2
 guid: d226a80acc775714aa78b85e16a00e9b
 TextureImporter:
+  fileIDToRecycleName: {}
   serializedVersion: 2
   mipmaps:
     mipMapMode: 0

二进制
spine-unity/Assets/spine-unity/Editor/GUI/icon-hingeChain.png


+ 47 - 0
spine-unity/Assets/spine-unity/Editor/GUI/icon-hingeChain.png.meta

@@ -0,0 +1,47 @@
+fileFormatVersion: 2
+guid: 2c2c6d283dcf3654baf40001c982891c
+TextureImporter:
+  fileIDToRecycleName: {}
+  serializedVersion: 2
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 1
+    linearTexture: 0
+    correctGamma: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: .25
+    normalMapFilter: 0
+  isReadable: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 0
+  seamlessCubemap: 0
+  textureFormat: -1
+  maxTextureSize: 1024
+  textureSettings:
+    filterMode: -1
+    aniso: -1
+    mipBias: -1
+    wrapMode: -1
+  nPOTScale: 1
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: .5, y: .5}
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spritePixelsToUnits: 100
+  alphaIsTransparency: 0
+  textureType: -1
+  buildTargetSettings: []
+  spriteSheet:
+    sprites: []
+  spritePackingTag: 
+  userData: 

二进制
spine-unity/Assets/spine-unity/Editor/GUI/icon-image.png


+ 47 - 0
spine-unity/Assets/spine-unity/Editor/GUI/icon-image.png.meta

@@ -0,0 +1,47 @@
+fileFormatVersion: 2
+guid: 2b3a6f35bbaa8414eb51a344743ee641
+TextureImporter:
+  fileIDToRecycleName: {}
+  serializedVersion: 2
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 0
+    linearTexture: 1
+    correctGamma: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: .25
+    normalMapFilter: 0
+  isReadable: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 0
+  seamlessCubemap: 0
+  textureFormat: -3
+  maxTextureSize: 1024
+  textureSettings:
+    filterMode: -1
+    aniso: 1
+    mipBias: -1
+    wrapMode: 1
+  nPOTScale: 0
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: .5, y: .5}
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spritePixelsToUnits: 100
+  alphaIsTransparency: 1
+  textureType: 2
+  buildTargetSettings: []
+  spriteSheet:
+    sprites: []
+  spritePackingTag: 
+  userData: 

二进制
spine-unity/Assets/spine-unity/Editor/GUI/icon-mesh.png


+ 47 - 0
spine-unity/Assets/spine-unity/Editor/GUI/icon-mesh.png.meta

@@ -0,0 +1,47 @@
+fileFormatVersion: 2
+guid: a309a2e14638a204091b915126910f45
+TextureImporter:
+  fileIDToRecycleName: {}
+  serializedVersion: 2
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 0
+    linearTexture: 1
+    correctGamma: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: .25
+    normalMapFilter: 0
+  isReadable: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 0
+  seamlessCubemap: 0
+  textureFormat: -1
+  maxTextureSize: 1024
+  textureSettings:
+    filterMode: -1
+    aniso: 1
+    mipBias: -1
+    wrapMode: 1
+  nPOTScale: 0
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: .5, y: .5}
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spritePixelsToUnits: 100
+  alphaIsTransparency: 1
+  textureType: 2
+  buildTargetSettings: []
+  spriteSheet:
+    sprites: []
+  spritePackingTag: 
+  userData: 

二进制
spine-unity/Assets/spine-unity/Editor/GUI/icon-null.png


+ 47 - 0
spine-unity/Assets/spine-unity/Editor/GUI/icon-null.png.meta

@@ -0,0 +1,47 @@
+fileFormatVersion: 2
+guid: d1de1604dfe4cb64c9d31246a8e43c78
+TextureImporter:
+  fileIDToRecycleName: {}
+  serializedVersion: 2
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 0
+    linearTexture: 1
+    correctGamma: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: .25
+    normalMapFilter: 0
+  isReadable: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 0
+  seamlessCubemap: 0
+  textureFormat: -3
+  maxTextureSize: 1024
+  textureSettings:
+    filterMode: -1
+    aniso: 1
+    mipBias: -1
+    wrapMode: 1
+  nPOTScale: 0
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: .5, y: .5}
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spritePixelsToUnits: 100
+  alphaIsTransparency: 1
+  textureType: 2
+  buildTargetSettings: []
+  spriteSheet:
+    sprites: []
+  spritePackingTag: 
+  userData: 

二进制
spine-unity/Assets/spine-unity/Editor/GUI/icon-poseBones.png


+ 47 - 0
spine-unity/Assets/spine-unity/Editor/GUI/icon-poseBones.png.meta

@@ -0,0 +1,47 @@
+fileFormatVersion: 2
+guid: da6f6d414e43aac46a57cc5a87208db4
+TextureImporter:
+  fileIDToRecycleName: {}
+  serializedVersion: 2
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 0
+    linearTexture: 1
+    correctGamma: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: .25
+    normalMapFilter: 0
+  isReadable: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 0
+  seamlessCubemap: 0
+  textureFormat: -3
+  maxTextureSize: 1024
+  textureSettings:
+    filterMode: -1
+    aniso: 1
+    mipBias: -1
+    wrapMode: 1
+  nPOTScale: 0
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: .5, y: .5}
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spritePixelsToUnits: 100
+  alphaIsTransparency: 1
+  textureType: 2
+  buildTargetSettings: []
+  spriteSheet:
+    sprites: []
+  spritePackingTag: 
+  userData: 

+ 1 - 0
spine-unity/Assets/spine-unity/Editor/GUI/icon-skeleton.png.meta

@@ -1,6 +1,7 @@
 fileFormatVersion: 2
 guid: f2216037084d99d4481810cb521ed96f
 TextureImporter:
+  fileIDToRecycleName: {}
   serializedVersion: 2
   mipmaps:
     mipMapMode: 0

二进制
spine-unity/Assets/spine-unity/Editor/GUI/icon-skeletonUtility.png


+ 47 - 0
spine-unity/Assets/spine-unity/Editor/GUI/icon-skeletonUtility.png.meta

@@ -0,0 +1,47 @@
+fileFormatVersion: 2
+guid: 5bb0631368b462047869d8788673cb48
+TextureImporter:
+  fileIDToRecycleName: {}
+  serializedVersion: 2
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 0
+    linearTexture: 1
+    correctGamma: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: .25
+    normalMapFilter: 0
+  isReadable: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 0
+  seamlessCubemap: 0
+  textureFormat: -3
+  maxTextureSize: 1024
+  textureSettings:
+    filterMode: -1
+    aniso: 1
+    mipBias: -1
+    wrapMode: 1
+  nPOTScale: 0
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: .5, y: .5}
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spritePixelsToUnits: 100
+  alphaIsTransparency: 1
+  textureType: 2
+  buildTargetSettings: []
+  spriteSheet:
+    sprites: []
+  spritePackingTag: 
+  userData: 

二进制
spine-unity/Assets/spine-unity/Editor/GUI/icon-skin.png


+ 47 - 0
spine-unity/Assets/spine-unity/Editor/GUI/icon-skin.png.meta

@@ -0,0 +1,47 @@
+fileFormatVersion: 2
+guid: bfd9f3d2607e9e44c97384d7575a17dc
+TextureImporter:
+  fileIDToRecycleName: {}
+  serializedVersion: 2
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 0
+    linearTexture: 1
+    correctGamma: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: .25
+    normalMapFilter: 0
+  isReadable: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 0
+  seamlessCubemap: 0
+  textureFormat: -3
+  maxTextureSize: 1024
+  textureSettings:
+    filterMode: -1
+    aniso: 1
+    mipBias: -1
+    wrapMode: 1
+  nPOTScale: 0
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: .5, y: .5}
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spritePixelsToUnits: 100
+  alphaIsTransparency: 1
+  textureType: 2
+  buildTargetSettings: []
+  spriteSheet:
+    sprites: []
+  spritePackingTag: 
+  userData: 

二进制
spine-unity/Assets/spine-unity/Editor/GUI/icon-skinPlaceholder.png


+ 47 - 0
spine-unity/Assets/spine-unity/Editor/GUI/icon-skinPlaceholder.png.meta

@@ -0,0 +1,47 @@
+fileFormatVersion: 2
+guid: 04c82a4acf7b5244e947f2709ec3a6cf
+TextureImporter:
+  fileIDToRecycleName: {}
+  serializedVersion: 2
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 0
+    linearTexture: 1
+    correctGamma: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: .25
+    normalMapFilter: 0
+  isReadable: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 0
+  seamlessCubemap: 0
+  textureFormat: -3
+  maxTextureSize: 1024
+  textureSettings:
+    filterMode: -1
+    aniso: 1
+    mipBias: -1
+    wrapMode: 1
+  nPOTScale: 0
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: .5, y: .5}
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spritePixelsToUnits: 100
+  alphaIsTransparency: 1
+  textureType: 2
+  buildTargetSettings: []
+  spriteSheet:
+    sprites: []
+  spritePackingTag: 
+  userData: 

+ 1 - 0
spine-unity/Assets/spine-unity/Editor/GUI/icon-skinsRoot.png.meta

@@ -1,6 +1,7 @@
 fileFormatVersion: 2
 guid: 8bd14c7643597a74ba2edc10a5e4c4ed
 TextureImporter:
+  fileIDToRecycleName: {}
   serializedVersion: 2
   mipmaps:
     mipMapMode: 0

二进制
spine-unity/Assets/spine-unity/Editor/GUI/icon-slot.png


+ 47 - 0
spine-unity/Assets/spine-unity/Editor/GUI/icon-slot.png.meta

@@ -0,0 +1,47 @@
+fileFormatVersion: 2
+guid: 0338faf3e7d93e2478fcbc022d13e081
+TextureImporter:
+  fileIDToRecycleName: {}
+  serializedVersion: 2
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 0
+    linearTexture: 1
+    correctGamma: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: .25
+    normalMapFilter: 0
+  isReadable: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 0
+  seamlessCubemap: 0
+  textureFormat: -3
+  maxTextureSize: 1024
+  textureSettings:
+    filterMode: -1
+    aniso: 1
+    mipBias: -1
+    wrapMode: 1
+  nPOTScale: 0
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: .5, y: .5}
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spritePixelsToUnits: 100
+  alphaIsTransparency: 1
+  textureType: 2
+  buildTargetSettings: []
+  spriteSheet:
+    sprites: []
+  spritePackingTag: 
+  userData: 

+ 1 - 0
spine-unity/Assets/spine-unity/Editor/GUI/icon-spine.png.meta

@@ -1,6 +1,7 @@
 fileFormatVersion: 2
 guid: 4e7c964fa5e07024c8bf1debecc3b7c8
 TextureImporter:
+  fileIDToRecycleName: {}
   serializedVersion: 2
   mipmaps:
     mipMapMode: 0

二进制
spine-unity/Assets/spine-unity/Editor/GUI/icon-subMeshRenderer.png


+ 47 - 0
spine-unity/Assets/spine-unity/Editor/GUI/icon-subMeshRenderer.png.meta

@@ -0,0 +1,47 @@
+fileFormatVersion: 2
+guid: f31c0c0d608e8ba4f9a1afb032092287
+TextureImporter:
+  fileIDToRecycleName: {}
+  serializedVersion: 2
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 1
+    linearTexture: 0
+    correctGamma: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: .25
+    normalMapFilter: 0
+  isReadable: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 0
+  seamlessCubemap: 0
+  textureFormat: -1
+  maxTextureSize: 1024
+  textureSettings:
+    filterMode: -1
+    aniso: -1
+    mipBias: -1
+    wrapMode: -1
+  nPOTScale: 1
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: .5, y: .5}
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spritePixelsToUnits: 100
+  alphaIsTransparency: 0
+  textureType: -1
+  buildTargetSettings: []
+  spriteSheet:
+    sprites: []
+  spritePackingTag: 
+  userData: 

二进制
spine-unity/Assets/spine-unity/Editor/GUI/icon-warning.png


+ 47 - 0
spine-unity/Assets/spine-unity/Editor/GUI/icon-warning.png.meta

@@ -0,0 +1,47 @@
+fileFormatVersion: 2
+guid: 754d724c1bd750048852e8cf3d4a05ee
+TextureImporter:
+  fileIDToRecycleName: {}
+  serializedVersion: 2
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 0
+    linearTexture: 1
+    correctGamma: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: .25
+    normalMapFilter: 0
+  isReadable: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 0
+  seamlessCubemap: 0
+  textureFormat: -3
+  maxTextureSize: 1024
+  textureSettings:
+    filterMode: -1
+    aniso: 1
+    mipBias: -1
+    wrapMode: 1
+  nPOTScale: 0
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: .5, y: .5}
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spritePixelsToUnits: 100
+  alphaIsTransparency: 1
+  textureType: 2
+  buildTargetSettings: []
+  spriteSheet:
+    sprites: []
+  spritePackingTag: 
+  userData: 

+ 37 - 0
spine-unity/Assets/spine-unity/Editor/SkeletonAnimationInspector.cs

@@ -31,16 +31,23 @@
 using System;
 using UnityEditor;
 using UnityEngine;
+using Spine;
 
 [CustomEditor(typeof(SkeletonAnimation))]
 public class SkeletonAnimationInspector : SkeletonRendererInspector {
 	protected SerializedProperty animationName, loop, timeScale;
+	protected bool isPrefab;
 
 	protected override void OnEnable () {
 		base.OnEnable();
 		animationName = serializedObject.FindProperty("_animationName");
 		loop = serializedObject.FindProperty("loop");
 		timeScale = serializedObject.FindProperty("timeScale");
+
+		if(PrefabUtility.GetPrefabType(this.target) == PrefabType.Prefab)
+			isPrefab = true;
+
+
 	}
 
 	protected override void gui () {
@@ -49,6 +56,25 @@ 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(loop.boolValue != currentState.Loop){
+					loop.boolValue = currentState.Loop;
+				}
+
+				if(timeScale.floatValue != currentState.TimeScale){
+					timeScale.floatValue = currentState.TimeScale;
+				}
+			}
+		}
+
+
 		// Animation name.
 		{
 			String[] animations = new String[component.skeleton.Data.Animations.Count + 1];
@@ -69,10 +95,21 @@ public class SkeletonAnimationInspector : SkeletonRendererInspector {
 			String selectedAnimationName = animationIndex == 0 ? null : animations[animationIndex];
 			component.AnimationName = selectedAnimationName;
 			animationName.stringValue = selectedAnimationName;
+
+
 		}
 
 		EditorGUILayout.PropertyField(loop);
 		EditorGUILayout.PropertyField(timeScale);
 		component.timeScale = Math.Max(component.timeScale, 0);
+		EditorGUILayout.Space();
+
+		if(!isPrefab){
+			if(component.GetComponent<SkeletonUtility>() == null){
+				if(GUILayout.Button(new GUIContent("Add Skeleton Utility", SpineEditorUtilities.Icons.skeletonUtility), GUILayout.Height(30))){
+					component.gameObject.AddComponent<SkeletonUtility>();
+				}
+			}
+		}
 	}
 }

+ 25 - 12
spine-unity/Assets/spine-unity/Editor/SkeletonDataAssetInspector.cs

@@ -28,6 +28,11 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *****************************************************************************/
 
+/*****************************************************************************
+ * Automatic import and advanced preview added by Mitch Thompson
+ * Full irrevocable rights and permissions granted to Esoteric Software
+*****************************************************************************/
+
 using System;
 using System.Collections.Generic;
 using UnityEditor;
@@ -53,18 +58,26 @@ public class SkeletonDataAssetInspector : Editor {
 	private string m_skeletonDataAssetGUID;
 	
 	void OnEnable () {
-		atlasAsset = serializedObject.FindProperty("atlasAsset");
-		skeletonJSON = serializedObject.FindProperty("skeletonJSON");
-		scale = serializedObject.FindProperty("scale");
-		fromAnimation = serializedObject.FindProperty("fromAnimation");
-		toAnimation = serializedObject.FindProperty("toAnimation");
-		duration = serializedObject.FindProperty("duration");
-		defaultMix = serializedObject.FindProperty("defaultMix");
-		
-		m_skeletonDataAsset = (SkeletonDataAsset)target;
-		m_skeletonDataAssetGUID = AssetDatabase.AssetPathToGUID( AssetDatabase.GetAssetPath(m_skeletonDataAsset) );
-		
-		EditorApplication.update += Update;
+		try{
+
+			atlasAsset = serializedObject.FindProperty("atlasAsset");
+			skeletonJSON = serializedObject.FindProperty("skeletonJSON");
+			scale = serializedObject.FindProperty("scale");
+			fromAnimation = serializedObject.FindProperty("fromAnimation");
+			toAnimation = serializedObject.FindProperty("toAnimation");
+			duration = serializedObject.FindProperty("duration");
+			defaultMix = serializedObject.FindProperty("defaultMix");
+			
+			m_skeletonDataAsset = (SkeletonDataAsset)target;
+			m_skeletonDataAssetGUID = AssetDatabase.AssetPathToGUID( AssetDatabase.GetAssetPath(m_skeletonDataAsset) );
+			
+			EditorApplication.update += Update;
+
+		}
+		catch{
+
+
+		}
 	}
 	
 	void OnDestroy(){

+ 97 - 17
spine-unity/Assets/spine-unity/Editor/SpineEditorUtilities.cs

@@ -1,4 +1,38 @@
 #pragma warning disable 0219
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2.1
+ * 
+ * Copyright (c) 2013, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to install, execute and perform the Spine Runtimes
+ * Software (the "Software") solely for internal use. Without the written
+ * permission of Esoteric Software (typically granted by licensing Spine), you
+ * may not (a) modify, translate, adapt or otherwise create derivative works,
+ * improvements of the Software or develop new applications using the Software
+ * 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 SOFTARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) 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.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Spine Editor Utilities created by Mitch Thompson
+ * Full irrevocable rights and permissions granted to Esoteric Software
+*****************************************************************************/
 
 using UnityEngine;
 using UnityEditor;
@@ -15,6 +49,7 @@ public class SpineEditorUtilities : AssetPostprocessor {
 		public static Texture2D skeleton;
 		public static Texture2D nullBone;
 		public static Texture2D bone;
+		public static Texture2D poseBones;
 		public static Texture2D boneNib;
 		public static Texture2D slot;
 		public static Texture2D skinPlaceholder;
@@ -27,6 +62,11 @@ public class SpineEditorUtilities : AssetPostprocessor {
 		public static Texture2D animationRoot;
 		public static Texture2D spine;
 		public static Texture2D _event;
+		public static Texture2D constraintNib;
+		public static Texture2D warning;
+		public static Texture2D skeletonUtility;
+		public static Texture2D hingeChain;
+		public static Texture2D subMeshRenderer;
 
 		public static Mesh boneMesh{
 			get{
@@ -61,6 +101,7 @@ public class SpineEditorUtilities : AssetPostprocessor {
 			skeleton = (Texture2D)AssetDatabase.LoadMainAssetAtPath( SpineEditorUtilities.editorGUIPath + "/icon-skeleton.png");
 			nullBone = (Texture2D)AssetDatabase.LoadMainAssetAtPath( SpineEditorUtilities.editorGUIPath + "/icon-null.png");
 			bone = (Texture2D)AssetDatabase.LoadMainAssetAtPath( SpineEditorUtilities.editorGUIPath + "/icon-bone.png");
+			poseBones = (Texture2D)AssetDatabase.LoadMainAssetAtPath( SpineEditorUtilities.editorGUIPath + "/icon-poseBones.png");
 			boneNib = (Texture2D)AssetDatabase.LoadMainAssetAtPath( SpineEditorUtilities.editorGUIPath + "/icon-boneNib.png");
 			slot = (Texture2D)AssetDatabase.LoadMainAssetAtPath( SpineEditorUtilities.editorGUIPath + "/icon-slot.png");
 			skinPlaceholder = (Texture2D)AssetDatabase.LoadMainAssetAtPath( SpineEditorUtilities.editorGUIPath + "/icon-skinPlaceholder.png");
@@ -73,6 +114,12 @@ public class SpineEditorUtilities : AssetPostprocessor {
 			animationRoot = (Texture2D)AssetDatabase.LoadMainAssetAtPath( SpineEditorUtilities.editorGUIPath + "/icon-animationRoot.png");
 			spine = (Texture2D)AssetDatabase.LoadMainAssetAtPath( SpineEditorUtilities.editorGUIPath + "/icon-spine.png");
 			_event = (Texture2D)AssetDatabase.LoadMainAssetAtPath( SpineEditorUtilities.editorGUIPath + "/icon-event.png");
+			constraintNib = (Texture2D)AssetDatabase.LoadMainAssetAtPath( SpineEditorUtilities.editorGUIPath + "/icon-constraintNib.png");
+			warning = (Texture2D)AssetDatabase.LoadMainAssetAtPath( SpineEditorUtilities.editorGUIPath + "/icon-warning.png");
+			skeletonUtility = (Texture2D)AssetDatabase.LoadMainAssetAtPath( SpineEditorUtilities.editorGUIPath + "/icon-skeletonUtility.png");
+			hingeChain = (Texture2D)AssetDatabase.LoadMainAssetAtPath( SpineEditorUtilities.editorGUIPath + "/icon-hingeChain.png");
+			subMeshRenderer = (Texture2D)AssetDatabase.LoadMainAssetAtPath( SpineEditorUtilities.editorGUIPath + "/icon-subMeshRenderer.png");
+
 		}
 
 	}
@@ -81,8 +128,9 @@ public class SpineEditorUtilities : AssetPostprocessor {
 
 	public static string editorPath = "";
 	public static string editorGUIPath = "";
-
-	static List<int> skeletonRendererInstanceIDs;
+	
+	static Dictionary<int, GameObject> skeletonRendererTable;
+	static Dictionary<int, SkeletonUtilityBone> skeletonUtilityBoneTable;
 
 	public static float defaultScale = 0.01f;
 	public static float defaultMix = 0.2f;
@@ -96,7 +144,8 @@ public class SpineEditorUtilities : AssetPostprocessor {
 
 		Icons.Initialize();
 
-		skeletonRendererInstanceIDs = new List<int>();
+		skeletonRendererTable = new Dictionary<int, GameObject>();
+		skeletonUtilityBoneTable = new Dictionary<int, SkeletonUtilityBone>();
 
 		EditorApplication.hierarchyWindowChanged += HierarchyWindowChanged;
 		EditorApplication.hierarchyWindowItemOnGUI += HierarchyWindowItemOnGUI;
@@ -105,22 +154,49 @@ public class SpineEditorUtilities : AssetPostprocessor {
 	}
 
 	static void HierarchyWindowChanged(){
-		skeletonRendererInstanceIDs.Clear();
+		skeletonRendererTable.Clear();
+		skeletonUtilityBoneTable.Clear();
 
 		SkeletonRenderer[] arr = Object.FindObjectsOfType<SkeletonRenderer>();
 
 		foreach(SkeletonRenderer r in arr)
-			skeletonRendererInstanceIDs.Add(r.gameObject.GetInstanceID());
+			skeletonRendererTable.Add( r.gameObject.GetInstanceID(), r.gameObject );
+
+		SkeletonUtilityBone[] boneArr = Object.FindObjectsOfType<SkeletonUtilityBone>();
+		foreach(SkeletonUtilityBone b in boneArr)
+			skeletonUtilityBoneTable.Add(b.gameObject.GetInstanceID(), b);
 	}
 
 	static void HierarchyWindowItemOnGUI(int instanceId, Rect selectionRect){
-		if(skeletonRendererInstanceIDs.Contains(instanceId)){
+		if(skeletonRendererTable.ContainsKey(instanceId)){
 			Rect r = new Rect (selectionRect); 
 			r.x = r.width - 15;
 			r.width = 15;
 
 			GUI.Label(r, Icons.spine);
 		}
+		else if(skeletonUtilityBoneTable.ContainsKey(instanceId)){
+			Rect r = new Rect (selectionRect); 
+			//r.x = r.width - 15;
+			r.x -= 26;
+
+			if(skeletonUtilityBoneTable[instanceId] != null){
+				if( skeletonUtilityBoneTable[instanceId].transform.childCount == 0 )
+					r.x += 15;
+				
+
+				r.width = 15;
+
+				if( skeletonUtilityBoneTable[instanceId].mode == SkeletonUtilityBone.Mode.Follow ){
+					GUI.Label(r, Icons.bone);
+				}
+				else{
+					GUI.Label(r, Icons.poseBones);
+				}
+			}
+
+		}
+
 	}
 	
 	[MenuItem("Assets/Spine/Ingest")]
@@ -335,20 +411,24 @@ public class SpineEditorUtilities : AssetPostprocessor {
 
 		string primaryName = Path.GetFileNameWithoutExtension(spineJson.name);
 		string assetPath = Path.GetDirectoryName( AssetDatabase.GetAssetPath(spineJson));
+		string filePath = assetPath + "/" + primaryName + "_SkeletonData.asset";
 
 		if(spineJson != null && atlasAsset != null){
 
-			SkeletonDataAsset skelDataAsset = SkeletonDataAsset.CreateInstance<SkeletonDataAsset>();
-			skelDataAsset.atlasAsset = atlasAsset;
-			skelDataAsset.skeletonJSON = spineJson;
-			skelDataAsset.fromAnimation = new string[0];
-			skelDataAsset.toAnimation = new string[0];
-			skelDataAsset.duration = new float[0];
-			skelDataAsset.defaultMix = defaultMix;
-			skelDataAsset.scale = defaultScale;
-
-			AssetDatabase.CreateAsset(skelDataAsset, assetPath + "/" + primaryName + "_SkeletonData.asset");
-			AssetDatabase.SaveAssets();
+			SkeletonDataAsset skelDataAsset = (SkeletonDataAsset)AssetDatabase.LoadAssetAtPath(filePath, typeof(SkeletonDataAsset));
+			if(skelDataAsset == null){
+				skelDataAsset = SkeletonDataAsset.CreateInstance<SkeletonDataAsset>();
+				skelDataAsset.atlasAsset = atlasAsset;
+				skelDataAsset.skeletonJSON = spineJson;
+				skelDataAsset.fromAnimation = new string[0];
+				skelDataAsset.toAnimation = new string[0];
+				skelDataAsset.duration = new float[0];
+				skelDataAsset.defaultMix = defaultMix;
+				skelDataAsset.scale = defaultScale;
+				
+				AssetDatabase.CreateAsset(skelDataAsset, filePath);
+				AssetDatabase.SaveAssets();
+			}
 
 			return skelDataAsset;
 		}

+ 69 - 0
spine-unity/Assets/spine-unity/Shaders/Bones.shader

@@ -0,0 +1,69 @@
+Shader "Spine/Bones" {
+Properties {
+	_Color ("Color", Color) = (0.5,0.5,0.5,0.5)
+	_MainTex ("Particle Texture", 2D) = "white" {}
+}
+
+Category {
+	Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }
+	Blend SrcAlpha OneMinusSrcAlpha
+	AlphaTest Greater .01
+	ColorMask RGB
+	
+	Lighting Off Cull Off ZTest Always ZWrite Off Fog { Mode Off }
+
+	SubShader {
+		Pass {
+		
+			CGPROGRAM
+			#pragma vertex vert
+			#pragma fragment frag
+			#pragma multi_compile_particles
+			
+			#include "UnityCG.cginc"
+
+			sampler2D _MainTex;
+			fixed4 _Color;
+			
+			struct appdata_t {
+				float4 vertex : POSITION;
+				fixed4 color : COLOR;
+				float2 texcoord : TEXCOORD0;
+			};
+
+			struct v2f {
+				float4 vertex : SV_POSITION;
+				fixed4 color : COLOR;
+				float2 texcoord : TEXCOORD0;
+				#ifdef SOFTPARTICLES_ON
+				float4 projPos : TEXCOORD1;
+				#endif
+			};
+			
+			float4 _MainTex_ST;
+
+			v2f vert (appdata_t v)
+			{
+				v2f o;
+				o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
+				#ifdef SOFTPARTICLES_ON
+				o.projPos = ComputeScreenPos (o.vertex);
+				COMPUTE_EYEDEPTH(o.projPos.z);
+				#endif
+				o.color = v.color;
+				o.texcoord = TRANSFORM_TEX(v.texcoord,_MainTex);
+				return o;
+			}
+
+			sampler2D_float _CameraDepthTexture;
+
+			
+			fixed4 frag (v2f i) : SV_Target
+			{				
+				return 2.0f * i.color * _Color * tex2D(_MainTex, i.texcoord);
+			}
+			ENDCG 
+		}
+	}	
+}
+}

+ 5 - 0
spine-unity/Assets/spine-unity/Shaders/Bones.shader.meta

@@ -0,0 +1,5 @@
+fileFormatVersion: 2
+guid: 66988de88a15abd4e8846c6805485f57
+ShaderImporter:
+  defaultTextures: []
+  userData: 

二进制
spine-unity/Assets/spine-unity/Shaders/HiddenPass.mat


+ 4 - 0
spine-unity/Assets/spine-unity/Shaders/HiddenPass.mat.meta

@@ -0,0 +1,4 @@
+fileFormatVersion: 2
+guid: 43227e5adadc6f24bb4bf74b92a56fb4
+NativeFormatImporter:
+  userData: 

+ 21 - 0
spine-unity/Assets/spine-unity/Shaders/HiddenPass.shader

@@ -0,0 +1,21 @@
+Shader "Spine/HiddenPass" {
+    SubShader
+
+    {
+
+        Tags {"Queue" = "Geometry-1" }
+
+        Lighting Off
+
+        Pass
+
+        {
+
+            ZWrite Off
+
+            ColorMask 0     
+
+        }
+
+    }
+}

+ 5 - 0
spine-unity/Assets/spine-unity/Shaders/HiddenPass.shader.meta

@@ -0,0 +1,5 @@
+fileFormatVersion: 2
+guid: 913475501bf19374c84390868a9d6d3d
+ShaderImporter:
+  defaultTextures: []
+  userData: 

+ 16 - 2
spine-unity/Assets/spine-unity/SkeletonAnimation.cs

@@ -42,7 +42,9 @@ public class SkeletonAnimation : SkeletonRenderer {
 	public Spine.AnimationState state;
 
 	public delegate void UpdateBonesDelegate(SkeletonAnimation skeleton);
-	public UpdateBonesDelegate UpdateBones;
+	public UpdateBonesDelegate UpdateLocal;
+	public UpdateBonesDelegate UpdateWorld;
+	public UpdateBonesDelegate UpdateComplete;
 
 	[SerializeField]
 	private String _animationName;
@@ -83,7 +85,19 @@ public class SkeletonAnimation : SkeletonRenderer {
 		skeleton.Update(deltaTime);
 		state.Update(deltaTime);
 		state.Apply(skeleton);
-		if (UpdateBones != null) UpdateBones(this);
+
+		if (UpdateLocal != null) 
+			UpdateLocal(this);
+
 		skeleton.UpdateWorldTransform();
+
+		if (UpdateWorld != null){ 
+			UpdateWorld(this);
+			skeleton.UpdateWorldTransform();
+		}
+
+		if (UpdateComplete != null){ 
+			UpdateComplete(this);
+		}
 	}
 }

+ 36 - 1
spine-unity/Assets/spine-unity/SkeletonExtensions.cs

@@ -1,4 +1,39 @@
-using UnityEngine;
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2.1
+ * 
+ * Copyright (c) 2013, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to install, execute and perform the Spine Runtimes
+ * Software (the "Software") solely for internal use. Without the written
+ * permission of Esoteric Software (typically granted by licensing Spine), you
+ * may not (a) modify, translate, adapt or otherwise create derivative works,
+ * improvements of the Software or develop new applications using the Software
+ * 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 SOFTARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) 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.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Spine Extensions created by Mitch Thompson
+ * Full irrevocable rights and permissions granted to Esoteric Software
+*****************************************************************************/
+
+using UnityEngine;
 using System.Collections;
 using Spine;
 

+ 8 - 3
spine-unity/Assets/spine-unity/SkeletonRenderer.cs

@@ -37,6 +37,10 @@ using Spine;
 /// <summary>Renders a skeleton.</summary>
 [ExecuteInEditMode, RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))]
 public class SkeletonRenderer : MonoBehaviour {
+
+	public delegate void SkeletonRendererDelegate(SkeletonRenderer skeletonRenderer);
+	public SkeletonRendererDelegate OnReset;
+	
 	[System.NonSerialized]
 	public bool valid;
 	[System.NonSerialized]
@@ -96,6 +100,7 @@ public class SkeletonRenderer : MonoBehaviour {
 		skeleton = new Skeleton(skeletonData);
 		if (initialSkinName != null && initialSkinName.Length > 0 && initialSkinName != "default")
 			skeleton.SetSkin(initialSkinName);
+		if(OnReset != null) OnReset(this);
 	}
 	
 	public void Awake () {
@@ -112,7 +117,6 @@ public class SkeletonRenderer : MonoBehaviour {
 	
 	public virtual void LateUpdate () {
 		if (!valid) return;
-		
 		// Count vertices and submesh triangles.
 		int vertexCount = 0;
 		int submeshTriangleCount = 0, submeshFirstVertex = 0, submeshStartSlotIndex = 0;
@@ -122,7 +126,8 @@ public class SkeletonRenderer : MonoBehaviour {
 		int drawOrderCount = drawOrder.Count;
 		bool renderMeshes = this.renderMeshes;
 		for (int i = 0; i < drawOrderCount; i++) {
-			Attachment attachment = drawOrder[i].attachment;
+			Slot slot = drawOrder[i];
+			Attachment attachment = slot.attachment;
 			
 			object rendererObject;
 			int attachmentVertexCount, attachmentTriangleCount;
@@ -149,7 +154,7 @@ public class SkeletonRenderer : MonoBehaviour {
 
 			// Populate submesh when material changes.
 			Material material = (Material)((AtlasRegion)rendererObject).page.rendererObject;
-			if (lastMaterial != material && lastMaterial != null) {
+			if ((lastMaterial != material && lastMaterial != null) || slot.Data.name[0] == '*') {
 				AddSubmesh(lastMaterial, submeshStartSlotIndex, i, submeshTriangleCount, submeshFirstVertex, false);
 				submeshTriangleCount = 0;
 				submeshFirstVertex = vertexCount;

+ 5 - 0
spine-unity/Assets/spine-unity/SkeletonUtility.meta

@@ -0,0 +1,5 @@
+fileFormatVersion: 2
+guid: f6e0caaafe294de48af468a6a9321473
+folderAsset: yes
+DefaultImporter:
+  userData: 

+ 5 - 0
spine-unity/Assets/spine-unity/SkeletonUtility/Editor.meta

@@ -0,0 +1,5 @@
+fileFormatVersion: 2
+guid: a751a9d1e3e26d64d997b66a781df8e9
+folderAsset: yes
+DefaultImporter:
+  userData: 

+ 287 - 0
spine-unity/Assets/spine-unity/SkeletonUtility/Editor/SkeletonUtilityBoneInspector.cs

@@ -0,0 +1,287 @@
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2.1
+ * 
+ * Copyright (c) 2013, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to install, execute and perform the Spine Runtimes
+ * Software (the "Software") solely for internal use. Without the written
+ * permission of Esoteric Software (typically granted by licensing Spine), you
+ * may not (a) modify, translate, adapt or otherwise create derivative works,
+ * improvements of the Software or develop new applications using the Software
+ * 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 SOFTARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) 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.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Skeleton Utility created by Mitch Thompson
+ * Full irrevocable rights and permissions granted to Esoteric Software
+*****************************************************************************/
+
+using UnityEngine;
+using UnityEditor;
+using System.Collections;
+using System.Collections.Generic;
+using Spine;
+
+[CustomEditor(typeof(SkeletonUtilityBone)), CanEditMultipleObjects]
+public class SkeletonUtilityBoneInspector : Editor {
+	SerializedProperty mode, boneName, zPosition, position, rotation, scale, overrideAlpha, parentReference;
+
+	//multi selected flags
+	bool containsFollows, containsOverrides, multiObject;
+	
+	//single selected helpers
+	SkeletonUtilityBone utilityBone;
+	SkeletonUtility skeletonUtility;
+	bool canCreateHingeChain = false;
+	
+	void OnEnable(){
+		mode = this.serializedObject.FindProperty("mode");
+		boneName = this.serializedObject.FindProperty("boneName");
+		zPosition = this.serializedObject.FindProperty("zPosition");
+		position = this.serializedObject.FindProperty("position");
+		rotation = this.serializedObject.FindProperty("rotation");
+		scale = this.serializedObject.FindProperty("scale");
+		overrideAlpha = this.serializedObject.FindProperty("overrideAlpha");
+		parentReference = this.serializedObject.FindProperty("parentReference");
+
+		EvaluateFlags();
+
+		if(utilityBone.valid == false && skeletonUtility != null && skeletonUtility.skeletonRenderer != null){
+			skeletonUtility.skeletonRenderer.Reset();
+		}
+
+		canCreateHingeChain = CanCreateHingeChain();
+	}
+
+	/// <summary>
+	/// Evaluates the flags.
+	/// </summary>
+	void EvaluateFlags(){
+		utilityBone = (SkeletonUtilityBone)target;
+		skeletonUtility = utilityBone.skeletonUtility;
+
+		if(Selection.objects.Length == 1){
+			containsFollows = utilityBone.mode == SkeletonUtilityBone.Mode.Follow;
+			containsOverrides = utilityBone.mode == SkeletonUtilityBone.Mode.Override;
+		}
+		else{
+			int boneCount = 0;
+			foreach(Object o in Selection.objects){
+				if(o is GameObject){
+					GameObject go = (GameObject)o;
+					SkeletonUtilityBone sub = go.GetComponent<SkeletonUtilityBone>();
+					if(sub != null){
+						boneCount++;
+						if(sub.mode == SkeletonUtilityBone.Mode.Follow)
+							containsFollows = true;
+						if(sub.mode == SkeletonUtilityBone.Mode.Override)
+							containsOverrides = true;
+					}
+				}
+			}
+			
+			if(boneCount > 1)
+				multiObject = true;
+		}
+	}
+	
+	public override void OnInspectorGUI ()
+	{
+		serializedObject.Update();
+
+		EditorGUI.BeginChangeCheck();
+		EditorGUILayout.PropertyField(mode);
+		if(EditorGUI.EndChangeCheck()){
+			containsOverrides = mode.enumValueIndex == 1;
+			containsFollows = mode.enumValueIndex == 0;
+		}
+
+		EditorGUI.BeginDisabledGroup( multiObject );
+		{
+			string str = boneName.stringValue;
+			if(str == "") str = "<None>";
+			if(multiObject) str = "<Multiple>";
+
+			GUILayout.BeginHorizontal();
+			EditorGUILayout.PrefixLabel("Bone");
+
+			if(GUILayout.Button( str, EditorStyles.popup )){
+				BoneSelectorContextMenu( str, ((SkeletonUtilityBone)target).skeletonUtility.skeletonRenderer.skeleton.Bones, "<None>", TargetBoneSelected );
+			}
+
+			GUILayout.EndHorizontal();
+		}
+		EditorGUI.EndDisabledGroup();
+
+		EditorGUILayout.PropertyField(zPosition);
+		EditorGUILayout.PropertyField(position);
+		EditorGUILayout.PropertyField(rotation);
+		EditorGUILayout.PropertyField(scale);
+
+		EditorGUI.BeginDisabledGroup( containsFollows );
+		{
+			EditorGUILayout.PropertyField(overrideAlpha);
+			EditorGUILayout.PropertyField(parentReference);
+		}
+		EditorGUI.EndDisabledGroup();
+
+		EditorGUILayout.Space();
+
+		GUILayout.BeginHorizontal();
+		{
+			EditorGUI.BeginDisabledGroup( multiObject || !utilityBone.valid || utilityBone.bone == null || utilityBone.bone.Children.Count == 0);
+			{
+				if(GUILayout.Button(new GUIContent("Add Child", SpineEditorUtilities.Icons.bone), GUILayout.Width(150), GUILayout.Height(24)))
+					BoneSelectorContextMenu( "", utilityBone.bone.Children, "<Recursively>", SpawnChildBoneSelected);
+			}
+			EditorGUI.EndDisabledGroup();
+
+			EditorGUI.BeginDisabledGroup( multiObject || !utilityBone.valid || utilityBone.bone == null || containsOverrides);
+			{
+				if(GUILayout.Button(new GUIContent("Add Override", SpineEditorUtilities.Icons.poseBones), GUILayout.Width(150), GUILayout.Height(24)))
+					SpawnOverride();
+			}
+			EditorGUI.EndDisabledGroup();
+
+			EditorGUI.BeginDisabledGroup( multiObject || !utilityBone.valid || !canCreateHingeChain );
+			{
+				if(GUILayout.Button(new GUIContent("Create Hinge Chain", SpineEditorUtilities.Icons.hingeChain), GUILayout.Width(150), GUILayout.Height(24)))
+					CreateHingeChain();
+			}
+			EditorGUI.EndDisabledGroup();
+
+		}
+		GUILayout.EndHorizontal();
+
+		serializedObject.ApplyModifiedProperties();
+	}
+
+	void BoneSelectorContextMenu(string current, List<Bone> bones, string topValue, GenericMenu.MenuFunction2 callback){
+		GenericMenu menu = new GenericMenu();
+
+		if(topValue != "")
+			menu.AddItem( new GUIContent(topValue), current == topValue, callback, null );
+
+		for(int i = 0; i < bones.Count; i++){
+			menu.AddItem( new GUIContent(bones[i].Data.Name), bones[i].Data.Name == current, callback, bones[i] );
+		}
+
+		menu.ShowAsContext();
+
+	}
+
+
+	void TargetBoneSelected(object obj){
+		if(obj == null){
+			boneName.stringValue = "";
+			serializedObject.ApplyModifiedProperties();
+		}
+		else{
+			Bone bone = (Bone)obj;
+			boneName.stringValue = bone.Data.Name;
+			serializedObject.ApplyModifiedProperties();
+
+			utilityBone.Reset();
+		}
+	}
+
+	void SpawnChildBoneSelected(object obj){
+		if(obj == null){
+			//add recursively
+			foreach(var bone in utilityBone.bone.Children){
+				GameObject go = skeletonUtility.SpawnBoneRecursively( bone, utilityBone.transform, utilityBone.mode, utilityBone.position, utilityBone.rotation, utilityBone.scale );
+				SkeletonUtilityBone[] newUtilityBones = go.GetComponentsInChildren<SkeletonUtilityBone>();
+				foreach(SkeletonUtilityBone utilBone in newUtilityBones)
+					SkeletonUtilityInspector.AttachIcon(utilBone);
+			}
+		}
+		else{
+			Bone bone = (Bone)obj;
+			GameObject go = skeletonUtility.SpawnBone( bone, utilityBone.transform, utilityBone.mode, utilityBone.position, utilityBone.rotation, utilityBone.scale );
+			SkeletonUtilityInspector.AttachIcon(go.GetComponent<SkeletonUtilityBone>());
+			Selection.activeGameObject = go;
+			EditorGUIUtility.PingObject(go);
+		}
+	}
+
+	void SpawnOverride(){
+		GameObject go = skeletonUtility.SpawnBone( utilityBone.bone, utilityBone.transform.parent, SkeletonUtilityBone.Mode.Override, utilityBone.position, utilityBone.rotation, utilityBone.scale);
+		go.name = go.name + " [Override]";
+		SkeletonUtilityInspector.AttachIcon(go.GetComponent<SkeletonUtilityBone>());
+		Selection.activeGameObject = go;
+		EditorGUIUtility.PingObject(go);
+	}
+
+	bool CanCreateHingeChain(){
+		if(utilityBone == null) return false;
+		if(utilityBone.rigidbody != null) return false;
+		if(utilityBone.bone != null && utilityBone.bone.Children.Count == 0) return false;
+
+		Rigidbody[] rigidbodies = utilityBone.GetComponentsInChildren<Rigidbody>();
+
+		if(rigidbodies.Length > 0) return false;
+
+		return true;
+	}
+
+	void CreateHingeChain(){
+		var utilBoneArr = utilityBone.GetComponentsInChildren<SkeletonUtilityBone>();
+
+		foreach(var utilBone in utilBoneArr){
+			AttachRigidbody(utilBone);
+		}
+
+		utilityBone.rigidbody.isKinematic = true;
+
+		foreach(var utilBone in utilBoneArr){
+			if(utilBone == utilityBone)
+				continue;
+
+			utilBone.mode = SkeletonUtilityBone.Mode.Override;
+
+			HingeJoint joint = utilBone.gameObject.AddComponent<HingeJoint>();
+			joint.axis = Vector3.forward;
+			joint.connectedBody = utilBone.transform.parent.rigidbody;
+			joint.useLimits = true;
+			JointLimits limits = new JointLimits();
+			limits.min = -20;
+			limits.max = 20;
+			joint.limits = limits;
+			utilBone.rigidbody.mass = utilBone.transform.parent.rigidbody.mass * 0.75f;
+		}
+	}
+	
+	void AttachRigidbody(SkeletonUtilityBone utilBone){
+		if(utilBone.GetComponent<Collider>() == null){
+			if(utilBone.bone.Data.Length == 0){
+				SphereCollider sphere = utilBone.gameObject.AddComponent<SphereCollider>();
+				sphere.radius = 0.1f;
+			}
+			else{
+				float length = utilBone.bone.Data.Length;
+				BoxCollider box = utilBone.gameObject.AddComponent<BoxCollider>();
+				box.size = new Vector3( length, length / 3, 0.2f);
+				box.center = new Vector3(length / 2, 0, 0);
+			}
+		}
+
+		utilBone.gameObject.AddComponent<Rigidbody>();
+	}
+}

+ 8 - 0
spine-unity/Assets/spine-unity/SkeletonUtility/Editor/SkeletonUtilityBoneInspector.cs.meta

@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: b3ae20b4bcc31f645afd6f5b64f82473
+MonoImporter:
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 

+ 316 - 0
spine-unity/Assets/spine-unity/SkeletonUtility/Editor/SkeletonUtilityInspector.cs

@@ -0,0 +1,316 @@
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2.1
+ * 
+ * Copyright (c) 2013, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to install, execute and perform the Spine Runtimes
+ * Software (the "Software") solely for internal use. Without the written
+ * permission of Esoteric Software (typically granted by licensing Spine), you
+ * may not (a) modify, translate, adapt or otherwise create derivative works,
+ * improvements of the Software or develop new applications using the Software
+ * 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 SOFTARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) 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.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Skeleton Utility created by Mitch Thompson
+ * Full irrevocable rights and permissions granted to Esoteric Software
+*****************************************************************************/
+
+using UnityEngine;
+using UnityEditor;
+#if UNITY_4_3
+//nothing
+#else
+using UnityEditor.AnimatedValues;
+#endif
+using System.Collections;
+using System.Collections.Generic;
+using Spine;
+
+using System.Reflection;
+
+[CustomEditor(typeof(SkeletonUtility))]
+public class SkeletonUtilityInspector : Editor {
+
+	public static void AttachIcon(SkeletonUtilityBone utilityBone){
+		Skeleton skeleton = utilityBone.skeletonUtility.skeletonRenderer.skeleton;
+		Texture2D icon;
+		if(utilityBone.bone.Data.Length == 0)
+			icon = SpineEditorUtilities.Icons.nullBone;
+		else
+			icon = SpineEditorUtilities.Icons.boneNib;
+		
+		foreach(IkConstraint c in skeleton.IkConstraints){
+			if(c.Target == utilityBone.bone){
+				icon = SpineEditorUtilities.Icons.constraintNib;
+				break;
+			}
+		}
+		
+		
+		
+		typeof(EditorGUIUtility).InvokeMember("SetIconForObject", BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.NonPublic, null, null, new object[2]{ utilityBone.gameObject, icon});
+	}
+
+	static void AttachIconsToChildren(Transform root){
+		if(root != null){
+			var utilityBones = root.GetComponentsInChildren<SkeletonUtilityBone>();
+			foreach(var utilBone in utilityBones){
+				AttachIcon(utilBone);
+			}
+		}
+	}
+
+
+
+	static SkeletonUtilityInspector(){
+		#if UNITY_4_3
+		showSlots = false;
+		#else
+		showSlots = new AnimBool(false);
+		#endif
+	}
+
+	SkeletonUtility skeletonUtility;
+
+	Skeleton skeleton;
+	SkeletonRenderer skeletonRenderer;
+	Transform transform;
+	bool isPrefab;
+
+
+	Dictionary<Slot, List<Attachment>> attachmentTable = new Dictionary<Slot, List<Attachment>>();
+
+
+	//GUI stuff
+#if UNITY_4_3
+	static bool showSlots;
+#else
+	static AnimBool showSlots;
+#endif
+
+	void OnEnable(){
+		skeletonUtility = (SkeletonUtility)target;
+		skeletonRenderer = skeletonUtility.GetComponent<SkeletonRenderer>();
+		skeleton = skeletonRenderer.skeleton;
+		transform = skeletonRenderer.transform;
+
+		if(skeleton == null){
+			skeletonRenderer.Reset();
+			skeletonRenderer.LateUpdate();
+
+			skeleton = skeletonRenderer.skeleton;
+		}
+
+		UpdateAttachments();
+
+		if(PrefabUtility.GetPrefabType(this.target) == PrefabType.Prefab)
+			isPrefab = true;
+
+	}
+
+	void OnDestroy(){
+
+	}
+
+	void OnSceneGUI(){
+		if(skeleton == null){
+			OnEnable();
+			return;
+		}
+
+		float flipRotation = skeleton.FlipX ? -1 : 1;
+
+		foreach(Bone b in skeleton.Bones){
+			Vector3 vec = transform.TransformPoint(new Vector3(b.WorldX, b.WorldY, 0));
+
+			Quaternion rot = Quaternion.Euler(0,0,b.WorldRotation * flipRotation);
+			Vector3 forward = transform.TransformDirection( rot * Vector3.right);
+			forward *= flipRotation;
+
+			SpineEditorUtilities.Icons.boneMaterial.SetPass(0);
+			Graphics.DrawMeshNow( SpineEditorUtilities.Icons.boneMesh, Matrix4x4.TRS ( vec, Quaternion.LookRotation(transform.forward, forward), Vector3.one * b.Data.Length * b.WorldScaleX));
+		}
+	}
+
+	void UpdateAttachments(){
+		attachmentTable = new Dictionary<Slot, List<Attachment>>();
+		Skin skin = skeleton.Skin;
+
+		if(skin == null){
+			skin = skeletonRenderer.skeletonDataAsset.GetSkeletonData(true).DefaultSkin;
+		}
+
+		for(int i = skeleton.Slots.Count-1; i >= 0; i--){
+			List<Attachment> attachments = new List<Attachment>();
+			skin.FindAttachmentsForSlot(i, attachments);
+
+			attachmentTable.Add(skeleton.Slots[i], attachments);
+		}
+	}
+
+	void SpawnHierarchyButton(string label, string tooltip, SkeletonUtilityBone.Mode mode, bool pos, bool rot, bool sca, params GUILayoutOption[] options){
+		GUIContent content = new GUIContent(label, tooltip);
+		if(GUILayout.Button(content, options)){
+			if(skeletonUtility.skeletonRenderer == null)
+				skeletonUtility.skeletonRenderer = skeletonUtility.GetComponent<SkeletonRenderer>();
+
+			if(skeletonUtility.boneRoot != null){
+				return;
+			}
+
+			skeletonUtility.SpawnHierarchy(mode, pos, rot, sca);
+
+			SkeletonUtilityBone[] boneComps = skeletonUtility.GetComponentsInChildren<SkeletonUtilityBone>();
+			foreach(SkeletonUtilityBone b in boneComps) 
+				AttachIcon(b);
+		}
+	}
+
+	public override void OnInspectorGUI ()
+	{
+		if(isPrefab){
+			GUILayout.Label(new GUIContent("Cannot edit Prefabs", SpineEditorUtilities.Icons.warning));
+			return;
+		}
+
+		skeletonUtility.boneRoot = (Transform)EditorGUILayout.ObjectField( "Bone Root", skeletonUtility.boneRoot, typeof(Transform), true);
+
+		GUILayout.BeginHorizontal();
+		EditorGUI.BeginDisabledGroup(skeletonUtility.boneRoot != null);
+		{
+			if(GUILayout.Button(new GUIContent("Spawn Hierarchy", SpineEditorUtilities.Icons.skeleton), GUILayout.Width(150), GUILayout.Height(24)))
+				SpawnHierarchyContextMenu();
+		}
+		EditorGUI.EndDisabledGroup();
+
+		if(GUILayout.Button(new GUIContent("Spawn Submeshes", SpineEditorUtilities.Icons.subMeshRenderer), GUILayout.Width(150), GUILayout.Height(24)))
+			skeletonUtility.SpawnSubRenderers(true);
+		GUILayout.EndHorizontal();
+
+		EditorGUI.BeginChangeCheck();
+		skeleton.FlipX = EditorGUILayout.ToggleLeft("Flip X", skeleton.FlipX);
+		skeleton.FlipY = EditorGUILayout.ToggleLeft("Flip Y", skeleton.FlipY);
+		if(EditorGUI.EndChangeCheck()){
+			skeletonRenderer.LateUpdate();
+			SceneView.RepaintAll();
+		}
+
+#if UNITY_4_3
+		showSlots = EditorGUILayout.Foldout(showSlots, "Slots");
+#else
+		showSlots.target = EditorGUILayout.Foldout(showSlots.target, "Slots");
+		if(EditorGUILayout.BeginFadeGroup(showSlots.faded)){
+#endif
+			foreach(KeyValuePair<Slot, List<Attachment>> pair in attachmentTable){
+
+				Slot slot = pair.Key;
+
+				EditorGUILayout.BeginHorizontal();
+				EditorGUI.indentLevel = 1;
+				EditorGUILayout.LabelField(new GUIContent(slot.Data.Name, SpineEditorUtilities.Icons.slot), GUILayout.ExpandWidth(false));
+
+				EditorGUI.BeginChangeCheck();
+				Color c = EditorGUILayout.ColorField(new Color(slot.R, slot.G, slot.B, slot.A), GUILayout.Width(60));
+
+				if(EditorGUI.EndChangeCheck()){
+					slot.SetColor( c );
+					skeletonRenderer.LateUpdate();
+				}
+
+				EditorGUILayout.EndHorizontal();
+
+
+
+				foreach(Attachment attachment in pair.Value){
+
+					if(slot.Attachment == attachment){
+						GUI.contentColor = Color.white;
+					}
+					else{
+						GUI.contentColor = Color.grey;
+					}
+
+					EditorGUI.indentLevel = 2;
+					bool isAttached = attachment == slot.Attachment;
+
+					Texture2D icon = null;
+
+					if(attachment is MeshAttachment || attachment is SkinnedMeshAttachment)
+						icon = SpineEditorUtilities.Icons.mesh;
+					else
+						icon = SpineEditorUtilities.Icons.image;
+
+					bool swap = EditorGUILayout.ToggleLeft( new GUIContent( attachment.Name, icon ), attachment == slot.Attachment );
+
+					if(!isAttached && swap){
+						slot.Attachment = attachment;
+						skeletonRenderer.LateUpdate();
+					}
+					else if(isAttached && !swap){
+						slot.Attachment = null;
+						skeletonRenderer.LateUpdate();
+					}
+
+					GUI.contentColor = Color.white;
+				}
+			}
+			#if UNITY_4_3
+
+#else
+		}
+		EditorGUILayout.EndFadeGroup();
+		if(showSlots.isAnimating)
+			Repaint();
+#endif
+	}
+
+	void SpawnHierarchyContextMenu(){
+		GenericMenu menu = new GenericMenu();
+
+		menu.AddItem(new GUIContent("Follow"), false, SpawnFollowHierarchy);
+		menu.AddItem(new GUIContent("Follow (Root Only)"), false, SpawnFollowHierarchyRootOnly);
+		menu.AddSeparator("");
+		menu.AddItem(new GUIContent("Override"), false, SpawnOverrideHierarchy);
+		menu.AddItem(new GUIContent("Override (Root Only)"), false, SpawnOverrideHierarchyRootOnly);
+
+		menu.ShowAsContext();
+	}
+
+	void SpawnFollowHierarchy(){
+		Selection.activeGameObject = skeletonUtility.SpawnHierarchy( SkeletonUtilityBone.Mode.Follow, true, true, true );
+		AttachIconsToChildren( skeletonUtility.boneRoot );
+	}
+
+	void SpawnFollowHierarchyRootOnly(){
+		Selection.activeGameObject = skeletonUtility.SpawnRoot( SkeletonUtilityBone.Mode.Follow, true, true, true );
+		AttachIconsToChildren( skeletonUtility.boneRoot );
+	}
+
+	void SpawnOverrideHierarchy(){
+		Selection.activeGameObject = skeletonUtility.SpawnHierarchy( SkeletonUtilityBone.Mode.Override, true, true, true );
+		AttachIconsToChildren( skeletonUtility.boneRoot );
+	}
+	
+	void SpawnOverrideHierarchyRootOnly(){
+		Selection.activeGameObject = skeletonUtility.SpawnRoot( SkeletonUtilityBone.Mode.Override, true, true, true );
+		AttachIconsToChildren( skeletonUtility.boneRoot );
+	}
+}

+ 8 - 0
spine-unity/Assets/spine-unity/SkeletonUtility/Editor/SkeletonUtilityInspector.cs.meta

@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: a5b90df955eb8c2429ac67c8b2de6c5c
+MonoImporter:
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 

+ 335 - 0
spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtility.cs

@@ -0,0 +1,335 @@
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2.1
+ * 
+ * Copyright (c) 2013, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to install, execute and perform the Spine Runtimes
+ * Software (the "Software") solely for internal use. Without the written
+ * permission of Esoteric Software (typically granted by licensing Spine), you
+ * may not (a) modify, translate, adapt or otherwise create derivative works,
+ * improvements of the Software or develop new applications using the Software
+ * 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 SOFTARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) 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.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Skeleton Utility created by Mitch Thompson
+ * Full irrevocable rights and permissions granted to Esoteric Software
+*****************************************************************************/
+
+using UnityEngine;
+using System.Collections;
+using System.Collections.Generic;
+using Spine;
+
+[RequireComponent(typeof(SkeletonAnimation))]
+[ExecuteInEditMode]
+public class SkeletonUtility : MonoBehaviour {
+	public delegate void SkeletonUtilityDelegate();
+	public event SkeletonUtilityDelegate OnReset;
+
+	public Transform boneRoot;
+
+	void Update(){
+		if(boneRoot != null && skeletonRenderer.skeleton != null){
+			Vector3 flipScale = Vector3.one;
+			if(skeletonRenderer.skeleton.FlipX)
+				flipScale.x = -1;
+
+			if(skeletonRenderer.skeleton.FlipY)
+				flipScale.y = -1;
+
+			boneRoot.localScale = flipScale;
+		}
+	}
+
+	[HideInInspector]
+	public SkeletonRenderer skeletonRenderer;
+
+	[HideInInspector]
+	public SkeletonAnimation skeletonAnimation;
+
+	[System.NonSerialized]
+	public List<SkeletonUtilityBone> utilityBones = new List<SkeletonUtilityBone>();
+
+	[System.NonSerialized]
+	public List<SkeletonUtilityConstraint> utilityConstraints = new List<SkeletonUtilityConstraint>();
+//	Dictionary<Bone, SkeletonUtilityBone> utilityBoneTable;
+
+
+	protected bool hasTransformBones;
+	protected bool hasUtilityConstraints;
+	protected bool needToReprocessBones;
+
+	void OnEnable(){
+		if(skeletonRenderer == null){
+			skeletonRenderer = GetComponent<SkeletonRenderer>();
+		}
+
+		if(skeletonAnimation == null){
+			skeletonAnimation = GetComponent<SkeletonAnimation>();
+		}
+
+		skeletonRenderer.OnReset -= HandleRendererReset;
+		skeletonRenderer.OnReset += HandleRendererReset;
+
+		if(skeletonAnimation != null){
+			skeletonAnimation.UpdateLocal -= UpdateLocal;
+			skeletonAnimation.UpdateLocal += UpdateLocal;
+		}
+
+
+		CollectBones();
+	}
+
+	void Start(){
+		//recollect because order of operations failure when switching between game mode and edit mode...
+//		CollectBones();
+	}
+
+
+
+	void OnDisable(){
+		skeletonRenderer.OnReset -= HandleRendererReset;
+
+		if(skeletonAnimation != null){
+			skeletonAnimation.UpdateLocal -= UpdateLocal;
+			skeletonAnimation.UpdateWorld -= UpdateWorld;
+			skeletonAnimation.UpdateComplete -= UpdateComplete;
+		}
+	}
+	
+	void HandleRendererReset(SkeletonRenderer r){
+		if(OnReset != null)
+			OnReset();
+
+		CollectBones();
+	}
+
+	public void RegisterBone(SkeletonUtilityBone bone){
+		if(utilityBones.Contains(bone))
+			return;
+		else{
+			utilityBones.Add(bone);
+			needToReprocessBones = true;
+		}
+	}
+
+	public void UnregisterBone(SkeletonUtilityBone bone){
+		utilityBones.Remove(bone);
+	}
+
+	public void RegisterConstraint(SkeletonUtilityConstraint constraint){
+
+		if(utilityConstraints.Contains(constraint))
+			return;
+		else{
+			utilityConstraints.Add(constraint);
+			needToReprocessBones = true;
+		}
+	}
+	
+	public void UnregisterConstraint(SkeletonUtilityConstraint constraint){
+		utilityConstraints.Remove(constraint);
+	}
+
+	public void CollectBones(){
+		if(skeletonRenderer.skeleton == null)
+			return;
+
+		if(boneRoot != null){
+			List<string> constraintTargetNames = new List<string>();
+
+			foreach(IkConstraint c in skeletonRenderer.skeleton.IkConstraints){
+				constraintTargetNames.Add(c.Target.Data.Name);
+			}
+
+			foreach(var b in utilityBones){
+				if(b.bone == null){
+					return;
+				}
+				if(b.mode == SkeletonUtilityBone.Mode.Override){
+					hasTransformBones = true;
+				}
+
+				if(constraintTargetNames.Contains( b.bone.Data.Name )){
+					hasUtilityConstraints = true;
+				}
+			}
+
+			if(utilityConstraints.Count > 0)
+				hasUtilityConstraints = true;
+
+			if(skeletonAnimation != null){
+				skeletonAnimation.UpdateWorld -= UpdateWorld;
+				skeletonAnimation.UpdateComplete -= UpdateComplete;
+
+				if(hasTransformBones || hasUtilityConstraints){
+					skeletonAnimation.UpdateWorld += UpdateWorld;
+				}
+
+				if(hasUtilityConstraints){
+					skeletonAnimation.UpdateComplete += UpdateComplete;
+				}
+			}
+
+			needToReprocessBones = false;
+		}
+		else{
+			utilityBones.Clear();
+			utilityConstraints.Clear();
+		}
+
+	}
+
+	void UpdateLocal(SkeletonAnimation anim){
+
+		if(needToReprocessBones)
+			CollectBones();
+
+		if(utilityBones == null)
+			return;
+
+		foreach(SkeletonUtilityBone b in utilityBones){
+			b.transformLerpComplete = false;
+		}
+
+		UpdateAllBones();
+	}
+
+	void UpdateWorld(SkeletonAnimation anim){
+		UpdateAllBones();
+
+		foreach(SkeletonUtilityConstraint c in utilityConstraints)
+			c.DoUpdate();
+	}
+
+	void UpdateComplete(SkeletonAnimation anim){
+		UpdateAllBones();
+	}
+
+	void UpdateAllBones(){
+		if(boneRoot == null){
+			CollectBones();
+		}
+
+		if(utilityBones == null)
+			return;
+
+		foreach(SkeletonUtilityBone b in utilityBones){
+			b.DoUpdate();
+		}
+	}
+
+	public Transform GetBoneRoot(){
+		if(boneRoot != null)
+			return boneRoot;
+
+		boneRoot = new GameObject("SkeletonUtility-Root").transform;
+		boneRoot.parent = transform;
+		boneRoot.localPosition = Vector3.zero;
+		boneRoot.localRotation = Quaternion.identity;
+		boneRoot.localScale = Vector3.one;
+
+		return boneRoot;
+	}
+
+	public GameObject SpawnRoot(SkeletonUtilityBone.Mode mode, bool pos, bool rot, bool sca){
+		GetBoneRoot();
+		Skeleton skeleton = this.skeletonRenderer.skeleton;
+
+		GameObject go = SpawnBone( skeleton.RootBone, boneRoot, mode, pos, rot, sca );
+
+		CollectBones();
+
+		return go;
+	}
+
+	public GameObject SpawnHierarchy(SkeletonUtilityBone.Mode mode, bool pos, bool rot, bool sca){
+		GetBoneRoot();
+
+		Skeleton skeleton = this.skeletonRenderer.skeleton;
+
+		GameObject go = SpawnBoneRecursively(skeleton.RootBone, boneRoot, mode, pos, rot, sca);
+
+		CollectBones();
+
+		return go;
+	}
+
+	public GameObject SpawnBoneRecursively(Bone bone, Transform parent, SkeletonUtilityBone.Mode mode, bool pos, bool rot, bool sca){
+		GameObject go = SpawnBone(bone, parent, mode, pos, rot, sca);
+
+		foreach(Bone child in bone.Children){
+			SpawnBoneRecursively(child, go.transform, mode, pos, rot, sca);
+		}
+
+		return go;
+	}
+	
+	public GameObject SpawnBone(Bone bone, Transform parent, SkeletonUtilityBone.Mode mode, bool pos, bool rot, bool sca){
+		GameObject go = new GameObject(bone.Data.Name);
+		go.transform.parent = parent;
+		
+		SkeletonUtilityBone b = go.AddComponent<SkeletonUtilityBone>();
+		b.skeletonUtility = this;
+		b.position = pos;
+		b.rotation = rot;
+		b.scale = sca;
+		b.mode = mode;
+		b.zPosition = true;
+		b.Reset();
+		b.bone = bone;
+		b.boneName = bone.Data.Name;
+		b.valid = true;
+
+		if(mode == SkeletonUtilityBone.Mode.Override){
+			if(rot)
+				go.transform.localRotation = Quaternion.Euler(0, 0, b.bone.RotationIK);
+			
+			if(pos)
+				go.transform.localPosition = new Vector3(b.bone.X, b.bone.Y, 0);
+			
+			go.transform.localScale = new Vector3(b.bone.scaleX, b.bone.scaleY, 0);
+		}
+
+		return go;
+	}
+	
+	public void SpawnSubRenderers(bool disablePrimaryRenderer){
+		int submeshCount = GetComponent<MeshFilter>().sharedMesh.subMeshCount;
+
+		for(int i = 0; i < submeshCount; i++){
+			GameObject go = new GameObject("Submesh " + i, typeof(MeshFilter), typeof(MeshRenderer));
+			go.transform.parent = transform;
+			go.transform.localPosition = Vector3.zero;
+			go.transform.localRotation = Quaternion.identity;
+			go.transform.localScale = Vector3.one;
+
+			SkeletonUtilitySubmeshRenderer s = go.AddComponent<SkeletonUtilitySubmeshRenderer>();
+			s.sortingOrder = i * 10;
+			s.submeshIndex = i;
+			s.Initialize( renderer );
+			s.Update();
+		}
+
+		if(disablePrimaryRenderer)
+			renderer.enabled = false;
+	}
+}

+ 8 - 0
spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtility.cs.meta

@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 7f726fb798ad621458c431cb9966d91d
+MonoImporter:
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 

+ 218 - 0
spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtilityBone.cs

@@ -0,0 +1,218 @@
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2.1
+ * 
+ * Copyright (c) 2013, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to install, execute and perform the Spine Runtimes
+ * Software (the "Software") solely for internal use. Without the written
+ * permission of Esoteric Software (typically granted by licensing Spine), you
+ * may not (a) modify, translate, adapt or otherwise create derivative works,
+ * improvements of the Software or develop new applications using the Software
+ * 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 SOFTARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) 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.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Skeleton Utility created by Mitch Thompson
+ * Full irrevocable rights and permissions granted to Esoteric Software
+*****************************************************************************/
+
+using System;
+using System.IO;
+using System.Collections.Generic;
+using UnityEngine;
+using Spine;
+
+/// <summary>Sets a GameObject's transform to match a bone on a Spine skeleton.</summary>
+[ExecuteInEditMode]
+[AddComponentMenu("Spine/SkeletonUtilityBone")]
+public class SkeletonUtilityBone : MonoBehaviour {
+
+	public enum Mode { Follow, Override }
+
+	[System.NonSerialized]
+	public bool valid;
+
+	[System.NonSerialized]
+	public SkeletonUtility skeletonUtility;
+
+	[System.NonSerialized]
+	public Bone bone;
+
+	public Mode mode;
+	
+	public bool zPosition = true;
+	public bool position;
+	public bool rotation;
+	public bool scale;
+
+	[Range(0f,1f)]
+	public float overrideAlpha = 1;
+
+	/// <summary>If a bone isn't set, boneName is used to find the bone.</summary>
+	public String boneName;
+
+	public Transform parentReference;
+
+	[HideInInspector]
+	public bool transformLerpComplete;
+
+	protected Transform cachedTransform;
+	protected Transform skeletonTransform;
+
+	public bool NonUniformScaleWarning{
+		get{
+			return nonUniformScaleWarning;
+		}
+	}
+	private bool nonUniformScaleWarning;
+
+	public void Reset () {
+		bone = null;
+		cachedTransform = transform;
+		valid = skeletonUtility != null && skeletonUtility.skeletonRenderer != null && skeletonUtility.skeletonRenderer.valid;
+		if (!valid) return;
+		skeletonTransform = skeletonUtility.transform;
+
+		skeletonUtility.OnReset -= HandleOnReset;
+		skeletonUtility.OnReset += HandleOnReset;
+
+		DoUpdate();
+	}
+
+	void OnEnable(){
+		skeletonUtility = GetComponentInParent<SkeletonUtility>();
+		if(skeletonUtility == null)
+			return;
+
+		skeletonUtility.RegisterBone(this);
+
+		skeletonUtility.OnReset += HandleOnReset;
+	}
+
+	void HandleOnReset ()
+	{
+		Reset ();
+	}
+
+	void OnDisable(){
+		if(skeletonUtility != null){
+			skeletonUtility.OnReset -= HandleOnReset;
+
+			skeletonUtility.UnregisterBone(this);
+		}
+	}
+
+	public void DoUpdate () {
+
+		if (!valid) {
+			Reset();
+			return;
+		}
+
+		Spine.Skeleton skeleton = skeletonUtility.skeletonRenderer.skeleton;
+
+		if (bone == null) {
+			if (boneName == null || boneName.Length == 0) return;
+			bone = skeleton.FindBone(boneName);
+			if (bone == null) {
+				Debug.LogError("Bone not found: " + boneName, this);
+				return;
+			}
+		}
+
+
+		float flipRotation = (skeleton.flipX ^ skeleton.flipY) ? -1f : 1f;
+
+
+		if(mode == Mode.Follow){
+			if(position){
+				cachedTransform.localPosition = new Vector3(bone.x, bone.y, 0);
+			}
+			
+			if(rotation){
+
+				if(bone.Data.InheritRotation){
+					cachedTransform.localRotation = Quaternion.Euler(0,0,bone.rotationIK);
+				}
+				else{
+					Vector3 euler = skeletonTransform.rotation.eulerAngles;
+					cachedTransform.rotation = Quaternion.Euler(euler.x, euler.y, skeletonTransform.rotation.eulerAngles.z + (bone.worldRotation * flipRotation) );
+				}
+
+			}
+
+			if(scale){
+				cachedTransform.localScale = new Vector3(bone.scaleX, bone.scaleY, 1);
+
+				nonUniformScaleWarning = (bone.scaleX != bone.scaleY);
+			}
+
+		}
+		else if(mode == Mode.Override){
+			if(transformLerpComplete)
+				return;
+
+			if(parentReference == null){
+				if(position){
+					bone.x = Mathf.Lerp(bone.x, cachedTransform.localPosition.x, overrideAlpha);
+					bone.y = Mathf.Lerp(bone.y, cachedTransform.localPosition.y, overrideAlpha);
+				}
+
+				if(rotation){
+					bone.rotation = Mathf.LerpAngle(bone.Rotation, cachedTransform.localRotation.eulerAngles.z, overrideAlpha);
+				}
+
+				if(scale){
+					bone.scaleX = Mathf.Lerp(bone.scaleX, cachedTransform.localScale.x, overrideAlpha);
+					bone.scaleY = Mathf.Lerp(bone.scaleY, cachedTransform.localScale.y, overrideAlpha);
+
+					nonUniformScaleWarning = (bone.scaleX != bone.scaleY);
+				}
+			}
+			else{
+				if(position){
+					Vector3 pos = parentReference.InverseTransformPoint(cachedTransform.position);
+					bone.x = Mathf.Lerp(bone.x, pos.x, overrideAlpha);
+					bone.y = Mathf.Lerp(bone.y, pos.y, overrideAlpha);
+				}
+				
+				if(rotation){
+					bone.rotation = Mathf.LerpAngle(bone.Rotation, Quaternion.LookRotation( Vector3.forward, parentReference.InverseTransformDirection( cachedTransform.up ) ).eulerAngles.z, overrideAlpha);
+				}
+
+				//TODO: Something about this
+				if(scale){
+					bone.scaleX = Mathf.Lerp(bone.scaleX, cachedTransform.localScale.x, overrideAlpha);
+					bone.scaleY = Mathf.Lerp(bone.scaleY, cachedTransform.localScale.y, overrideAlpha);
+					
+					nonUniformScaleWarning = (bone.scaleX != bone.scaleY);
+				}
+			}
+
+			transformLerpComplete = true;
+		}		
+	}
+
+	void OnDrawGizmos(){
+		if(NonUniformScaleWarning){
+			Gizmos.DrawIcon(transform.position + new Vector3(0,0.128f,0), "icon-warning");
+		}
+	}
+}

+ 8 - 0
spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtilityBone.cs.meta

@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: b238dfcde8209044b97d23f62bcaadf6
+MonoImporter:
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 

+ 22 - 0
spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtilityConstraint.cs

@@ -0,0 +1,22 @@
+using UnityEngine;
+using System.Collections;
+
+[RequireComponent(typeof(SkeletonUtilityBone)), ExecuteInEditMode]
+
+public abstract class SkeletonUtilityConstraint : MonoBehaviour {
+
+	protected SkeletonUtilityBone utilBone;
+	protected SkeletonUtility skeletonUtility;
+
+	protected virtual void OnEnable(){
+		utilBone = GetComponent<SkeletonUtilityBone>();
+		skeletonUtility = GetComponentInParent<SkeletonUtility>();
+		skeletonUtility.RegisterConstraint(this);
+	}
+
+	protected virtual void OnDisable(){
+		skeletonUtility.UnregisterConstraint(this);
+	}
+
+	public abstract void DoUpdate();
+}

+ 8 - 0
spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtilityConstraint.cs.meta

@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 522dbfcc6c916df4396f14f35048d185
+MonoImporter:
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 

+ 61 - 0
spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtilityEyeConstraint.cs

@@ -0,0 +1,61 @@
+using UnityEngine;
+using System.Collections;
+
+public class SkeletonUtilityEyeConstraint : SkeletonUtilityConstraint {
+
+	public Transform[] eyes;
+	public float radius = 0.5f;
+	public Transform target;
+	public Vector3 targetPosition;
+
+	public float speed = 10;
+	
+	Vector3[] origins;
+	Vector3 centerPoint;
+	
+	protected override void OnEnable ()
+	{
+		if(!Application.isPlaying)
+			return;
+
+		base.OnEnable ();
+
+		Bounds centerBounds = new Bounds( eyes[0].localPosition, Vector3.zero );
+		origins = new Vector3[eyes.Length];
+		for(int i = 0; i < eyes.Length; i++){
+			origins[i] = eyes[i].localPosition;
+			centerBounds.Encapsulate( origins[i] );
+		}
+
+		centerPoint = centerBounds.center;
+	}
+	
+	protected override void OnDisable ()
+	{
+		if(!Application.isPlaying)
+			return;
+
+		base.OnDisable ();
+	}
+	
+	public override void DoUpdate ()
+	{
+
+		if(target != null)
+			targetPosition = target.position;
+
+		Vector3 goal = targetPosition;
+
+		Vector3 center = transform.TransformPoint(centerPoint);
+		Vector3 dir = goal - center;
+
+		if(dir.magnitude > 1) 
+			dir.Normalize();
+
+		for(int i = 0; i < eyes.Length; i++){
+			center = transform.TransformPoint(origins[i]);
+			eyes[i].position = Vector3.MoveTowards(eyes[i].position, center + (dir * radius), speed * Time.deltaTime);
+		}
+		
+	}	
+}

+ 8 - 0
spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtilityEyeConstraint.cs.meta

@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 0d994c65b6daec64f80ae2ae04e9d999
+MonoImporter:
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 

+ 58 - 0
spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtilityGroundConstraint.cs

@@ -0,0 +1,58 @@
+using UnityEngine;
+using System.Collections;
+
+[RequireComponent(typeof(SkeletonUtilityBone)), ExecuteInEditMode]
+public class SkeletonUtilityGroundConstraint : SkeletonUtilityConstraint {
+
+	public LayerMask groundMask;
+	public bool use2D = true;
+	public float castDistance = 5f;
+	Vector3 rayOrigin;
+	Vector3 rayDir = new Vector3(0,-1,0);
+	float hitY;
+
+	protected override void OnEnable ()
+	{
+		base.OnEnable ();
+	}
+
+	protected override void OnDisable ()
+	{
+		base.OnDisable ();
+	}
+
+	public override void DoUpdate()
+	{
+		rayOrigin = transform.position + new Vector3(0,castDistance,0);
+
+		hitY = float.MinValue;
+		if(use2D){
+			RaycastHit2D hit = Physics2D.Raycast(rayOrigin , rayDir, castDistance, groundMask);
+			if(hit.collider != null){
+				hitY = hit.point.y;
+			}
+		}
+		else{
+			RaycastHit hit;
+			if(Physics.Raycast( rayOrigin, rayDir, out hit, castDistance, groundMask)){
+				hitY = hit.point.y;
+			}
+		}
+
+		Vector3 v = transform.position;
+		v.y = Mathf.Clamp(v.y, hitY, float.MaxValue);
+		transform.position = v;
+		
+		utilBone.bone.X = transform.localPosition.x;
+		utilBone.bone.Y = transform.localPosition.y;
+
+	}
+
+	void OnDrawGizmos(){
+		Vector3 hitEnd = rayOrigin + (rayDir * Mathf.Min(castDistance, rayOrigin.y - hitY));
+		Vector3 clearEnd = rayOrigin + (rayDir * castDistance);
+		Gizmos.DrawLine(rayOrigin, hitEnd);
+		Gizmos.color = Color.red;
+		Gizmos.DrawLine(hitEnd, clearEnd);
+	}
+}

+ 8 - 0
spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtilityGroundConstraint.cs.meta

@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 3662334b99de5fe4396ab24e30c4fd12
+MonoImporter:
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 

+ 81 - 0
spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtilityKinematicShadow.cs

@@ -0,0 +1,81 @@
+using UnityEngine;
+using System.Collections;
+using System.Collections.Generic;
+
+public class SkeletonUtilityKinematicShadow : MonoBehaviour {
+
+	public bool hideShadow = true;
+
+	Dictionary<Transform, Transform> shadowTable;
+	GameObject shadowRoot;
+
+	void Start(){
+		shadowRoot = (GameObject)Instantiate(gameObject);
+		if(hideShadow)
+			shadowRoot.hideFlags = HideFlags.HideInHierarchy;
+
+		shadowRoot.transform.parent = transform.root;
+
+		shadowTable = new Dictionary<Transform, Transform>();
+
+		Destroy(shadowRoot.GetComponent<SkeletonUtilityKinematicShadow>());
+
+		shadowRoot.transform.position = transform.position;
+		shadowRoot.transform.rotation = transform.rotation;
+
+		Vector3 scaleRef = transform.TransformPoint(Vector3.right);
+		float scale = Vector3.Distance(transform.position, scaleRef);
+		shadowRoot.transform.localScale = Vector3.one;
+
+		var shadowJoints = shadowRoot.GetComponentsInChildren<Joint>();
+		foreach(Joint j in shadowJoints){
+			j.connectedAnchor *= scale;
+		}
+
+		var joints = GetComponentsInChildren<Joint>();
+		foreach(var j in joints)
+			Destroy(j);
+
+		var rbs = GetComponentsInChildren<Rigidbody>();
+		foreach(var rb in rbs)
+			Destroy(rb);
+
+		var colliders = GetComponentsInChildren<Collider>();
+		foreach(var c in colliders)
+			Destroy(c);
+
+
+		//match by bone name
+		var shadowBones = shadowRoot.GetComponentsInChildren<SkeletonUtilityBone>();
+		var bones = GetComponentsInChildren<SkeletonUtilityBone>();
+
+		//build bone lookup
+		foreach(var b in bones){
+			if(b.gameObject == gameObject)
+				continue;
+
+			foreach(var sb in shadowBones){
+				if(sb.rigidbody == null)
+					continue;
+
+				if(sb.boneName == b.boneName){
+					shadowTable.Add( sb.transform, b.transform );
+					break;
+				}
+			}
+		}
+
+		foreach(var b in shadowBones)
+			Destroy(b);
+	}
+
+	void FixedUpdate(){
+		shadowRoot.rigidbody.MovePosition( transform.position );
+		shadowRoot.rigidbody.MoveRotation( transform.rotation );
+
+		foreach(var pair in shadowTable){
+			pair.Value.localPosition = pair.Key.localPosition;
+			pair.Value.localRotation = pair.Key.localRotation;
+		}
+	}
+}

+ 8 - 0
spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtilityKinematicShadow.cs.meta

@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: cfeac06b8a6aa1645813700e3e4c0863
+MonoImporter:
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 

+ 102 - 0
spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtilitySubmeshRenderer.cs

@@ -0,0 +1,102 @@
+using UnityEngine;
+using System.Collections;
+
+[ExecuteInEditMode]
+public class SkeletonUtilitySubmeshRenderer : MonoBehaviour {
+
+	public Renderer parentRenderer;
+
+	[System.NonSerialized]
+	public Mesh mesh;
+
+	public int submeshIndex = 0;
+	public int sortingOrder = 0;
+	public int sortingLayerID = 0;
+
+	public Material hiddenPassMaterial;
+	Renderer cachedRenderer;
+	MeshFilter filter;
+	Material[] sharedMaterials;
+	MeshFilter parentFilter;
+
+	void Awake(){
+		cachedRenderer = renderer;
+		sharedMaterials = cachedRenderer.sharedMaterials;
+		filter = GetComponent<MeshFilter>();
+
+		if(parentRenderer != null)
+			Initialize( parentRenderer );
+	}
+
+	void OnEnable(){
+		parentRenderer = transform.parent.GetComponent<Renderer>();
+		parentRenderer.GetComponent<SkeletonRenderer>().OnReset += HandleSkeletonReset;
+	}
+
+	void OnDisable(){
+		parentRenderer.GetComponent<SkeletonRenderer>().OnReset -= HandleSkeletonReset;
+	}
+
+	void HandleSkeletonReset(SkeletonRenderer r){
+		if(parentRenderer != null)
+			Initialize(parentRenderer);
+	}
+
+	public void Initialize(Renderer parentRenderer){
+		this.parentRenderer = parentRenderer;
+		parentFilter = parentRenderer.GetComponent<MeshFilter>();
+		mesh = parentFilter.sharedMesh;
+		filter.sharedMesh = mesh;
+		Debug.Log("Mesh: " + mesh);
+	}
+
+	public void Update(){
+		if(mesh == null || mesh != parentFilter.sharedMesh){
+			mesh = parentFilter.sharedMesh;
+			filter.sharedMesh = mesh;
+		}
+
+		if(cachedRenderer == null)
+			cachedRenderer = renderer;
+
+		if(mesh == null || submeshIndex > mesh.subMeshCount - 1){
+			cachedRenderer.enabled = false;
+			return;
+		}
+		else{
+			renderer.enabled = true;
+		}
+
+		bool changed = false;
+
+		if(sharedMaterials.Length != parentRenderer.sharedMaterials.Length){
+			sharedMaterials = parentRenderer.sharedMaterials;
+			changed = true;
+		}
+
+
+
+		for(int i = 0; i < renderer.sharedMaterials.Length; i++){
+			if(i == submeshIndex)
+				continue;
+
+			if(sharedMaterials[i] != hiddenPassMaterial){
+				sharedMaterials[i] = hiddenPassMaterial;
+				changed = true;
+			}
+		}
+
+		if(sharedMaterials[submeshIndex] != parentRenderer.sharedMaterials[submeshIndex]){
+			sharedMaterials[submeshIndex] = parentRenderer.sharedMaterials[submeshIndex];
+			changed = true;
+		}
+
+		if(changed){
+			cachedRenderer.sharedMaterials = sharedMaterials;
+		}
+
+
+		cachedRenderer.sortingLayerID = sortingLayerID;
+		cachedRenderer.sortingOrder = sortingOrder;
+	}
+}

部分文件因为文件数量过多而无法显示