2023-07-30 01:20:46 +02:00
using System ;
using System.Collections.Generic ;
using ABI.CCK.Components ;
2024-08-03 22:24:42 +02:00
using ABI.CCK.Scripts.Editor ;
2023-07-30 01:20:46 +02:00
using UnityEditor ;
using UnityEditorInternal ;
using UnityEngine ;
using AnimatorController = UnityEditor . Animations . AnimatorController ;
2024-08-03 22:24:42 +02:00
using AnimatorControllerParameter = UnityEngine . AnimatorControllerParameter ;
2023-07-30 01:20:46 +02:00
using AnimatorControllerParameterType = UnityEngine . AnimatorControllerParameterType ;
2024-08-03 22:24:42 +02:00
[CustomEditor(typeof(AnimatorDriver))]
public class CCK_AnimatorDriverEditor : Editor
2023-07-30 01:20:46 +02:00
{
private AnimatorDriver _animatorDriver ;
private ReorderableList _onEnterList ;
private ReorderableList _onExitList ;
2024-08-03 22:24:42 +02:00
private readonly List < string > _animatorParamNames = new List < string > ( ) ;
private readonly List < AnimatorDriverTask . ParameterType > _animatorParamTypes = new List < AnimatorDriverTask . ParameterType > ( ) ;
2023-07-30 01:20:46 +02:00
2024-08-03 22:24:42 +02:00
private bool _showLocalOnlyHelp ;
private bool _isInPlayMode ;
#region Unity Events
2023-07-30 01:20:46 +02:00
2024-08-03 22:24:42 +02:00
private void OnEnable ( )
{
if ( target = = null ) return ;
_animatorDriver = ( AnimatorDriver ) target ;
2023-07-30 01:20:46 +02:00
2024-08-03 22:24:42 +02:00
_isInPlayMode = Application . isPlaying ;
_animatorParamNames . Clear ( ) ;
_animatorParamTypes . Clear ( ) ;
// TODO: add parameter type display in parameter dropdown
2023-07-30 01:20:46 +02:00
var behaviorContext = AnimatorController . FindStateMachineBehaviourContext ( _animatorDriver ) ;
if ( behaviorContext . Length > 0 )
{
2024-08-03 22:24:42 +02:00
AnimatorController controller = behaviorContext [ 0 ] . animatorController ;
foreach ( AnimatorControllerParameter parameter in controller . parameters )
2023-07-30 01:20:46 +02:00
{
switch ( parameter . type )
{
case AnimatorControllerParameterType . Bool :
2024-08-03 22:24:42 +02:00
_animatorParamNames . Add ( $"{parameter.name}" ) ;
_animatorParamTypes . Add ( AnimatorDriverTask . ParameterType . Bool ) ;
2023-07-30 01:20:46 +02:00
break ;
case AnimatorControllerParameterType . Float :
2024-08-03 22:24:42 +02:00
_animatorParamNames . Add ( $"{parameter.name}" ) ;
_animatorParamTypes . Add ( AnimatorDriverTask . ParameterType . Float ) ;
2023-07-30 01:20:46 +02:00
break ;
case AnimatorControllerParameterType . Int :
2024-08-03 22:24:42 +02:00
_animatorParamNames . Add ( $"{parameter.name}" ) ;
_animatorParamTypes . Add ( AnimatorDriverTask . ParameterType . Int ) ;
2023-07-30 01:20:46 +02:00
break ;
case AnimatorControllerParameterType . Trigger :
2024-08-03 22:24:42 +02:00
_animatorParamNames . Add ( $"{parameter.name}" ) ;
_animatorParamTypes . Add ( AnimatorDriverTask . ParameterType . Trigger ) ;
2023-07-30 01:20:46 +02:00
break ;
}
}
}
2024-08-03 22:24:42 +02:00
2023-07-30 01:20:46 +02:00
if ( _onEnterList = = null )
{
_onEnterList = new ReorderableList ( _animatorDriver . EnterTasks , typeof ( AnimatorDriverTask ) ,
2024-08-03 22:24:42 +02:00
true , true , ! _isInPlayMode , ! _isInPlayMode )
{
drawHeaderCallback = OnDrawHeaderTaskEnter ,
drawElementCallback = OnDrawElementTaskEnter ,
elementHeightCallback = OnHeightElementTaskEnter
} ;
2023-07-30 01:20:46 +02:00
}
if ( _onExitList = = null )
{
_onExitList = new ReorderableList ( _animatorDriver . ExitTasks , typeof ( AnimatorDriverTask ) ,
2024-08-03 22:24:42 +02:00
true , true , ! _isInPlayMode , ! _isInPlayMode )
{
drawHeaderCallback = OnDrawHeaderTaskExit ,
drawElementCallback = OnDrawElementTaskExit ,
elementHeightCallback = OnHeightElementTaskExit
} ;
2023-07-30 01:20:46 +02:00
}
2024-08-03 22:24:42 +02:00
}
2023-07-30 01:20:46 +02:00
2024-08-03 22:24:42 +02:00
public override void OnInspectorGUI ( )
{
if ( _animatorDriver = = null )
return ;
EditorGUI . BeginChangeCheck ( ) ;
using ( new EditorGUILayout . VerticalScope ( new GUIStyle ( ) { padding = new RectOffset ( 10 , 10 , 10 , 10 ) } ) )
{
DrawLocalOnlyToggle ( ) ;
DrawTaskLists ( ) ;
}
if ( EditorGUI . EndChangeCheck ( ) )
EditorUtility . SetDirty ( _animatorDriver ) ;
}
#endregion
#region GUI Drawing
private void DrawLocalOnlyToggle ( )
{
using ( new EditorGUILayout . HorizontalScope ( ) )
{
_animatorDriver . localOnly = EditorGUILayout . Toggle ( "Local Only" , _animatorDriver . localOnly ) ;
if ( GUILayout . Button ( "?" , GUILayout . Width ( 25 ) ) )
_showLocalOnlyHelp = ! _showLocalOnlyHelp ;
GUILayout . Space ( 5 ) ;
}
if ( _showLocalOnlyHelp )
{
// TODO: Add to LocalizationProvider
EditorGUILayout . HelpBox ( "When 'Local Only' is enabled, the animator driver is executed locally and not for remote users." , MessageType . Info ) ;
EditorGUILayout . Space ( 5 ) ;
EditorGUILayout . HelpBox ( "Avatars: Only the wearer.\nSpawnables: Only the prop's owner.\nWorlds: This option is ignored." , MessageType . Info ) ;
}
EditorGUILayout . Space ( ) ;
}
private void DrawTaskLists ( )
{
EditorGUILayout . Space ( ) ;
_onEnterList . DoLayoutList ( ) ;
EditorGUILayout . Space ( ) ;
2023-07-30 01:20:46 +02:00
_onExitList . DoLayoutList ( ) ;
}
2024-08-03 22:24:42 +02:00
#endregion
#region ReorderableList Drawing
2023-07-30 01:20:46 +02:00
private void OnDrawHeaderTaskEnter ( Rect rect )
{
2024-08-03 22:24:42 +02:00
Rect labelRect = new Rect ( rect . x , rect . y , rect . width - 35 , EditorGUIUtility . singleLineHeight ) ;
GUI . Label ( labelRect , "On Enter Tasks" ) ;
EditorGUIExtensions . UtilityMenu ( rect , _onEnterList ,
appendAdditionalMenuItems : ( menuBuilder , list ) = > {
AppendComponentMenu ( menuBuilder , list , true ) ;
} ) ;
2023-07-30 01:20:46 +02:00
}
private void OnDrawElementTaskEnter ( Rect rect , int index , bool isactive , bool isfocused )
{
2024-08-03 22:24:42 +02:00
if ( index > = _animatorDriver . EnterTasks . Count ) return ;
2023-07-30 01:20:46 +02:00
AnimatorDriverTask element = _animatorDriver . EnterTasks [ index ] ;
2024-08-03 22:24:42 +02:00
using ( new EditorGUI . DisabledScope ( _isInPlayMode ) )
RenderTask ( rect , element ) ;
2023-07-30 01:20:46 +02:00
}
private float OnHeightElementTaskEnter ( int index )
{
2024-08-03 22:24:42 +02:00
const int length = 3 ;
if ( index > = _animatorDriver . EnterTasks . Count )
return 1.25f * length * EditorGUIUtility . singleLineHeight ;
2023-07-30 01:20:46 +02:00
AnimatorDriverTask task = _animatorDriver . EnterTasks [ index ] ;
return ( length + TaskHeight ( task ) ) * 1.25f * EditorGUIUtility . singleLineHeight ;
}
private void OnDrawHeaderTaskExit ( Rect rect )
{
2024-08-03 22:24:42 +02:00
Rect labelRect = new Rect ( rect . x , rect . y , rect . width - 35 , EditorGUIUtility . singleLineHeight ) ;
GUI . Label ( labelRect , "On Exit Tasks" ) ;
EditorGUIExtensions . UtilityMenu ( rect , _onExitList ,
appendAdditionalMenuItems : ( menuBuilder , list ) = > {
AppendComponentMenu ( menuBuilder , list , false ) ;
} ) ;
2023-07-30 01:20:46 +02:00
}
private void OnDrawElementTaskExit ( Rect rect , int index , bool isactive , bool isfocused )
{
2024-08-03 22:24:42 +02:00
if ( index > = _animatorDriver . ExitTasks . Count ) return ;
2023-07-30 01:20:46 +02:00
AnimatorDriverTask element = _animatorDriver . ExitTasks [ index ] ;
2024-08-03 22:24:42 +02:00
using ( new EditorGUI . DisabledScope ( _isInPlayMode ) )
RenderTask ( rect , element ) ;
2023-07-30 01:20:46 +02:00
}
private float OnHeightElementTaskExit ( int index )
{
2024-08-03 22:24:42 +02:00
const int length = 3 ;
if ( index > = _animatorDriver . ExitTasks . Count )
return 1.25f * length * EditorGUIUtility . singleLineHeight ;
2023-07-30 01:20:46 +02:00
AnimatorDriverTask task = _animatorDriver . ExitTasks [ index ] ;
return ( length + TaskHeight ( task ) ) * 1.25f * EditorGUIUtility . singleLineHeight ;
}
2024-08-03 22:24:42 +02:00
#endregion
#region AnimatorDriverTask Drawing
2023-07-30 01:20:46 +02:00
private int TaskHeight ( AnimatorDriverTask task )
{
2024-08-03 22:24:42 +02:00
int length = 2 ;
if ( task . aType = = AnimatorDriverTask . SourceType . Random )
2023-07-30 01:20:46 +02:00
length + = 1 ;
2024-08-03 22:24:42 +02:00
if ( task . op = = AnimatorDriverTask . Operator . Set )
return length ;
2023-07-30 01:20:46 +02:00
2024-08-03 22:24:42 +02:00
length + = task . bType = = AnimatorDriverTask . SourceType . Random ? 3 : 2 ;
2023-07-30 01:20:46 +02:00
return length ;
}
private void RenderTask ( Rect rect , AnimatorDriverTask task )
{
2024-08-03 22:24:42 +02:00
Rect _rect = new Rect ( rect . x , rect . y + 2 , rect . width , EditorGUIUtility . singleLineHeight ) ;
2023-07-30 01:20:46 +02:00
2024-08-03 22:24:42 +02:00
float spacing = EditorGUIUtility . singleLineHeight * 1.25f ;
float originalLabelWidth = EditorGUIUtility . labelWidth ;
EditorGUIUtility . labelWidth = 100 ;
2023-07-30 01:20:46 +02:00
2024-08-03 22:24:42 +02:00
task . targetName = EditorGUIExtensions . AdvancedDropdownInput ( _rect , task . targetName , _animatorParamNames ,
"Parameter" , "No Parameters" ) ;
_rect . y + = spacing ;
2023-07-30 01:20:46 +02:00
2024-08-03 22:24:42 +02:00
var formulaDisplay = $"{task.targetName} = " ;
int parameterIndex = Math . Max ( _animatorParamNames . FindIndex ( m = > m = = task . targetName ) , 0 ) ;
if ( _animatorParamNames . Count ! = 0 & & _animatorParamTypes . Count > = parameterIndex )
task . targetType = _animatorParamTypes [ parameterIndex ] ;
task . op = ( AnimatorDriverTask . Operator ) EditorGUI . EnumPopup ( _rect , "Operation" , task . op ) ;
_rect . y + = spacing ;
2023-07-30 01:20:46 +02:00
2024-08-03 22:24:42 +02:00
task . aType = ( AnimatorDriverTask . SourceType ) EditorGUI . EnumPopup ( _rect , "A Type" , task . aType ) ;
_rect . y + = spacing ;
2023-07-30 01:20:46 +02:00
switch ( task . aType )
{
case AnimatorDriverTask . SourceType . Static :
2024-08-03 22:24:42 +02:00
task . aValue = EditorGUI . FloatField ( _rect , "A Value" , task . aValue ) ;
_rect . y + = spacing ;
2023-07-30 01:20:46 +02:00
formulaDisplay + = $"{task.aValue} " ;
break ;
case AnimatorDriverTask . SourceType . Parameter :
2024-08-03 22:24:42 +02:00
task . aName = EditorGUIExtensions . AdvancedDropdownInput ( _rect , task . aName , _animatorParamNames ,
"Parameter A" , "No Parameters" ) ;
_rect . y + = spacing ;
parameterIndex = Math . Max ( _animatorParamNames . FindIndex ( m = > m = = task . aName ) , 0 ) ;
task . aParamType = _animatorParamTypes [ parameterIndex ] ;
2023-07-30 01:20:46 +02:00
formulaDisplay + = $"{task.aName} " ;
break ;
case AnimatorDriverTask . SourceType . Random :
2024-08-03 22:24:42 +02:00
task . aValue = EditorGUI . FloatField ( _rect , "A Min" , task . aValue ) ;
_rect . y + = spacing ;
task . aMax = EditorGUI . FloatField ( _rect , "A Max" , task . aMax ) ;
_rect . y + = spacing ;
2023-07-30 01:20:46 +02:00
formulaDisplay + = $"Rand({task.aValue}, {task.aMax}) " ;
break ;
}
if ( task . op ! = AnimatorDriverTask . Operator . Set )
{
switch ( task . op )
{
case AnimatorDriverTask . Operator . Addition :
formulaDisplay + = "+ " ;
break ;
case AnimatorDriverTask . Operator . Subtraction :
formulaDisplay + = "- " ;
break ;
case AnimatorDriverTask . Operator . Multiplication :
formulaDisplay + = "* " ;
break ;
case AnimatorDriverTask . Operator . Division :
formulaDisplay + = "/ " ;
break ;
case AnimatorDriverTask . Operator . Modulo :
formulaDisplay + = "% " ;
break ;
case AnimatorDriverTask . Operator . Power :
formulaDisplay + = "pow " ;
break ;
case AnimatorDriverTask . Operator . Log :
formulaDisplay + = "log " ;
break ;
case AnimatorDriverTask . Operator . Equal :
formulaDisplay + = "== " ;
break ;
case AnimatorDriverTask . Operator . LessThen :
formulaDisplay + = "< " ;
break ;
case AnimatorDriverTask . Operator . LessEqual :
formulaDisplay + = "<= " ;
break ;
case AnimatorDriverTask . Operator . MoreThen :
formulaDisplay + = "> " ;
break ;
case AnimatorDriverTask . Operator . MoreEqual :
formulaDisplay + = ">= " ;
break ;
case AnimatorDriverTask . Operator . NotEqual :
formulaDisplay + = "!= " ;
break ;
}
2024-08-03 22:24:42 +02:00
task . bType = ( AnimatorDriverTask . SourceType ) EditorGUI . EnumPopup ( _rect , "B Type" , task . bType ) ;
_rect . y + = spacing ;
2023-07-30 01:20:46 +02:00
switch ( task . bType )
{
case AnimatorDriverTask . SourceType . Static :
2024-08-03 22:24:42 +02:00
task . bValue = EditorGUI . FloatField ( _rect , "B Value" , task . bValue ) ;
_rect . y + = spacing ;
2023-07-30 01:20:46 +02:00
formulaDisplay + = $"{task.bValue} " ;
break ;
case AnimatorDriverTask . SourceType . Parameter :
2024-08-03 22:24:42 +02:00
task . bName = EditorGUIExtensions . AdvancedDropdownInput ( _rect , task . bName , _animatorParamNames ,
"Parameter B" , "No Parameters" ) ;
_rect . y + = spacing ;
parameterIndex = Math . Max ( _animatorParamNames . FindIndex ( m = > m = = task . bName ) , 0 ) ;
task . bParamType = _animatorParamTypes [ parameterIndex ] ;
2023-07-30 01:20:46 +02:00
formulaDisplay + = $"{task.bName} " ;
break ;
case AnimatorDriverTask . SourceType . Random :
2024-08-03 22:24:42 +02:00
task . bValue = EditorGUI . FloatField ( _rect , "B Min" , task . bValue ) ;
_rect . y + = spacing ;
task . bMax = EditorGUI . FloatField ( _rect , "B Max" , task . bMax ) ;
_rect . y + = spacing ;
2023-07-30 01:20:46 +02:00
formulaDisplay + = $"Rand({task.bValue}, {task.bMax}) " ;
break ;
}
}
2024-08-03 22:24:42 +02:00
EditorGUI . LabelField ( _rect , task . targetName = = "" ? "No Parameter" : formulaDisplay ,
new GUIStyle ( GUI . skin . label ) { fontStyle = FontStyle . Bold } ) ;
2023-07-30 01:20:46 +02:00
2024-08-03 22:24:42 +02:00
EditorGUIUtility . labelWidth = originalLabelWidth ;
}
#endregion
#region Utility
private void AppendComponentMenu ( GenericMenuBuilder genericMenuBuilder , ReorderableList list , bool isEnterList )
{
bool hasSelectedTask = list . index ! = - 1 ;
bool hasTasks = list . count > 0 ;
if ( isEnterList )
{
genericMenuBuilder . AddMenuItem ( "To Exit Task" , hasTasks & & hasSelectedTask , ( ) = > ConvertSelectedTask ( list , _animatorDriver . EnterTasks , _animatorDriver . ExitTasks ) ) ;
genericMenuBuilder . AddMenuItem ( "All to Exit Task" , hasTasks , ConvertAllTasksToExit ) ;
}
else
{
genericMenuBuilder . AddMenuItem ( "To Enter Task" , hasTasks & & hasSelectedTask , ( ) = > ConvertSelectedTask ( list , _animatorDriver . ExitTasks , _animatorDriver . EnterTasks ) ) ;
genericMenuBuilder . AddMenuItem ( "All to Enter Task" , hasTasks , ConvertAllTasksToEnter ) ;
}
}
private void ConvertAllTasksToEnter ( )
{
_animatorDriver . EnterTasks . AddRange ( _animatorDriver . ExitTasks ) ;
_animatorDriver . ExitTasks . Clear ( ) ;
}
private void ConvertAllTasksToExit ( )
{
_animatorDriver . ExitTasks . AddRange ( _animatorDriver . EnterTasks ) ;
_animatorDriver . EnterTasks . Clear ( ) ;
}
private void ConvertSelectedTask ( ReorderableList list , List < AnimatorDriverTask > fromTasks , List < AnimatorDriverTask > toTasks )
{
if ( list . index = = - 1 | | list . index > = fromTasks . Count ) return ;
AnimatorDriverTask selectedTask = fromTasks [ list . index ] ;
fromTasks . RemoveAt ( list . index ) ;
toTasks . Add ( selectedTask ) ;
2023-07-30 01:20:46 +02:00
}
2024-08-03 22:24:42 +02:00
#endregion
2023-07-30 01:20:46 +02:00
}