瀏覽代碼

Merge branch '3.6-beta' of https://github.com/esotericsoftware/spine-runtimes into 3.6-beta

badlogic 8 年之前
父節點
當前提交
bdce56767e
共有 28 個文件被更改,包括 299 次插入137 次删除
  1. 1 0
      spine-c/spine-c/include/spine/AnimationState.h
  2. 4 4
      spine-csharp/src/AnimationState.cs
  3. 15 6
      spine-csharp/src/Skin.cs
  4. 13 3
      spine-libgdx/spine-skeletonviewer/src/com/esotericsoftware/spine/SkeletonViewer.java
  5. 10 1
      spine-starling/spine-starling/src/spine/starling/SkeletonSprite.as
  6. 1 1
      spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonAnimationComponent.h
  7. 1 0
      spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonComponent.h
  8. 1 1
      spine-ue4/README.md
  9. 1 1
      spine-unity/Assets/Examples/Getting Started/Scripts/BasicPlatformerController.cs
  10. 2 2
      spine-unity/Assets/Examples/Getting Started/Scripts/SpineBeginnerTwo.cs
  11. 1 1
      spine-unity/Assets/Examples/Getting Started/Scripts/SpineBlinkPlayer.cs
  12. 5 5
      spine-unity/Assets/Examples/Getting Started/Scripts/SpineboyBeginnerView.cs
  13. 3 3
      spine-unity/Assets/Examples/Scripts/AttackSpineboy.cs
  14. 4 4
      spine-unity/Assets/Examples/Scripts/FootSoldierExample.cs
  15. 6 6
      spine-unity/Assets/Examples/Scripts/Goblins.cs
  16. 1 1
      spine-unity/Assets/Examples/Scripts/MixAndMatch.cs
  17. 8 8
      spine-unity/Assets/Examples/Scripts/Spineboy.cs
  18. 50 40
      spine-unity/Assets/spine-unity/Asset Types/Editor/SkeletonDataAssetInspector.cs
  19. 20 19
      spine-unity/Assets/spine-unity/Asset Types/SkeletonDataAsset.cs
  20. 13 7
      spine-unity/Assets/spine-unity/Editor/BoneFollowerInspector.cs
  21. 3 2
      spine-unity/Assets/spine-unity/Editor/SkeletonRendererInspector.cs
  22. 9 4
      spine-unity/Assets/spine-unity/Editor/SpineEditorUtilities.cs
  23. 1 1
      spine-unity/Assets/spine-unity/Mesh Generation/Arrays/ArraysSimpleMeshGenerator.cs
  24. 1 1
      spine-unity/Assets/spine-unity/Modules/AttachmentTools/AttachmentTools.cs
  25. 13 13
      spine-unity/Assets/spine-unity/Modules/BoundingBoxFollower/BoundingBoxFollower.cs
  26. 103 3
      spine-unity/Assets/spine-unity/SkeletonExtensions.cs
  27. 1 0
      spine-unity/Assets/spine-unity/version.txt
  28. 8 0
      spine-unity/Assets/spine-unity/version.txt.meta

+ 1 - 0
spine-c/spine-c/include/spine/AnimationState.h

