using System.Collections.Generic; using System.Linq; using Chronos.Controls.Editor; using Chronos.Reflection.Internal; using UnityEditor; using UnityEditor.Animations; using UnityEngine; namespace Chronos.Reflection.Editor { [CustomPropertyDrawer(typeof(AnimatorParameter))] public class AnimatorParameterDrawer : TargetedDrawer { #region Fields /// /// The inspected property, of type AnimatorParameter. /// protected SerializedProperty property; /// /// The UnityMember.name of the inspected property, of type string. /// protected SerializedProperty nameProperty; /// /// The targeted animators. /// protected Animator[] targets; #endregion /// protected override void Update(SerializedProperty property) { // Update the targeted drawer base.Update(property); // Assign the property and sub-properties this.property = property; nameProperty = property.FindPropertyRelative("_name"); // Find the targets targets = FindTargets(); } /// protected override void RenderMemberControl(Rect position) { var options = GetNameOptions(); DropdownOption selectedOption = null; DropdownOption noneOption = new DropdownOption(null, "No Parameter"); AnimatorParameter value = GetValue(); if (value != null) { string label = value.name; AnimatorParameter valueInOptions = options.Select(option => option.value).FirstOrDefault(ap => ap.Corresponds(value)); if (valueInOptions != null) { selectedOption = new DropdownOption(valueInOptions, label); } else { selectedOption = new DropdownOption(value, label); } } // Make sure the callback uses the property of this drawer, not at its later value. var propertyNow = property; bool enabled = targets.Any(target => target != null); if (!enabled) EditorGUI.BeginDisabledGroup(true); DropdownGUI.PopupSingle ( position, newValue => { Update(propertyNow); SetValue(newValue); propertyNow.serializedObject.ApplyModifiedProperties(); }, options, selectedOption, noneOption, nameProperty.hasMultipleDifferentValues ); if (!enabled) EditorGUI.EndDisabledGroup(); } #region Value /// /// Returns an animator parameter constructed from the current property values. /// protected AnimatorParameter GetValue() { if (nameProperty.hasMultipleDifferentValues || string.IsNullOrEmpty(nameProperty.stringValue)) { return null; } string name = nameProperty.stringValue; if (name == string.Empty) name = null; return new AnimatorParameter(name); } /// /// Assigns the property values from a specified animator parameter. /// protected void SetValue(AnimatorParameter value) { if (value != null) { nameProperty.stringValue = value.name; } else { nameProperty.stringValue = null; } } #endregion #region Targetting /// protected override Object GetSelfTarget(Object obj) { if (obj is GameObject) { return ((GameObject)obj).GetComponent(); } else if (obj is Component) { return ((Component)obj).GetComponent(); } else { return null; } } /// /// Gets the list of targets on the inspected objects. /// protected Animator[] FindTargets() { IEnumerable objects = targetProperty.Multiple().Select(p => p.objectReferenceValue); var childrenAnimators = objects.OfType().SelectMany(gameObject => gameObject.GetComponents()); var siblingAnimators = objects.OfType().SelectMany(component => component.GetComponents()); return childrenAnimators.Concat(siblingAnimators).ToArray(); } #endregion #region Reflection /// /// Gets the list of shared parameter names as popup options. /// protected List> GetNameOptions() { var options = new List>(); List names = targets .Select(animator => ((AnimatorController)animator.runtimeAnimatorController)) .Where(animatorController => animatorController != null) .Select(animatorController => animatorController.parameters) .Select(parameters => parameters.Select(parameter => parameter.name)) .IntersectAll() .Distinct() .ToList(); foreach (string name in names) { options.Add(new DropdownOption(new AnimatorParameter(name), name)); } return options; } #endregion } }