// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. See LICENSE in the project root for license information. using UnityEngine; using System; using HoloToolkit.Unity; namespace HoloToolkit.Unity.Buttons { /// <summary> /// Anim controller button offers as simple way to link button states to animation controller parameters /// </summary> [RequireComponent(typeof(CompoundButton))] public class CompoundButtonAnim : MonoBehaviour { [DropDownComponent] public Animator TargetAnimator; /// <summary> /// List of animation actions /// </summary> [HideInMRTKInspector] public AnimatorControllerAction[] AnimActions; private void Awake() { GetComponent<Button>().StateChange += StateChange; if (TargetAnimator == null) { TargetAnimator = GetComponent<Animator>(); } } /// <summary> /// State change /// </summary> void StateChange(ButtonStateEnum newState) { if (TargetAnimator == null) { return; } if (AnimActions == null) { return; } if (!gameObject.activeSelf) return; for (int i = 0; i < AnimActions.Length; i++) { if (AnimActions[i].ButtonState == newState) { if (!string.IsNullOrEmpty(AnimActions[i].ParamName)) { switch (AnimActions[i].ParamType) { case AnimatorControllerParameterType.Bool: TargetAnimator.SetBool(AnimActions[i].ParamName, AnimActions[i].BoolValue); break; case AnimatorControllerParameterType.Float: TargetAnimator.SetFloat(AnimActions[i].ParamName, AnimActions[i].FloatValue); break; case AnimatorControllerParameterType.Int: TargetAnimator.SetInteger(AnimActions[i].ParamName, AnimActions[i].IntValue); break; case AnimatorControllerParameterType.Trigger: TargetAnimator.SetTrigger(AnimActions[i].ParamName); break; default: throw new ArgumentOutOfRangeException(); } } break; } } } #if UNITY_EDITOR [UnityEditor.CustomEditor(typeof(CompoundButtonAnim))] public class CustomEditor : MRTKEditor { /// <summary> /// Draw a custom editor for AnimatorControllerActions to make them easier to edit /// </summary> protected override void DrawCustomFooter() { CompoundButtonAnim acb = (CompoundButtonAnim)target; Animator animator = acb.TargetAnimator; AnimatorControllerParameter[] animParams = null; // Validate the AnimButton controls - make sure there's one control for each button state ButtonStateEnum[] buttonStates = (ButtonStateEnum[])System.Enum.GetValues(typeof(ButtonStateEnum)); if (acb.AnimActions == null || acb.AnimActions.Length != buttonStates.Length) { acb.AnimActions = new AnimatorControllerAction[buttonStates.Length]; } // Don't allow user to change setup during play mode if (!Application.isPlaying && !string.IsNullOrEmpty (acb.gameObject.scene.name)) { // Get the available animation parameters animParams = animator.parameters; for (int i = 0; i < buttonStates.Length; i++) { acb.AnimActions[i].ButtonState = buttonStates[i]; } // Now make sure all animation parameters are found for (int i = 0; i < acb.AnimActions.Length; i++) { if (!string.IsNullOrEmpty(acb.AnimActions[i].ParamName)) { bool invalidParam = true; foreach (AnimatorControllerParameter animParam in animParams) { if (acb.AnimActions[i].ParamName == animParam.name) { // Update the type while we're here invalidParam = false; acb.AnimActions[i].ParamType = animParam.type; break; } } // If we didn't find it, mark it as invalid acb.AnimActions[i].InvalidParam = invalidParam; } } } UnityEditor.EditorGUILayout.BeginVertical(UnityEditor.EditorStyles.helpBox); UnityEditor.EditorGUILayout.LabelField("Animation states:", UnityEditor.EditorStyles.miniBoldLabel); // Draw the editor for all the animation actions for (int i = 0; i < acb.AnimActions.Length; i++) { acb.AnimActions[i] = DrawAnimActionEditor(acb.AnimActions[i], animParams); } UnityEditor.EditorGUILayout.EndVertical(); } AnimatorControllerAction DrawAnimActionEditor(AnimatorControllerAction action, AnimatorControllerParameter[] animParams) { bool actionIsEmpty = string.IsNullOrEmpty(action.ParamName); UnityEditor.EditorGUILayout.BeginHorizontal(); UnityEditor.EditorGUILayout.LabelField(action.ButtonState.ToString(), GUILayout.MaxWidth(150f), GUILayout.MinWidth(150f)); if (animParams != null && animParams.Length > 0) { // Show a dropdown string[] options = new string[animParams.Length + 1]; options[0] = "(None)"; int currentIndex = 0; for (int i = 0; i < animParams.Length; i++) { options[i + 1] = animParams[i].name; if (animParams[i].name == action.ParamName) { currentIndex = i + 1; } } GUI.color = actionIsEmpty ? Color.gray : Color.white; int newIndex = UnityEditor.EditorGUILayout.Popup(currentIndex, options, GUILayout.MinWidth(150f), GUILayout.MaxWidth(150f)); if (newIndex == 0) { action.ParamName = string.Empty; } else { action.ParamName = animParams[newIndex - 1].name; action.ParamType = animParams[newIndex - 1].type; } } else { // Just show a label GUI.color = action.InvalidParam ? Color.yellow : Color.white; UnityEditor.EditorGUILayout.LabelField(actionIsEmpty ? "(None)" : action.ParamName, GUILayout.MinWidth(75f), GUILayout.MaxWidth(75f)); } GUI.color = Color.white; if (!actionIsEmpty) { UnityEditor.EditorGUILayout.LabelField(action.ParamType.ToString(), UnityEditor.EditorStyles.miniLabel, GUILayout.MinWidth(75f), GUILayout.MaxWidth(75f)); switch (action.ParamType) { case AnimatorControllerParameterType.Bool: action.BoolValue = UnityEditor.EditorGUILayout.Toggle(action.BoolValue); break; case AnimatorControllerParameterType.Float: action.FloatValue = UnityEditor.EditorGUILayout.FloatField(action.FloatValue); break; case AnimatorControllerParameterType.Int: action.IntValue = UnityEditor.EditorGUILayout.IntField(action.IntValue); break; case AnimatorControllerParameterType.Trigger: break; default: break; } } UnityEditor.EditorGUILayout.EndHorizontal(); return action; } } #endif } }