@@ -64,6 +64,7 @@ struct spTrackEntry {
 	float* timelinesRotation;
 	int timelinesRotationCount;
 	void* rendererObject;
+	void* userData;
 
 #ifdef __cplusplus
 	spTrackEntry() :

+ 4 - 4
spine-csharp/src/AnimationState.cs

@@ -845,7 +845,7 @@ namespace Spine {
 	}
 
 	class EventQueue {
-		private readonly ExposedList<EventQueueEntry> eventQueueEntries = new ExposedList<EventQueueEntry>();
+		private readonly List<EventQueueEntry> eventQueueEntries = new List<EventQueueEntry>();
 		public bool drainDisabled;
 
 		private readonly AnimationState state;
@@ -905,11 +905,11 @@ namespace Spine {
 			drainDisabled = true;
 
 			var entries = this.eventQueueEntries;
-			var entriesItems = entries.Items;
 			AnimationState state = this.state;
 
-			for (int i = 0, n = entries.Count; i < n; i++) {
-				var queueEntry = entriesItems[i];
+			// Don't cache entries.Count so callbacks can queue their own events (eg, call SetAnimation in AnimationState_Complete).
+			for (int i = 0; i < entries.Count; i++) {
+				var queueEntry = entries[i];
 				TrackEntry trackEntry = queueEntry.entry;
 
 				switch (queueEntry.type) {

+ 15 - 6
spine-csharp/src/Skin.cs

@@ -32,38 +32,47 @@ using System;
 using System.Collections.Generic;
 
 namespace Spine {
-	/// <summary>Stores attachments by slot index and attachment name.</summary>
+	/// <summary>Stores attachments by slot index and attachment name.
+	/// <para>See SkeletonData <see cref="Spine.SkeletonData.DefaultSkin"/>, Skeleton <see cref="Spine.Skeleton.Skin"/>, and 
+	/// <a href="http://esotericsoftware.com/spine-runtime-skins">Runtime skins</a> in the Spine Runtimes Guide.</para>
+	/// </summary>
 	public class Skin {
 		internal String name;
 		private Dictionary<AttachmentKeyTuple, Attachment> attachments =
 			new Dictionary<AttachmentKeyTuple, Attachment>(AttachmentKeyTupleComparer.Instance);
 
-		public String Name { get { return name; } }
+		public string Name { get { return name; } }
 		public Dictionary<AttachmentKeyTuple, Attachment> Attachments { get { return attachments; } }
 
-		public Skin (String name) {
+		public Skin (string name) {
 			if (name == null) throw new ArgumentNullException("name", "name cannot be null.");
 			this.name = name;
 		}
 
-		public void AddAttachment (int slotIndex, String name, Attachment attachment) {
+		public void AddAttachment (int slotIndex, string name, Attachment attachment) {
 			if (attachment == null) throw new ArgumentNullException("attachment", "attachment cannot be null.");
 			attachments[new AttachmentKeyTuple(slotIndex, name)] = attachment;
 		}
 
 		/// <returns>May be null.</returns>
-		public Attachment GetAttachment (int slotIndex, String name) {
+		public Attachment GetAttachment (int slotIndex, string name) {
 			Attachment attachment;
 			attachments.TryGetValue(new AttachmentKeyTuple(slotIndex, name), out attachment);
 			return attachment;
 		}
 
-		public void FindNamesForSlot (int slotIndex, List<String> names) {
+		/// <summary>Finds the skin keys for a given slot. The results are added to the passed List(names).</summary>
+		/// <param name="slotIndex">The target slotIndex. To find the slot index, use <see cref="Spine.Skeleton.FindSlotIndex"/> or <see cref="Spine.SkeletonData.FindSlotIndex"/>
+		/// <param name="names">Found skin key names will be added to this list.</param>
+		public void FindNamesForSlot (int slotIndex, List<string> names) {
 			if (names == null) throw new ArgumentNullException("names", "names cannot be null.");
 			foreach (AttachmentKeyTuple key in attachments.Keys)
 				if (key.slotIndex == slotIndex) names.Add(key.name);
 		}
 
+		/// <summary>Finds the attachments for a given slot. The results are added to the passed List(Attachment).</summary>
+		/// <param name="slotIndex">The target slotIndex. To find the slot index, use <see cref="Spine.Skeleton.FindSlotIndex"/> or <see cref="Spine.SkeletonData.FindSlotIndex"/>
+		/// <param name="attachments">Found Attachments will be added to this list.</param>
 		public void FindAttachmentsForSlot (int slotIndex, List<Attachment> attachments) {
 			if (attachments == null) throw new ArgumentNullException("attachments", "attachments cannot be null.");
 			foreach (KeyValuePair<AttachmentKeyTuple, Attachment> entry in this.attachments)

+ 13 - 3
spine-libgdx/spine-skeletonviewer/src/com/esotericsoftware/spine/SkeletonViewer.java

@@ -664,14 +664,24 @@ public class SkeletonViewer extends ApplicationAdapter {
 				button.addListener(trackButtonListener);
 
 			Gdx.input.setInputProcessor(new InputMultiplexer(stage, new InputAdapter() {
+				float offsetX;
+				float offsetY;
+				
 				public boolean touchDown (int screenX, int screenY, int pointer, int button) {
-					touchDragged(screenX, screenY, pointer);
+					offsetX = screenX;
+					offsetY = Gdx.graphics.getHeight() - screenY;					
 					return false;
 				}
 
 				public boolean touchDragged (int screenX, int screenY, int pointer) {
-					skeletonX = screenX;
-					skeletonY = Gdx.graphics.getHeight() - screenY;
+					float deltaX = screenX - offsetX;
+					float deltaY = Gdx.graphics.getHeight() - screenY - offsetY;
+					
+					skeletonX += deltaX;
+					skeletonY += deltaY;
+					
+					offsetX = screenX;
+					offsetY = Gdx.graphics.getHeight() - screenY;
 					return false;
 				}
 

+ 10 - 1
spine-starling/spine-starling/src/spine/starling/SkeletonSprite.as

@@ -69,7 +69,7 @@ public class SkeletonSprite extends DisplayObject {
 	}
 
 	override public function render (painter:Painter) : void {
-		alpha *= this.alpha * skeleton.color.a;
+		painter.state.alpha *= skeleton.a;
 		var originalBlendMode:String = painter.state.blendMode;
 		var r:Number = skeleton.color.r * 255;
 		var g:Number = skeleton.color.g * 255;
@@ -164,6 +164,7 @@ public class SkeletonSprite extends DisplayObject {
 					mesh.setTexCoords(ii, uvs[iii], uvs[iii+1]);
 				}
 				vertexData.numVertices = verticesCount;
+				painter.state.blendMode = blendModes[slot.data.blendMode.ordinal];
 				// FIXME set smoothing/filter
 				painter.batchMesh(mesh);
 			}
@@ -180,6 +181,7 @@ public class SkeletonSprite extends DisplayObject {
 		var maxX:Number = -Number.MAX_VALUE, maxY:Number = -Number.MAX_VALUE;
 		var slots:Vector.<Slot> = skeleton.slots;
 		var worldVertices:Vector.<Number> = _tempVertices;
+		var empty:Boolean = true;
 		for (var i:int = 0, n:int = slots.length; i < n; ++i) {
 			var slot:Slot = slots[i];
 			var attachment:Attachment = slot.attachment;
@@ -196,6 +198,10 @@ public class SkeletonSprite extends DisplayObject {
 				mesh.computeWorldVertices(slot, 0, verticesLength, worldVertices, 0, 2);
 			} else
 				continue;
+				
+			if (verticesLength != 0)
+				empty = false;
+			  
 			for (var ii:int = 0; ii < verticesLength; ii += 2) {
 				var x:Number = worldVertices[ii], y:Number = worldVertices[ii + 1];
 				minX = minX < x ? minX : x;
@@ -204,6 +210,9 @@ public class SkeletonSprite extends DisplayObject {
 				maxY = maxY > y ? maxY : y;
 			}
 		}
+		
+		if (empty)
+			return null;
 
 		var temp:Number;
 		if (maxX < minX) {

+ 1 - 1
spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonAnimationComponent.h

@@ -82,7 +82,7 @@ public:
 	UTrackEntry () { }		
 
 	void SetTrackEntry (spTrackEntry* entry);
-	spTrackEntry* GetTrackEntry();
+	spTrackEntry* GetTrackEntry() { return entry; }
 	
 	UFUNCTION(BlueprintCallable, Category="Components|Spine|TrackEntry")
 	int GetTrackIndex () { return entry ? entry->trackIndex : 0; }

+ 1 - 0
spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonComponent.h

@@ -31,6 +31,7 @@
 #pragma once
 
 #include "Components/ActorComponent.h"
+#include "SpineSkeletonDataAsset.h"
 #include "spine/spine.h"
 #include "SpineSkeletonComponent.generated.h"
 

+ 1 - 1
spine-ue4/README.md

@@ -1,7 +1,7 @@
 # spine-ue4
 The spine-ue4 runtime provides functionality to load, manipulate and render [Spine](http://esotericsoftware.com) skeletal animation data using [Unreal Engine 4.15+](https://www.unrealengine.com/). spine-ue4 is based on [spine-c](https://github.com/EsotericSoftware/spine-runtimes/tree/master/spine-c).
 
-# WARNING This plugin will only work with Unreal Engine 4.15 and later versions as these include a [fix](https://github.com/EpicGames/UnrealEngine/pull/3015) for compiling plain `.c` files in Visual Studio.
+### WARNING This plugin will only work with Unreal Engine 4.15 and later when this [fix](https://github.com/EpicGames/UnrealEngine/pull/3185) for compiling plain `.c` files is applied.
 
 ## Licensing
 

+ 1 - 1
spine-unity/Assets/Examples/Getting Started/Scripts/BasicPlatformerController.cs

@@ -93,7 +93,7 @@ namespace Spine.Unity.Examples {
 
 		void Start () {
 			// Register a callback for Spine Events (in this case, Footstep)
-			skeletonAnimation.state.Event += HandleEvent;
+			skeletonAnimation.AnimationState.Event += HandleEvent;
 		}
 
 		void HandleEvent (Spine.TrackEntry trackEntry, Spine.Event e) {

+ 2 - 2
spine-unity/Assets/Examples/Getting Started/Scripts/SpineBeginnerTwo.cs

@@ -59,8 +59,8 @@ namespace Spine.Unity.Examples {
 		void Start () {
 			// Make sure you get these AnimationState and Skeleton references in Start or Later. Getting and using them in Awake is not guaranteed by default execution order.
 			skeletonAnimation = GetComponent<SkeletonAnimation>();
-			spineAnimationState = skeletonAnimation.state;
-			skeleton = skeletonAnimation.skeleton;
+			spineAnimationState = skeletonAnimation.AnimationState;
+			skeleton = skeletonAnimation.Skeleton;
 
 			StartCoroutine(DoDemoRoutine());
 		}

+ 1 - 1
spine-unity/Assets/Examples/Getting Started/Scripts/SpineBlinkPlayer.cs

@@ -44,7 +44,7 @@ namespace Spine.Unity.Examples {
 		IEnumerator Start () {
 			var skeletonAnimation = GetComponent<SkeletonAnimation>(); if (skeletonAnimation == null) yield break;
 			while (true) {
-				skeletonAnimation.state.SetAnimation(SpineBlinkPlayer.BlinkTrack, blinkAnimation, false);
+				skeletonAnimation.AnimationState.SetAnimation(SpineBlinkPlayer.BlinkTrack, blinkAnimation, false);
 				yield return new WaitForSeconds(Random.Range(minimumDelay, maximumDelay));
 			}
 		}

+ 5 - 5
spine-unity/Assets/Examples/Getting Started/Scripts/SpineboyBeginnerView.cs

@@ -57,7 +57,7 @@ namespace Spine.Unity.Examples {
 		void Start () {
 			if (skeletonAnimation == null) return;
 			model.ShootEvent += PlayShoot;
-			skeletonAnimation.state.Event += HandleEvent;
+			skeletonAnimation.AnimationState.Event += HandleEvent;
 		}
 
 		void HandleEvent (Spine.TrackEntry trackEntry, Spine.Event e) {
@@ -104,7 +104,7 @@ namespace Spine.Unity.Examples {
 				}
 			}
 
-			skeletonAnimation.state.SetAnimation(0, nextAnimation, true);
+			skeletonAnimation.AnimationState.SetAnimation(0, nextAnimation, true);
 		}
 
 		void PlayFootstepSound () {
@@ -114,7 +114,7 @@ namespace Spine.Unity.Examples {
 
 		[ContextMenu("Check Tracks")]
 		void CheckTracks () {
-			var state = skeletonAnimation.state;
+			var state = skeletonAnimation.AnimationState;
 			Debug.Log(state.GetCurrent(0));
 			Debug.Log(state.GetCurrent(1));
 		}
@@ -122,7 +122,7 @@ namespace Spine.Unity.Examples {
 		#region Transient Actions
 		public void PlayShoot () {
 			// Play the shoot animation on track 1.
-			skeletonAnimation.state.SetAnimation(1, shoot, false);
+			skeletonAnimation.AnimationState.SetAnimation(1, shoot, false);
 			//skeletonAnimation.state.AddEmptyAnimation(1, 0.1f, 0f);
 			gunSource.pitch = GetRandomPitch(gunsoundPitchOffset);
 			gunSource.Play();
@@ -131,7 +131,7 @@ namespace Spine.Unity.Examples {
 		}
 
 		public void Turn (bool facingLeft) {
-			skeletonAnimation.skeleton.FlipX = facingLeft;
+			skeletonAnimation.Skeleton.FlipX = facingLeft;
 			// Maybe play a transient turning animation too, then call ChangeStableAnimation.
 		}
 		#endregion

+ 3 - 3
spine-unity/Assets/Examples/Scripts/AttackSpineboy.cs

@@ -47,13 +47,13 @@ namespace Spine.Unity.Examples {
 				healthText.text = currentHealth + "/" + maxHealth;
 
 				if (currentHealth > 0) {
-					spineboy.state.SetAnimation(0, "hit", false);
-					spineboy.state.AddAnimation(0, "idle", true, 0);
+					spineboy.AnimationState.SetAnimation(0, "hit", false);
+					spineboy.AnimationState.AddAnimation(0, "idle", true, 0);
 					gauge.fillPercent = (float)currentHealth/(float)maxHealth;
 				} else {
 					if (currentHealth >= 0) {
 						gauge.fillPercent = 0;
-						spineboy.state.SetAnimation(0, "death", false).TrackEnd = float.PositiveInfinity;
+						spineboy.AnimationState.SetAnimation(0, "death", false).TrackEnd = float.PositiveInfinity;
 					}
 				}
 			}

+ 4 - 4
spine-unity/Assets/Examples/Scripts/FootSoldierExample.cs

@@ -80,11 +80,11 @@ namespace Spine.Unity.Examples {
 			} else {
 				if (Input.GetKey(rightKey)) {
 					skeletonAnimation.AnimationName = moveAnimation;
-					skeletonAnimation.skeleton.FlipX = false;
+					skeletonAnimation.Skeleton.FlipX = false;
 					transform.Translate(moveSpeed * Time.deltaTime, 0, 0);
 				} else if(Input.GetKey(leftKey)) {
 					skeletonAnimation.AnimationName = moveAnimation;
-					skeletonAnimation.skeleton.FlipX = true;
+					skeletonAnimation.Skeleton.FlipX = true;
 					transform.Translate(-moveSpeed * Time.deltaTime, 0, 0);
 				} else {
 					skeletonAnimation.AnimationName = idleAnimation;
@@ -95,9 +95,9 @@ namespace Spine.Unity.Examples {
 		IEnumerator Blink() {
 			while (true) {
 				yield return new WaitForSeconds(Random.Range(0.25f, 3f));
-				skeletonAnimation.skeleton.SetAttachment(eyesSlot, blinkAttachment);
+				skeletonAnimation.Skeleton.SetAttachment(eyesSlot, blinkAttachment);
 				yield return new WaitForSeconds(blinkDuration);
-				skeletonAnimation.skeleton.SetAttachment(eyesSlot, eyesOpenAttachment);
+				skeletonAnimation.Skeleton.SetAttachment(eyesSlot, eyesOpenAttachment);
 			}
 		}
 	}

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

@@ -43,7 +43,7 @@ namespace Spine.Unity.Examples {
 		
 		public void Start () {
 			skeletonAnimation = GetComponent<SkeletonAnimation>();
-			headBone = skeletonAnimation.skeleton.FindBone("head");
+			headBone = skeletonAnimation.Skeleton.FindBone("head");
 			skeletonAnimation.UpdateLocal += UpdateLocal;
 		}
 
@@ -53,16 +53,16 @@ namespace Spine.Unity.Examples {
 		}
 		
 		public void OnMouseDown () {
-			skeletonAnimation.skeleton.SetSkin(girlSkin ? "goblin" : "goblingirl");
-			skeletonAnimation.skeleton.SetSlotsToSetupPose();
+			skeletonAnimation.Skeleton.SetSkin(girlSkin ? "goblin" : "goblingirl");
+			skeletonAnimation.Skeleton.SetSlotsToSetupPose();
 			
 			girlSkin = !girlSkin;
 			
 			if (girlSkin) {
-				skeletonAnimation.skeleton.SetAttachment("right hand item", null);
-				skeletonAnimation.skeleton.SetAttachment("left hand item", "spear");
+				skeletonAnimation.Skeleton.SetAttachment("right hand item", null);
+				skeletonAnimation.Skeleton.SetAttachment("left hand item", "spear");
 			} else
-				skeletonAnimation.skeleton.SetAttachment("left hand item", "dagger");
+				skeletonAnimation.Skeleton.SetAttachment("left hand item", "dagger");
 		}
 	}
 }

+ 1 - 1
spine-unity/Assets/Examples/Scripts/MixAndMatch.cs

@@ -76,7 +76,7 @@ namespace Spine.Unity.Examples {
 			// Case 1: Create an attachment from an atlas.
 			RegionAttachment newHand = handSource.GetAtlas().FindRegion(handRegion).ToRegionAttachment("new hand");
 			newHand.SetPositionOffset(newHandOffset);
-			newHand.rotation = newHandRotation;
+			newHand.Rotation = newHandRotation;
 			newHand.UpdateOffset();
 			int handSlotIndex = skeleton.FindSlotIndex(handSlot);
 			handTexture = newHand.GetRegion().ToTexture();

+ 8 - 8
spine-unity/Assets/Examples/Scripts/Spineboy.cs

@@ -29,7 +29,6 @@
  *****************************************************************************/
 
 using UnityEngine;
-
 using Spine;
 using Spine.Unity;
 
@@ -39,21 +38,22 @@ namespace Spine.Unity.Examples {
 
 		public void Start () {
 			skeletonAnimation = GetComponent<SkeletonAnimation>(); // Get the SkeletonAnimation component for the GameObject this script is attached to.
+			var animationState = skeletonAnimation.AnimationState;
 
-			skeletonAnimation.state.Event += HandleEvent;; // Call our method any time an animation fires an event.
-			skeletonAnimation.state.End += (entry) => Debug.Log("start: " + entry.trackIndex); // A lambda can be used for the callback instead of a method.
+			animationState.Event += HandleEvent;; // Call our method any time an animation fires an event.
+			animationState.End += (entry) => Debug.Log("start: " + entry.TrackIndex); // A lambda can be used for the callback instead of a method.
 
-			skeletonAnimation.state.AddAnimation(0, "jump", false, 2);	// Queue jump to be played on track 0 two seconds after the starting animation.
-			skeletonAnimation.state.AddAnimation(0, "run", true, 0); // Queue walk to be looped on track 0 after the jump animation.
+			animationState.AddAnimation(0, "jump", false, 2);	// Queue jump to be played on track 0 two seconds after the starting animation.
+			animationState.AddAnimation(0, "run", true, 0); // Queue walk to be looped on track 0 after the jump animation.
 		}
 
 		void HandleEvent (TrackEntry trackEntry, Spine.Event e) {
-			Debug.Log(trackEntry.trackIndex + " " + trackEntry.animation.name + ": event " + e + ", " + e.Int);
+			Debug.Log(trackEntry.TrackIndex + " " + trackEntry.Animation.Name + ": event " + e + ", " + e.Int);
 		}
 
 		public void OnMouseDown () {
-			skeletonAnimation.state.SetAnimation(0, "jump", false); // Set jump to be played on track 0 immediately.
-			skeletonAnimation.state.AddAnimation(0, "run", true, 0); // Queue walk to be looped on track 0 after the jump animation.
+			skeletonAnimation.AnimationState.SetAnimation(0, "jump", false); // Set jump to be played on track 0 immediately.
+			skeletonAnimation.AnimationState.AddAnimation(0, "run", true, 0); // Queue walk to be looped on track 0 after the jump animation.
 		}
 	}
 

+ 50 - 40
spine-unity/Assets/spine-unity/Asset Types/Editor/SkeletonDataAssetInspector.cs

@@ -83,24 +83,6 @@ namespace Spine.Unity.Editor {
 			SpineEditorUtilities.ConfirmInitialization();
 			m_skeletonDataAsset = (SkeletonDataAsset)target;
 
-			// Clear empty atlas array items.
-			{
-				bool hasNulls = false;
-				foreach (var a in m_skeletonDataAsset.atlasAssets) {
-					if (a == null) {
-						hasNulls = true;
-						break;
-					}
-				}
-				if (hasNulls) {
-					var trimmedAtlasAssets = new List<AtlasAsset>();
-					foreach (var a in m_skeletonDataAsset.atlasAssets) {
-						if (a != null) trimmedAtlasAssets.Add(a);
-					}
-					m_skeletonDataAsset.atlasAssets = trimmedAtlasAssets.ToArray();
-				}
-			}
-
 			atlasAssets = serializedObject.FindProperty("atlasAssets");
 			skeletonJSON = serializedObject.FindProperty("skeletonJSON");
 			scale = serializedObject.FindProperty("scale");
@@ -126,8 +108,8 @@ namespace Spine.Unity.Editor {
 
 			m_skeletonDataAssetGUID = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(m_skeletonDataAsset));
 			EditorApplication.update += EditorUpdate;
-			m_skeletonData = m_skeletonDataAsset.GetSkeletonData(false);
 			RepopulateWarnings();
+			m_skeletonData = warnings.Count == 0 ? m_skeletonDataAsset.GetSkeletonData(false) : null;
 		}
 
 		void OnDestroy () {
@@ -228,8 +210,9 @@ namespace Spine.Unity.Editor {
 						m_previewUtility.Cleanup();
 						m_previewUtility = null;
 					}
-					RepopulateWarnings();
-					OnEnable();
+					m_skeletonDataAsset.Clear();
+					m_skeletonData = null;
+					OnEnable(); // Should call RepopulateWarnings.
 					return;
 				}
 			}
@@ -258,7 +241,6 @@ namespace Spine.Unity.Editor {
 				using (new EditorGUI.DisabledGroupScope(skeletonJSON.objectReferenceValue == null)) {
 					if (GUILayout.Button(new GUIContent("Attempt Reimport", Icons.warning))) {
 						DoReimport();
-						return;
 					}
 				}
 				#else
@@ -376,15 +358,12 @@ namespace Spine.Unity.Editor {
 
 		void DoReimport () {
 			SpineEditorUtilities.ImportSpineContent(new string[] { AssetDatabase.GetAssetPath(skeletonJSON.objectReferenceValue) }, true);
-
 			if (m_previewUtility != null) {
 				m_previewUtility.Cleanup();
 				m_previewUtility = null;
 			}
 
-			RepopulateWarnings();
-			OnEnable();
-
+			OnEnable(); // Should call RepopulateWarnings.
 			EditorUtility.SetDirty(m_skeletonDataAsset);
 		}
 
@@ -535,6 +514,25 @@ namespace Spine.Unity.Editor {
 		void RepopulateWarnings () {
 			warnings.Clear();
 
+			// Clear null entries.
+			{
+				bool hasNulls = false;
+				foreach (var a in m_skeletonDataAsset.atlasAssets) {
+					if (a == null) {
+						hasNulls = true;
+						break;
+					}
+				}
+				if (hasNulls) {
+					var trimmedAtlasAssets = new List<AtlasAsset>();
+					foreach (var a in m_skeletonDataAsset.atlasAssets) {
+						if (a != null) trimmedAtlasAssets.Add(a);
+					}
+					m_skeletonDataAsset.atlasAssets = trimmedAtlasAssets.ToArray();
+				}
+				serializedObject.Update();
+			}
+
 			if (skeletonJSON.objectReferenceValue == null) {
 				warnings.Add("Missing Skeleton JSON");
 			} else {
@@ -544,12 +542,13 @@ namespace Spine.Unity.Editor {
 					#if !SPINE_TK2D
 					bool detectedNullAtlasEntry = false;
 					var atlasList = new List<Atlas>();
-					for (int i = 0; i < atlasAssets.arraySize; i++) {
-						if (atlasAssets.GetArrayElementAtIndex(i).objectReferenceValue == null) {
+					var actualAtlasAssets = m_skeletonDataAsset.atlasAssets;
+					for (int i = 0; i < actualAtlasAssets.Length; i++) {
+						if (m_skeletonDataAsset.atlasAssets[i] == null) {
 							detectedNullAtlasEntry = true;
 							break;
 						} else {
-							atlasList.Add(((AtlasAsset)atlasAssets.GetArrayElementAtIndex(i).objectReferenceValue).GetAtlas());
+							atlasList.Add(actualAtlasAssets[i].GetAtlas());
 						}
 					}
 
@@ -642,26 +641,37 @@ namespace Spine.Unity.Editor {
 		void CreatePreviewInstances () {
 			this.DestroyPreviewInstances();
 
+			if (warnings.Count > 0) {
+				m_skeletonDataAsset.Clear();
+				return;
+			}
+
 			var skeletonDataAsset = (SkeletonDataAsset)target;
 			if (skeletonDataAsset.GetSkeletonData(false) == null)
 				return;
 
 			if (this.m_previewInstance == null) {
 				string skinName = EditorPrefs.GetString(m_skeletonDataAssetGUID + "_lastSkin", "");
-				m_previewInstance = SpineEditorUtilities.InstantiateSkeletonAnimation(skeletonDataAsset, skinName).gameObject;
 
-				if (m_previewInstance != null) {
-					m_previewInstance.hideFlags = HideFlags.HideAndDontSave;
-					m_previewInstance.layer = 0x1f;
-					m_skeletonAnimation = m_previewInstance.GetComponent<SkeletonAnimation>();
-					m_skeletonAnimation.initialSkinName = skinName;
-					m_skeletonAnimation.LateUpdate();
-					m_skeletonData = m_skeletonAnimation.skeletonDataAsset.GetSkeletonData(true);
-					m_previewInstance.GetComponent<Renderer>().enabled = false;
-					m_initialized = true;
+				try {
+					m_previewInstance = SpineEditorUtilities.InstantiateSkeletonAnimation(skeletonDataAsset, skinName).gameObject;
+
+					if (m_previewInstance != null) {
+						m_previewInstance.hideFlags = HideFlags.HideAndDontSave;
+						m_previewInstance.layer = 0x1f;
+						m_skeletonAnimation = m_previewInstance.GetComponent<SkeletonAnimation>();
+						m_skeletonAnimation.initialSkinName = skinName;
+						m_skeletonAnimation.LateUpdate();
+						m_skeletonData = m_skeletonAnimation.skeletonDataAsset.GetSkeletonData(true);
+						m_previewInstance.GetComponent<Renderer>().enabled = false;
+						m_initialized = true;
+					}
+
+					AdjustCameraGoals(true);
+				} catch {
+					DestroyPreviewInstances();
 				}
 
-				AdjustCameraGoals(true);
 			}
 		}
 

+ 20 - 19
spine-unity/Assets/spine-unity/Asset Types/SkeletonDataAsset.cs

@@ -89,14 +89,6 @@ namespace Spine.Unity {
 		}
 
 		public SkeletonData GetSkeletonData (bool quiet) {
-			if (atlasAssets == null) {
-				atlasAssets = new AtlasAsset[0];
-				if (!quiet)
-					Debug.LogError("Atlas not set for SkeletonData asset: " + name, this);
-				Clear();
-				return null;
-			}
-
 			if (skeletonJSON == null) {
 				if (!quiet)
 					Debug.LogError("Skeleton JSON file not set for SkeletonData asset: " + name, this);
@@ -104,17 +96,26 @@ namespace Spine.Unity {
 				return null;
 			}
 
-			#if !SPINE_TK2D
-			if (atlasAssets.Length == 0) {
-				Clear();
-				return null;
-			}
-			#else
-			if (atlasAssets.Length == 0 && spriteCollection == null) {
-				Clear();
-				return null;
-			}
-			#endif
+			// Support attachmentless/skinless SkeletonData.
+//			if (atlasAssets == null) {
+//				atlasAssets = new AtlasAsset[0];
+//				if (!quiet)
+//					Debug.LogError("Atlas not set for SkeletonData asset: " + name, this);
+//				Clear();
+//				return null;
+//			}
+//			#if !SPINE_TK2D
+//			if (atlasAssets.Length == 0) {
+//				Clear();
+//				return null;
+//			}
+//			#else
+//			if (atlasAssets.Length == 0 && spriteCollection == null) {
+//				Clear();
+//				return null;
+//			}
+//			#endif
+
 			if (skeletonData != null)
 				return skeletonData;
 

+ 13 - 7
spine-unity/Assets/spine-unity/Editor/BoneFollowerInspector.cs

@@ -74,6 +74,13 @@ namespace Spine.Unity.Editor {
 			targetBoneFollower = (BoneFollower)target;
 			if (targetBoneFollower.SkeletonRenderer != null)
 				targetBoneFollower.SkeletonRenderer.Initialize(false);
+
+			if (!targetBoneFollower.valid || needsReset) {
+				targetBoneFollower.Initialize();
+				targetBoneFollower.LateUpdate();
+				needsReset = false;
+				SceneView.RepaintAll();
+			}
 		}
 
 		public void OnSceneGUI () {
@@ -114,7 +121,7 @@ namespace Spine.Unity.Editor {
 				return;
 			}
 
-			if (needsReset) {
+			if (needsReset && UnityEngine.Event.current.type == EventType.Layout) {
 				targetBoneFollower.Initialize();
 				targetBoneFollower.LateUpdate();
 				needsReset = false;
@@ -143,11 +150,8 @@ namespace Spine.Unity.Editor {
 			if (targetBoneFollower.valid) {
 				EditorGUI.BeginChangeCheck();
 				EditorGUILayout.PropertyField(boneName);
-				if (EditorGUI.EndChangeCheck()) {
-					serializedObject.ApplyModifiedProperties();
-					needsReset = true;
-					serializedObject.Update();
-				}
+				needsReset |= EditorGUI.EndChangeCheck();
+
 				EditorGUILayout.PropertyField(followBoneRotation);
 				EditorGUILayout.PropertyField(followZPosition);
 				EditorGUILayout.PropertyField(followLocalScale);
@@ -169,8 +173,10 @@ namespace Spine.Unity.Editor {
 
 			var current = UnityEngine.Event.current;
 			bool wasUndo = (current.type == EventType.ValidateCommand && current.commandName == "UndoRedoPerformed");
-			if (serializedObject.ApplyModifiedProperties() || wasUndo)
+			if (wasUndo)
 				targetBoneFollower.Initialize();
+
+			serializedObject.ApplyModifiedProperties();
 		}
 	}
 

+ 3 - 2
spine-unity/Assets/spine-unity/Editor/SkeletonRendererInspector.cs

@@ -214,8 +214,9 @@ namespace Spine.Unity.Editor {
 						if (skinNameString == initialSkinName.stringValue)
 							skinIndex = i;
 					}
-					skinIndex = EditorGUILayout.Popup("Initial Skin", skinIndex, skins);			
-					initialSkinName.stringValue = skins[skinIndex];
+					skinIndex = EditorGUILayout.Popup("Initial Skin", skinIndex, skins);
+					if (skins.Length > 0) // Support attachmentless/skinless SkeletonData.
+						initialSkinName.stringValue = skins[skinIndex];
 				}
 			}
 

+ 9 - 4
spine-unity/Assets/spine-unity/Editor/SpineEditorUtilities.cs

@@ -603,7 +603,7 @@ namespace Spine.Unity.Editor {
 				var localAtlases = FindAtlasesAtPath(dir);
 				var requiredPaths = GetRequiredAtlasRegions(sp);
 				var atlasMatch = GetMatchingAtlas(requiredPaths, localAtlases);
-				if (atlasMatch != null) {
+				if (atlasMatch != null || requiredPaths.Count == 0) {
 					IngestSpineProject(AssetDatabase.LoadAssetAtPath(sp, typeof(TextAsset)) as TextAsset, atlasMatch);
 				} else {
 					bool resolved = false;
@@ -830,6 +830,8 @@ namespace Spine.Unity.Editor {
 			StringReader reader = new StringReader(spineJson.text);
 			var root = Json.Deserialize(reader) as Dictionary<string, object>;
 
+			if (!root.ContainsKey("skins"))
+				return requiredPaths;			
 
 			foreach (KeyValuePair<string, object> entry in (Dictionary<string, object>)root["skins"]) {
 				foreach (KeyValuePair<string, object> slotEntry in (Dictionary<string, object>)entry.Value) {
@@ -1326,9 +1328,12 @@ namespace Spine.Unity.Editor {
 				throw e;
 			}
 
-			skin = skin ?? data.DefaultSkin ?? data.Skins.Items[0];
-			newSkeletonAnimation.skeleton.SetSkin(skin);
-			newSkeletonAnimation.initialSkinName = skin.Name;
+			bool noSkins = data.DefaultSkin == null && (data.Skins == null || data.Skins.Count == 0); // Support attachmentless/skinless SkeletonData.
+			skin = skin ?? data.DefaultSkin ?? (noSkins ? null : data.Skins.Items[0]);
+			if (skin != null) {
+				newSkeletonAnimation.initialSkinName = skin.Name;
+				newSkeletonAnimation.skeleton.SetSkin(skin);
+			}
 
 			newSkeletonAnimation.skeleton.Update(0);
 			newSkeletonAnimation.state.Update(0);

+ 1 - 1
spine-unity/Assets/spine-unity/Mesh Generation/Arrays/ArraysSimpleMeshGenerator.cs

@@ -96,7 +96,7 @@ namespace Spine.Unity.MeshGeneration {
 
 				// Apply scale to vertices
 				meshBoundsMax.x *= scale; meshBoundsMax.y *= scale;
-				meshBoundsMin.x *= scale; meshBoundsMax.y *= scale;
+				meshBoundsMin.x *= scale; meshBoundsMin.y *= scale;
 				var vertices = this.meshVertices;
 				for (int i = 0; i < totalVertexCount; i++) {
 					Vector3 p = vertices[i];

+ 1 - 1
spine-unity/Assets/spine-unity/Modules/AttachmentTools/AttachmentTools.cs

@@ -341,7 +341,7 @@ namespace Spine.Unity.Modules.AttachmentTools {
 			return Sprite.Create(ar.GetMainTexture(), ar.GetUnityRect(), new Vector2(0.5f, 0.5f), pixelsPerUnit);
 		}
 
-		internal static Texture2D ToTexture (this AtlasRegion ar, bool applyImmediately = true) {
+		public static Texture2D ToTexture (this AtlasRegion ar, bool applyImmediately = true) {
 			Texture2D sourceTexture = ar.GetMainTexture();
 			Rect r = ar.GetUnityRect(sourceTexture.height);
 			Texture2D output = new Texture2D((int)r.width, (int)r.height);

+ 13 - 13
spine-unity/Assets/spine-unity/Modules/BoundingBoxFollower/BoundingBoxFollower.cs

@@ -160,25 +160,25 @@ namespace Spine.Unity {
 		}
 
 		void DisposeColliders () {
-			#if UNITY_EDITOR
 			var colliders = GetComponents<PolygonCollider2D>();
 			if (colliders.Length == 0) return;
 
-			if (Application.isPlaying) {
-				foreach (var c in colliders) {
-					if (c != null)
-						Destroy(c);
+			if (Application.isEditor) {
+				if (Application.isPlaying) {
+					foreach (var c in colliders) {
+						if (c != null)
+							Destroy(c);
+					}
+				} else {
+					foreach (var c in colliders)
+						if (c != null) 
+							DestroyImmediate(c);
 				}
 			} else {
-				foreach (var c in colliders)
-					if (c != null) 
-						DestroyImmediate(c);
+				foreach (PolygonCollider2D c in colliders)
+					if (c != null)
+						Destroy(c);
 			}
-			#else
-			foreach (PolygonCollider2D c in colliderTable.Values)
-				if (c != null)
-					Destroy(c);
-			#endif
 
 			slot = null;
 			currentAttachment = null;

+ 103 - 3
spine-unity/Assets/spine-unity/SkeletonExtensions.cs

@@ -100,16 +100,19 @@ namespace Spine.Unity {
 		#endregion
 
 		#region Bone
+		/// <summary>Sets the bone's (local) X and Y according to a Vector2</summary>
 		public static void SetPosition (this Bone bone, Vector2 position) {
 			bone.X = position.x;
 			bone.Y = position.y;
 		}
 
+		/// <summary>Sets the bone's (local) X and Y according to a Vector3. The z component is ignored.</summary>
 		public static void SetPosition (this Bone bone, Vector3 position) {
 			bone.X = position.x;
 			bone.Y = position.y;
 		}
 
+		/// <summary>Gets the bone's local X and Y as a Vector2.</summary>
 		public static Vector2 GetLocalPosition (this Bone bone) {
 			return new Vector2(bone.x, bone.y);
 		}
@@ -126,10 +129,12 @@ namespace Spine.Unity {
 			return o;
 		}
 
-		public static Vector3 GetWorldPosition (this Bone bone, UnityEngine.Transform parentTransform) {		
-			return parentTransform.TransformPoint(new Vector3(bone.worldX, bone.worldY));
+		/// <summary>Gets the bone's Unity World position using its Spine GameObject Transform. UpdateWorldTransform needs to have been called for this to return the correct, updated value.</summary>
+		public static Vector3 GetWorldPosition (this Bone bone, UnityEngine.Transform spineGameObjectTransform) {		
+			return spineGameObjectTransform.TransformPoint(new Vector3(bone.worldX, bone.worldY));
 		}
 
+		/// <summary>Gets the internal bone matrix as a Unity bonespace-to-skeletonspace transformation matrix.</summary>
 		public static Matrix4x4 GetMatrix4x4 (this Bone bone) {
 			return new Matrix4x4 {
 				m00 = bone.a, m01 = bone.b, m03 = bone.worldX,
@@ -138,7 +143,7 @@ namespace Spine.Unity {
 			};
 		}
 
-		/// <summary>Outputs a 2x2 Transformation Matrix that can convert a skeleton-space position to a bone-local position.</summary>
+		/// <summary>Calculates a 2x2 Transformation Matrix that can convert a skeleton-space position to a bone-local position.</summary>
 		public static void GetWorldToLocalMatrix (this Bone bone, out float ia, out float ib, out float ic, out float id) {
 			float a = bone.a, b = bone.b, c = bone.c, d = bone.d;
 			float invDet = 1 / (a * d - b * c);
@@ -236,6 +241,8 @@ namespace Spine.Unity {
 }
 
 namespace Spine {
+	using System.Collections.Generic;
+
 	public static class SkeletonExtensions {
 		public static bool IsWeighted (this VertexAttachment va) {
 			return va.bones != null && va.bones.Length > 0;
@@ -259,6 +266,85 @@ namespace Spine {
 			animation.Apply(skeleton, lastTime, time, loop, events, 1f, false, false);
 		}
 
+		internal static void SetPropertyToSetupPose (this Skeleton skeleton, int propertyID) {
+			int tt = propertyID >> 24;
+			var timelineType = (TimelineType)tt;
+			int i = propertyID - (tt << 24);
+
+			Bone bone;
+			IkConstraint ikc;
+			PathConstraint pc;
+
+			switch (timelineType) {
+			// Bone
+			case TimelineType.Rotate:
+				bone = skeleton.bones.Items[i];
+				bone.rotation = bone.data.rotation;
+				break;
+			case TimelineType.Translate:
+				bone = skeleton.bones.Items[i];
+				bone.x = bone.data.x;
+				bone.y = bone.data.y;
+				break;
+			case TimelineType.Scale:
+				bone = skeleton.bones.Items[i];
+				bone.scaleX = bone.data.scaleX;
+				bone.scaleY = bone.data.scaleY;
+				break;
+			case TimelineType.Shear:
+				bone = skeleton.bones.Items[i];
+				bone.shearX = bone.data.shearX;
+				bone.shearY = bone.data.shearY;
+				break;
+			
+			// Slot
+			case TimelineType.Attachment:
+				skeleton.SetSlotAttachmentToSetupPose(i);
+				break;
+			case TimelineType.Color:
+				skeleton.slots.Items[i].SetColorToSetupPose();
+				break;
+			case TimelineType.Deform:
+				skeleton.slots.Items[i].attachmentVertices.Clear();
+				break;
+			
+			// Skeleton
+			case TimelineType.DrawOrder:
+				skeleton.SetDrawOrderToSetupPose();
+				break;
+
+			// IK Constraint
+			case TimelineType.IkConstraint:
+				ikc = skeleton.ikConstraints.Items[i];
+				ikc.mix = ikc.data.mix;
+				ikc.bendDirection = ikc.data.bendDirection;
+				break;
+			case TimelineType.TransformConstraint:
+				var tc = skeleton.transformConstraints.Items[i];
+				var tcData = tc.data;
+				tc.rotateMix = tcData.rotateMix;
+				tc.translateMix = tcData.translateMix;
+				tc.scaleMix = tcData.scaleMix;
+				tc.shearMix = tcData.shearMix;
+				break;
+
+			// Path Constraint
+			case TimelineType.PathConstraintPosition:
+				pc = skeleton.pathConstraints.Items[i];
+				pc.position = pc.data.position;
+				break;
+			case TimelineType.PathConstraintSpacing:
+				pc = skeleton.pathConstraints.Items[i];
+				pc.spacing = pc.data.spacing;
+				break;
+			case TimelineType.PathConstraintMix:
+				pc = skeleton.pathConstraints.Items[i];
+				pc.rotateMix = pc.data.rotateMix;
+				pc.translateMix = pc.data.translateMix;
+				break;
+			}
+		}
+
 		/// <summary>Resets the DrawOrder to the Setup Pose's draw order</summary>
 		public static void SetDrawOrderToSetupPose (this Skeleton skeleton) {
 			var slotsItems = skeleton.slots.Items;
@@ -315,5 +401,19 @@ namespace Spine {
 			animation.Apply(skeleton, 0, 0, false, null, 0, true, true);
 		}
 		#endregion
+
+		#region Skins
+		/// <summary><see cref="Spine.Skin.FindNamesForSlot(int,List)"/></summary>
+		public static void FindNamesForSlot (this Skin skin, string slotName, SkeletonData skeletonData, List<string> results) {
+			int slotIndex = skeletonData.FindSlotIndex(slotName);
+			skin.FindNamesForSlot(slotIndex, results);
+		}
+
+		/// <summary><see cref="Spine.Skin.FindAttachmentsForSlot(int,List)"/></summary>
+		public static void FindAttachmentsForSlot (this Skin skin, string slotName, SkeletonData skeletonData, List<Attachment> results) {
+			int slotIndex = skeletonData.FindSlotIndex(slotName);
+			skin.FindAttachmentsForSlot(slotIndex, results);
+		}
+		#endregion
 	}
 }

+ 1 - 0
spine-unity/Assets/spine-unity/version.txt

@@ -0,0 +1 @@
+This Spine-Unity runtime works with data exported from Spine Editor version: 3.5.xx

+ 8 - 0
spine-unity/Assets/spine-unity/version.txt.meta

@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 80c06a67282e71043a4b1fad3e0c5654
+timeCreated: 1485965987
+licenseType: Free
+TextScriptImporter:
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: