diff --git a/spine-unity/Assets/Examples/Scenes/Attributes and AtlasRegions.unity b/spine-unity/Assets/Examples/Scenes/Attributes and AtlasRegions.unity index eadbc4873..cc0f39f32 100644 Binary files a/spine-unity/Assets/Examples/Scenes/Attributes and AtlasRegions.unity and b/spine-unity/Assets/Examples/Scenes/Attributes and AtlasRegions.unity differ diff --git a/spine-unity/Assets/Examples/Scenes/Mix and Match.unity b/spine-unity/Assets/Examples/Scenes/Mix and Match.unity index 06731d193..775cb6bf7 100644 Binary files a/spine-unity/Assets/Examples/Scenes/Mix and Match.unity and b/spine-unity/Assets/Examples/Scenes/Mix and Match.unity differ diff --git a/spine-unity/Assets/Examples/Scenes/Spineboy Movement.unity b/spine-unity/Assets/Examples/Scenes/Spineboy Movement.unity index c625ed8a6..bea46f183 100644 Binary files a/spine-unity/Assets/Examples/Scenes/Spineboy Movement.unity and b/spine-unity/Assets/Examples/Scenes/Spineboy Movement.unity differ diff --git a/spine-unity/Assets/Examples/Scripts/BasicPlatformerController.cs b/spine-unity/Assets/Examples/Scripts/BasicPlatformerController.cs index 071dc48cc..761ac31af 100644 --- a/spine-unity/Assets/Examples/Scripts/BasicPlatformerController.cs +++ b/spine-unity/Assets/Examples/Scripts/BasicPlatformerController.cs @@ -71,17 +71,17 @@ public class BasicPlatformerController : MonoBehaviour { #if UNITY_4_5 [Header("Animation")] #endif - [SpineAnimation(dataSource: "skeletonAnimation")] + [SpineAnimation(dataField: "skeletonAnimation")] public string walkName = "Walk"; - [SpineAnimation(dataSource: "skeletonAnimation")] + [SpineAnimation(dataField: "skeletonAnimation")] public string runName = "Run"; - [SpineAnimation(dataSource: "skeletonAnimation")] + [SpineAnimation(dataField: "skeletonAnimation")] public string idleName = "Idle"; - [SpineAnimation(dataSource: "skeletonAnimation")] + [SpineAnimation(dataField: "skeletonAnimation")] public string jumpName = "Jump"; - [SpineAnimation(dataSource: "skeletonAnimation")] + [SpineAnimation(dataField: "skeletonAnimation")] public string fallName = "Fall"; - [SpineAnimation(dataSource: "skeletonAnimation")] + [SpineAnimation(dataField: "skeletonAnimation")] public string crouchName = "Crouch"; #if UNITY_4_5 diff --git a/spine-unity/Assets/Examples/Scripts/Chimera.cs b/spine-unity/Assets/Examples/Scripts/Chimera.cs index 35cab0362..27f831f86 100644 --- a/spine-unity/Assets/Examples/Scripts/Chimera.cs +++ b/spine-unity/Assets/Examples/Scripts/Chimera.cs @@ -1,11 +1,45 @@ -using UnityEngine; +/****************************************************************************** + * Spine Runtimes Software License + * Version 2.1 + * + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable and + * non-transferable license to install, execute and perform the Spine Runtimes + * Software (the "Software") solely for internal use. Without the written + * permission of Esoteric Software (typically granted by licensing Spine), you + * may not (a) modify, translate, adapt or otherwise create derivative works, + * improvements of the Software or develop new applications using the Software + * or (b) remove, delete, alter or obscure any trademarks or any copyright, + * trademark, patent or other intellectual property or proprietary rights + * notices on or in the Software, including any copy thereof. Redistributions + * in binary or source form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +/***************************************************************************** + * Basic Platformer Controller created by Mitch Thompson + * Full irrevocable rights and permissions granted to Esoteric Software +*****************************************************************************/ +using UnityEngine; using System.Collections; public class Chimera : MonoBehaviour { public SkeletonDataAsset skeletonDataSource; - [SpineAttachment(currentSkinOnly: false, returnFullPath: true, dataSource: "skeletonDataSource")] + [SpineAttachment(currentSkinOnly: false, returnAttachmentPath: true, dataField: "skeletonDataSource")] public string attachmentPath; [SpineSlot] diff --git a/spine-unity/Assets/Examples/Scripts/DynamicSpineBone.cs b/spine-unity/Assets/Examples/Scripts/DynamicSpineBone.cs new file mode 100644 index 000000000..c638f696e --- /dev/null +++ b/spine-unity/Assets/Examples/Scripts/DynamicSpineBone.cs @@ -0,0 +1,66 @@ +using UnityEngine; +using System.Collections; + +public class DynamicSpineBone : MonoBehaviour { + + public Transform speedReference; + + [SpineBone] + public string boneName; + + [Range(-90, 90)] + public float minRotation = -45; + [Range(-90, 90)] + public float maxRotation = 45; + + [Range(-2000, 2000)] + public float rotationFactor = 300; + + [Range(5, 30)] + public float returnSpeed = 10; + + [Range(100, 1000)] + public float boneSpeed = 300; + + public float returnThreshhold = 0.01f; + + public bool useAcceleration; + + + SkeletonAnimation skeletonAnimation; + float goalRotation; + Spine.Bone bone; + Vector3 velocity; + Vector3 acceleration; + Vector3 lastPosition; + + void Start() { + if (speedReference == null) + speedReference = transform; + + skeletonAnimation = GetComponent(); + bone = SpineBone.GetBone(boneName, skeletonAnimation); + skeletonAnimation.UpdateLocal += UpdateLocal; + lastPosition = speedReference.position; + } + + void FixedUpdate() { + acceleration = (speedReference.position - lastPosition) - velocity; + velocity = speedReference.position - lastPosition; + lastPosition = speedReference.position; + } + + void UpdateLocal(SkeletonAnimation animation) { + Vector3 vec = useAcceleration ? acceleration : velocity; + + if (Mathf.Abs(vec.x) < returnThreshhold) + goalRotation = Mathf.Lerp(goalRotation, 0, returnSpeed * Time.deltaTime); + else + goalRotation += vec.x * rotationFactor * Time.deltaTime * (bone.WorldFlipX ? -1 : 1); + + goalRotation = Mathf.Clamp(goalRotation, minRotation, maxRotation); + + bone.Rotation = Mathf.Lerp(bone.Rotation, bone.Rotation + goalRotation, boneSpeed * Time.deltaTime); + + } +} diff --git a/spine-unity/Assets/Examples/Scripts/DynamicSpineBone.cs.meta b/spine-unity/Assets/Examples/Scripts/DynamicSpineBone.cs.meta new file mode 100644 index 000000000..6aac53582 --- /dev/null +++ b/spine-unity/Assets/Examples/Scripts/DynamicSpineBone.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7dae3f4db9a24bf4abe2059526bfd689 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/spine-unity/Assets/Examples/Scripts/FootSoldierExample.cs b/spine-unity/Assets/Examples/Scripts/FootSoldierExample.cs index d85f99847..655445869 100644 --- a/spine-unity/Assets/Examples/Scripts/FootSoldierExample.cs +++ b/spine-unity/Assets/Examples/Scripts/FootSoldierExample.cs @@ -1,4 +1,38 @@ -using UnityEngine; +/****************************************************************************** + * Spine Runtimes Software License + * Version 2.1 + * + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable and + * non-transferable license to install, execute and perform the Spine Runtimes + * Software (the "Software") solely for internal use. Without the written + * permission of Esoteric Software (typically granted by licensing Spine), you + * may not (a) modify, translate, adapt or otherwise create derivative works, + * improvements of the Software or develop new applications using the Software + * or (b) remove, delete, alter or obscure any trademarks or any copyright, + * trademark, patent or other intellectual property or proprietary rights + * notices on or in the Software, including any copy thereof. Redistributions + * in binary or source form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +/***************************************************************************** + * FootSoldierExample created by Mitch Thompson + * Full irrevocable rights and permissions granted to Esoteric Software +*****************************************************************************/ +using UnityEngine; using System.Collections; public class FootSoldierExample : MonoBehaviour { @@ -11,10 +45,10 @@ public class FootSoldierExample : MonoBehaviour { [SpineSlot] public string eyesSlot; - [SpineAttachment(currentSkinOnly: true, slot: "eyesSlot")] + [SpineAttachment(currentSkinOnly: true, slotField: "eyesSlot")] public string eyesOpenAttachment; - [SpineAttachment(currentSkinOnly: true, slot: "eyesSlot")] + [SpineAttachment(currentSkinOnly: true, slotField: "eyesSlot")] public string blinkAttachment; [Range(0, 0.2f)] diff --git a/spine-unity/Assets/Examples/Scripts/SpineboyController.cs b/spine-unity/Assets/Examples/Scripts/SpineboyController.cs index 4cebc7de3..064152df4 100644 --- a/spine-unity/Assets/Examples/Scripts/SpineboyController.cs +++ b/spine-unity/Assets/Examples/Scripts/SpineboyController.cs @@ -1,4 +1,38 @@ -using UnityEngine; +/****************************************************************************** + * Spine Runtimes Software License + * Version 2.1 + * + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable and + * non-transferable license to install, execute and perform the Spine Runtimes + * Software (the "Software") solely for internal use. Without the written + * permission of Esoteric Software (typically granted by licensing Spine), you + * may not (a) modify, translate, adapt or otherwise create derivative works, + * improvements of the Software or develop new applications using the Software + * or (b) remove, delete, alter or obscure any trademarks or any copyright, + * trademark, patent or other intellectual property or proprietary rights + * notices on or in the Software, including any copy thereof. Redistributions + * in binary or source form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +/***************************************************************************** + * SpineboyController created by Mitch Thompson + * Full irrevocable rights and permissions granted to Esoteric Software +*****************************************************************************/ +using UnityEngine; using System.Collections; [RequireComponent(typeof(SkeletonAnimation), typeof(Rigidbody2D))] diff --git a/spine-unity/Assets/Examples/Spine/Spineboy/spineboy_Atlas.asset b/spine-unity/Assets/Examples/Spine/Spineboy/spineboy_Atlas.asset index ae8491bec..ae6be64dc 100644 Binary files a/spine-unity/Assets/Examples/Spine/Spineboy/spineboy_Atlas.asset and b/spine-unity/Assets/Examples/Spine/Spineboy/spineboy_Atlas.asset differ diff --git a/spine-unity/Assets/spine-unity/CustomSkin.cs b/spine-unity/Assets/spine-unity/CustomSkin.cs new file mode 100644 index 000000000..5b98ad66c --- /dev/null +++ b/spine-unity/Assets/spine-unity/CustomSkin.cs @@ -0,0 +1,36 @@ +using UnityEngine; +using System.Collections; +using Spine; + +public class CustomSkin : MonoBehaviour { + + + [System.Serializable] + public class SkinPair { + [SpineAttachment(currentSkinOnly: false, returnAttachmentPath: true, dataField: "skinSource")] + public string sourceAttachment; + [SpineSlot] + public string targetSlot; + [SpineAttachment(currentSkinOnly: true, placeholdersOnly: true)] + public string targetAttachment; + } + + public SkeletonDataAsset skinSource; + public SkinPair[] skinning; + public Skin customSkin; + + 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); + customSkin.AddAttachment(skeleton.FindSlotIndex(pair.targetSlot), pair.targetAttachment, attachment); + } + + skeleton.SetSkin(customSkin); + } +} diff --git a/spine-unity/Assets/spine-unity/CustomSkin.cs.meta b/spine-unity/Assets/spine-unity/CustomSkin.cs.meta new file mode 100644 index 000000000..3a7ba7a9d --- /dev/null +++ b/spine-unity/Assets/spine-unity/CustomSkin.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6e55c8477eccddc4cb5c3551a3945ca7 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/spine-unity/Assets/spine-unity/Editor/AtlasAssetInspector.cs b/spine-unity/Assets/spine-unity/Editor/AtlasAssetInspector.cs index fc07c6fae..a58fc9340 100644 --- a/spine-unity/Assets/spine-unity/Editor/AtlasAssetInspector.cs +++ b/spine-unity/Assets/spine-unity/Editor/AtlasAssetInspector.cs @@ -50,9 +50,27 @@ public class AtlasAssetInspector : Editor { serializedObject.Update(); AtlasAsset asset = (AtlasAsset)target; + EditorGUI.BeginChangeCheck(); EditorGUILayout.PropertyField(atlasFile); EditorGUILayout.PropertyField(materials, true); + if (EditorGUI.EndChangeCheck()) + serializedObject.ApplyModifiedProperties(); + if (materials.arraySize == 0) { + EditorGUILayout.LabelField(new GUIContent("Error: Missing materials", SpineEditorUtilities.Icons.warning)); + return; + } + + for (int i = 0; i < materials.arraySize; i++) { + SerializedProperty prop = materials.GetArrayElementAtIndex(i); + Material mat = (Material)prop.objectReferenceValue; + if (mat == null) { + EditorGUILayout.LabelField(new GUIContent("Error: Materials cannot be null", SpineEditorUtilities.Icons.warning)); + return; + } + } + + if (atlasFile.objectReferenceValue != null) { Atlas atlas = asset.GetAtlas(); diff --git a/spine-unity/Assets/spine-unity/Editor/GUI/icon-weights.png.meta b/spine-unity/Assets/spine-unity/Editor/GUI/icon-weights.png.meta index fa7031fc0..067337d4f 100644 --- a/spine-unity/Assets/spine-unity/Editor/GUI/icon-weights.png.meta +++ b/spine-unity/Assets/spine-unity/Editor/GUI/icon-weights.png.meta @@ -21,7 +21,7 @@ TextureImporter: grayScaleToAlpha: 0 generateCubemap: 0 seamlessCubemap: 0 - textureFormat: -1 + textureFormat: -3 maxTextureSize: 1024 textureSettings: filterMode: -1 diff --git a/spine-unity/Assets/spine-unity/Editor/SkeletonDataAssetInspector.cs b/spine-unity/Assets/spine-unity/Editor/SkeletonDataAssetInspector.cs index 0edf206e6..d2dfb0dbe 100644 --- a/spine-unity/Assets/spine-unity/Editor/SkeletonDataAssetInspector.cs +++ b/spine-unity/Assets/spine-unity/Editor/SkeletonDataAssetInspector.cs @@ -102,12 +102,18 @@ public class SkeletonDataAssetInspector : Editor { EditorGUILayout.PropertyField(skeletonJSON); EditorGUILayout.PropertyField(scale); if (EditorGUI.EndChangeCheck()) { - if (m_previewUtility != null) { - m_previewUtility.Cleanup(); - m_previewUtility = null; - } + if (serializedObject.ApplyModifiedProperties()) { - RepopulateWarnings(); + if (m_previewUtility != null) { + m_previewUtility.Cleanup(); + m_previewUtility = null; + } + + RepopulateWarnings(); + OnEnable(); + return; + } + } @@ -118,6 +124,8 @@ public class SkeletonDataAssetInspector : Editor { DrawSlotList(); } else { + + DrawReimportButton(); //Show Warnings foreach (var str in warnings) EditorGUILayout.LabelField(new GUIContent(str, SpineEditorUtilities.Icons.warning)); @@ -132,6 +140,24 @@ public class SkeletonDataAssetInspector : Editor { } } + void DrawReimportButton() { + EditorGUI.BeginDisabledGroup(skeletonJSON.objectReferenceValue == null); + if (GUILayout.Button(new GUIContent("Attempt Reimport", SpineEditorUtilities.Icons.warning))) { + SpineEditorUtilities.ImportSpineContent(new string[] { AssetDatabase.GetAssetPath(skeletonJSON.objectReferenceValue) }, true); + + if (m_previewUtility != null) { + m_previewUtility.Cleanup(); + m_previewUtility = null; + } + + RepopulateWarnings(); + OnEnable(); + return; + + } + EditorGUI.EndDisabledGroup(); + } + void DrawAnimationStateInfo() { showAnimationStateData = EditorGUILayout.Foldout(showAnimationStateData, "Animation State Data"); if (!showAnimationStateData) diff --git a/spine-unity/Assets/spine-unity/Editor/SpineAttributeDrawers.cs b/spine-unity/Assets/spine-unity/Editor/SpineAttributeDrawers.cs index cdc9a0e37..9d9e3b3d2 100644 --- a/spine-unity/Assets/spine-unity/Editor/SpineAttributeDrawers.cs +++ b/spine-unity/Assets/spine-unity/Editor/SpineAttributeDrawers.cs @@ -1,4 +1,38 @@ -using UnityEngine; +/****************************************************************************** + * Spine Runtimes Software License + * Version 2.1 + * + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable and + * non-transferable license to install, execute and perform the Spine Runtimes + * Software (the "Software") solely for internal use. Without the written + * permission of Esoteric Software (typically granted by licensing Spine), you + * may not (a) modify, translate, adapt or otherwise create derivative works, + * improvements of the Software or develop new applications using the Software + * or (b) remove, delete, alter or obscure any trademarks or any copyright, + * trademark, patent or other intellectual property or proprietary rights + * notices on or in the Software, including any copy thereof. Redistributions + * in binary or source form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +/***************************************************************************** + * Spine Attribute Drawers created by Mitch Thompson + * Full irrevocable rights and permissions granted to Esoteric Software +*****************************************************************************/ +using UnityEngine; using UnityEditor; using System.Collections; using System.Collections.Generic; @@ -32,13 +66,13 @@ public class SpineSlotDrawer : PropertyDrawer { SpineSlot attrib = (SpineSlot)attribute; - var skeletonDataAssetProperty = property.serializedObject.FindProperty(attrib.dataSource); + var dataProperty = property.serializedObject.FindProperty(attrib.dataField); - if (skeletonDataAssetProperty != null) { - if (skeletonDataAssetProperty.objectReferenceValue is SkeletonDataAsset) { - skeletonDataAsset = (SkeletonDataAsset)skeletonDataAssetProperty.objectReferenceValue; - } else if (skeletonDataAssetProperty.objectReferenceValue is SkeletonRenderer) { - var renderer = (SkeletonRenderer)skeletonDataAssetProperty.objectReferenceValue; + if (dataProperty != null) { + if (dataProperty.objectReferenceValue is SkeletonDataAsset) { + skeletonDataAsset = (SkeletonDataAsset)dataProperty.objectReferenceValue; + } else if (dataProperty.objectReferenceValue is SkeletonRenderer) { + var renderer = (SkeletonRenderer)dataProperty.objectReferenceValue; if (renderer != null) skeletonDataAsset = renderer.skeletonDataAsset; } else { @@ -102,7 +136,6 @@ public class SpineSlotDrawer : PropertyDrawer { public class SpineSkinDrawer : PropertyDrawer { SkeletonDataAsset skeletonDataAsset; - 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"); @@ -111,13 +144,13 @@ public class SpineSkinDrawer : PropertyDrawer { SpineSkin attrib = (SpineSkin)attribute; - var skeletonDataAssetProperty = property.serializedObject.FindProperty(attrib.dataSource); + var dataProperty = property.serializedObject.FindProperty(attrib.dataField); - if (skeletonDataAssetProperty != null) { - if (skeletonDataAssetProperty.objectReferenceValue is SkeletonDataAsset) { - skeletonDataAsset = (SkeletonDataAsset)skeletonDataAssetProperty.objectReferenceValue; - } else if (skeletonDataAssetProperty.objectReferenceValue is SkeletonRenderer) { - var renderer = (SkeletonRenderer)skeletonDataAssetProperty.objectReferenceValue; + if (dataProperty != null) { + if (dataProperty.objectReferenceValue is SkeletonDataAsset) { + skeletonDataAsset = (SkeletonDataAsset)dataProperty.objectReferenceValue; + } else if (dataProperty.objectReferenceValue is SkeletonRenderer) { + var renderer = (SkeletonRenderer)dataProperty.objectReferenceValue; if (renderer != null) skeletonDataAsset = renderer.skeletonDataAsset; } else { @@ -256,13 +289,13 @@ public class SpineAnimationDrawer : PropertyDrawer { SpineAnimation attrib = (SpineAnimation)attribute; - var skeletonDataAssetProperty = property.serializedObject.FindProperty(attrib.dataSource); + var dataProperty = property.serializedObject.FindProperty(attrib.dataField); - if (skeletonDataAssetProperty != null) { - if (skeletonDataAssetProperty.objectReferenceValue is SkeletonDataAsset) { - skeletonDataAsset = (SkeletonDataAsset)skeletonDataAssetProperty.objectReferenceValue; - } else if (skeletonDataAssetProperty.objectReferenceValue is SkeletonRenderer) { - var renderer = (SkeletonRenderer)skeletonDataAssetProperty.objectReferenceValue; + if (dataProperty != null) { + if (dataProperty.objectReferenceValue is SkeletonDataAsset) { + skeletonDataAsset = (SkeletonDataAsset)dataProperty.objectReferenceValue; + } else if (dataProperty.objectReferenceValue is SkeletonRenderer) { + var renderer = (SkeletonRenderer)dataProperty.objectReferenceValue; if (renderer != null) skeletonDataAsset = renderer.skeletonDataAsset; } else { @@ -332,13 +365,13 @@ public class SpineAttachmentDrawer : PropertyDrawer { SpineAttachment attrib = (SpineAttachment)attribute; - var skeletonDataAssetProperty = property.serializedObject.FindProperty(attrib.dataSource); + var dataProperty = property.serializedObject.FindProperty(attrib.dataField); - if (skeletonDataAssetProperty != null) { - if (skeletonDataAssetProperty.objectReferenceValue is SkeletonDataAsset) { - skeletonDataAsset = (SkeletonDataAsset)skeletonDataAssetProperty.objectReferenceValue; - } else if (skeletonDataAssetProperty.objectReferenceValue is SkeletonRenderer) { - var renderer = (SkeletonRenderer)skeletonDataAssetProperty.objectReferenceValue; + if (dataProperty != null) { + if (dataProperty.objectReferenceValue is SkeletonDataAsset) { + skeletonDataAsset = (SkeletonDataAsset)dataProperty.objectReferenceValue; + } else if (dataProperty.objectReferenceValue is SkeletonRenderer) { + var renderer = (SkeletonRenderer)dataProperty.objectReferenceValue; if (renderer != null) skeletonDataAsset = renderer.skeletonDataAsset; else { @@ -381,8 +414,11 @@ public class SpineAttachmentDrawer : PropertyDrawer { List validSkins = new List(); if (skeletonRenderer != null && attrib.currentSkinOnly) { - if (skeletonRenderer.skeleton.Skin != null) + if (skeletonRenderer.skeleton.Skin != null) { validSkins.Add(skeletonRenderer.skeleton.Skin); + } else { + validSkins.Add(data.Skins[0]); + } } else { foreach (Skin skin in data.Skins) { if (skin != null) @@ -392,6 +428,8 @@ public class SpineAttachmentDrawer : PropertyDrawer { GenericMenu menu = new GenericMenu(); List attachmentNames = new List(); + List placeholderNames = new List(); + string prefix = ""; if (skeletonRenderer != null && attrib.currentSkinOnly) @@ -405,7 +443,7 @@ public class SpineAttachmentDrawer : PropertyDrawer { Skin defaultSkin = data.Skins[0]; - SerializedProperty slotProperty = property.serializedObject.FindProperty(attrib.slotSource); + SerializedProperty slotProperty = property.serializedObject.FindProperty(attrib.slotField); string slotMatch = ""; if (slotProperty != null) { if (slotProperty.propertyType == SerializedPropertyType.String) { @@ -424,18 +462,31 @@ public class SpineAttachmentDrawer : PropertyDrawer { continue; attachmentNames.Clear(); + placeholderNames.Clear(); + skin.FindNamesForSlot(i, attachmentNames); - if (skin != defaultSkin) + if (skin != defaultSkin) { defaultSkin.FindNamesForSlot(i, attachmentNames); + skin.FindNamesForSlot(i, placeholderNames); + } + for (int a = 0; a < attachmentNames.Count; a++) { + string attachmentPath = attachmentNames[a]; string menuPath = prefix + data.Slots[i].Name + "/" + attachmentPath; string name = attachmentNames[a]; - if (attrib.returnFullPath) + if (attrib.returnAttachmentPath) name = skin.Name + "/" + data.Slots[i].Name + "/" + attachmentPath; - menu.AddItem(new GUIContent(menuPath), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); + + if (attrib.placeholdersOnly && placeholderNames.Contains(attachmentPath) == false) { + menu.AddDisabledItem(new GUIContent(menuPath)); + } else { + menu.AddItem(new GUIContent(menuPath), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); + } + + } } } @@ -454,3 +505,81 @@ public class SpineAttachmentDrawer : PropertyDrawer { return 18; } } + +[CustomPropertyDrawer(typeof(SpineBone))] +public class SpineBoneDrawer : PropertyDrawer { + SkeletonDataAsset skeletonDataAsset; + + 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; + } + + SpineBone attrib = (SpineBone)attribute; + + var dataProperty = property.serializedObject.FindProperty(attrib.dataField); + + if (dataProperty != null) { + if (dataProperty.objectReferenceValue is SkeletonDataAsset) { + skeletonDataAsset = (SkeletonDataAsset)dataProperty.objectReferenceValue; + } else if (dataProperty.objectReferenceValue is SkeletonRenderer) { + var renderer = (SkeletonRenderer)dataProperty.objectReferenceValue; + if (renderer != null) + skeletonDataAsset = renderer.skeletonDataAsset; + } else { + EditorGUI.LabelField(position, "ERROR:", "Invalid reference type"); + return; + } + + } else if (property.serializedObject.targetObject is Component) { + var component = (Component)property.serializedObject.targetObject; + if (component.GetComponent() != null) { + var skeletonRenderer = component.GetComponent(); + skeletonDataAsset = skeletonRenderer.skeletonDataAsset; + } + } + + if (skeletonDataAsset == null) { + EditorGUI.LabelField(position, "ERROR:", "Must have reference to a SkeletonDataAsset"); + return; + } + + position = EditorGUI.PrefixLabel(position, label); + + if (GUI.Button(position, property.stringValue, EditorStyles.popup)) { + Selector(property); + } + + } + + void Selector(SerializedProperty property) { + SpineBone attrib = (SpineBone)attribute; + SkeletonData data = skeletonDataAsset.GetSkeletonData(true); + if (data == null) + return; + + GenericMenu menu = new GenericMenu(); + + menu.AddDisabledItem(new GUIContent(skeletonDataAsset.name)); + menu.AddSeparator(""); + + for (int i = 0; i < data.Bones.Count; i++) { + string name = data.Bones[i].Name; + if (name.StartsWith(attrib.startsWith)) + menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); + } + + menu.ShowAsContext(); + } + + void HandleSelect(object val) { + var pair = (SpineDrawerValuePair)val; + pair.property.stringValue = pair.str; + pair.property.serializedObject.ApplyModifiedProperties(); + } + + public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { + return 18; + } +} diff --git a/spine-unity/Assets/spine-unity/Editor/SpineEditorUtilities.cs b/spine-unity/Assets/spine-unity/Editor/SpineEditorUtilities.cs index f939ce455..428860341 100644 --- a/spine-unity/Assets/spine-unity/Editor/SpineEditorUtilities.cs +++ b/spine-unity/Assets/spine-unity/Editor/SpineEditorUtilities.cs @@ -209,8 +209,10 @@ public class SpineEditorUtilities : AssetPostprocessor { } - static void OnPostprocessAllAssets(string[] imported, string[] deleted, string[] moved, string[] movedFromAssetPaths) { + ImportSpineContent(imported, false); + } + public static void ImportSpineContent(string[] imported, bool reimport = false) { List atlasPaths = new List(); List imagePaths = new List(); @@ -242,6 +244,9 @@ public class SpineEditorUtilities : AssetPostprocessor { //import atlases first foreach (string ap in atlasPaths) { + if (!reimport && CheckForValidAtlas(ap)) + continue; + TextAsset atlasText = (TextAsset)AssetDatabase.LoadAssetAtPath(ap, typeof(TextAsset)); AtlasAsset atlas = IngestSpineAtlas(atlasText); atlases.Add(atlas); @@ -250,7 +255,14 @@ public class SpineEditorUtilities : AssetPostprocessor { //import skeletons and match them with atlases bool abortSkeletonImport = false; foreach (string sp in skeletonPaths) { + if (!reimport && CheckForValidSkeletonData(sp)) { + Debug.Log("Automatically skipping: " + sp); + continue; + } + + string dir = Path.GetDirectoryName(sp); + var localAtlases = FindAtlasesAtPath(dir); var requiredPaths = GetRequiredAtlasRegions(sp); var atlasMatch = GetMatchingAtlas(requiredPaths, localAtlases); @@ -307,6 +319,48 @@ public class SpineEditorUtilities : AssetPostprocessor { //TODO: any post processing of images } + static bool CheckForValidSkeletonData(string skeletonJSONPath) { + + string dir = Path.GetDirectoryName(skeletonJSONPath); + TextAsset textAsset = (TextAsset)AssetDatabase.LoadAssetAtPath(skeletonJSONPath, typeof(TextAsset)); + DirectoryInfo dirInfo = new DirectoryInfo(dir); + + FileInfo[] files = dirInfo.GetFiles("*.asset"); + + foreach (var f in files) { + string localPath = dir + "/" + f.Name; + var obj = AssetDatabase.LoadAssetAtPath(localPath, typeof(Object)); + if (obj is SkeletonDataAsset) { + var skeletonDataAsset = (SkeletonDataAsset)obj; + if (skeletonDataAsset.skeletonJSON == textAsset) + return true; + } + } + + return false; + } + + static bool CheckForValidAtlas(string atlasPath) { + + string dir = Path.GetDirectoryName(atlasPath); + TextAsset textAsset = (TextAsset)AssetDatabase.LoadAssetAtPath(atlasPath, typeof(TextAsset)); + DirectoryInfo dirInfo = new DirectoryInfo(dir); + + FileInfo[] files = dirInfo.GetFiles("*.asset"); + + foreach (var f in files) { + string localPath = dir + "/" + f.Name; + var obj = AssetDatabase.LoadAssetAtPath(localPath, typeof(Object)); + if (obj is AtlasAsset) { + var atlasAsset = (AtlasAsset)obj; + if (atlasAsset.atlasFile == textAsset) + return true; + } + } + + return false; + } + static List MultiAtlasDialog(List requiredPaths, string initialDirectory, string header = "") { List atlasAssets = new List(); diff --git a/spine-unity/Assets/spine-unity/SpineAttributes.cs b/spine-unity/Assets/spine-unity/SpineAttributes.cs index 727196d49..1bf15bca2 100644 --- a/spine-unity/Assets/spine-unity/SpineAttributes.cs +++ b/spine-unity/Assets/spine-unity/SpineAttributes.cs @@ -1,87 +1,138 @@ -using UnityEngine; +/****************************************************************************** + * Spine Runtimes Software License + * Version 2.1 + * + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable and + * non-transferable license to install, execute and perform the Spine Runtimes + * Software (the "Software") solely for internal use. Without the written + * permission of Esoteric Software (typically granted by licensing Spine), you + * may not (a) modify, translate, adapt or otherwise create derivative works, + * improvements of the Software or develop new applications using the Software + * or (b) remove, delete, alter or obscure any trademarks or any copyright, + * trademark, patent or other intellectual property or proprietary rights + * notices on or in the Software, including any copy thereof. Redistributions + * in binary or source form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +/***************************************************************************** + * Spine Attributes created by Mitch Thompson + * Full irrevocable rights and permissions granted to Esoteric Software +*****************************************************************************/ +using UnityEngine; using System.Collections; public class SpineSlot : PropertyAttribute { public string startsWith = ""; - public string dataSource = ""; + public string dataField = ""; /// - /// + /// Smart popup menu for Spine Slots /// - /// - /// SerializedProperty name containing a reference to either a SkeletonRenderer or a SkeletonDataAsset - public SpineSlot(string startsWith = "", string dataSource = "") { + /// Filters popup results to elements that begin with supplied string. + /// 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 SpineSlot(string startsWith = "", string dataField = "") { this.startsWith = startsWith; - this.dataSource = dataSource; + this.dataField = dataField; } } public class SpineSkin : PropertyAttribute { public string startsWith = ""; - public string dataSource = ""; + public string dataField = ""; /// - /// + /// Smart popup menu for Spine Skins /// - /// - /// SerializedProperty name containing a reference to either a SkeletonRenderer or a SkeletonDataAsset - public SpineSkin(string startsWith = "", string dataSource = "") { + /// Filters popup results to elements that begin with supplied string. + /// 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 SpineSkin(string startsWith = "", string dataField = "") { this.startsWith = startsWith; - this.dataSource = dataSource; + this.dataField = dataField; } } - -public class SpineAtlasRegion : PropertyAttribute { - -} - public class SpineAnimation : PropertyAttribute { public string startsWith = ""; - public string dataSource = ""; + public string dataField = ""; /// - /// + /// Smart popup menu for Spine Animations /// - /// - /// SerializedProperty name containing a reference to either a SkeletonRenderer or a SkeletonDataAsset - public SpineAnimation(string startsWith = "", string dataSource = "") { + /// Filters popup results to elements that begin with supplied string. + /// 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 SpineAnimation(string startsWith = "", string dataField = "") { this.startsWith = startsWith; - this.dataSource = dataSource; + this.dataField = dataField; } } public class SpineAttachment : PropertyAttribute { - public bool returnFullPath; - public bool currentSkinOnly; - public string dataSource = ""; - public string slotSource = ""; + public bool returnAttachmentPath = false; + public bool currentSkinOnly = false; + public bool placeholdersOnly = false; + public string dataField = ""; + public string slotField = ""; public SpineAttachment() { } - public SpineAttachment(bool currentSkinOnly = true, bool returnFullPath = false, string slot = "", string dataSource = "") { + /// + /// 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" + /// 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 = "") { this.currentSkinOnly = currentSkinOnly; - this.returnFullPath = returnFullPath; - this.slotSource = slot; - this.dataSource = dataSource; + this.returnAttachmentPath = returnAttachmentPath; + this.placeholdersOnly = placeholdersOnly; + this.slotField = slotField; + this.dataField = dataField; } public static Hierarchy GetHierarchy(string fullPath) { return new Hierarchy(fullPath); } - public static Spine.Attachment GetAttachment(string fullPath, Spine.SkeletonData skeletonData) { - var hierarchy = SpineAttachment.GetHierarchy(fullPath); + public static Spine.Attachment GetAttachment(string attachmentPath, Spine.SkeletonData skeletonData) { + var hierarchy = SpineAttachment.GetHierarchy(attachmentPath); if (hierarchy.name == "") return null; return skeletonData.FindSkin(hierarchy.skin).GetAttachment(skeletonData.FindSlotIndex(hierarchy.slot), hierarchy.name); } - public static Spine.Attachment GetAttachment(string fullPath, SkeletonDataAsset skeletonDataAsset) { - return GetAttachment(fullPath, skeletonDataAsset.GetSkeletonData(true)); + public static Spine.Attachment GetAttachment(string attachmentPath, SkeletonDataAsset skeletonDataAsset) { + return GetAttachment(attachmentPath, skeletonDataAsset.GetSkeletonData(true)); } public struct Hierarchy { @@ -108,4 +159,40 @@ public class SpineAttachment : PropertyAttribute { } } } +} + +public class SpineBone : PropertyAttribute { + public string startsWith = ""; + public string dataField = ""; + + /// + /// Smart popup menu for Spine Bones + /// + /// Filters popup results to elements that begin with supplied string. + /// 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 SpineBone(string startsWith = "", string dataField = "") { + this.startsWith = startsWith; + this.dataField = dataField; + } + + public static Spine.Bone GetBone(string boneName, SkeletonRenderer renderer) { + if (renderer.skeleton == null) + return null; + + return renderer.skeleton.FindBone(boneName); + } + + public static Spine.BoneData GetBoneData(string boneName, SkeletonDataAsset skeletonDataAsset) { + var data = skeletonDataAsset.GetSkeletonData(true); + + return data.FindBone(boneName); + } +} + +public class SpineAtlasRegion : PropertyAttribute { + //TODO: Standardize with Skeleton attributes + //NOTE: For now, relies on locally scoped field named "atlasAsset" for source. } \ No newline at end of file