diff --git a/spine-c/spine-c/include/spine/AnimationState.h b/spine-c/spine-c/include/spine/AnimationState.h index ebfba21f8..a2d68500d 100644 --- a/spine-c/spine-c/include/spine/AnimationState.h +++ b/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() : diff --git a/spine-csharp/src/AnimationState.cs b/spine-csharp/src/AnimationState.cs index 0acb79e4a..8d89ccfd7 100644 --- a/spine-csharp/src/AnimationState.cs +++ b/spine-csharp/src/AnimationState.cs @@ -845,7 +845,7 @@ namespace Spine { } class EventQueue { - private readonly ExposedList eventQueueEntries = new ExposedList(); + private readonly List eventQueueEntries = new List(); 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) { diff --git a/spine-csharp/src/Skin.cs b/spine-csharp/src/Skin.cs index 318779f77..973e49129 100644 --- a/spine-csharp/src/Skin.cs +++ b/spine-csharp/src/Skin.cs @@ -32,38 +32,47 @@ using System; using System.Collections.Generic; namespace Spine { - /// Stores attachments by slot index and attachment name. + /// Stores attachments by slot index and attachment name. + /// See SkeletonData , Skeleton , and + /// Runtime skins in the Spine Runtimes Guide. + /// public class Skin { internal String name; private Dictionary attachments = new Dictionary(AttachmentKeyTupleComparer.Instance); - public String Name { get { return name; } } + public string Name { get { return name; } } public Dictionary 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; } /// May be null. - 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 names) { + /// Finds the skin keys for a given slot. The results are added to the passed List(names). + /// The target slotIndex. To find the slot index, use or + /// Found skin key names will be added to this list. + public void FindNamesForSlot (int slotIndex, List 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); } + /// Finds the attachments for a given slot. The results are added to the passed List(Attachment). + /// The target slotIndex. To find the slot index, use or + /// Found Attachments will be added to this list. public void FindAttachmentsForSlot (int slotIndex, List attachments) { if (attachments == null) throw new ArgumentNullException("attachments", "attachments cannot be null."); foreach (KeyValuePair entry in this.attachments) diff --git a/spine-libgdx/spine-skeletonviewer/src/com/esotericsoftware/spine/SkeletonViewer.java b/spine-libgdx/spine-skeletonviewer/src/com/esotericsoftware/spine/SkeletonViewer.java index dbf8d33db..c493a028a 100644 --- a/spine-libgdx/spine-skeletonviewer/src/com/esotericsoftware/spine/SkeletonViewer.java +++ b/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; } diff --git a/spine-starling/spine-starling/src/spine/starling/SkeletonSprite.as b/spine-starling/spine-starling/src/spine/starling/SkeletonSprite.as index 3624b3f9d..cec7aae52 100644 --- a/spine-starling/spine-starling/src/spine/starling/SkeletonSprite.as +++ b/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. = skeleton.slots; var worldVertices:Vector. = _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) { diff --git a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonAnimationComponent.h b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonAnimationComponent.h index e551e9bd6..899093b23 100644 --- a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonAnimationComponent.h +++ b/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; } diff --git a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonComponent.h b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonComponent.h index 182a02ae1..9356f1ae1 100644 --- a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonComponent.h +++ b/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" diff --git a/spine-ue4/README.md b/spine-ue4/README.md index 2bf134974..15f78d977 100644 --- a/spine-ue4/README.md +++ b/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 diff --git a/spine-unity/Assets/Examples/Getting Started/Scripts/BasicPlatformerController.cs b/spine-unity/Assets/Examples/Getting Started/Scripts/BasicPlatformerController.cs index 9209dbc73..63590c0fe 100644 --- a/spine-unity/Assets/Examples/Getting Started/Scripts/BasicPlatformerController.cs +++ b/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) { diff --git a/spine-unity/Assets/Examples/Getting Started/Scripts/SpineBeginnerTwo.cs b/spine-unity/Assets/Examples/Getting Started/Scripts/SpineBeginnerTwo.cs index b53e1613f..ab89ae727 100644 --- a/spine-unity/Assets/Examples/Getting Started/Scripts/SpineBeginnerTwo.cs +++ b/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(); - spineAnimationState = skeletonAnimation.state; - skeleton = skeletonAnimation.skeleton; + spineAnimationState = skeletonAnimation.AnimationState; + skeleton = skeletonAnimation.Skeleton; StartCoroutine(DoDemoRoutine()); } diff --git a/spine-unity/Assets/Examples/Getting Started/Scripts/SpineBlinkPlayer.cs b/spine-unity/Assets/Examples/Getting Started/Scripts/SpineBlinkPlayer.cs index cf9393511..8b815702b 100644 --- a/spine-unity/Assets/Examples/Getting Started/Scripts/SpineBlinkPlayer.cs +++ b/spine-unity/Assets/Examples/Getting Started/Scripts/SpineBlinkPlayer.cs @@ -44,7 +44,7 @@ namespace Spine.Unity.Examples { IEnumerator Start () { var skeletonAnimation = GetComponent(); 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)); } } diff --git a/spine-unity/Assets/Examples/Getting Started/Scripts/SpineboyBeginnerView.cs b/spine-unity/Assets/Examples/Getting Started/Scripts/SpineboyBeginnerView.cs index 8c382c534..e5a711123 100644 --- a/spine-unity/Assets/Examples/Getting Started/Scripts/SpineboyBeginnerView.cs +++ b/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 diff --git a/spine-unity/Assets/Examples/Scripts/AttackSpineboy.cs b/spine-unity/Assets/Examples/Scripts/AttackSpineboy.cs index 5579ed32d..ef50581e6 100644 --- a/spine-unity/Assets/Examples/Scripts/AttackSpineboy.cs +++ b/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; } } } diff --git a/spine-unity/Assets/Examples/Scripts/FootSoldierExample.cs b/spine-unity/Assets/Examples/Scripts/FootSoldierExample.cs index 4ae760744..08cf1a6a7 100644 --- a/spine-unity/Assets/Examples/Scripts/FootSoldierExample.cs +++ b/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); } } } diff --git a/spine-unity/Assets/Examples/Scripts/Goblins.cs b/spine-unity/Assets/Examples/Scripts/Goblins.cs index 2dc3232c3..1fe1f5b55 100644 --- a/spine-unity/Assets/Examples/Scripts/Goblins.cs +++ b/spine-unity/Assets/Examples/Scripts/Goblins.cs @@ -43,7 +43,7 @@ namespace Spine.Unity.Examples { public void Start () { skeletonAnimation = GetComponent(); - 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"); } } } \ No newline at end of file diff --git a/spine-unity/Assets/Examples/Scripts/MixAndMatch.cs b/spine-unity/Assets/Examples/Scripts/MixAndMatch.cs index c404dbdc2..35dd08d9a 100644 --- a/spine-unity/Assets/Examples/Scripts/MixAndMatch.cs +++ b/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(); diff --git a/spine-unity/Assets/Examples/Scripts/Spineboy.cs b/spine-unity/Assets/Examples/Scripts/Spineboy.cs index 3705ad99e..5afc98e8c 100644 --- a/spine-unity/Assets/Examples/Scripts/Spineboy.cs +++ b/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(); // 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. } } diff --git a/spine-unity/Assets/spine-unity/Asset Types/Editor/SkeletonDataAssetInspector.cs b/spine-unity/Assets/spine-unity/Asset Types/Editor/SkeletonDataAssetInspector.cs index e3af97f98..7b0375b1d 100644 --- a/spine-unity/Assets/spine-unity/Asset Types/Editor/SkeletonDataAssetInspector.cs +++ b/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(); - 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(); + 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(); - 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(); - m_skeletonAnimation.initialSkinName = skinName; - m_skeletonAnimation.LateUpdate(); - m_skeletonData = m_skeletonAnimation.skeletonDataAsset.GetSkeletonData(true); - m_previewInstance.GetComponent().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(); + m_skeletonAnimation.initialSkinName = skinName; + m_skeletonAnimation.LateUpdate(); + m_skeletonData = m_skeletonAnimation.skeletonDataAsset.GetSkeletonData(true); + m_previewInstance.GetComponent().enabled = false; + m_initialized = true; + } + + AdjustCameraGoals(true); + } catch { + DestroyPreviewInstances(); } - AdjustCameraGoals(true); } } diff --git a/spine-unity/Assets/spine-unity/Asset Types/SkeletonDataAsset.cs b/spine-unity/Assets/spine-unity/Asset Types/SkeletonDataAsset.cs index 8fb9ee307..d995ce854 100644 --- a/spine-unity/Assets/spine-unity/Asset Types/SkeletonDataAsset.cs +++ b/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; diff --git a/spine-unity/Assets/spine-unity/Editor/BoneFollowerInspector.cs b/spine-unity/Assets/spine-unity/Editor/BoneFollowerInspector.cs index f7ecf47d9..58ac0fce0 100644 --- a/spine-unity/Assets/spine-unity/Editor/BoneFollowerInspector.cs +++ b/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(); } } diff --git a/spine-unity/Assets/spine-unity/Editor/SkeletonRendererInspector.cs b/spine-unity/Assets/spine-unity/Editor/SkeletonRendererInspector.cs index c3fe1d03a..c72e03421 100644 --- a/spine-unity/Assets/spine-unity/Editor/SkeletonRendererInspector.cs +++ b/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]; } } diff --git a/spine-unity/Assets/spine-unity/Editor/SpineEditorUtilities.cs b/spine-unity/Assets/spine-unity/Editor/SpineEditorUtilities.cs index 05333e11e..b8b0b6b32 100644 --- a/spine-unity/Assets/spine-unity/Editor/SpineEditorUtilities.cs +++ b/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; + if (!root.ContainsKey("skins")) + return requiredPaths; foreach (KeyValuePair entry in (Dictionary)root["skins"]) { foreach (KeyValuePair slotEntry in (Dictionary)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); diff --git a/spine-unity/Assets/spine-unity/Mesh Generation/Arrays/ArraysSimpleMeshGenerator.cs b/spine-unity/Assets/spine-unity/Mesh Generation/Arrays/ArraysSimpleMeshGenerator.cs index 51284d47d..0e9ead5d1 100644 --- a/spine-unity/Assets/spine-unity/Mesh Generation/Arrays/ArraysSimpleMeshGenerator.cs +++ b/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]; diff --git a/spine-unity/Assets/spine-unity/Modules/AttachmentTools/AttachmentTools.cs b/spine-unity/Assets/spine-unity/Modules/AttachmentTools/AttachmentTools.cs index 09d7e3c08..b94145e19 100644 --- a/spine-unity/Assets/spine-unity/Modules/AttachmentTools/AttachmentTools.cs +++ b/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); diff --git a/spine-unity/Assets/spine-unity/Modules/BoundingBoxFollower/BoundingBoxFollower.cs b/spine-unity/Assets/spine-unity/Modules/BoundingBoxFollower/BoundingBoxFollower.cs index 9a71ae8dc..2e5820c12 100644 --- a/spine-unity/Assets/spine-unity/Modules/BoundingBoxFollower/BoundingBoxFollower.cs +++ b/spine-unity/Assets/spine-unity/Modules/BoundingBoxFollower/BoundingBoxFollower.cs @@ -160,25 +160,25 @@ namespace Spine.Unity { } void DisposeColliders () { - #if UNITY_EDITOR var colliders = GetComponents(); 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; diff --git a/spine-unity/Assets/spine-unity/SkeletonExtensions.cs b/spine-unity/Assets/spine-unity/SkeletonExtensions.cs index 8f04b0a8b..0e6e43987 100644 --- a/spine-unity/Assets/spine-unity/SkeletonExtensions.cs +++ b/spine-unity/Assets/spine-unity/SkeletonExtensions.cs @@ -100,16 +100,19 @@ namespace Spine.Unity { #endregion #region Bone + /// Sets the bone's (local) X and Y according to a Vector2 public static void SetPosition (this Bone bone, Vector2 position) { bone.X = position.x; bone.Y = position.y; } + /// Sets the bone's (local) X and Y according to a Vector3. The z component is ignored. public static void SetPosition (this Bone bone, Vector3 position) { bone.X = position.x; bone.Y = position.y; } + /// Gets the bone's local X and Y as a Vector2. 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)); + /// 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. + public static Vector3 GetWorldPosition (this Bone bone, UnityEngine.Transform spineGameObjectTransform) { + return spineGameObjectTransform.TransformPoint(new Vector3(bone.worldX, bone.worldY)); } + /// Gets the internal bone matrix as a Unity bonespace-to-skeletonspace transformation matrix. 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 { }; } - /// Outputs a 2x2 Transformation Matrix that can convert a skeleton-space position to a bone-local position. + /// Calculates a 2x2 Transformation Matrix that can convert a skeleton-space position to a bone-local position. 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; + } + } + /// Resets the DrawOrder to the Setup Pose's draw order 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 + /// + public static void FindNamesForSlot (this Skin skin, string slotName, SkeletonData skeletonData, List results) { + int slotIndex = skeletonData.FindSlotIndex(slotName); + skin.FindNamesForSlot(slotIndex, results); + } + + /// + public static void FindAttachmentsForSlot (this Skin skin, string slotName, SkeletonData skeletonData, List results) { + int slotIndex = skeletonData.FindSlotIndex(slotName); + skin.FindAttachmentsForSlot(slotIndex, results); + } + #endregion } } diff --git a/spine-unity/Assets/spine-unity/version.txt b/spine-unity/Assets/spine-unity/version.txt new file mode 100644 index 000000000..dedefb0b3 --- /dev/null +++ b/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 \ No newline at end of file diff --git a/spine-unity/Assets/spine-unity/version.txt.meta b/spine-unity/Assets/spine-unity/version.txt.meta new file mode 100644 index 000000000..d34c5b7dc --- /dev/null +++ b/spine-unity/Assets/spine-unity/version.txt.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 80c06a67282e71043a4b1fad3e0c5654 +timeCreated: 1485965987 +licenseType: Free +TextScriptImporter: + userData: + assetBundleName: + assetBundleVariant: