diff --git a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/BoneFollowerInspector.cs b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/BoneFollowerInspector.cs index f1cb32fd0..dd44a34c5 100644 --- a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/BoneFollowerInspector.cs +++ b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/BoneFollowerInspector.cs @@ -46,7 +46,7 @@ namespace Spine.Unity.Editor { [MenuItem ("CONTEXT/SkeletonRenderer/Add BoneFollower GameObject")] static void AddBoneFollowerGameObject (MenuCommand cmd) { var skeletonRenderer = cmd.context as SkeletonRenderer; - var go = new GameObject("BoneFollower"); + var go = new GameObject("New BoneFollower"); var t = go.transform; t.SetParent(skeletonRenderer.transform); t.localPosition = Vector3.zero; @@ -65,8 +65,20 @@ namespace Spine.Unity.Editor { var skeletonRenderer = cmd.context as SkeletonRenderer; return skeletonRenderer.valid; } + + [MenuItem("CONTEXT/BoneFollower/Rename BoneFollower GameObject")] + static void RenameGameObject (MenuCommand cmd) { + AutonameGameObject(cmd.context as BoneFollower); + } #endregion + static void AutonameGameObject (BoneFollower boneFollower) { + if (boneFollower == null) return; + + string boneName = boneFollower.boneName; + boneFollower.gameObject.name = string.IsNullOrEmpty(boneName) ? "BoneFollower" : string.Format("{0} (BoneFollower)", boneName); + } + void OnEnable () { skeletonRenderer = serializedObject.FindProperty("skeletonRenderer"); boneName = serializedObject.FindProperty("boneName"); @@ -195,7 +207,7 @@ namespace Spine.Unity.Editor { bool missingRigidBody = (hasCollider2D && component.GetComponent() == null) || (hasCollider3D && component.GetComponent() == null); if (missingRigidBody) { using (new SpineInspectorUtility.BoxScope()) { - EditorGUILayout.HelpBox("Collider detected. Unity recommends adding a Rigidbody to the parent Transforms of any colliders that are intended to be dynamically repositioned and rotated.", MessageType.Warning); + EditorGUILayout.HelpBox("Collider detected. Unity recommends adding a Rigidbody to the Transforms of any colliders that are intended to be dynamically repositioned and rotated.", MessageType.Warning); var rbType = hasCollider2D ? typeof(Rigidbody2D) : typeof(Rigidbody); string rbLabel = string.Format("Add {0}", rbType.Name); var rbContent = SpineInspectorUtility.TempContent(rbLabel, SpineInspectorUtility.UnityIcon(rbType), "Add a rigidbody to this GameObject to be the Physics body parent of the attached collider."); diff --git a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/SkeletonAnimationInspector.cs b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/SkeletonAnimationInspector.cs index b5755dcda..4f9d5d189 100644 --- a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/SkeletonAnimationInspector.cs +++ b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/SkeletonAnimationInspector.cs @@ -56,16 +56,12 @@ namespace Spine.Unity.Editor { bool sameData = SpineInspectorUtility.TargetsUseSameData(serializedObject); if (multi) { - foreach (var o in targets) - TrySetAnimation(o, multi); + foreach (var o in targets) + TrySetAnimation(o as SkeletonAnimation, multi); EditorGUILayout.Space(); if (!sameData) { - #if UNITY_5_3_OR_NEWER EditorGUILayout.DelayedTextField(animationName); - #else - animationName.stringValue = EditorGUILayout.TextField(animationName.displayName, animationName.stringValue); - #endif } else { EditorGUI.BeginChangeCheck(); EditorGUILayout.PropertyField(animationName); @@ -78,7 +74,7 @@ namespace Spine.Unity.Editor { component.timeScale = Mathf.Max(component.timeScale, 0); } } else { - TrySetAnimation(target, multi); + TrySetAnimation(target as SkeletonAnimation, multi); EditorGUILayout.Space(); EditorGUI.BeginChangeCheck(); @@ -99,31 +95,33 @@ namespace Spine.Unity.Editor { } } - protected void TrySetAnimation (Object o, bool multi) { - var skeletonAnimation = o as SkeletonAnimation; + protected void TrySetAnimation (SkeletonAnimation skeletonAnimation, bool multi) { if (skeletonAnimation == null) return; if (!skeletonAnimation.valid) return; if (!isInspectingPrefab) { if (wasAnimationNameChanged) { + var skeleton = skeletonAnimation.Skeleton; + var state = skeletonAnimation.AnimationState; + if (!Application.isPlaying) { - if (skeletonAnimation.state != null) skeletonAnimation.state.ClearTrack(0); - skeletonAnimation.skeleton.SetToSetupPose(); + if (state != null) state.ClearTrack(0); + skeleton.SetToSetupPose(); } - Spine.Animation animationToUse = skeletonAnimation.skeleton.Data.FindAnimation(animationName.stringValue); + Spine.Animation animationToUse = skeleton.Data.FindAnimation(animationName.stringValue); if (!Application.isPlaying) { - if (animationToUse != null) animationToUse.PoseSkeleton(skeletonAnimation.Skeleton, 0f); - skeletonAnimation.Update(0); + if (animationToUse != null) animationToUse.PoseSkeleton(skeleton, 0f); + skeleton.UpdateWorldTransform(); skeletonAnimation.LateUpdate(); requireRepaint = true; } else { if (animationToUse != null) - skeletonAnimation.state.SetAnimation(0, animationToUse, loop.boolValue); + state.SetAnimation(0, animationToUse, loop.boolValue); else - skeletonAnimation.state.ClearTrack(0); + state.ClearTrack(0); } wasAnimationNameChanged = false; @@ -131,7 +129,7 @@ namespace Spine.Unity.Editor { // Reflect animationName serialized property in the inspector even if SetAnimation API was used. if (!multi && Application.isPlaying) { - TrackEntry current = skeletonAnimation.state.GetCurrent(0); + TrackEntry current = skeletonAnimation.AnimationState.GetCurrent(0); if (current != null) { if (skeletonAnimation.AnimationName != animationName.stringValue) animationName.stringValue = current.Animation.Name; diff --git a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/SkeletonRendererInspector.cs b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/SkeletonRendererInspector.cs index 79858f680..1750fa54d 100644 --- a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/SkeletonRendererInspector.cs +++ b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/SkeletonRendererInspector.cs @@ -49,14 +49,20 @@ namespace Spine.Unity.Editor { protected SerializedProperty skeletonDataAsset, initialSkinName; protected SerializedProperty initialFlipX, initialFlipY; protected SerializedProperty singleSubmesh, separatorSlotNames, clearStateOnDisable, immutableTriangles; - protected SerializedProperty normals, tangents, meshes, zSpacing, pmaVertexColors, tintBlack; // MeshGenerator settings + protected SerializedProperty normals, tangents, zSpacing, pmaVertexColors, tintBlack; // MeshGenerator settings protected SpineInspectorUtility.SerializedSortingProperties sortingProperties; + protected bool isInspectingPrefab; + protected bool forceReloadQueued = false; protected GUIContent SkeletonDataAssetLabel, SkeletonUtilityButtonContent; - protected GUIContent PMAVertexColorsLabel, ClearStateOnDisableLabel, ZSpacingLabel, MeshesLabel, ImmubleTrianglesLabel, TintBlackLabel, SingleSubmeshLabel; + protected GUIContent PMAVertexColorsLabel, ClearStateOnDisableLabel, ZSpacingLabel, ImmubleTrianglesLabel, TintBlackLabel, SingleSubmeshLabel; protected GUIContent NormalsLabel, TangentsLabel; - const string ReloadButtonLabel = "Reload"; + + const string ReloadButtonString = "Reload"; + static GUILayoutOption reloadButtonWidth; + static GUILayoutOption ReloadButtonWidth { get { return reloadButtonWidth = reloadButtonWidth ?? GUILayout.Width(GUI.skin.label.CalcSize(new GUIContent(ReloadButtonString)).x + 20); } } + static GUIStyle ReloadButtonStyle { get { return EditorStyles.miniButtonRight; } } protected bool TargetIsValid { get { @@ -76,13 +82,12 @@ namespace Spine.Unity.Editor { protected virtual void OnEnable () { isInspectingPrefab = (PrefabUtility.GetPrefabType(target) == PrefabType.Prefab); - + SpineEditorUtilities.ConfirmInitialization(); // Labels SkeletonDataAssetLabel = new GUIContent("SkeletonData Asset", Icons.spine); SkeletonUtilityButtonContent = new GUIContent("Add Skeleton Utility", Icons.skeletonUtility); - MeshesLabel = new GUIContent("Render MeshAttachments", "Disable to optimize rendering for skeletons that don't use Mesh Attachments"); ImmubleTrianglesLabel = new GUIContent("Immutable Triangles", "Enable to optimize rendering for skeletons that never change attachment visbility"); PMAVertexColorsLabel = new GUIContent("PMA Vertex Colors", "Use this if you are using the default Spine/Skeleton shader or any premultiply-alpha shader."); ClearStateOnDisableLabel = new GUIContent("Clear State On Disable", "Use this if you are pooling or enabling/disabling your Spine GameObject."); @@ -90,7 +95,7 @@ namespace Spine.Unity.Editor { NormalsLabel = new GUIContent("Add Normals", "Use this if your shader requires vertex normals. A more efficient solution for 2D setups is to modify the shader to assume a single normal value for the whole mesh."); TangentsLabel = new GUIContent("Solve Tangents", "Calculates the tangents per frame. Use this if you are using lit shaders (usually with normal maps) that require vertex tangents."); TintBlackLabel = new GUIContent("Tint Black (!)", "Adds black tint vertex data to the mesh as UV2 and UV3. Black tinting requires that the shader interpret UV2 and UV3 as black tint colors for this effect to work. You may also use the default [Spine/Skeleton Tint Black] shader.\n\nIf you only need to tint the whole skeleton and not individual parts, the [Spine/Skeleton Tint] shader is recommended for better efficiency and changing/animating the _Black material property via MaterialPropertyBlock."); - SingleSubmeshLabel = new GUIContent("Use Single Submesh", "Simplifies submesh determination by assuming you are only using one Material and need only one submesh. This is will disable render separation and custom slot materials."); + SingleSubmeshLabel = new GUIContent("Use Single Submesh", "Simplifies submesh generation by assuming you are only using one Material and need only one submesh. This is will disable multiple materials, render separation, and custom slot materials."); var so = this.serializedObject; skeletonDataAsset = so.FindProperty("skeletonDataAsset"); @@ -99,7 +104,6 @@ namespace Spine.Unity.Editor { initialFlipY = so.FindProperty("initialFlipY"); normals = so.FindProperty("addNormals"); tangents = so.FindProperty("calculateTangents"); - meshes = so.FindProperty("renderMeshes"); immutableTriangles = so.FindProperty("immutableTriangles"); pmaVertexColors = so.FindProperty("pmaVertexColors"); clearStateOnDisable = so.FindProperty("clearStateOnDisable"); @@ -111,80 +115,115 @@ namespace Spine.Unity.Editor { zSpacing = so.FindProperty("zSpacing"); - SerializedObject rso = SpineInspectorUtility.GetRenderersSerializedObject(serializedObject); - sortingProperties = new SpineInspectorUtility.SerializedSortingProperties(rso); + SerializedObject renderersSerializedObject = SpineInspectorUtility.GetRenderersSerializedObject(serializedObject); // Allows proper multi-edit behavior. + sortingProperties = new SpineInspectorUtility.SerializedSortingProperties(renderersSerializedObject); } - GUIContent[] skins; - ExposedList loadedSkinList; + public void OnSceneGUI () { + var skeletonRenderer = (SkeletonRenderer)target; + var skeleton = skeletonRenderer.Skeleton; + var transform = skeletonRenderer.transform; + if (skeleton == null) return; + + SpineHandles.DrawBones(transform, skeleton); + } + + override public void OnInspectorGUI () { + bool multi = serializedObject.isEditingMultipleObjects; + DrawInspectorGUI(multi); + if (serializedObject.ApplyModifiedProperties() || SpineInspectorUtility.UndoRedoPerformed(Event.current)) { + if (!Application.isPlaying) { + if (multi) { + foreach (var o in targets) EditorForceInitializeComponent((SkeletonRenderer)o); + } else { + EditorForceInitializeComponent((SkeletonRenderer)target); + } + SceneView.RepaintAll(); + } + } + + if (!Application.isPlaying && Event.current.type == EventType.Layout) { + bool mismatchDetected = false; + if (multi) { + foreach (var o in targets) + mismatchDetected |= UpdateIfSkinMismatch((SkeletonRenderer)o); + } else { + mismatchDetected |= UpdateIfSkinMismatch(target as SkeletonRenderer); + } + + if (mismatchDetected) { + mismatchDetected = false; + SceneView.RepaintAll(); + } + } + } protected virtual void DrawInspectorGUI (bool multi) { - bool valid = TargetIsValid; - var reloadWidth = GUILayout.Width(GUI.skin.label.CalcSize(new GUIContent(ReloadButtonLabel)).x + 20); - var reloadButtonStyle = EditorStyles.miniButtonRight; - - if (multi) { - using (new EditorGUILayout.HorizontalScope(EditorStyles.helpBox)) { - SpineInspectorUtility.PropertyFieldFitLabel(skeletonDataAsset, SkeletonDataAssetLabel); - if (GUILayout.Button(ReloadButtonLabel, reloadButtonStyle, reloadWidth)) { + // Initialize. + if (Event.current.type == EventType.Layout) { + if (forceReloadQueued) { + forceReloadQueued = false; + if (multi) { + foreach (var c in targets) + EditorForceReloadSkeletonDataAssetAndComponent(c as SkeletonRenderer); + } else { + EditorForceReloadSkeletonDataAssetAndComponent(target as SkeletonRenderer); + } + } else { + if (multi) { foreach (var c in targets) { var component = c as SkeletonRenderer; - if (component.skeletonDataAsset != null) { - foreach (AtlasAssetBase aa in component.skeletonDataAsset.atlasAssets) { - if (aa != null) - aa.Clear(); - } - component.skeletonDataAsset.Clear(); + if (!component.valid) { + EditorForceInitializeComponent(component); + if (!component.valid) continue; } - component.Initialize(true); } + } else { + var component = (SkeletonRenderer)target; + if (!component.valid) + EditorForceInitializeComponent(component); } } - foreach (var c in targets) { - var component = c as SkeletonRenderer; - if (!component.valid) { - if (Event.current.type == EventType.Layout) { - component.Initialize(true); - component.LateUpdate(); + #if NO_PREFAB_MESH + if (isInspectingPrefab) { + if (multi) { + foreach (var c in targets) { + var component = (SkeletonRenderer)c; + MeshFilter meshFilter = component.GetComponent(); + if (meshFilter != null && meshFilter.sharedMesh != null) + meshFilter.sharedMesh = null; } - if (!component.valid) - continue; - } - - #if NO_PREFAB_MESH - if (isInspectingPrefab) { + } else { + var component = (SkeletonRenderer)target; MeshFilter meshFilter = component.GetComponent(); if (meshFilter != null && meshFilter.sharedMesh != null) meshFilter.sharedMesh = null; } - #endif } - - if (valid) - EditorGUILayout.PropertyField(initialSkinName); + #endif + } + + bool valid = TargetIsValid; + + // Fields. + if (multi) { + using (new EditorGUILayout.HorizontalScope(EditorStyles.helpBox)) { + SpineInspectorUtility.PropertyFieldFitLabel(skeletonDataAsset, SkeletonDataAssetLabel); + if (GUILayout.Button(ReloadButtonString, ReloadButtonStyle, ReloadButtonWidth)) + forceReloadQueued = true; + } + + if (valid) EditorGUILayout.PropertyField(initialSkinName, SpineInspectorUtility.TempContent("Initial Skin")); } else { var component = (SkeletonRenderer)target; - if (!component.valid && Event.current.type == EventType.Layout) { - component.Initialize(true); - component.LateUpdate(); - } - using (new EditorGUILayout.HorizontalScope(EditorStyles.helpBox)) { SpineInspectorUtility.PropertyFieldFitLabel(skeletonDataAsset, SkeletonDataAssetLabel); if (component.valid) { - if (GUILayout.Button(ReloadButtonLabel, reloadButtonStyle, reloadWidth)) { - if (component.skeletonDataAsset != null) { - foreach (AtlasAssetBase aa in component.skeletonDataAsset.atlasAssets) { - if (aa != null) - aa.Clear(); - } - component.skeletonDataAsset.Clear(); - } - component.Initialize(true); - } + if (GUILayout.Button(ReloadButtonString, ReloadButtonStyle, ReloadButtonWidth)) + forceReloadQueued = true; } } @@ -193,38 +232,14 @@ namespace Spine.Unity.Editor { return; } - #if NO_PREFAB_MESH - if (isInspectingPrefab) { - MeshFilter meshFilter = component.GetComponent(); - if (meshFilter != null && meshFilter.sharedMesh != null) - meshFilter.sharedMesh = null; + if (!SkeletonDataAssetIsValid(component.skeletonDataAsset)) { + EditorGUILayout.HelpBox("Skeleton Data Asset error. Please check Skeleton Data Asset.", MessageType.Error); + return; } - #endif - // Initial skin name. - if (component.valid) { - var skeletonDataSkins = component.skeleton.Data.Skins; - int skinCount = skeletonDataSkins.Count; - if (loadedSkinList != skeletonDataSkins) { - skins = new GUIContent[skinCount]; - loadedSkinList = skeletonDataSkins; - for (int i = 0; i < skins.Length; i++) { - string skinNameString = skeletonDataSkins.Items[i].Name; - skins[i] = new GUIContent(skinNameString, Icons.skin); - } - } - - int skinIndex = 0; - for (int i = 0; i < skins.Length; i++) { - string skinNameString = skeletonDataSkins.Items[i].Name; - if (skinNameString == initialSkinName.stringValue) - skinIndex = i; - } - - skinIndex = EditorGUILayout.Popup(SpineInspectorUtility.TempContent("Initial Skin"), skinIndex, skins); - if (skins.Length > 0) // Support attachmentless/skinless SkeletonData. - initialSkinName.stringValue = skins[skinIndex].text; - } + if (valid) + EditorGUILayout.PropertyField(initialSkinName, SpineInspectorUtility.TempContent("Initial Skin")); + } EditorGUILayout.Space(); @@ -232,8 +247,9 @@ namespace Spine.Unity.Editor { // Sorting Layers SpineInspectorUtility.SortingPropertyFields(sortingProperties, applyModifiedProperties: true); - if (!TargetIsValid) return; - + if (!valid) + return; + // More Render Options... using (new SpineInspectorUtility.BoxScope()) { EditorGUI.BeginChangeCheck(); @@ -250,11 +266,11 @@ namespace Spine.Unity.Editor { EditorGUILayout.EndHorizontal(); if (advancedFoldout) { - + using (new SpineInspectorUtility.IndentScope()) { using (new EditorGUILayout.HorizontalScope()) { SpineInspectorUtility.ToggleLeftLayout(initialFlipX); - SpineInspectorUtility.ToggleLeftLayout(initialFlipY); + SpineInspectorUtility.ToggleLeftLayout(initialFlipY); EditorGUILayout.Space(); } @@ -263,7 +279,6 @@ namespace Spine.Unity.Editor { using (new SpineInspectorUtility.LabelWidthScope()) { // Optimization options if (singleSubmesh != null) EditorGUILayout.PropertyField(singleSubmesh, SingleSubmeshLabel); - //if (meshes != null) EditorGUILayout.PropertyField(meshes, MeshesLabel); if (immutableTriangles != null) EditorGUILayout.PropertyField(immutableTriangles, ImmubleTrianglesLabel); EditorGUILayout.PropertyField(clearStateOnDisable, ClearStateOnDisableLabel); EditorGUILayout.Space(); @@ -290,7 +305,7 @@ namespace Spine.Unity.Editor { EditorGUILayout.Space(); - if (TargetIsValid && !isInspectingPrefab) { + if (valid && !isInspectingPrefab) { if (multi) { // Support multi-edit SkeletonUtility button. // EditorGUILayout.Space(); @@ -302,7 +317,7 @@ namespace Spine.Unity.Editor { // } } else { var component = (Component)target; - if (component.GetComponent() == null) { + if (component.GetComponent() == null) { if (SpineInspectorUtility.CenteredButton(SkeletonUtilityButtonContent, 21, true, 200f)) component.gameObject.AddComponent(); } @@ -330,7 +345,7 @@ namespace Spine.Unity.Editor { var sr = separatorSlotNames.serializedObject.targetObject as ISkeletonComponent; var skeleton = sr.Skeleton; int lastSlot = skeleton.Slots.Count - 1; - if (skeleton != null) { + if (skeleton != null) { for (int i = 0, n = separatorSlotNames.arraySize; i < n; i++) { int index = skeleton.FindSlotIndex(separatorSlotNames.GetArrayElementAtIndex(i).stringValue); if (index == 0 || index == lastSlot) { @@ -360,27 +375,50 @@ namespace Spine.Unity.Editor { } } - public void OnSceneGUI () { - var skeletonRenderer = (SkeletonRenderer)target; - var skeleton = skeletonRenderer.skeleton; - var transform = skeletonRenderer.transform; - if (skeleton == null) return; + static bool UpdateIfSkinMismatch (SkeletonRenderer skeletonRenderer) { + if (!skeletonRenderer.valid) return false; - SpineHandles.DrawBones(transform, skeleton); + var skin = skeletonRenderer.Skeleton.Skin; + string skeletonSkinName = skin != null ? skin.Name : null; + string componentSkinName = skeletonRenderer.initialSkinName; + bool defaultCase = skin == null && string.IsNullOrEmpty(componentSkinName); + bool fieldMatchesSkin = defaultCase || string.Equals(componentSkinName, skeletonSkinName, System.StringComparison.Ordinal); + + if (!fieldMatchesSkin) { + Skin skinToSet = string.IsNullOrEmpty(componentSkinName) ? null : skeletonRenderer.Skeleton.Data.FindSkin(componentSkinName); + skeletonRenderer.Skeleton.Skin = skinToSet; + skeletonRenderer.Skeleton.SetSlotsToSetupPose(); + skeletonRenderer.LateUpdate(); + return true; + } + return false; } - override public void OnInspectorGUI () { - bool multi = serializedObject.isEditingMultipleObjects; - DrawInspectorGUI(multi); - if (serializedObject.ApplyModifiedProperties() || SpineInspectorUtility.UndoRedoPerformed(Event.current)) { - if (!Application.isPlaying) { - if (multi) - foreach (var o in targets) - ((SkeletonRenderer)o).Initialize(true); - else - ((SkeletonRenderer)target).Initialize(true); + static void EditorForceReloadSkeletonDataAssetAndComponent (SkeletonRenderer component) { + if (component == null) return; + + // Clear all and reload. + if (component.skeletonDataAsset != null) { + foreach (AtlasAssetBase aa in component.skeletonDataAsset.atlasAssets) { + if (aa != null) aa.Clear(); } + component.skeletonDataAsset.Clear(); } + component.skeletonDataAsset.GetSkeletonData(true); + + // Reinitialize. + EditorForceInitializeComponent(component); + } + + static void EditorForceInitializeComponent (SkeletonRenderer component) { + if (component == null) return; + if (!SkeletonDataAssetIsValid(component.SkeletonDataAsset)) return; + component.Initialize(true); + component.LateUpdate(); + } + + static bool SkeletonDataAssetIsValid (SkeletonDataAsset asset) { + return asset != null && asset.GetSkeletonData(quiet: true) != null; } } diff --git a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/SpineAttributeDrawers.cs b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/SpineAttributeDrawers.cs index f66134ec7..4e71bf522 100644 --- a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/SpineAttributeDrawers.cs +++ b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/SpineAttributeDrawers.cs @@ -39,24 +39,24 @@ using Spine; namespace Spine.Unity.Editor { public struct SpineDrawerValuePair { - public string str; + public string stringValue; public SerializedProperty property; public SpineDrawerValuePair (string val, SerializedProperty property) { - this.str = val; + this.stringValue = val; this.property = property; } } public abstract class SpineTreeItemDrawerBase : PropertyDrawer where T:SpineAttributeBase { protected SkeletonDataAsset skeletonDataAsset; - internal const string NoneString = ""; + internal const string NoneStringConstant = ""; - // Analysis disable once StaticFieldInGenericType - static GUIContent noneLabel; - static GUIContent NoneLabel (Texture2D image = null) { - if (noneLabel == null) - noneLabel = new GUIContent(NoneString); + internal virtual string NoneString { get { return NoneStringConstant; } } + + GUIContent noneLabel; + GUIContent NoneLabel (Texture2D image = null) { + if (noneLabel == null) noneLabel = new GUIContent(NoneString); noneLabel.image = image; return noneLabel; } @@ -74,6 +74,12 @@ namespace Spine.Unity.Editor { return; } + // Handle multi-editing when instances don't use the same SkeletonDataAsset. + if (!SpineInspectorUtility.TargetsUseSameData(property.serializedObject)) { + EditorGUI.DelayedTextField(position, property, label); + return; + } + SerializedProperty dataField = property.FindBaseOrSiblingProperty(TargetAttribute.dataField); if (dataField != null) { @@ -116,8 +122,8 @@ namespace Spine.Unity.Editor { position = EditorGUI.PrefixLabel(position, label); - var image = Icon; - var propertyStringValue = (property.hasMultipleDifferentValues) ? SpineInspectorUtility.EmDash : property.stringValue; + Texture2D image = Icon; + string propertyStringValue = (property.hasMultipleDifferentValues) ? SpineInspectorUtility.EmDash : property.stringValue; if (GUI.Button(position, string.IsNullOrEmpty(propertyStringValue) ? NoneLabel(image) : SpineInspectorUtility.TempContent(propertyStringValue, image), EditorStyles.popup)) Selector(property); } @@ -151,8 +157,10 @@ namespace Spine.Unity.Editor { protected virtual void HandleSelect (object menuItemObject) { var clickedItem = (SpineDrawerValuePair)menuItemObject; - clickedItem.property.stringValue = clickedItem.str; - clickedItem.property.serializedObject.ApplyModifiedProperties(); + var serializedProperty = clickedItem.property; + if (serializedProperty.serializedObject.isEditingMultipleObjects) serializedProperty.stringValue = "oaifnoiasf°ñ123526"; // HACK: to trigger change on multi-editing. + serializedProperty.stringValue = clickedItem.stringValue; + serializedProperty.serializedObject.ApplyModifiedProperties(); } public override float GetPropertyHeight (SerializedProperty property, GUIContent label) { @@ -167,9 +175,8 @@ namespace Spine.Unity.Editor { protected override Texture2D Icon { get { return SpineEditorUtilities.Icons.slot; } } protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineSlot targetAttribute, SkeletonData data) { - if (TargetAttribute.includeNone) - menu.AddItem(new GUIContent(NoneString), string.IsNullOrEmpty(property.stringValue), HandleSelect, new SpineDrawerValuePair(string.Empty, property)); + menu.AddItem(new GUIContent(NoneString), !property.hasMultipleDifferentValues && string.IsNullOrEmpty(property.stringValue), HandleSelect, new SpineDrawerValuePair(string.Empty, property)); for (int i = 0; i < data.Slots.Count; i++) { string name = data.Slots.Items[i].Name; @@ -186,7 +193,7 @@ namespace Spine.Unity.Editor { var bbAttachment = attachment as BoundingBoxAttachment; if (bbAttachment != null) { string menuLabel = bbAttachment.IsWeighted() ? name + " (!)" : name; - menu.AddItem(new GUIContent(menuLabel), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); + menu.AddItem(new GUIContent(menuLabel), !property.hasMultipleDifferentValues && name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); hasBoundingBox = true; break; } @@ -196,7 +203,7 @@ namespace Spine.Unity.Editor { menu.AddDisabledItem(new GUIContent(name)); } else { - menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); + menu.AddItem(new GUIContent(name), !property.hasMultipleDifferentValues && name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); } } @@ -208,28 +215,33 @@ namespace Spine.Unity.Editor { [CustomPropertyDrawer(typeof(SpineSkin))] public class SpineSkinDrawer : SpineTreeItemDrawerBase { + const string DefaultSkinName = "default"; protected override Texture2D Icon { get { return SpineEditorUtilities.Icons.skin; } } - public static void GetSkinMenuItems (SkeletonData data, List animationNames, List menuItems, bool includeNone = true) { + internal override string NoneString { get { return TargetAttribute.defaultAsEmptyString ? DefaultSkinName : NoneStringConstant; } } + + public static void GetSkinMenuItems (SkeletonData data, List outputNames, List outputMenuItems, bool includeNone = true) { if (data == null) return; + if (outputNames == null) return; + if (outputMenuItems == null) return; var skins = data.Skins; - animationNames.Clear(); - menuItems.Clear(); + outputNames.Clear(); + outputMenuItems.Clear(); var icon = SpineEditorUtilities.Icons.skin; if (includeNone) { - animationNames.Add(""); - menuItems.Add(new GUIContent(NoneString, icon)); + outputNames.Add(""); + outputMenuItems.Add(new GUIContent(NoneStringConstant, icon)); } foreach (var s in skins) { - var skinName = s.Name; - animationNames.Add(skinName); - menuItems.Add(new GUIContent(skinName, icon)); + string skinName = s.Name; + outputNames.Add(skinName); + outputMenuItems.Add(new GUIContent(skinName, icon)); } } @@ -239,9 +251,13 @@ namespace Spine.Unity.Editor { for (int i = 0; i < data.Skins.Count; i++) { string name = data.Skins.Items[i].Name; - if (name.StartsWith(targetAttribute.startsWith, StringComparison.Ordinal)) - menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); - } + if (name.StartsWith(targetAttribute.startsWith, StringComparison.Ordinal)) { + bool isDefault = string.Equals(name, DefaultSkinName, StringComparison.Ordinal); + string choiceValue = TargetAttribute.defaultAsEmptyString && isDefault ? string.Empty : name; + menu.AddItem(new GUIContent(name), !property.hasMultipleDifferentValues && choiceValue == property.stringValue, HandleSelect, new SpineDrawerValuePair(choiceValue, property)); + } + + } } } @@ -251,23 +267,25 @@ namespace Spine.Unity.Editor { protected override Texture2D Icon { get { return SpineEditorUtilities.Icons.animation; } } - public static void GetAnimationMenuItems (SkeletonData data, List animationNames, List menuItems, bool includeNone = true) { + public static void GetAnimationMenuItems (SkeletonData data, List outputNames, List outputMenuItems, bool includeNone = true) { if (data == null) return; + if (outputNames == null) return; + if (outputMenuItems == null) return; var animations = data.Animations; - animationNames.Clear(); - menuItems.Clear(); + outputNames.Clear(); + outputMenuItems.Clear(); if (includeNone) { - animationNames.Add(""); - menuItems.Add(new GUIContent(NoneString, SpineEditorUtilities.Icons.animation)); + outputNames.Add(""); + outputMenuItems.Add(new GUIContent(NoneStringConstant, SpineEditorUtilities.Icons.animation)); } foreach (var a in animations) { - var animationName = a.Name; - animationNames.Add(animationName); - menuItems.Add(new GUIContent(animationName, SpineEditorUtilities.Icons.animation)); + string animationName = a.Name; + outputNames.Add(animationName); + outputMenuItems.Add(new GUIContent(animationName, SpineEditorUtilities.Icons.animation)); } } @@ -275,12 +293,12 @@ namespace Spine.Unity.Editor { var animations = skeletonDataAsset.GetAnimationStateData().SkeletonData.Animations; if (TargetAttribute.includeNone) - menu.AddItem(new GUIContent(NoneString), string.IsNullOrEmpty(property.stringValue), HandleSelect, new SpineDrawerValuePair(string.Empty, property)); + menu.AddItem(new GUIContent(NoneString), !property.hasMultipleDifferentValues && string.IsNullOrEmpty(property.stringValue), HandleSelect, new SpineDrawerValuePair(string.Empty, property)); for (int i = 0; i < animations.Count; i++) { string name = animations.Items[i].Name; if (name.StartsWith(targetAttribute.startsWith, StringComparison.Ordinal)) - menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); + menu.AddItem(new GUIContent(name), !property.hasMultipleDifferentValues && name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); } } @@ -301,7 +319,7 @@ namespace Spine.Unity.Editor { if (includeNone) { eventNames.Add(""); - menuItems.Add(new GUIContent(NoneString, SpineEditorUtilities.Icons.userEvent)); + menuItems.Add(new GUIContent(NoneStringConstant, SpineEditorUtilities.Icons.userEvent)); } foreach (var a in animations) { @@ -315,12 +333,12 @@ namespace Spine.Unity.Editor { var events = skeletonDataAsset.GetSkeletonData(false).Events; if (TargetAttribute.includeNone) - menu.AddItem(new GUIContent(NoneString), string.IsNullOrEmpty(property.stringValue), HandleSelect, new SpineDrawerValuePair(string.Empty, property)); + menu.AddItem(new GUIContent(NoneString), !property.hasMultipleDifferentValues && string.IsNullOrEmpty(property.stringValue), HandleSelect, new SpineDrawerValuePair(string.Empty, property)); for (int i = 0; i < events.Count; i++) { string name = events.Items[i].Name; if (name.StartsWith(targetAttribute.startsWith, StringComparison.Ordinal)) - menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); + menu.AddItem(new GUIContent(name), !property.hasMultipleDifferentValues && name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); } } @@ -335,12 +353,12 @@ namespace Spine.Unity.Editor { var constraints = skeletonDataAsset.GetSkeletonData(false).IkConstraints; if (TargetAttribute.includeNone) - menu.AddItem(new GUIContent(NoneString), string.IsNullOrEmpty(property.stringValue), HandleSelect, new SpineDrawerValuePair(string.Empty, property)); + menu.AddItem(new GUIContent(NoneString), !property.hasMultipleDifferentValues && string.IsNullOrEmpty(property.stringValue), HandleSelect, new SpineDrawerValuePair(string.Empty, property)); for (int i = 0; i < constraints.Count; i++) { string name = constraints.Items[i].Name; if (name.StartsWith(targetAttribute.startsWith, StringComparison.Ordinal)) - menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); + menu.AddItem(new GUIContent(name), !property.hasMultipleDifferentValues && name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); } } @@ -355,12 +373,12 @@ namespace Spine.Unity.Editor { var constraints = skeletonDataAsset.GetSkeletonData(false).TransformConstraints; if (TargetAttribute.includeNone) - menu.AddItem(new GUIContent(NoneString), string.IsNullOrEmpty(property.stringValue), HandleSelect, new SpineDrawerValuePair(string.Empty, property)); + menu.AddItem(new GUIContent(NoneString), !property.hasMultipleDifferentValues && string.IsNullOrEmpty(property.stringValue), HandleSelect, new SpineDrawerValuePair(string.Empty, property)); for (int i = 0; i < constraints.Count; i++) { string name = constraints.Items[i].Name; if (name.StartsWith(targetAttribute.startsWith, StringComparison.Ordinal)) - menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); + menu.AddItem(new GUIContent(name), !property.hasMultipleDifferentValues && name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); } } } @@ -374,12 +392,12 @@ namespace Spine.Unity.Editor { var constraints = skeletonDataAsset.GetSkeletonData(false).PathConstraints; if (TargetAttribute.includeNone) - menu.AddItem(new GUIContent(NoneString), string.IsNullOrEmpty(property.stringValue), HandleSelect, new SpineDrawerValuePair(string.Empty, property)); + menu.AddItem(new GUIContent(NoneString), !property.hasMultipleDifferentValues && string.IsNullOrEmpty(property.stringValue), HandleSelect, new SpineDrawerValuePair(string.Empty, property)); for (int i = 0; i < constraints.Count; i++) { string name = constraints.Items[i].Name; if (name.StartsWith(targetAttribute.startsWith, StringComparison.Ordinal)) - menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); + menu.AddItem(new GUIContent(name), !property.hasMultipleDifferentValues && name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); } } } @@ -422,7 +440,7 @@ namespace Spine.Unity.Editor { menu.AddSeparator(""); if (TargetAttribute.includeNone) { const string NullAttachmentName = ""; - menu.AddItem(new GUIContent("Null"), property.stringValue == NullAttachmentName, HandleSelect, new SpineDrawerValuePair(NullAttachmentName, property)); + menu.AddItem(new GUIContent("Null"), !property.hasMultipleDifferentValues && property.stringValue == NullAttachmentName, HandleSelect, new SpineDrawerValuePair(NullAttachmentName, property)); menu.AddSeparator(""); } @@ -465,7 +483,7 @@ namespace Spine.Unity.Editor { if (targetAttribute.placeholdersOnly && !placeholderNames.Contains(attachmentPath)) { menu.AddDisabledItem(new GUIContent(menuPath)); } else { - menu.AddItem(new GUIContent(menuPath), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); + menu.AddItem(new GUIContent(menuPath), !property.hasMultipleDifferentValues && name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); } } @@ -485,12 +503,12 @@ namespace Spine.Unity.Editor { menu.AddSeparator(""); if (TargetAttribute.includeNone) - menu.AddItem(new GUIContent(NoneString), string.IsNullOrEmpty(property.stringValue), HandleSelect, new SpineDrawerValuePair(string.Empty, property)); + menu.AddItem(new GUIContent(NoneString), !property.hasMultipleDifferentValues && string.IsNullOrEmpty(property.stringValue), HandleSelect, new SpineDrawerValuePair(string.Empty, property)); for (int i = 0; i < data.Bones.Count; i++) { string name = data.Bones.Items[i].Name; if (name.StartsWith(targetAttribute.startsWith, StringComparison.Ordinal)) - menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); + menu.AddItem(new GUIContent(name), !property.hasMultipleDifferentValues && name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); } } @@ -540,7 +558,7 @@ namespace Spine.Unity.Editor { for (int i = 0; i < regions.Count; i++) { string name = regions[i].name; - menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); + menu.AddItem(new GUIContent(name), !property.hasMultipleDifferentValues && name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); } menu.ShowAsContext(); @@ -548,7 +566,7 @@ namespace Spine.Unity.Editor { static void HandleSelect (object val) { var pair = (SpineDrawerValuePair)val; - pair.property.stringValue = pair.str; + pair.property.stringValue = pair.stringValue; pair.property.serializedObject.ApplyModifiedProperties(); } diff --git a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/SpineEditorUtilities.cs b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/SpineEditorUtilities.cs index 5043aa1fd..282ffdfdd 100644 --- a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/SpineEditorUtilities.cs +++ b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/SpineEditorUtilities.cs @@ -1202,7 +1202,7 @@ namespace Spine.Unity.Editor { var data = skeletonDataAsset.GetSkeletonData(false); bool noSkins = data.DefaultSkin == null && (data.Skins == null || data.Skins.Count == 0); // Support attachmentless/skinless SkeletonData. skin = skin ?? data.DefaultSkin ?? (noSkins ? null : data.Skins.Items[0]); - if (skin != null) { + if (skin != null && skin != data.DefaultSkin) { skeletonRenderer.initialSkinName = skin.Name; } } diff --git a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/SpineInspectorUtility.cs b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/SpineInspectorUtility.cs index cf395daec..d1cdcc0cd 100644 --- a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/SpineInspectorUtility.cs +++ b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/SpineInspectorUtility.cs @@ -165,8 +165,9 @@ namespace Spine.Unity.Editor { public static GUIStyle GrayMiniLabel { get { if (grayMiniLabel == null) { - grayMiniLabel = new GUIStyle(EditorStyles.centeredGreyMiniLabel); - grayMiniLabel.alignment = TextAnchor.UpperLeft; + grayMiniLabel = new GUIStyle(EditorStyles.centeredGreyMiniLabel) { + alignment = TextAnchor.UpperLeft + }; } return grayMiniLabel; } diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonRenderer.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonRenderer.cs index 9b1f3f52c..17eacbfc8 100644 --- a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonRenderer.cs +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonRenderer.cs @@ -48,6 +48,7 @@ namespace Spine.Unity { public SkeletonDataAsset skeletonDataAsset; public SkeletonDataAsset SkeletonDataAsset { get { return skeletonDataAsset; } } // ISkeletonComponent + [SpineSkin(defaultAsEmptyString:true)] public string initialSkinName; public bool initialFlipX, initialFlipY; diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/SpineAttributes.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/SpineAttributes.cs index 5f3d7ed91..acb5b5ec0 100644 --- a/spine-unity/Assets/Spine/Runtime/spine-unity/SpineAttributes.cs +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/SpineAttributes.cs @@ -149,17 +149,23 @@ namespace Spine.Unity { /// Filters popup results to elements that begin with supplied string. /// If true, the dropdown list will include a "none" option which stored as an empty string. /// If true, and an animation list source can't be found, the field will fall back to a normal text field. If false, it will show an error. + /// If true, the default choice will be serialized as an empty 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 = "", bool includeNone = true, bool fallbackToTextField = false) { + + public bool defaultAsEmptyString = false; + + public SpineSkin (string startsWith = "", string dataField = "", bool includeNone = true, bool fallbackToTextField = false, bool defaultAsEmptyString = false) { this.startsWith = startsWith; this.dataField = dataField; this.includeNone = includeNone; this.fallbackToTextField = fallbackToTextField; + this.defaultAsEmptyString = defaultAsEmptyString; } } + public class SpineAnimation : SpineAttributeBase { /// /// Smart popup menu for Spine Animations