/****************************************************************************** * Spine Runtimes License Agreement * Last updated July 28, 2023. Replaces all prior versions. * * Copyright (c) 2013-2023, Esoteric Software LLC * * Integration of the Spine Runtimes into software or otherwise creating * derivative works of the Spine Runtimes is permitted under the terms and * conditions of Section 2 of the Spine Editor License Agreement: * http://esotericsoftware.com/spine-editor-license * * Otherwise, it is permitted to integrate the Spine Runtimes into software or * otherwise create derivative works of the Spine Runtimes (collectively, * "Products"), provided that each user of the Products must obtain their own * Spine Editor license and redistribution of the Products in any form must * include this license and copyright notice. * * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 SOFTWARE LLC BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, * BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) 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 THE * SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ // Contributed by: Mitch Thompson using UnityEditor; using UnityEngine; namespace Spine.Unity.Editor { [CustomEditor(typeof(SkeletonMecanim))] [CanEditMultipleObjects] public class SkeletonMecanimInspector : SkeletonRendererInspector { public static bool mecanimSettingsFoldout; protected SerializedProperty autoReset; protected SerializedProperty useCustomMixMode; protected SerializedProperty layerMixModes; protected SerializedProperty layerBlendModes; protected override void OnEnable () { base.OnEnable(); SerializedProperty mecanimTranslator = serializedObject.FindProperty("translator"); autoReset = mecanimTranslator.FindPropertyRelative("autoReset"); useCustomMixMode = mecanimTranslator.FindPropertyRelative("useCustomMixMode"); layerMixModes = mecanimTranslator.FindPropertyRelative("layerMixModes"); layerBlendModes = mecanimTranslator.FindPropertyRelative("layerBlendModes"); } protected override void DrawInspectorGUI (bool multi) { AddRootMotionComponentIfEnabled(); base.DrawInspectorGUI(multi); using (new SpineInspectorUtility.BoxScope()) { mecanimSettingsFoldout = EditorGUILayout.Foldout(mecanimSettingsFoldout, "Mecanim Translator"); if (mecanimSettingsFoldout) { EditorGUILayout.PropertyField(autoReset, new GUIContent("Auto Reset", "When set to true, the skeleton state is mixed out to setup-" + "pose when an animation finishes, according to the " + "animation's keyed items.")); EditorGUILayout.PropertyField(useCustomMixMode, new GUIContent("Custom MixMode", "When disabled, the recommended MixMode is used according to the layer blend mode. Enable to specify a custom MixMode for each Mecanim layer.")); if (useCustomMixMode.hasMultipleDifferentValues || useCustomMixMode.boolValue == true) { DrawLayerSettings(); EditorGUILayout.Space(); } } } } protected void AddRootMotionComponentIfEnabled () { foreach (UnityEngine.Object t in targets) { Component component = t as Component; Animator animator = component.GetComponent(); if (animator != null && animator.applyRootMotion) { if (component.GetComponent() == null) { component.gameObject.AddComponent(); } } } } protected void DrawLayerSettings () { string[] layerNames = GetLayerNames(); float widthLayerColumn = 140; float widthMixColumn = 84; using (new GUILayout.HorizontalScope()) { Rect rect = GUILayoutUtility.GetRect(EditorGUIUtility.currentViewWidth, EditorGUIUtility.singleLineHeight); rect.width = widthLayerColumn; EditorGUI.LabelField(rect, SpineInspectorUtility.TempContent("Mecanim Layer"), EditorStyles.boldLabel); int savedIndent = EditorGUI.indentLevel; EditorGUI.indentLevel = 0; rect.position += new Vector2(rect.width, 0); rect.width = widthMixColumn; EditorGUI.LabelField(rect, SpineInspectorUtility.TempContent("Mix Mode"), EditorStyles.boldLabel); EditorGUI.indentLevel = savedIndent; } using (new SpineInspectorUtility.IndentScope()) { int layerCount = layerMixModes.arraySize; for (int i = 0; i < layerCount; ++i) { using (new GUILayout.HorizontalScope()) { string layerName = i < layerNames.Length ? layerNames[i] : ("Layer " + i); Rect rect = GUILayoutUtility.GetRect(EditorGUIUtility.currentViewWidth, EditorGUIUtility.singleLineHeight); rect.width = widthLayerColumn; EditorGUI.PrefixLabel(rect, SpineInspectorUtility.TempContent(layerName)); int savedIndent = EditorGUI.indentLevel; EditorGUI.indentLevel = 0; SerializedProperty mixMode = layerMixModes.GetArrayElementAtIndex(i); rect.position += new Vector2(rect.width, 0); rect.width = widthMixColumn; EditorGUI.PropertyField(rect, mixMode, GUIContent.none); EditorGUI.indentLevel = savedIndent; } } } } protected string[] GetLayerNames () { int maxLayerCount = 0; int maxIndex = 0; for (int i = 0; i < targets.Length; ++i) { SkeletonMecanim skeletonMecanim = ((SkeletonMecanim)targets[i]); int count = skeletonMecanim.Translator.MecanimLayerCount; if (count > maxLayerCount) { maxLayerCount = count; maxIndex = i; } } if (maxLayerCount == 0) return new string[0]; SkeletonMecanim skeletonMecanimMaxLayers = ((SkeletonMecanim)targets[maxIndex]); return skeletonMecanimMaxLayers.Translator.MecanimLayerNames; } } }