From 53693c3faff900864ec8a4c89c8ed1a2f1866723 Mon Sep 17 00:00:00 2001 From: John Date: Sun, 24 Jan 2016 10:17:32 +0800 Subject: [PATCH 1/8] [Unity] Comments for CustomSkin.cs --- spine-unity/Assets/spine-unity/CustomSkin.cs | 24 ++++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/spine-unity/Assets/spine-unity/CustomSkin.cs b/spine-unity/Assets/spine-unity/CustomSkin.cs index ea74d572a..fab223b14 100644 --- a/spine-unity/Assets/spine-unity/CustomSkin.cs +++ b/spine-unity/Assets/spine-unity/CustomSkin.cs @@ -35,33 +35,47 @@ using Spine; public class CustomSkin : MonoBehaviour { - [System.Serializable] public class SkinPair { + /// SpineAttachment attachment path to help find the attachment. + /// This use of SpineAttachment generates an attachment path string that can only be used by SpineAttachment.GetAttachment. [SpineAttachment(currentSkinOnly: false, returnAttachmentPath: true, dataField: "skinSource")] - public string sourceAttachment; + [UnityEngine.Serialization.FormerlySerializedAs("sourceAttachment")] + public string sourceAttachmentPath; + [SpineSlot] public string targetSlot; + + /// The name of the skin placeholder/skin dictionary entry this attachment should be associated with. + /// This name is used by the skin dictionary, used in the method Skin.AddAttachment as well as setting a slot attachment [SpineAttachment(currentSkinOnly: true, placeholdersOnly: true)] public string targetAttachment; } + #region Inspector public SkeletonDataAsset skinSource; - public SkinPair[] skinning; + + [UnityEngine.Serialization.FormerlySerializedAs("skinning")] + public SkinPair[] skinItems; + public Skin customSkin; + #endregion SkeletonRenderer skeletonRenderer; + void Start () { skeletonRenderer = GetComponent(); Skeleton skeleton = skeletonRenderer.skeleton; customSkin = new Skin("CustomSkin"); - foreach (var pair in skinning) { - var attachment = SpineAttachment.GetAttachment(pair.sourceAttachment, skinSource); + foreach (var pair in skinItems) { + var attachment = SpineAttachment.GetAttachment(pair.sourceAttachmentPath, skinSource); customSkin.AddAttachment(skeleton.FindSlotIndex(pair.targetSlot), pair.targetAttachment, attachment); } + // The custom skin does not need to be added to the skeleton data for it to work. + // But it's useful for your script to keep a reference to it. skeleton.SetSkin(customSkin); } } From b8ccd5cbda410d13f1d05741c8fcd00a0395f53c Mon Sep 17 00:00:00 2001 From: John Date: Sun, 24 Jan 2016 10:20:31 +0800 Subject: [PATCH 2/8] [Unity] Comments for SpineAttachment Attribute --- .../Assets/spine-unity/SpineAttributes.cs | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/spine-unity/Assets/spine-unity/SpineAttributes.cs b/spine-unity/Assets/spine-unity/SpineAttributes.cs index 73eeebf4d..667d8eb73 100644 --- a/spine-unity/Assets/spine-unity/SpineAttributes.cs +++ b/spine-unity/Assets/spine-unity/SpineAttributes.cs @@ -85,14 +85,14 @@ public class SpineAttachment : SpineAttributeBase { /// Smart popup menu for Spine Attachments /// /// Filters popup results to only include the current Skin. Only valid when a SkeletonRenderer is the data source. - /// Returns a fully qualified path for an Attachment in the format "Skin/Slot/AttachmentName" + /// Returns a fully qualified path for an Attachment in the format "Skin/Slot/AttachmentName". This path format is only used by the SpineAttachment helper methods like SpineAttachment.GetAttachment and .GetHierarchy. Do not use full path anywhere else in Spine's system. /// Filters popup results to exclude attachments that are not children of Skin Placeholders /// If specified, a locally scoped field with the name supplied by in slotField will be used to limit the popup results to children of a named slot /// If specified, a locally scoped field with the name supplied by in dataField will be used to fill the popup results. /// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives) /// If left empty and the script the attribute is applied to is derived from Component, GetComponent() will be called as a fallback. /// - public SpineAttachment(bool currentSkinOnly = true, bool returnAttachmentPath = false, bool placeholdersOnly = false, string slotField = "", string dataField = "") { + public SpineAttachment (bool currentSkinOnly = true, bool returnAttachmentPath = false, bool placeholdersOnly = false, string slotField = "", string dataField = "") { this.currentSkinOnly = currentSkinOnly; this.returnAttachmentPath = returnAttachmentPath; this.placeholdersOnly = placeholdersOnly; @@ -100,11 +100,11 @@ public class SpineAttachment : SpineAttributeBase { this.dataField = dataField; } - public static Hierarchy GetHierarchy(string fullPath) { - return new Hierarchy(fullPath); + public static SpineAttachment.Hierarchy GetHierarchy (string fullPath) { + return new SpineAttachment.Hierarchy(fullPath); } - public static Spine.Attachment GetAttachment(string attachmentPath, Spine.SkeletonData skeletonData) { + public static Spine.Attachment GetAttachment (string attachmentPath, Spine.SkeletonData skeletonData) { var hierarchy = SpineAttachment.GetHierarchy(attachmentPath); if (hierarchy.name == "") return null; @@ -112,16 +112,18 @@ public class SpineAttachment : SpineAttributeBase { return skeletonData.FindSkin(hierarchy.skin).GetAttachment(skeletonData.FindSlotIndex(hierarchy.slot), hierarchy.name); } - public static Spine.Attachment GetAttachment(string attachmentPath, SkeletonDataAsset skeletonDataAsset) { + public static Spine.Attachment GetAttachment (string attachmentPath, SkeletonDataAsset skeletonDataAsset) { return GetAttachment(attachmentPath, skeletonDataAsset.GetSkeletonData(true)); } + /// + /// A struct that represents 3 strings that help identify and locate an attachment in a skeleton. public struct Hierarchy { public string skin; public string slot; public string name; - public Hierarchy(string fullPath) { + public Hierarchy (string fullPath) { string[] chunks = fullPath.Split(new char[]{'/'}, System.StringSplitOptions.RemoveEmptyEntries); if (chunks.Length == 0) { skin = ""; @@ -157,15 +159,11 @@ public class SpineBone : SpineAttributeBase { } public static Spine.Bone GetBone(string boneName, SkeletonRenderer renderer) { - if (renderer.skeleton == null) - return null; - - return renderer.skeleton.FindBone(boneName); + return renderer.skeleton == null ? null : renderer.skeleton.FindBone(boneName); } public static Spine.BoneData GetBoneData(string boneName, SkeletonDataAsset skeletonDataAsset) { var data = skeletonDataAsset.GetSkeletonData(true); - return data.FindBone(boneName); } } From 6e6e5a9438d575cc2b7655fa27c0f7c63417bfa8 Mon Sep 17 00:00:00 2001 From: pharan Date: Tue, 26 Jan 2016 07:29:40 +0800 Subject: [PATCH 3/8] [Unity] SkeletonAnimation.AddToGameObject() method for instantiation at runtime. --- .../Assets/spine-unity/SkeletonAnimation.cs | 43 ++++++------------- .../Assets/spine-unity/SkeletonRenderer.cs | 19 ++++++++ 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/spine-unity/Assets/spine-unity/SkeletonAnimation.cs b/spine-unity/Assets/spine-unity/SkeletonAnimation.cs index 00e37f4a0..b8479b016 100644 --- a/spine-unity/Assets/spine-unity/SkeletonAnimation.cs +++ b/spine-unity/Assets/spine-unity/SkeletonAnimation.cs @@ -104,39 +104,25 @@ public class SkeletonAnimation : SkeletonRenderer, ISkeletonAnimation { public bool loop; /// - /// The rate at which animations progress over time. 1 means 100%. 0.5 means 50%. - /// AnimationState and TrackEntry also have their own timeScale. These are combined multiplicatively. + /// The rate at which animations progress over time. 1 means 100%. 0.5 means 50%. + /// AnimationState and TrackEntry also have their own timeScale. These are combined multiplicatively. #if UNITY_5 [Tooltip("The rate at which animations progress over time. 1 means 100%. 0.5 means 50%.")] #endif public float timeScale = 1; - #region AutoReset - /** - [Tooltip("Setting this to true makes the SkeletonAnimation behave similar to Spine editor. New animations will not inherit the pose from a previous animation. If you need to intermittently and programmatically pose your skeleton, leave this false.")] - [SerializeField] - protected bool autoReset = false; - - /// - /// Setting this to true makes the SkeletonAnimation behave similar to Spine editor. - /// New animations will not inherit the pose from a previous animation. - /// If you need to intermittently and programmatically pose your skeleton, leave this false. - public bool AutoReset { - get { return this.autoReset; } - set { - if (!autoReset && value) { - state.Start -= HandleNewAnimationAutoreset; // make sure there isn't a double-subscription. - state.Start += HandleNewAnimationAutoreset; - } - autoReset = value; - } + #region Runtime Instantiation + /// Adds and prepares a SkeletonAnimation component to a GameObject at runtime. + /// The newly instantiated SkeletonAnimation + public static SkeletonAnimation AddToGameObject (GameObject gameObject, SkeletonDataAsset skeletonDataAsset) { + return SkeletonRenderer.AddSpineComponent(gameObject, skeletonDataAsset); } - protected virtual void HandleNewAnimationAutoreset (Spine.AnimationState state, int trackIndex) { - if (!autoReset) return; - if (skeleton != null) skeleton.SetToSetupPose(); + /// Instantiates a new UnityEngine.GameObject and adds a prepared SkeletonAnimation component to it. + /// The newly instantiated SkeletonAnimation component. + public static SkeletonAnimation NewSkeletonAnimationGameObject (SkeletonDataAsset skeletonDataAsset) { + return SkeletonRenderer.NewSpineGameObject(skeletonDataAsset); } - */ #endregion public override void Reset () { @@ -146,12 +132,6 @@ public class SkeletonAnimation : SkeletonRenderer, ISkeletonAnimation { state = new Spine.AnimationState(skeletonDataAsset.GetAnimationStateData()); - /* - if (autoReset) { - state.Start += HandleNewAnimationAutoreset; - } - */ - if (_animationName != null && _animationName.Length > 0) { state.SetAnimation(0, _animationName, loop); Update(0); @@ -185,4 +165,5 @@ public class SkeletonAnimation : SkeletonRenderer, ISkeletonAnimation { _UpdateComplete(this); } } + } diff --git a/spine-unity/Assets/spine-unity/SkeletonRenderer.cs b/spine-unity/Assets/spine-unity/SkeletonRenderer.cs index aad0dcaf8..5a2109e4e 100644 --- a/spine-unity/Assets/spine-unity/SkeletonRenderer.cs +++ b/spine-unity/Assets/spine-unity/SkeletonRenderer.cs @@ -76,6 +76,25 @@ public class SkeletonRenderer : MonoBehaviour { private readonly ExposedList submeshes = new ExposedList(); private SkeletonUtilitySubmeshRenderer[] submeshRenderers; + #region Runtime Instantiation + /// Add and prepare a Spine component that derives from SkeletonRenderer to a GameObject at runtime. + /// T should be SkeletonRenderer or any of its derived classes. + public static T AddSpineComponent (GameObject gameObject, SkeletonDataAsset skeletonDataAsset) where T : SkeletonRenderer { + var c = gameObject.AddComponent(); + + if (skeletonDataAsset != null) { + c.skeletonDataAsset = skeletonDataAsset; + c.Reset(); // Method signature will change. + } + + return c; + } + + public static T NewSpineGameObject (SkeletonDataAsset skeletonDataAsset) where T : SkeletonRenderer { + return SkeletonRenderer.AddSpineComponent(new GameObject("New Spine GameObject"), skeletonDataAsset); + } + #endregion + public virtual void Awake () { Reset(); } From 324cbc89cc3f06728e97cdd43666854508b46fb8 Mon Sep 17 00:00:00 2001 From: pharan Date: Tue, 26 Jan 2016 07:39:39 +0800 Subject: [PATCH 4/8] [Unity] Some cleanup to runtime instantiation. --- spine-unity/Assets/spine-unity/SkeletonRenderer.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/spine-unity/Assets/spine-unity/SkeletonRenderer.cs b/spine-unity/Assets/spine-unity/SkeletonRenderer.cs index 5a2109e4e..678108678 100644 --- a/spine-unity/Assets/spine-unity/SkeletonRenderer.cs +++ b/spine-unity/Assets/spine-unity/SkeletonRenderer.cs @@ -80,11 +80,16 @@ public class SkeletonRenderer : MonoBehaviour { /// Add and prepare a Spine component that derives from SkeletonRenderer to a GameObject at runtime. /// T should be SkeletonRenderer or any of its derived classes. public static T AddSpineComponent (GameObject gameObject, SkeletonDataAsset skeletonDataAsset) where T : SkeletonRenderer { + + // Assist [RequireComponent] enforcement. + gameObject.AddComponent(); + gameObject.AddComponent(); + var c = gameObject.AddComponent(); if (skeletonDataAsset != null) { c.skeletonDataAsset = skeletonDataAsset; - c.Reset(); // Method signature will change. + c.Reset(); // TODO: Method name will change. } return c; From 6de6a4712d48746b0b60352bf10f3cefc033292a Mon Sep 17 00:00:00 2001 From: pharan Date: Tue, 26 Jan 2016 07:41:49 +0800 Subject: [PATCH 5/8] [Unity] Internal check enforces RequireComponent better. --- spine-unity/Assets/spine-unity/SkeletonRenderer.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/spine-unity/Assets/spine-unity/SkeletonRenderer.cs b/spine-unity/Assets/spine-unity/SkeletonRenderer.cs index 678108678..3569686c5 100644 --- a/spine-unity/Assets/spine-unity/SkeletonRenderer.cs +++ b/spine-unity/Assets/spine-unity/SkeletonRenderer.cs @@ -80,11 +80,7 @@ public class SkeletonRenderer : MonoBehaviour { /// Add and prepare a Spine component that derives from SkeletonRenderer to a GameObject at runtime. /// T should be SkeletonRenderer or any of its derived classes. public static T AddSpineComponent (GameObject gameObject, SkeletonDataAsset skeletonDataAsset) where T : SkeletonRenderer { - - // Assist [RequireComponent] enforcement. - gameObject.AddComponent(); - gameObject.AddComponent(); - + var c = gameObject.AddComponent(); if (skeletonDataAsset != null) { From d2c2ce0e1409d9e92d4e67ff4f61d83473654195 Mon Sep 17 00:00:00 2001 From: pharan Date: Thu, 4 Feb 2016 23:13:33 +0800 Subject: [PATCH 6/8] [Unity] SkeletonAnimationInspector now uses SpineAnimation attribute. Bypasses Spine.AnimationState in edit mode. --- .../Editor/SkeletonAnimationInspector.cs | 62 ++++++++++--------- .../Editor/SpineAttributeDrawers.cs | 29 +++++---- .../Assets/spine-unity/SkeletonAnimation.cs | 1 + 3 files changed, 51 insertions(+), 41 deletions(-) diff --git a/spine-unity/Assets/spine-unity/Editor/SkeletonAnimationInspector.cs b/spine-unity/Assets/spine-unity/Editor/SkeletonAnimationInspector.cs index c009dca73..9cef3e48d 100644 --- a/spine-unity/Assets/spine-unity/Editor/SkeletonAnimationInspector.cs +++ b/spine-unity/Assets/spine-unity/Editor/SkeletonAnimationInspector.cs @@ -38,7 +38,7 @@ using Spine; public class SkeletonAnimationInspector : SkeletonRendererInspector { protected SerializedProperty animationName, loop, timeScale, autoReset; protected bool m_isPrefab; - protected GUIContent autoResetLabel; + protected bool wasAnimationNameChanged; protected override void OnEnable () { base.OnEnable(); @@ -56,43 +56,45 @@ public class SkeletonAnimationInspector : SkeletonRendererInspector { SkeletonAnimation component = (SkeletonAnimation)target; if (!component.valid) return; + + if (wasAnimationNameChanged) { + if (!Application.isPlaying) { + component.state.ClearTrack(0); + component.skeleton.SetToSetupPose(); + } - //catch case where SetAnimation was used to set track 0 without using AnimationName + Spine.Animation animationToUse = component.skeleton.Data.FindAnimation(animationName.stringValue); + + if (!Application.isPlaying) { + if (animationToUse != null) animationToUse.Apply(component.skeleton, 0f, 0f, false, null); + component.Update(); + component.LateUpdate(); + SceneView.RepaintAll(); + } else { + if (animationToUse != null) + component.state.SetAnimation(0, animationToUse, loop.boolValue); + else + component.state.ClearTrack(0); + } + + wasAnimationNameChanged = false; + } + + // Reflect animationName serialized property in the inspector even if SetAnimation API was used. if (Application.isPlaying) { - TrackEntry currentState = component.state.GetCurrent(0); - if (currentState != null) { + TrackEntry current = component.state.GetCurrent(0); + if (current != null) { if (component.AnimationName != animationName.stringValue) { - animationName.stringValue = currentState.Animation.Name; + animationName.stringValue = current.Animation.Name; } } } EditorGUILayout.Space(); - - //TODO: Refactor this to use GenericMenu and callbacks to avoid interfering with control by other behaviours. - // Animation name. - { - String[] animations = new String[component.skeleton.Data.Animations.Count + 1]; - animations[0] = ""; - int animationIndex = 0; - for (int i = 0; i < animations.Length - 1; i++) { - String name = component.skeleton.Data.Animations.Items[i].Name; - animations[i + 1] = name; - if (name == animationName.stringValue) - animationIndex = i + 1; - } - - animationIndex = EditorGUILayout.Popup("Animation", animationIndex, animations); - - String selectedAnimationName = animationIndex == 0 ? null : animations[animationIndex]; - if (component.AnimationName != selectedAnimationName) { - component.AnimationName = selectedAnimationName; - animationName.stringValue = selectedAnimationName; - } - - - } - + EditorGUI.BeginChangeCheck(); + EditorGUILayout.PropertyField(animationName); + wasAnimationNameChanged |= EditorGUI.EndChangeCheck(); + EditorGUILayout.PropertyField(loop); EditorGUILayout.PropertyField(timeScale); component.timeScale = Math.Max(component.timeScale, 0); diff --git a/spine-unity/Assets/spine-unity/Editor/SpineAttributeDrawers.cs b/spine-unity/Assets/spine-unity/Editor/SpineAttributeDrawers.cs index 7cdc9080e..ddf90af0a 100644 --- a/spine-unity/Assets/spine-unity/Editor/SpineAttributeDrawers.cs +++ b/spine-unity/Assets/spine-unity/Editor/SpineAttributeDrawers.cs @@ -5,17 +5,19 @@ *****************************************************************************/ using UnityEngine; using UnityEditor; +using System; using System.Collections; using System.Collections.Generic; using System.Reflection; using Spine; + public struct SpineDrawerValuePair { public string str; public SerializedProperty property; - public SpineDrawerValuePair(string val, SerializedProperty property) { + public SpineDrawerValuePair (string val, SerializedProperty property) { this.str = val; this.property = property; } @@ -23,6 +25,7 @@ public struct SpineDrawerValuePair { public abstract class SpineTreeItemDrawerBase : PropertyDrawer where T:SpineAttributeBase { protected SkeletonDataAsset skeletonDataAsset; + protected T TargetAttribute { get { return (T)attribute; } } public override void OnGUI (Rect position, SerializedProperty property, GUIContent label) { @@ -72,7 +75,7 @@ public abstract class SpineTreeItemDrawerBase : PropertyDrawer where T:SpineA return; GenericMenu menu = new GenericMenu(); - PopulateMenu (menu, property, this.TargetAttribute, data); + PopulateMenu(menu, property, this.TargetAttribute, data); menu.ShowAsContext(); } @@ -84,7 +87,7 @@ public abstract class SpineTreeItemDrawerBase : PropertyDrawer where T:SpineA pair.property.serializedObject.ApplyModifiedProperties(); } - public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { + public override float GetPropertyHeight (SerializedProperty property, GUIContent label) { return 18; } @@ -96,7 +99,7 @@ public class SpineSlotDrawer : SpineTreeItemDrawerBase { protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineSlot targetAttribute, SkeletonData data) { for (int i = 0; i < data.Slots.Count; i++) { string name = data.Slots.Items[i].Name; - if (name.StartsWith(targetAttribute.startsWith)) { + if (name.StartsWith(targetAttribute.startsWith, StringComparison.Ordinal)) { if (targetAttribute.containsBoundingBoxes) { int slotIndex = i; @@ -139,7 +142,7 @@ public class SpineSkinDrawer : SpineTreeItemDrawerBase { for (int i = 0; i < data.Skins.Count; i++) { string name = data.Skins.Items[i].Name; - if (name.StartsWith(targetAttribute.startsWith)) + if (name.StartsWith(targetAttribute.startsWith, StringComparison.Ordinal)) menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); } } @@ -150,9 +153,13 @@ public class SpineSkinDrawer : SpineTreeItemDrawerBase { public class SpineAnimationDrawer : SpineTreeItemDrawerBase { protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineAnimation targetAttribute, SkeletonData data) { var animations = skeletonDataAsset.GetAnimationStateData().SkeletonData.Animations; + + // item + menu.AddItem(new GUIContent(""), property.stringValue == "", HandleSelect, new SpineDrawerValuePair("", property)); + for (int i = 0; i < animations.Count; i++) { string name = animations.Items[i].Name; - if (name.StartsWith(targetAttribute.startsWith)) + if (name.StartsWith(targetAttribute.startsWith, StringComparison.Ordinal)) menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); } } @@ -165,7 +172,7 @@ public class SpineEventNameDrawer : SpineTreeItemDrawerBase { var events = skeletonDataAsset.GetSkeletonData(false).Events; for (int i = 0; i < events.Count; i++) { string name = events.Items[i].Name; - if (name.StartsWith(targetAttribute.startsWith)) + if (name.StartsWith(targetAttribute.startsWith, StringComparison.Ordinal)) menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); } } @@ -289,8 +296,8 @@ public class SpineBoneDrawer : SpineTreeItemDrawerBase { public class SpineAtlasRegionDrawer : PropertyDrawer { Component component; SerializedProperty atlasProp; - - public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { + + public override void OnGUI (Rect position, SerializedProperty property, GUIContent label) { if (property.propertyType != SerializedPropertyType.String) { EditorGUI.LabelField(position, "ERROR:", "May only apply to type string"); return; @@ -321,7 +328,7 @@ public class SpineAtlasRegionDrawer : PropertyDrawer { } } - + void Selector (SerializedProperty property) { GenericMenu menu = new GenericMenu(); AtlasAsset atlasAsset = (AtlasAsset)atlasProp.objectReferenceValue; @@ -337,7 +344,7 @@ public class SpineAtlasRegionDrawer : PropertyDrawer { menu.ShowAsContext(); } - + static void HandleSelect (object val) { var pair = (SpineDrawerValuePair)val; pair.property.stringValue = pair.str; diff --git a/spine-unity/Assets/spine-unity/SkeletonAnimation.cs b/spine-unity/Assets/spine-unity/SkeletonAnimation.cs index b8479b016..e2739dded 100644 --- a/spine-unity/Assets/spine-unity/SkeletonAnimation.cs +++ b/spine-unity/Assets/spine-unity/SkeletonAnimation.cs @@ -68,6 +68,7 @@ public class SkeletonAnimation : SkeletonRenderer, ISkeletonAnimation { } [SerializeField] + [SpineAnimation] private String _animationName; public String AnimationName { From e978f2e519892f1c553982532242c1853b06d883 Mon Sep 17 00:00:00 2001 From: pharan Date: Fri, 5 Feb 2016 19:28:15 +0800 Subject: [PATCH 7/8] [Unity] Prevent SkeletonAnimation from using AnimationState in edit mode. --- .../Assets/spine-unity/SkeletonAnimation.cs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/spine-unity/Assets/spine-unity/SkeletonAnimation.cs b/spine-unity/Assets/spine-unity/SkeletonAnimation.cs index e2739dded..af31742c0 100644 --- a/spine-unity/Assets/spine-unity/SkeletonAnimation.cs +++ b/spine-unity/Assets/spine-unity/SkeletonAnimation.cs @@ -133,10 +133,24 @@ public class SkeletonAnimation : SkeletonRenderer, ISkeletonAnimation { state = new Spine.AnimationState(skeletonDataAsset.GetAnimationStateData()); - if (_animationName != null && _animationName.Length > 0) { + #if UNITY_EDITOR + if (!string.IsNullOrEmpty(_animationName)) { + if (Application.isPlaying) { + state.SetAnimation(0, _animationName, loop); + } else { + // Assume SkeletonAnimation is valid for skeletonData and skeleton. Checked above. + var animationObject = skeletonDataAsset.GetSkeletonData(false).FindAnimation(_animationName); + if (animationObject != null) + animationObject.Apply(skeleton, 0f, 0f, false, null); + } + Update(0); + } + #else + if (!string.IsNullOrEmpty(_animationName)) { state.SetAnimation(0, _animationName, loop); Update(0); } + #endif } public virtual void Update () { From d4901b74d05b8e8c8a2d34090e827a482756f8d0 Mon Sep 17 00:00:00 2001 From: pharan Date: Fri, 5 Feb 2016 20:49:51 +0800 Subject: [PATCH 8/8] [Unity] GetColor, Bone position and Skeleton pose extensions. --- .../Assets/spine-unity/SkeletonExtensions.cs | 82 ++++++++++++++----- 1 file changed, 60 insertions(+), 22 deletions(-) diff --git a/spine-unity/Assets/spine-unity/SkeletonExtensions.cs b/spine-unity/Assets/spine-unity/SkeletonExtensions.cs index 03cf57536..19616b2d3 100644 --- a/spine-unity/Assets/spine-unity/SkeletonExtensions.cs +++ b/spine-unity/Assets/spine-unity/SkeletonExtensions.cs @@ -1,7 +1,7 @@ /***************************************************************************** - * Spine Extensions created by Mitch Thompson + * Spine Extensions by Mitch Thompson and John Dy * Full irrevocable rights and permissions granted to Esoteric Software *****************************************************************************/ @@ -11,6 +11,14 @@ using Spine; public static class SkeletonExtensions { + const float ByteToFloat = 1f / 255f; + + #region Colors + public static Color GetColor (this Skeleton s) { return new Color(s.r, s.g, s.b, s.a); } + public static Color GetColor (this RegionAttachment a) { return new Color(a.r, a.g, a.b, a.a); } + public static Color GetColor (this MeshAttachment a) { return new Color(a.r, a.g, a.b, a.a); } + public static Color GetColor (this SkinnedMeshAttachment a) { return new Color(a.r, a.g, a.b, a.a); } + public static void SetColor (this Skeleton skeleton, Color color) { skeleton.A = color.a; skeleton.R = color.r; @@ -19,10 +27,10 @@ public static class SkeletonExtensions { } public static void SetColor (this Skeleton skeleton, Color32 color) { - skeleton.A = color.a / 255f; - skeleton.R = color.r / 255f; - skeleton.G = color.g / 255f; - skeleton.B = color.b / 255f; + skeleton.A = color.a * ByteToFloat; + skeleton.R = color.r * ByteToFloat; + skeleton.G = color.g * ByteToFloat; + skeleton.B = color.b * ByteToFloat; } public static void SetColor (this Slot slot, Color color) { @@ -33,10 +41,10 @@ public static class SkeletonExtensions { } public static void SetColor (this Slot slot, Color32 color) { - slot.A = color.a / 255f; - slot.R = color.r / 255f; - slot.G = color.g / 255f; - slot.B = color.b / 255f; + slot.A = color.a * ByteToFloat; + slot.R = color.r * ByteToFloat; + slot.G = color.g * ByteToFloat; + slot.B = color.b * ByteToFloat; } public static void SetColor (this RegionAttachment attachment, Color color) { @@ -47,10 +55,10 @@ public static class SkeletonExtensions { } public static void SetColor (this RegionAttachment attachment, Color32 color) { - attachment.A = color.a / 255f; - attachment.R = color.r / 255f; - attachment.G = color.g / 255f; - attachment.B = color.b / 255f; + attachment.A = color.a * ByteToFloat; + attachment.R = color.r * ByteToFloat; + attachment.G = color.g * ByteToFloat; + attachment.B = color.b * ByteToFloat; } public static void SetColor (this MeshAttachment attachment, Color color) { @@ -61,10 +69,10 @@ public static class SkeletonExtensions { } public static void SetColor (this MeshAttachment attachment, Color32 color) { - attachment.A = color.a / 255f; - attachment.R = color.r / 255f; - attachment.G = color.g / 255f; - attachment.B = color.b / 255f; + attachment.A = color.a * ByteToFloat; + attachment.R = color.r * ByteToFloat; + attachment.G = color.g * ByteToFloat; + attachment.B = color.b * ByteToFloat; } public static void SetColor (this SkinnedMeshAttachment attachment, Color color) { @@ -75,12 +83,14 @@ public static class SkeletonExtensions { } public static void SetColor (this SkinnedMeshAttachment attachment, Color32 color) { - attachment.A = color.a / 255f; - attachment.R = color.r / 255f; - attachment.G = color.g / 255f; - attachment.B = color.b / 255f; + attachment.A = color.a * ByteToFloat; + attachment.R = color.r * ByteToFloat; + attachment.G = color.g * ByteToFloat; + attachment.B = color.b * ByteToFloat; } + #endregion + #region Bone Position public static void SetPosition (this Bone bone, Vector2 position) { bone.X = position.x; bone.Y = position.y; @@ -91,10 +101,36 @@ public static class SkeletonExtensions { bone.Y = position.y; } + public static Vector2 GetSkeletonSpacePosition (this Bone bone) { + // TODO: This changes in v3.0 + return new Vector2(bone.worldX, bone.worldY); + } + + public static Vector3 GetWorldPosition (this Bone bone, UnityEngine.Transform parentTransform) { + return parentTransform.TransformPoint(new Vector3(bone.worldX, bone.worldY)); + } + #endregion + + #region Posing + /// + /// Shortcut for posing a skeleton at a specific time. Time is in seconds. (frameNumber / 30f) will give you seconds. + /// If you need to do this often, you should get the Animation object yourself using skeleton.data.FindAnimation. and call Apply on that. + /// The skeleton to pose. + /// The name of the animation to use. + /// The time of the pose within the animation. + /// Wraps the time around if it is longer than the duration of the animation. + public static void PoseWithAnimation (this Skeleton skeleton, string animationName, float time, bool loop) { + // Fail loud when skeleton.data is null. + Spine.Animation animation = skeleton.data.FindAnimation(animationName); + if (animation == null) return; + animation.Apply(skeleton, 0, time, loop, null); + } + #endregion + + #region Unity Sprite To Attachments public static Attachment AttachUnitySprite (this Skeleton skeleton, string slotName, Sprite sprite, string shaderName = "Spine/Skeleton") { var att = sprite.ToRegionAttachment(shaderName); skeleton.FindSlot(slotName).Attachment = att; - return att; } @@ -117,4 +153,6 @@ public static class SkeletonExtensions { loader = null; return att; } + #endregion + } \ No newline at end of file