From 09fac79d03539d895b8f9602a5063d218537cd34 Mon Sep 17 00:00:00 2001 From: NathanSweet Date: Fri, 25 Apr 2014 18:21:00 +0200 Subject: [PATCH] Meshes, FFD and skinning for spine-unity. --- spine-unity/Assets/Spine/AtlasAsset.cs | 11 +- spine-unity/Assets/Spine/BoneComponent.cs | 40 +- .../Spine/Editor/BoneComponentInspector.cs | 17 +- spine-unity/Assets/Spine/Editor/Menus.cs | 6 +- .../Editor/SkeletonAnimationInspector.cs | 9 +- ...pector.cs => SkeletonRendererInspector.cs} | 9 +- ...meta => SkeletonRendererInspector.cs.meta} | 2 +- spine-unity/Assets/Spine/SkeletonAnimation.cs | 34 +- .../Assets/Spine/SkeletonAnimation.cs.meta | 2 +- spine-unity/Assets/Spine/SkeletonComponent.cs | 334 ---------------- spine-unity/Assets/Spine/SkeletonDataAsset.cs | 7 +- spine-unity/Assets/Spine/SkeletonRenderer.cs | 366 ++++++++++++++++++ ...onent.cs.meta => SkeletonRenderer.cs.meta} | 2 +- .../Assets/examples/dragon/dragon.unity | Bin 20424 -> 20504 bytes .../Assets/examples/goblins/Goblins.cs | 4 +- .../Assets/examples/goblins/goblins.unity | Bin 20952 -> 20984 bytes .../Assets/examples/spineboy/Spineboy.cs | 4 +- .../examples/spineboy/data/spineboy.json.txt | 46 +-- .../Assets/examples/spineboy/spineboy.unity | Bin 20584 -> 20656 bytes 19 files changed, 468 insertions(+), 425 deletions(-) rename spine-unity/Assets/Spine/Editor/{SkeletonComponentInspector.cs => SkeletonRendererInspector.cs} (94%) rename spine-unity/Assets/Spine/Editor/{SkeletonComponentInspector.cs.meta => SkeletonRendererInspector.cs.meta} (78%) delete mode 100644 spine-unity/Assets/Spine/SkeletonComponent.cs create mode 100644 spine-unity/Assets/Spine/SkeletonRenderer.cs rename spine-unity/Assets/Spine/{SkeletonComponent.cs.meta => SkeletonRenderer.cs.meta} (78%) diff --git a/spine-unity/Assets/Spine/AtlasAsset.cs b/spine-unity/Assets/Spine/AtlasAsset.cs index 0c8bdf64a..76dc0d48c 100644 --- a/spine-unity/Assets/Spine/AtlasAsset.cs +++ b/spine-unity/Assets/Spine/AtlasAsset.cs @@ -33,6 +33,7 @@ using System.IO; using UnityEngine; using Spine; +/// Loads and stores a Spine atlas and list of materials. public class AtlasAsset : ScriptableObject { public TextAsset atlasFile; public Material[] materials; @@ -45,13 +46,13 @@ public class AtlasAsset : ScriptableObject { /// The atlas or null if it could not be loaded. public Atlas GetAtlas () { if (atlasFile == null) { - Debug.LogWarning("Atlas file not set for atlas asset: " + name, this); + Debug.LogError("Atlas file not set for atlas asset: " + name, this); Clear(); return null; } if (materials == null || materials.Length == 0) { - Debug.LogWarning("Materials not set for atlas asset: " + name, this); + Debug.LogError("Materials not set for atlas asset: " + name, this); Clear(); return null; } @@ -63,7 +64,7 @@ public class AtlasAsset : ScriptableObject { atlas = new Atlas(new StringReader(atlasFile.text), "", new MaterialsTextureLoader(this)); return atlas; } catch (Exception ex) { - Debug.Log("Error reading atlas file for atlas asset: " + name + "\n" + ex.Message + "\n" + ex.StackTrace, this); + Debug.LogError("Error reading atlas file for atlas asset: " + name + "\n" + ex.Message + "\n" + ex.StackTrace, this); return null; } } @@ -81,7 +82,7 @@ public class MaterialsTextureLoader : TextureLoader { Material material = null; foreach (Material other in atlasAsset.materials) { if (other.mainTexture == null) { - Debug.LogWarning("Material is missing texture: " + other.name, other); + Debug.LogError("Material is missing texture: " + other.name, other); return; } if (other.mainTexture.name == name) { @@ -90,7 +91,7 @@ public class MaterialsTextureLoader : TextureLoader { } } if (material == null) { - Debug.LogWarning("Material with texture name \"" + name + "\" not found for atlas asset: " + atlasAsset.name, atlasAsset); + Debug.LogError("Material with texture name \"" + name + "\" not found for atlas asset: " + atlasAsset.name, atlasAsset); return; } page.rendererObject = material; diff --git a/spine-unity/Assets/Spine/BoneComponent.cs b/spine-unity/Assets/Spine/BoneComponent.cs index 946ef09d9..ea88c227e 100644 --- a/spine-unity/Assets/Spine/BoneComponent.cs +++ b/spine-unity/Assets/Spine/BoneComponent.cs @@ -38,44 +38,52 @@ using Spine; [ExecuteInEditMode] [AddComponentMenu("Spine/BoneComponent")] public class BoneComponent : MonoBehaviour { - public SkeletonComponent skeletonComponent; + public bool valid; + public SkeletonRenderer skeletonRenderer; public Bone bone; /// If a bone isn't set, boneName is used to find the bone. public String boneName; protected Transform cachedTransform; - protected Transform skeletonComponentTransform; + protected Transform skeletonTransform; - void Awake () { + public void Reset () { + bone = null; cachedTransform = transform; + valid = skeletonRenderer != null && skeletonRenderer.valid; + if (!valid) return; + skeletonTransform = skeletonRenderer.transform; + } - if(skeletonComponent == null) return; - skeletonComponentTransform = skeletonComponent.transform; + public void Awake () { + Reset(); } public void LateUpdate () { - if (skeletonComponent == null || skeletonComponent.skeleton == null) return; + if (!valid) { + Reset(); + return; + } + if (bone == null) { - if (boneName == null) return; - bone = skeletonComponent.skeleton.FindBone(boneName); + if (boneName == null || boneName.Length == 0) return; + bone = skeletonRenderer.skeleton.FindBone(boneName); if (bone == null) { - Debug.Log("Bone not found: " + boneName, this); + Debug.LogError("Bone not found: " + boneName, this); return; } } - if (cachedTransform.parent == skeletonComponentTransform) { + if (cachedTransform.parent == skeletonTransform) { cachedTransform.localPosition = new Vector3(bone.worldX, bone.worldY, cachedTransform.localPosition.z); Vector3 rotation = cachedTransform.localRotation.eulerAngles; cachedTransform.localRotation = Quaternion.Euler(rotation.x, rotation.y, bone.worldRotation); } else { - cachedTransform.position = skeletonComponentTransform.TransformPoint(new Vector3(bone.worldX, bone.worldY, cachedTransform.position.z)); - Vector3 rotation = skeletonComponentTransform.rotation.eulerAngles; + cachedTransform.position = skeletonTransform.TransformPoint(new Vector3(bone.worldX, bone.worldY, cachedTransform.position.z)); + Vector3 rotation = skeletonTransform.rotation.eulerAngles; cachedTransform.rotation = Quaternion.Euler(rotation.x, rotation.y, - skeletonComponentTransform.rotation.eulerAngles.z + bone.worldRotation); + skeletonTransform.rotation.eulerAngles.z + bone.worldRotation); } - } - -} +} \ No newline at end of file diff --git a/spine-unity/Assets/Spine/Editor/BoneComponentInspector.cs b/spine-unity/Assets/Spine/Editor/BoneComponentInspector.cs index 4767372eb..494b3db2d 100644 --- a/spine-unity/Assets/Spine/Editor/BoneComponentInspector.cs +++ b/spine-unity/Assets/Spine/Editor/BoneComponentInspector.cs @@ -34,10 +34,10 @@ using UnityEngine; [CustomEditor(typeof(BoneComponent))] public class BoneComponentInspector : Editor { - private SerializedProperty boneName, skeletonComponent; + private SerializedProperty boneName, skeletonRenderer; void OnEnable () { - skeletonComponent = serializedObject.FindProperty("skeletonComponent"); + skeletonRenderer = serializedObject.FindProperty("skeletonRenderer"); boneName = serializedObject.FindProperty("boneName"); } @@ -45,13 +45,13 @@ public class BoneComponentInspector : Editor { serializedObject.Update(); BoneComponent component = (BoneComponent)target; - EditorGUILayout.PropertyField(skeletonComponent); + EditorGUILayout.PropertyField(skeletonRenderer); - if (component.skeletonComponent != null) { - String[] bones = new String[component.skeletonComponent.skeleton.Data.Bones.Count + 1]; + if (component.valid) { + String[] bones = new String[component.skeletonRenderer.skeleton.Data.Bones.Count + 1]; bones[0] = ""; for (int i = 0; i < bones.Length - 1; i++) - bones[i + 1] = component.skeletonComponent.skeleton.Data.Bones[i].Name; + bones[i + 1] = component.skeletonRenderer.skeleton.Data.Bones[i].Name; Array.Sort(bones); int boneIndex = Math.Max(0, Array.IndexOf(bones, boneName.stringValue)); @@ -61,14 +61,13 @@ public class BoneComponentInspector : Editor { boneIndex = EditorGUILayout.Popup(boneIndex, bones); EditorGUILayout.EndHorizontal(); - boneName.stringValue = bones[boneIndex];; + boneName.stringValue = boneIndex == 0 ? null : bones[boneIndex]; } if (serializedObject.ApplyModifiedProperties() || (Event.current.type == EventType.ValidateCommand && Event.current.commandName == "UndoRedoPerformed") ) { - component.bone = null; - component.LateUpdate(); + component.Reset(); } } } diff --git a/spine-unity/Assets/Spine/Editor/Menus.cs b/spine-unity/Assets/Spine/Editor/Menus.cs index 3a4405a78..bc10722a0 100644 --- a/spine-unity/Assets/Spine/Editor/Menus.cs +++ b/spine-unity/Assets/Spine/Editor/Menus.cs @@ -59,9 +59,9 @@ public class SpineEditor { Selection.activeObject = asset; } - [MenuItem("GameObject/Create Other/Spine SkeletonComponent")] - static public void CreateSkeletonComponentGameObject () { - GameObject gameObject = new GameObject("New SkeletonComponent", typeof(SkeletonComponent)); + [MenuItem("GameObject/Create Other/Spine SkeletonRenderer")] + static public void CreateSkeletonRendererGameObject () { + GameObject gameObject = new GameObject("New SkeletonRenderer", typeof(SkeletonRenderer)); EditorUtility.FocusProjectWindow(); Selection.activeObject = gameObject; } diff --git a/spine-unity/Assets/Spine/Editor/SkeletonAnimationInspector.cs b/spine-unity/Assets/Spine/Editor/SkeletonAnimationInspector.cs index c0b248af5..9f646f281 100644 --- a/spine-unity/Assets/Spine/Editor/SkeletonAnimationInspector.cs +++ b/spine-unity/Assets/Spine/Editor/SkeletonAnimationInspector.cs @@ -89,10 +89,8 @@ public class SkeletonAnimationInspector : Editor { animationIndex = EditorGUILayout.Popup(animationIndex, animations); EditorGUILayout.EndHorizontal(); - if (animationIndex == 0) - component.animationName = null; - else - component.animationName = animations[animationIndex]; + component.animationName = animationIndex == 0 ? null : animations[animationIndex]; + animationName.stringValue = component.animationName; } // Animation loop. @@ -109,8 +107,7 @@ public class SkeletonAnimationInspector : Editor { (Event.current.type == EventType.ValidateCommand && Event.current.commandName == "UndoRedoPerformed") ) { if (!Application.isPlaying) { - component.Clear(); - component.Update(); + component.Reset(); } } } diff --git a/spine-unity/Assets/Spine/Editor/SkeletonComponentInspector.cs b/spine-unity/Assets/Spine/Editor/SkeletonRendererInspector.cs similarity index 94% rename from spine-unity/Assets/Spine/Editor/SkeletonComponentInspector.cs rename to spine-unity/Assets/Spine/Editor/SkeletonRendererInspector.cs index e384db750..bb3916c61 100644 --- a/spine-unity/Assets/Spine/Editor/SkeletonComponentInspector.cs +++ b/spine-unity/Assets/Spine/Editor/SkeletonRendererInspector.cs @@ -32,8 +32,8 @@ using System; using UnityEditor; using UnityEngine; -[CustomEditor(typeof(SkeletonComponent))] -public class SkeletonComponentInspector : Editor { +[CustomEditor(typeof(SkeletonRenderer))] +public class SkeletonRendererInspector : Editor { private SerializedProperty skeletonDataAsset, initialSkinName, timeScale, normals, tangents; void OnEnable () { @@ -46,7 +46,7 @@ public class SkeletonComponentInspector : Editor { override public void OnInspectorGUI () { serializedObject.Update(); - SkeletonComponent component = (SkeletonComponent)target; + SkeletonRenderer component = (SkeletonRenderer)target; EditorGUILayout.PropertyField(skeletonDataAsset); @@ -78,8 +78,7 @@ public class SkeletonComponentInspector : Editor { (Event.current.type == EventType.ValidateCommand && Event.current.commandName == "UndoRedoPerformed") ) { if (!Application.isPlaying) { - component.Clear(); - component.Update(); + component.Reset(); } } } diff --git a/spine-unity/Assets/Spine/Editor/SkeletonComponentInspector.cs.meta b/spine-unity/Assets/Spine/Editor/SkeletonRendererInspector.cs.meta similarity index 78% rename from spine-unity/Assets/Spine/Editor/SkeletonComponentInspector.cs.meta rename to spine-unity/Assets/Spine/Editor/SkeletonRendererInspector.cs.meta index 1d38c4c42..d32cc952c 100644 --- a/spine-unity/Assets/Spine/Editor/SkeletonComponentInspector.cs.meta +++ b/spine-unity/Assets/Spine/Editor/SkeletonRendererInspector.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 75f45221af7dd564081b96d877373967 +guid: d0fc5db9788bce4418ad3252d43faa8a MonoImporter: serializedVersion: 2 defaultReferences: [] diff --git a/spine-unity/Assets/Spine/SkeletonAnimation.cs b/spine-unity/Assets/Spine/SkeletonAnimation.cs index 03722bfaa..63a4be1a5 100644 --- a/spine-unity/Assets/Spine/SkeletonAnimation.cs +++ b/spine-unity/Assets/Spine/SkeletonAnimation.cs @@ -34,10 +34,10 @@ using System.Collections.Generic; using UnityEngine; using Spine; -/** Extends SkeletonComponent to apply an animation. */ -[ExecuteInEditMode, RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))] +[ExecuteInEditMode] [AddComponentMenu("Spine/SkeletonAnimation")] -public class SkeletonAnimation : SkeletonComponent { +public class SkeletonAnimation : SkeletonRenderer { + public float timeScale = 1; public bool loop; public Spine.AnimationState state; @@ -60,23 +60,29 @@ public class SkeletonAnimation : SkeletonComponent { } } - override public void Initialize () { - if (Initialized) return; - - base.Initialize(); + public override void Reset () { + base.Reset(); + if (!valid) return; state = new Spine.AnimationState(skeletonDataAsset.GetAnimationStateData()); - if (_animationName != null && _animationName.Length > 0) state.SetAnimation(0, _animationName, loop); + if (_animationName != null && _animationName.Length > 0) { + state.SetAnimation(0, _animationName, loop); + Update(0); + } } - override public void UpdateSkeleton (float deltaTime) { - // Apply the animation. + public virtual void Update () { + Update(Time.deltaTime); + } + + public virtual void Update (float deltaTime) { + if (!valid) return; + + deltaTime *= timeScale; + skeleton.Update(deltaTime); state.Update(deltaTime * timeScale); state.Apply(skeleton); - if (UpdateBones != null) UpdateBones(this); - - // Call overridden method to call skeleton Update and UpdateWorldTransform. - base.UpdateSkeleton(deltaTime); + skeleton.UpdateWorldTransform(); } } diff --git a/spine-unity/Assets/Spine/SkeletonAnimation.cs.meta b/spine-unity/Assets/Spine/SkeletonAnimation.cs.meta index f497aa7e8..8469b39f1 100644 --- a/spine-unity/Assets/Spine/SkeletonAnimation.cs.meta +++ b/spine-unity/Assets/Spine/SkeletonAnimation.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: d247ba06193faa74d9335f5481b2b56c +guid: 77556b6aeacded74798bf9b33de3ae5a MonoImporter: serializedVersion: 2 defaultReferences: [] diff --git a/spine-unity/Assets/Spine/SkeletonComponent.cs b/spine-unity/Assets/Spine/SkeletonComponent.cs deleted file mode 100644 index ff3ff2cc1..000000000 --- a/spine-unity/Assets/Spine/SkeletonComponent.cs +++ /dev/null @@ -1,334 +0,0 @@ -/****************************************************************************** - * 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. - *****************************************************************************/ - -using System; -using System.IO; -using System.Collections.Generic; -using UnityEngine; -using Spine; - -/** Renders a skeleton. Extend to apply animations, get bones and manipulate them, etc. */ -[ExecuteInEditMode, RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))] -[AddComponentMenu("Spine/SkeletonComponent")] -public class SkeletonComponent : MonoBehaviour { - public SkeletonDataAsset skeletonDataAsset; - public Skeleton skeleton; - public String initialSkinName; - public float timeScale = 1; - public bool calculateNormals; - public bool calculateTangents; - public float zSpacing; - private MeshFilter meshFilter; - private Mesh mesh, mesh1, mesh2; - private bool useMesh1; - private float[] vertexPositions = new float[8]; - private int lastVertexCount; - private Vector3[] vertices; - private Color32[] colors; - private Vector2[] uvs; - private Material[] sharedMaterials = new Material[0]; - private List submeshMaterials = new List(); - private List submeshes = new List(); - - /// False if Initialize needs to be called. - public bool Initialized { - get { - if (skeletonDataAsset == null) return true; - SkeletonData skeletonData = skeletonDataAsset.GetSkeletonData(false); - if (skeletonData == null) return true; - return skeleton != null && skeleton.Data == skeletonData; - } - } - - public virtual void Clear () { - if (meshFilter != null) meshFilter.sharedMesh = null; - if (mesh != null) DestroyImmediate(mesh); - if (renderer != null) renderer.sharedMaterial = null; - mesh = null; - mesh1 = null; - mesh2 = null; - lastVertexCount = 0; - vertices = null; - colors = null; - uvs = null; - sharedMaterials = new Material[0]; - submeshMaterials.Clear(); - submeshes.Clear(); - skeleton = null; - } - - public virtual void Initialize () { - if (Initialized) return; - - meshFilter = GetComponent(); - mesh1 = newMesh(); - mesh2 = newMesh(); - - vertices = new Vector3[0]; - - skeleton = new Skeleton(skeletonDataAsset.GetSkeletonData(false)); - - if (initialSkinName != null && initialSkinName.Length > 0 && initialSkinName != "default") { - skeleton.SetSkin(initialSkinName); - skeleton.SetSlotsToSetupPose(); - } - } - - private Mesh newMesh () { - Mesh mesh = new Mesh(); - mesh.name = "Skeleton Mesh"; - mesh.hideFlags = HideFlags.HideAndDontSave; - mesh.MarkDynamic(); - return mesh; - } - - public virtual void UpdateSkeleton (float deltaTime) { - skeleton.Update(deltaTime * timeScale); - skeleton.UpdateWorldTransform(); - } - - public virtual void Update () { - if (skeletonDataAsset == null) { - Clear(); - return; - } - - SkeletonData skeletonData = skeletonDataAsset.GetSkeletonData(false); - - if (skeletonData == null) { - Clear(); - return; - } - - // Initialize fields. - if (skeleton == null || skeleton.Data != skeletonData) - Initialize(); - - UpdateSkeleton(Time.deltaTime); - - // Count quads and submeshes. - int quadCount = 0, submeshQuadCount = 0; - Material lastMaterial = null; - submeshMaterials.Clear(); - List drawOrder = skeleton.DrawOrder; - for (int i = 0, n = drawOrder.Count; i < n; i++) { - RegionAttachment regionAttachment = drawOrder[i].Attachment as RegionAttachment; - if (regionAttachment == null) - continue; - - // Add submesh when material changes. - Material material = (Material)((AtlasRegion)regionAttachment.RendererObject).page.rendererObject; - if (lastMaterial != material && lastMaterial != null) { - addSubmesh(lastMaterial, quadCount, submeshQuadCount, false); - submeshQuadCount = 0; - } - lastMaterial = material; - - quadCount++; - submeshQuadCount++; - } - addSubmesh(lastMaterial, quadCount, submeshQuadCount, true); - - // Set materials. - if (submeshMaterials.Count == sharedMaterials.Length) - submeshMaterials.CopyTo(sharedMaterials); - else - sharedMaterials = submeshMaterials.ToArray(); - renderer.sharedMaterials = sharedMaterials; - - // Double buffer mesh. - Mesh mesh = useMesh1 ? mesh1 : mesh2; - meshFilter.sharedMesh = mesh; - - // Ensure mesh data is the right size. - Vector3[] vertices = this.vertices; - int vertexCount = quadCount * 4; - bool newTriangles = vertexCount > vertices.Length; - if (newTriangles) { - // Not enough vertices, increase size. - this.vertices = vertices = new Vector3[vertexCount]; - this.colors = new Color32[vertexCount]; - this.uvs = new Vector2[vertexCount]; - mesh1.Clear(); - mesh2.Clear(); - } else { - // Too many vertices, zero the extra. - Vector3 zero = Vector3.zero; - for (int i = vertexCount, n = lastVertexCount; i < n; i++) - vertices[i] = zero; - } - lastVertexCount = vertexCount; - - // Setup mesh. - float[] vertexPositions = this.vertexPositions; - Vector2[] uvs = this.uvs; - Color32[] colors = this.colors; - int vertexIndex = 0; - Color32 color = new Color32(); - float a = skeleton.A * 255, r = skeleton.R, g = skeleton.G, b = skeleton.B, zSpacing = this.zSpacing; - for (int i = 0, n = drawOrder.Count; i < n; i++) { - Slot slot = drawOrder[i]; - RegionAttachment regionAttachment = slot.Attachment as RegionAttachment; - if (regionAttachment == null) - continue; - - regionAttachment.ComputeWorldVertices(skeleton.X, skeleton.Y, slot.Bone, vertexPositions); - - float z = i * zSpacing; - vertices[vertexIndex] = new Vector3(vertexPositions[RegionAttachment.X1], vertexPositions[RegionAttachment.Y1], z); - vertices[vertexIndex + 1] = new Vector3(vertexPositions[RegionAttachment.X4], vertexPositions[RegionAttachment.Y4], z); - vertices[vertexIndex + 2] = new Vector3(vertexPositions[RegionAttachment.X2], vertexPositions[RegionAttachment.Y2], z); - vertices[vertexIndex + 3] = new Vector3(vertexPositions[RegionAttachment.X3], vertexPositions[RegionAttachment.Y3], z); - - color.a = (byte)(a * slot.A); - color.r = (byte)(r * slot.R * color.a); - color.g = (byte)(g * slot.G * color.a); - color.b = (byte)(b * slot.B * color.a); - if (slot.Data.AdditiveBlending) color.a = 0; - colors[vertexIndex] = color; - colors[vertexIndex + 1] = color; - colors[vertexIndex + 2] = color; - colors[vertexIndex + 3] = color; - - float[] regionUVs = regionAttachment.UVs; - uvs[vertexIndex] = new Vector2(regionUVs[RegionAttachment.X1], 1 - regionUVs[RegionAttachment.Y1]); - uvs[vertexIndex + 1] = new Vector2(regionUVs[RegionAttachment.X4], 1 - regionUVs[RegionAttachment.Y4]); - uvs[vertexIndex + 2] = new Vector2(regionUVs[RegionAttachment.X2], 1 - regionUVs[RegionAttachment.Y2]); - uvs[vertexIndex + 3] = new Vector2(regionUVs[RegionAttachment.X3], 1 - regionUVs[RegionAttachment.Y3]); - - vertexIndex += 4; - } - - mesh.vertices = vertices; - mesh.colors32 = colors; - mesh.uv = uvs; - - int submeshCount = submeshMaterials.Count; - mesh.subMeshCount = submeshCount; - for (int i = 0; i < submeshCount; ++i) - mesh.SetTriangles(submeshes[i].indexes, i); - mesh.RecalculateBounds(); - - if (newTriangles && calculateNormals) { - Vector3[] normals = new Vector3[vertexCount]; - Vector3 normal = new Vector3(0, 0, -1); - for (int i = 0; i < vertexCount; i++) - normals[i] = normal; - (useMesh1 ? mesh2 : mesh1).vertices = vertices; // Set other mesh vertices. - mesh1.normals = normals; - mesh2.normals = normals; - - if (calculateTangents) { - Vector4[] tangents = new Vector4[vertexCount]; - Vector3 tangent = new Vector3(0, 0, 1); - for (int i = 0; i < vertexCount; i++) - tangents[i] = tangent; - mesh1.tangents = tangents; - mesh2.tangents = tangents; - } - } - - useMesh1 = !useMesh1; - } - - /** Adds a material. Adds submesh indexes if existing indexes aren't sufficient. */ - private void addSubmesh (Material material, int endQuadCount, int submeshQuadCount, bool lastSubmesh) { - int submeshIndex = submeshMaterials.Count; - submeshMaterials.Add(material); - - int indexCount = submeshQuadCount * 6; - int vertexIndex = (endQuadCount - submeshQuadCount) * 4; - - if (submeshes.Count <= submeshIndex) submeshes.Add(new Submesh()); - Submesh submesh = submeshes[submeshIndex]; - - int[] indexes = submesh.indexes; - if (lastSubmesh && submesh.indexCount > indexCount) { - // Last submesh may have more indices than required, so zero indexes to the end. - submesh.indexCount = indexCount; - for (int i = indexCount, n = indexes.Length; i < n; i++) - indexes[i] = 0; - } else if (indexes.Length != indexCount) { - // Reallocate indexes if not the right size. - submesh.indexes = indexes = new int[indexCount]; - submesh.indexCount = 0; - } - - // Set indexes if not already set. - if (submesh.firstVertex != vertexIndex || submesh.indexCount < indexCount) { - submesh.indexCount = indexCount; - submesh.firstVertex = vertexIndex; - for (int i = 0; i < indexCount; i += 6, vertexIndex += 4) { - indexes[i] = vertexIndex; - indexes[i + 1] = vertexIndex + 2; - indexes[i + 2] = vertexIndex + 1; - indexes[i + 3] = vertexIndex + 2; - indexes[i + 4] = vertexIndex + 3; - indexes[i + 5] = vertexIndex + 1; - } - } - } - - public virtual void OnEnable () { - Initialize(); - } - - public virtual void Reset () { - Initialize(); - } - -#if UNITY_EDITOR - void OnDrawGizmos() { - // Make selection easier by drawing a clear gizmo over the skeleton. - if (vertices == null) return; - Vector3 gizmosCenter = new Vector3(); - Vector3 gizmosSize = new Vector3(); - Vector3 min = new Vector3(float.MaxValue, float.MaxValue, 0f); - Vector3 max = new Vector3(float.MinValue, float.MinValue, 0f); - foreach (Vector3 vert in vertices) { - min = Vector3.Min (min, vert); - max = Vector3.Max (max, vert); - } - float width = max.x - min.x; - float height = max.y - min.y; - gizmosCenter = new Vector3(min.x + (width / 2f), min.y + (height / 2f), 0f); - gizmosSize = new Vector3(width, height, 1f); - Gizmos.color = Color.clear; - Gizmos.matrix = transform.localToWorldMatrix; - Gizmos.DrawCube(gizmosCenter, gizmosSize); - } -#endif -} - -class Submesh { - public int[] indexes = new int[0]; - public int firstVertex = -1; - public int indexCount; -} diff --git a/spine-unity/Assets/Spine/SkeletonDataAsset.cs b/spine-unity/Assets/Spine/SkeletonDataAsset.cs index 99a714e60..bb7486d82 100644 --- a/spine-unity/Assets/Spine/SkeletonDataAsset.cs +++ b/spine-unity/Assets/Spine/SkeletonDataAsset.cs @@ -52,14 +52,14 @@ public class SkeletonDataAsset : ScriptableObject { public SkeletonData GetSkeletonData (bool quiet) { if (atlasAsset == null) { if (!quiet) - Debug.LogWarning("Atlas not set for skeleton data asset: " + name, this); + Debug.LogError("Atlas not set for SkeletonData asset: " + name, this); Clear(); return null; } if (skeletonJSON == null) { if (!quiet) - Debug.LogWarning("Skeleton JSON file not set for skeleton data asset: " + name, this); + Debug.LogError("Skeleton JSON file not set for SkeletonData asset: " + name, this); Clear(); return null; } @@ -73,13 +73,14 @@ public class SkeletonDataAsset : ScriptableObject { if (skeletonData != null) return skeletonData; + atlas.FlipV(); SkeletonJson json = new SkeletonJson(atlas); json.Scale = scale; try { skeletonData = json.ReadSkeletonData(new StringReader(skeletonJSON.text)); } catch (Exception ex) { if (!quiet) - Debug.Log("Error reading skeleton JSON file for skeleton data asset: " + name + "\n" + ex.Message + "\n" + ex.StackTrace, this); + Debug.LogError("Error reading skeleton JSON file for SkeletonData asset: " + name + "\n" + ex.Message + "\n" + ex.StackTrace, this); return null; } diff --git a/spine-unity/Assets/Spine/SkeletonRenderer.cs b/spine-unity/Assets/Spine/SkeletonRenderer.cs new file mode 100644 index 000000000..eab839b7f --- /dev/null +++ b/spine-unity/Assets/Spine/SkeletonRenderer.cs @@ -0,0 +1,366 @@ +/****************************************************************************** + * 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. + *****************************************************************************/ + +using System; +using System.IO; +using System.Collections.Generic; +using UnityEngine; +using Spine; + +/// Renders a skeleton. +[ExecuteInEditMode, RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))] +public class SkeletonRenderer : MonoBehaviour { + public bool valid; + public Skeleton skeleton; + + public SkeletonDataAsset skeletonDataAsset; + public String initialSkinName; + public bool calculateNormals; + public bool calculateTangents; + public float zSpacing; + + private MeshFilter meshFilter; + private Mesh mesh, mesh1, mesh2; + private bool useMesh1; + private float[] tempVertices = new float[8]; + private int lastVertexCount; + private Vector3[] vertices; + private Color32[] colors; + private Vector2[] uvs; + private Material[] sharedMaterials = new Material[0]; + private readonly List submeshMaterials = new List(); + private readonly List submeshes = new List(); + private readonly int[] quadTriangles = {0, 2, 1, 2, 3, 1}; + + public virtual void Reset () { + if (meshFilter != null) meshFilter.sharedMesh = null; + if (mesh != null) DestroyImmediate(mesh); + if (renderer != null) renderer.sharedMaterial = null; + mesh = null; + mesh1 = null; + mesh2 = null; + lastVertexCount = 0; + vertices = null; + colors = null; + uvs = null; + sharedMaterials = new Material[0]; + submeshMaterials.Clear(); + submeshes.Clear(); + skeleton = null; + + if (!skeletonDataAsset) { + Debug.LogError("Missing SkeletonData asset.", this); + valid = false; + return; + } + valid = true; + + meshFilter = GetComponent(); + mesh1 = newMesh(); + mesh2 = newMesh(); + vertices = new Vector3[0]; + + skeleton = new Skeleton(skeletonDataAsset.GetSkeletonData(false)); + if (initialSkinName != null && initialSkinName.Length > 0 && initialSkinName != "default") + skeleton.SetSkin(initialSkinName); + } + + public void Awake () { + Reset(); + } + + private Mesh newMesh () { + Mesh mesh = new Mesh(); + mesh.name = "Skeleton Mesh"; + mesh.hideFlags = HideFlags.HideAndDontSave; + mesh.MarkDynamic(); + return mesh; + } + + public virtual void OnWillRenderObject () { + if (!valid) return; + + // Count vertices and submesh triangles. + int vertexCount = 0; + int submeshTriangleCount = 0, submeshFirstVertex = 0, submeshStartSlotIndex = 0; + Material lastMaterial = null; + submeshMaterials.Clear(); + List drawOrder = skeleton.DrawOrder; + int drawOrderCount = drawOrder.Count; + for (int i = 0; i < drawOrderCount; i++) { + Attachment attachment = drawOrder[i].attachment; + + object rendererObject; + int attachmentVertexCount, attachmentTriangleCount; + + if (attachment is RegionAttachment) { + rendererObject = ((RegionAttachment)attachment).RendererObject; + attachmentVertexCount = 4; + attachmentTriangleCount = 6; + } else if (attachment is MeshAttachment) { + MeshAttachment meshAttachment = (MeshAttachment)attachment; + rendererObject = meshAttachment.RendererObject; + attachmentVertexCount = meshAttachment.vertices.Length / 2; + attachmentTriangleCount = meshAttachment.triangles.Length; + } else if (attachment is SkinnedMeshAttachment) { + SkinnedMeshAttachment meshAttachment = (SkinnedMeshAttachment)attachment; + rendererObject = meshAttachment.RendererObject; + attachmentVertexCount = meshAttachment.uvs.Length / 2; + attachmentTriangleCount = meshAttachment.triangles.Length; + } else + continue; + + // Populate submesh when material changes. + Material material = (Material)((AtlasRegion)rendererObject).page.rendererObject; + if (lastMaterial != material && lastMaterial != null) { + addSubmesh(lastMaterial, submeshStartSlotIndex, i, submeshTriangleCount, submeshFirstVertex, false); + submeshTriangleCount = 0; + submeshFirstVertex = vertexCount; + submeshStartSlotIndex = i; + } + lastMaterial = material; + + submeshTriangleCount += attachmentTriangleCount; + vertexCount += attachmentVertexCount; + } + addSubmesh(lastMaterial, submeshStartSlotIndex, drawOrderCount, submeshTriangleCount, submeshFirstVertex, true); + + // Set materials. + if (submeshMaterials.Count == sharedMaterials.Length) + submeshMaterials.CopyTo(sharedMaterials); + else + sharedMaterials = submeshMaterials.ToArray(); + renderer.sharedMaterials = sharedMaterials; + + // Ensure mesh data is the right size. + Vector3[] vertices = this.vertices; + bool newTriangles = vertexCount > vertices.Length; + if (newTriangles) { + // Not enough vertices, increase size. + this.vertices = vertices = new Vector3[vertexCount]; + this.colors = new Color32[vertexCount]; + this.uvs = new Vector2[vertexCount]; + mesh1.Clear(); + mesh2.Clear(); + } else { + // Too many vertices, zero the extra. + Vector3 zero = Vector3.zero; + for (int i = vertexCount, n = lastVertexCount; i < n; i++) + vertices[i] = zero; + } + lastVertexCount = vertexCount; + + // Setup mesh. + float[] tempVertices = this.tempVertices; + Vector2[] uvs = this.uvs; + Color32[] colors = this.colors; + int vertexIndex = 0; + Color32 color = new Color32(); + float x = skeleton.x, y = skeleton.y, zSpacing = this.zSpacing; + float a = skeleton.a * 255, r = skeleton.r, g = skeleton.g, b = skeleton.b; + for (int i = 0; i < drawOrderCount; i++) { + Slot slot = drawOrder[i]; + Attachment attachment = slot.attachment; + if (attachment is RegionAttachment) { + RegionAttachment regionAttachment = (RegionAttachment)attachment; + regionAttachment.ComputeWorldVertices(x, y, slot.bone, tempVertices); + + float z = i * zSpacing; + vertices[vertexIndex] = new Vector3(tempVertices[RegionAttachment.X1], tempVertices[RegionAttachment.Y1], z); + vertices[vertexIndex + 1] = new Vector3(tempVertices[RegionAttachment.X4], tempVertices[RegionAttachment.Y4], z); + vertices[vertexIndex + 2] = new Vector3(tempVertices[RegionAttachment.X2], tempVertices[RegionAttachment.Y2], z); + vertices[vertexIndex + 3] = new Vector3(tempVertices[RegionAttachment.X3], tempVertices[RegionAttachment.Y3], z); + + color.a = (byte)(a * slot.a * regionAttachment.a); + color.r = (byte)(r * slot.r * regionAttachment.r * color.a); + color.g = (byte)(g * slot.g * regionAttachment.g * color.a); + color.b = (byte)(b * slot.b * regionAttachment.b * color.a); + if (slot.data.additiveBlending) color.a = 0; + colors[vertexIndex] = color; + colors[vertexIndex + 1] = color; + colors[vertexIndex + 2] = color; + colors[vertexIndex + 3] = color; + + float[] regionUVs = regionAttachment.uvs; + uvs[vertexIndex] = new Vector2(regionUVs[RegionAttachment.X1], regionUVs[RegionAttachment.Y1]); + uvs[vertexIndex + 1] = new Vector2(regionUVs[RegionAttachment.X4], regionUVs[RegionAttachment.Y4]); + uvs[vertexIndex + 2] = new Vector2(regionUVs[RegionAttachment.X2], regionUVs[RegionAttachment.Y2]); + uvs[vertexIndex + 3] = new Vector2(regionUVs[RegionAttachment.X3], regionUVs[RegionAttachment.Y3]); + + vertexIndex += 4; + } else if (attachment is MeshAttachment) { + MeshAttachment meshAttachment = (MeshAttachment)attachment; + int meshVertexCount = meshAttachment.vertices.Length; + if (tempVertices.Length < meshVertexCount) tempVertices = new float[meshVertexCount]; + meshAttachment.ComputeWorldVertices(x, y, slot, tempVertices); + + color.a = (byte)(a * slot.a * meshAttachment.a); + color.r = (byte)(r * slot.r * meshAttachment.r * color.a); + color.g = (byte)(g * slot.g * meshAttachment.g * color.a); + color.b = (byte)(b * slot.b * meshAttachment.b * color.a); + if (slot.data.additiveBlending) color.a = 0; + + float[] meshUVs = meshAttachment.uvs; + float z = i * zSpacing; + for (int ii = 0; ii < meshVertexCount; ii += 2, vertexIndex++) { + vertices[vertexIndex] = new Vector3(tempVertices[ii], tempVertices[ii + 1], z); + colors[vertexIndex] = color; + uvs[vertexIndex] = new Vector2(meshUVs[ii], meshUVs[ii + 1]); + } + } else if (attachment is SkinnedMeshAttachment) { + SkinnedMeshAttachment meshAttachment = (SkinnedMeshAttachment)attachment; + int meshVertexCount = meshAttachment.uvs.Length; + if (tempVertices.Length < meshVertexCount) tempVertices = new float[meshVertexCount]; + meshAttachment.ComputeWorldVertices(x, y, slot, tempVertices); + + color.a = (byte)(a * slot.a * meshAttachment.a); + color.r = (byte)(r * slot.r * meshAttachment.r * color.a); + color.g = (byte)(g * slot.g * meshAttachment.g * color.a); + color.b = (byte)(b * slot.b * meshAttachment.b * color.a); + if (slot.data.additiveBlending) color.a = 0; + + float[] meshUVs = meshAttachment.uvs; + float z = i * zSpacing; + for (int ii = 0; ii < meshVertexCount; ii += 2, vertexIndex++) { + vertices[vertexIndex] = new Vector3(tempVertices[ii], tempVertices[ii + 1], z); + colors[vertexIndex] = color; + uvs[vertexIndex] = new Vector2(meshUVs[ii], meshUVs[ii + 1]); + } + } + } + + // Double buffer mesh. + Mesh mesh = useMesh1 ? mesh1 : mesh2; + meshFilter.sharedMesh = mesh; + + mesh.vertices = vertices; + mesh.colors32 = colors; + mesh.uv = uvs; + + int submeshCount = submeshMaterials.Count; + mesh.subMeshCount = submeshCount; + for (int i = 0; i < submeshCount; ++i) + mesh.SetTriangles(submeshes[i].triangles, i); + mesh.RecalculateBounds(); + + if (newTriangles && calculateNormals) { + Vector3[] normals = new Vector3[vertexCount]; + Vector3 normal = new Vector3(0, 0, -1); + for (int i = 0; i < vertexCount; i++) + normals[i] = normal; + (useMesh1 ? mesh2 : mesh1).vertices = vertices; // Set other mesh vertices. + mesh1.normals = normals; + mesh2.normals = normals; + + if (calculateTangents) { + Vector4[] tangents = new Vector4[vertexCount]; + Vector3 tangent = new Vector3(0, 0, 1); + for (int i = 0; i < vertexCount; i++) + tangents[i] = tangent; + mesh1.tangents = tangents; + mesh2.tangents = tangents; + } + } + + useMesh1 = !useMesh1; + } + + /** Stores vertices and triangles for a single material. */ + private void addSubmesh (Material material, int startSlot, int endSlot, int triangleCount, int firstVertex, bool lastSubmesh) { + int submeshIndex = submeshMaterials.Count; + submeshMaterials.Add(material); + + if (submeshes.Count <= submeshIndex) submeshes.Add(new Submesh()); + Submesh submesh = submeshes[submeshIndex]; + + int[] triangles = submesh.triangles; + int trianglesCapacity = triangles.Length; + if (lastSubmesh && trianglesCapacity > triangleCount) { + // Last submesh may have more triangles than required, so zero triangles to the end. + for (int i = triangleCount; i < trianglesCapacity; i++) + triangles[i] = 0; + submesh.triangleCount = triangleCount; + } else if (trianglesCapacity != triangleCount) { + // Reallocate triangles when not the exact size needed. + submesh.triangles = triangles = new int[triangleCount]; + submesh.triangleCount = 0; + } + + List drawOrder = skeleton.DrawOrder; + for (int i = startSlot, triangleIndex = 0; i < endSlot; i++) { + Attachment attachment = drawOrder[i].attachment; + int[] attachmentTriangles; + int attachmentVertexCount; + if (attachment is RegionAttachment) { + attachmentVertexCount = 4; + attachmentTriangles = quadTriangles; + } else if (attachment is MeshAttachment) { + MeshAttachment meshAttachment = (MeshAttachment)attachment; + attachmentVertexCount = meshAttachment.vertices.Length / 2; + attachmentTriangles = meshAttachment.triangles; + } else if (attachment is SkinnedMeshAttachment) { + SkinnedMeshAttachment meshAttachment = (SkinnedMeshAttachment)attachment; + attachmentVertexCount = meshAttachment.uvs.Length / 2; + attachmentTriangles = meshAttachment.triangles; + } else + continue; + for (int ii = 0, nn = attachmentTriangles.Length; ii < nn; ii++, triangleIndex++) + triangles[triangleIndex] = firstVertex + attachmentTriangles[ii]; + firstVertex += attachmentVertexCount; + } + } + + #if UNITY_EDITOR + void OnDrawGizmos() { + // Make selection easier by drawing a clear gizmo over the skeleton. + if (vertices == null) return; + Vector3 gizmosCenter = new Vector3(); + Vector3 gizmosSize = new Vector3(); + Vector3 min = new Vector3(float.MaxValue, float.MaxValue, 0f); + Vector3 max = new Vector3(float.MinValue, float.MinValue, 0f); + foreach (Vector3 vert in vertices) { + min = Vector3.Min (min, vert); + max = Vector3.Max (max, vert); + } + float width = max.x - min.x; + float height = max.y - min.y; + gizmosCenter = new Vector3(min.x + (width / 2f), min.y + (height / 2f), 0f); + gizmosSize = new Vector3(width, height, 1f); + Gizmos.color = Color.clear; + Gizmos.matrix = transform.localToWorldMatrix; + Gizmos.DrawCube(gizmosCenter, gizmosSize); + } + #endif +} + +class Submesh { + public int[] triangles = new int[0]; + public int triangleCount; +} diff --git a/spine-unity/Assets/Spine/SkeletonComponent.cs.meta b/spine-unity/Assets/Spine/SkeletonRenderer.cs.meta similarity index 78% rename from spine-unity/Assets/Spine/SkeletonComponent.cs.meta rename to spine-unity/Assets/Spine/SkeletonRenderer.cs.meta index 255c3fe77..fff56a22b 100644 --- a/spine-unity/Assets/Spine/SkeletonComponent.cs.meta +++ b/spine-unity/Assets/Spine/SkeletonRenderer.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: a0d5e4e6acf3fd8489cb53ffe164e0b4 +guid: e075b9a3e08e2f74fbd651c858ab16ed MonoImporter: serializedVersion: 2 defaultReferences: [] diff --git a/spine-unity/Assets/examples/dragon/dragon.unity b/spine-unity/Assets/examples/dragon/dragon.unity index 4258bd4df3d9b5e0030e9a281949882842985d50..939e489cf3bbfa2269ab0efd5303fb582106ddb0 100644 GIT binary patch delta 3319 zcmb_fdrXs86#u>oZFv`pg}x}1_Yi22m-1=>r639hSmtJ;pQT8%+G1NYIuu;YqGn5A z$7~uiC1wwjWjn*%D*sqFLrlMua8%N7?4o#qgKzocnzpEpC5|aFcWHxxahv zx#!$_?&;U7XZRD7yq@D6a*pfH^oZeWfZ5hr`~0d#BgcpY8Sr z;bww&1?w$l)^E;xf%WI}#PI`#8M@P|#knLA<7FN2BrOGcWiK-PlFSU*4RV-~x!HKr zic_o~*J&n3l#mo-&gyIT25Y%Jg2&y@@#q8jM~!5z9g1VBGPEROV~~>LWGIto*W>kC z98$v}w`*Is5FBi8M1T(c3!w3de8z2C)@vtUr3lVm~_YD^7A zVh7R(JU$`F?e7^9oNn(m;IlvdeF?mzs|Gb3VN{XFAf%|4HyrvXy;+%35J84;*IG&d2z!0ZQkF<^_ z)+y|B`9WyVLLjaqIc$kVs-D=b#v4k8`*F^NJ%n;C0}scyF#Ffz#T%ZFH${7HOel&5 zHYAKi14~OVbLdo3jx5?$9i+yoVK$|dxujN!A=jyT*!Z|KE;Sh7`y4skQK|9<>7u1Z z>BNn3oEp2x<5}tp2t7%gmPLm;yYdKg#j6od>!haHxWni3;0<&+ou1)<+vjZ=_IN1E zDE;zaB3%tXYYLX@S(@R;%qe~h4r*)oNw};vA-~1=W5zW)LaKGW%*}CK2kU2bqPq!t z|3U_=O3+3J_kmt446z1L+hP!d{X!bwH8{*RJYQ@*?cSiZ8l&#<`GXiNOF#CW3n0~$ zkr6r9+nlSFHx6EPWnDa(Xw zCS5@ai&rcrBuX_4JzwwkxFnBN3+KR=7O5oVD zpamYT6?5#S#V~PD&c1hg^R(cr>tjJ(t9ym@3q?fyHo7Gh6~5swjO1~{bI5ZVE0Fg# zl04c-aem17PmSd$E1Ikr=b9#pb_?VC7(dDQXE-|sb4~fkQ*Fd&Y#P{T%RtFxt4Ds8 z@fpSq_98^q*-McJ?6lu$`x@l8?X>cVoorc}NoPxQ^|J@@RdWqa%{Lb#H@6V)Y$4@g z#!t79+3#9b^J8zemZ4bQO0m4uO7KyJTxqRhr?T*cXo7}ys!;>xGJUTRQO(Ro+KNO! z*hEwX$B?MxjY>rF5u`mx??^nG85)~pggqRJ-c8Q7*yMS08j&md`M(j3X zk$YTxGzv$1{{)Uk?C~37w-L+kL~yf2zauzLAdW{Q`U9~{g^1C$5ZyG?Z(li6_e0~o_uy>X^DyLyPnxvhO*V4e)n?R(6i1uPW&^=t!bw$V z)Nxl9+P+~cUqr`%(Q~#(A91(|*Y6q-hCM-cJUEovSQAWlPKPdR{Fa9!?RpsRs{I?{ Ci=Q6= delta 3735 zcmcImeN0nV6n`!7XrYBd3-l983+20%&oV_y`7DY8K^C`Zpwp_2#VVqL!+`!X$($P= zml!u@mu1VGW@D>c{BMg{jGAo1{xD|FiOG^}ZgVDP8~?d;Z{K~C53`uDmz;Ob{oQxZ zx%YR^J@1|lNv@ui7&xx=CdYML4Gh!HRN7$t;4q z*^PX6r0LDKFmonj6EpA4qg`4N^%l&3tF^#%YCbPGT_@ebhMMJisBf0B=Cm}xOjpdC z%)Ap*kCTXlme@jx7WT%f(<0O1`g>da0bg4a7mG92L06nwDfV=F2X}(Xp@A)N@$gNo zL1KVT-kNK~FC|Po94AFv7_-lR$VWYyh$r}fPm*L3D{Xrq=pEz+{r$e~t=>T7n=Cj| zqK0j8y(+S(8ZHnF?xBt?VBt$7RvIja1tR(h!E(i5Hfmkxiy2Q(Pre0K$vPNq$7S`* zydm4bOtW0vmT9RTdgU_Qn%IPja%8jJJM0^Pi8VSnC11-1+>^hSvY4R^keJl)Se^uD z6U5|@J4DZRG=hpB~CoYVfO6AXS;zLM$qAeyyr* zhEC3-E~62CP^D+?!0loW$q#Of<5E$r^qB}F@iB~%*|cM9O1sj`#o!LfGxlG3LH7h`BMTGD4_|XRchvSD5Lj7G3pJTR>wf z0HrA&Zkmb`c$|#5wL@&c@l3Zig5tsG+V2|}ULV{RIr}^uk*neBOp_!Y_Uf&gL}jFPO`1(Hln`#GphHU&_`v zW)~@a&wdz4USCR3KI8EQf*XB9gZl@3961$fWH3I|3F;+zE+kkwNw7uvK2d6*fBorN zWli^<;9z4gxXT}LtEEo=-K}mJwGx%Lu_#UV4bM$?UzJGC*(XY()W~y5Rt#PNo;ra~pm<;dM z+o_&KeIuO*joS&2)A^HCGI-8yph1ti%~VghO;q1@7lYZU0Cj_&x~*s^rrOtFrTRoe zSu~k{H8^o)_QnDntbnbJWCaR%wUIbez~_R#+i1bI5|WzosFT%A1<;nJfIUqlxB}j4 zBK-;&MOLPpOyJV-^mxxT6*1kxG1jm^KAqSnbH=*nlg#0UpP{?I{R1jj1 zp&US&6eJGy97-k1SAvZFI2Z50D&ogBDJDdYAQnR76xspe5?|a=1ZyyQC)&h0k=!0% z_A$hU(Po2fh{LM#W{*@6@xb``Pc7@s^Ey}wqA(GKHB zEXb#i?yo(K*fzu-<1>Idj2MSYoI;xgzKGZqV$nf9L+ln}(LsJ~5B28bb!9v%#&3vS zLd@KbV3#1jBY2B|*d7t&0b+9kwiz)Ze;}rf=eT~fHG(`u>=?APj;Klo&-YC{`mlAf z;?HJn(F<4L+tz2{%eEx>n2YQ$$A!SMl70!Cn!;TGaNZ^de0J9x6{{ZVZy?6is diff --git a/spine-unity/Assets/examples/goblins/Goblins.cs b/spine-unity/Assets/examples/goblins/Goblins.cs index 9c38c4af4..c30e677b0 100644 --- a/spine-unity/Assets/examples/goblins/Goblins.cs +++ b/spine-unity/Assets/examples/goblins/Goblins.cs @@ -44,8 +44,8 @@ public class Goblins : MonoBehaviour { } // This is called after the animation is applied to the skeleton and can be used to adjust the bones dynamically. - public void UpdateBones (SkeletonComponent skeletonAnimation) { - headBone.Rotation -= 15; + public void UpdateBones (SkeletonAnimation skeletonAnimation) { + headBone.Rotation += 15; } public void OnMouseDown () { diff --git a/spine-unity/Assets/examples/goblins/goblins.unity b/spine-unity/Assets/examples/goblins/goblins.unity index 645dae40908f42b8e5a39ffddab15461d563f99f..1854f9fdb94a2060e4028beea110830c158a48d7 100644 GIT binary patch delta 262 zcmcbynDNJAMgax}?`Q^wz#j|@44goA!A1cKmQB(p7&i;Bu`$Vodghf_Fq9?cWTr4M z0+mPrF$jRz44#t(I7}HOC)*pTOnzg`JNb{M#N@Ypyp!i}C^5=R-p`@TC_CAcOMy{t zau$~&uRKT#5P-BYPLAU26-~>@Pb^_5$;?d+PEO28WnclyC{Om|l$`vQlaoSiDx0vhZ$>-eu>0sxIjK$!pl delta 242 zcmeydnDNG9Mgax}Z*vBQz#9w<44goAz(xTJ7DmC%A9dd|ZnkH-$2d8kjdij;hZdv6 z$2p`Kr6+rGDKpAU&gD{Ilm&}gbMi6DfmsKP*e1W^(); // Call our method any time an animation fires an event. skeletonAnimation.state.Event += Event; - // Queue jump to be played on track 0 after the starting animation. - skeletonAnimation.state.AddAnimation(0, "jump", false, 0); + // Queue jump to be played on track 0 tree seconds after the starting animation. + skeletonAnimation.state.AddAnimation(0, "jump", false, 3); // Queue walk to be looped on track 0 after the jump animation. skeletonAnimation.state.AddAnimation(0, "run", true, 0); } diff --git a/spine-unity/Assets/examples/spineboy/data/spineboy.json.txt b/spine-unity/Assets/examples/spineboy/data/spineboy.json.txt index a673cd0f8..1ffa7aad5 100644 --- a/spine-unity/Assets/examples/spineboy/data/spineboy.json.txt +++ b/spine-unity/Assets/examples/spineboy/data/spineboy.json.txt @@ -1815,55 +1815,55 @@ "slots": { "front_fist": { "attachment": [ - { "time": 0, "name": "front_fist_closed" }, - { "time": 0.2666, "name": "front_fist_open" } + { "time": 0.1333, "name": "front_fist_closed" }, + { "time": 0.4, "name": "front_fist_open" } ] }, "mouth": { "attachment": [ - { "time": 0, "name": "mouth_grind" } + { "time": 0.1333, "name": "mouth_grind" } ] }, "muzzle": { "attachment": [ - { "time": 0, "name": "muzzle" }, - { "time": 0.1333, "name": null } + { "time": 0.1333, "name": "muzzle" }, + { "time": 0.2666, "name": null } ], "color": [ { - "time": 0, + "time": 0.1333, "color": "ffffff00", "curve": [ 0.118, 0.99, 0.75, 1 ] }, { - "time": 0.0333, + "time": 0.1666, "color": "ffffffff", "curve": [ 0.821, 0, 0.909, 0.89 ] }, - { "time": 0.1333, "color": "ffffff00" } + { "time": 0.2666, "color": "ffffff00" } ] } }, "bones": { "front_fist": { "scale": [ - { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, - { "time": 0.2666, "x": 1, "y": 1 } + { "time": 0.1333, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 0.4, "x": 1, "y": 1 } ] }, "gunTip": { "translate": [ - { "time": 0, "x": 0, "y": 0 }, - { "time": 0.0666, "x": 20.93, "y": 1.57 } + { "time": 0.1333, "x": 0, "y": 0 }, + { "time": 0.2, "x": 20.93, "y": 1.57 } ], "scale": [ - { "time": 0, "x": 1, "y": 1 }, - { "time": 0.0666, "x": 1.247, "y": 1.516 } + { "time": 0.1333, "x": 1, "y": 1 }, + { "time": 0.2, "x": 1.247, "y": 1.516 } ] }, "gun": { "rotate": [ - { "time": 0, "angle": 0 } + { "time": 0, "angle": 1.9 } ], "translate": [ { @@ -1872,13 +1872,13 @@ "y": 5.84, "curve": [ 0, 0.3, 0.678, 1 ] }, - { "time": 0.1666, "x": -9.3, "y": -1.41 }, - { "time": 0.2666, "x": 0, "y": 0 } + { "time": 0.3, "x": -9.3, "y": -1.41 }, + { "time": 0.4, "x": 0, "y": 0 } ] }, "rear_bracer": { "rotate": [ - { "time": 0, "angle": 0 } + { "time": 0, "angle": -30.47 } ], "translate": [ { @@ -1887,13 +1887,13 @@ "y": 0, "curve": [ 0, 0.3, 0.678, 1 ] }, - { "time": 0.1666, "x": -5.99, "y": -3.71 }, - { "time": 0.2666, "x": 0, "y": 0 } + { "time": 0.3, "x": -5.99, "y": -3.71 }, + { "time": 0.4, "x": 0, "y": 0 } ] }, "rear_upper_arm": { "rotate": [ - { "time": 0, "angle": 0 } + { "time": 0, "angle": 62.3 } ], "translate": [ { @@ -1902,8 +1902,8 @@ "y": 0, "curve": [ 0, 0.3, 0.678, 1 ] }, - { "time": 0.1666, "x": 2.81, "y": 11.41 }, - { "time": 0.2666, "x": 0, "y": 0 } + { "time": 0.3, "x": 2.81, "y": 11.41 }, + { "time": 0.4, "x": 0, "y": 0 } ] } } diff --git a/spine-unity/Assets/examples/spineboy/spineboy.unity b/spine-unity/Assets/examples/spineboy/spineboy.unity index 1afdb4ab8847172d818e93022b9d63f5d41e4e8e..f5ebb5abcc844552733575b02667522787ad0d3a 100644 GIT binary patch delta 835 zcmaKpOK1~e5Xa|h(udGwD@nD|rlxc&Irzj%l}Z!!U}A-A5<{hkCYUV^T~mo`i?ooQ zq=FZjgVKwMpa-E+*;;!mc*><9c=4dcLl2?83cZN<`hQ`SD%gSl&dhITcjlX~QWSF~ z(L@v(AkyZDXdB|IcbbBt@u{}#h$kaC^C*p_jckSlUbjHfWK0qHz;~LNKdvT5bVD~s zb8$VF(erw~kFNbgr~$>h_}zY$-+Ej;tJL%Dp6#NQONxgdJH4Wfmza?2uWx=W5fEBD+5$#*g#$B!>;(y&1U9DSn<1zQm<{@J=ZIYS2q9-n= z4V|2rzLQ0phrhWMp>m_LPjpo$-XkaGqT*#aZ@?-klRbrDl>da~;jN9eGoc!y6ylw> z=D~_!D)J6n&%nxH=aI{HQVU>y2T=&|30u#>QefT4f7m_VpDkXx2X+e#w^OKI(Fec) z{^AFW+4=|;w%H_@)F-fEn_WOI^%-m$EQi=@YaQ$hn1&p#Dy=d&5w88yxY{>XDGW}$ z29$;0?Fr*34AlL-g#_3%SO|5$Pj?Qi45os0*h+$}gKch%lxRG;@aV~#mD6_@@Atmy zTYHENz+9_56MkTwjC>W{SO#II6xx`jdiab!l)i45h&J0)P}a#aji-$fE=5|bss4F~ i5PT?lm=9`BHli<;8?k7x;lPfiQ(E>#-Hea>HQz5tNyg9s delta 796 zcmaKqPiPZS5XR@FNu!7SBUK~uPiof+)?!7`79@!jX=6&8HWo@Nx=~hIOm>SdUMghu zAn3_53cUzhPofBwReNj&>9rm_c@P60f>j%+UJ6#fT{fbEKKOVuznz`=-rJp<6;)G& zi82wQ+&B?!M%*zSPFZ`xy@HMSyw&en7Hl5(wMqF)N>R}@wOcDqYO12@NxJekqD^QL z^h%Z{uu zpX3!0D{^R6SSimt#{*vt>=*y;9vA;5#$^0jFmp~+#Rr6U@%#BoPGjKpi2U9 z(A8(Kw9Br6+4=$|yX*{dTdQCOn2NZ`RSm2LmP3xsQLZ{VmG=D_?93gkmPe;v1NNcl zyX$ZWg^9*LZy^t6fTdAaKiz4tMKB4h+0`&u73}xHNQv6@$MW>rqn`VRznzS=jV<8@ zumT_WeeVOVWg4v;nRWcD*{vQU#g)uHFdxfVzqOow(!j<*2Y{F72HqT)dH6MXuOcs$ Y#&5rT8;x`m84E-vT-D{YCi^4(1Zl0)#Q*>R