// Property with all the transitions.
private SerializedProperty _transitions ;
// _fromStates and _transitionsByFromStates form an Object ->Transitions dictionary.
// _fromStates and _transitionsByFromStates form a State ->Transitions dictionary.
// _toggles for the opened states. Only one should be active at a time .
private bool [ ] _toggles ;
// Index of the state currently toggled on, -1 if none is .
internal int _toggledIndex = - 1 ;
// Helper class to add new transitions.
private AddTransitionHelper _addTransitionHelper ;
private void OnEnable ( )
{
_addTransitionHelper = new AddTransitionHelper ( this ) ;
Undo . undoRedoPerformed + = ResetIfRequired ;
Undo . undoRedoPerformed + = Reset ;
Undo . undoRedoPerformed - = ResetIfRequired ;
Undo . undoRedoPerformed - = Reset ;
_addTransitionHelper ? . Dispose ( ) ;
}
internal void Reset ( )
{
serializedObject . Update ( ) ;
var toggledState = _toggledIndex > - 1 ? _fromStates [ _toggledIndex ] : null ;
_toggles = new bool [ _fromStates . Count ] ;
_toggledIndex = toggledState ? _fromStates . IndexOf ( toggledState ) : - 1 ;
}
public override void OnInspectorGUI ( )
Separator ( ) ;
// Back button
if ( GUILayout . Button ( EditorGUIUtility . IconContent ( "scrollleft" ) , GUILayout . Width ( 3 5 ) , GUILayout . Height ( 2 0 ) ) )
if ( GUILayout . Button ( EditorGUIUtility . IconContent ( "scrollleft" ) , GUILayout . Width ( 3 5 ) , GUILayout . Height ( 2 0 ) )
| | _cachedStateEditor . serializedObject = = null )
{
return ;
}
Separator ( ) ;
Separator ( ) ;
EditorGUILayout . HelpBox ( "Click on any State's name to see the Transitions it contains, or click the Pencil/Wrench icon to see its Actions." , MessageType . Info ) ;
Separator ( ) ;
serializedObject . UpdateIfRequiredOrScript ( ) ;
// For each fromState
for ( int i = 0 ; i < _fromStates . Count ; i + + )
{
var toggleRect = headerRect ;
toggleRect . width - = 1 4 0 ;
_toggles [ i ] = EditorGUI . BeginFoldoutHeaderGroup ( toggleRect ,
foldout : _toggles [ i ] ,
content : label ,
style : ContentStyle . StateListStyle ) ;
_toggledIndex =
EditorGUI . BeginFoldoutHeaderGroup ( toggleRect , _toggledIndex = = i , label , ContentStyle . StateListStyle ) ?
i : _toggledIndex = = i ? - 1 : _toggledIndex ;
}
Separator ( ) ;
{
bool Button ( Rect position , string icon ) = > GUI . Button ( position , EditorGUIUtility . IconContent ( icon ) ) ;
var buttonRect = new Rect (
x : headerRect . width - 1 0 5 ,
y : headerRect . y ,
width : 3 5 ,
height : 2 0 ) ;
var buttonRect = new Rect ( x : headerRect . width - 2 5 , y : headerRect . y , width : 3 5 , height : 2 0 ) ;
// Switch to state editor
if ( Button ( buttonRect , "SceneViewTools" ) )
// Move state down
if ( i < _fromStates . Count - 1 )
if ( _cachedStateEditor = = null )
_cachedStateEditor = CreateEditor ( transitions [ 0 ] . SerializedTransition . FromState . objectReferenceValue , typeof ( StateEditor ) ) ;
else
CreateCachedEditor ( transitions [ 0 ] . SerializedTransition . FromState . objectReferenceValue , typeof ( StateEditor ) , ref _cachedStateEditor ) ;
_displayStateEditor = true ;
return ;
if ( Button ( buttonRect , "scrolldown" ) )
{
ReorderState ( i , false ) ;
EarlyOut ( ) ;
return ;
}
buttonRect . x - = 4 0 ;
buttonRect . x + = 4 0 ;
if ( Button ( buttonRect , "scrollup" ) )
if ( i > 0 )
if ( ReorderState ( i , true ) )
if ( Button ( buttonRect , "scrollup" ) )
{
ReorderState ( i , true ) ;
EarlyOut ( ) ;
}
buttonRect . x - = 4 0 ;
buttonRect . x + = 4 0 ;
// Move state down
if ( Button ( buttonRect , "scrolldown" ) )
// Switch to state editor
if ( Button ( buttonRect , "SceneViewTools" ) )
if ( ReorderState ( i , false ) )
return ;
DisplayStateEditor ( transitions [ 0 ] . SerializedTransition . FromState . objectReferenceValue ) ;
EarlyOut ( ) ;
return ;
}
void EarlyOut ( )
{
EndHorizontal ( ) ;
EndFoldoutHeaderGroup ( ) ;
EndVertical ( ) ;
EndHorizontal ( ) ;
// If state is open
if ( _toggles [ i ] )
if ( _toggledIndex = = i )
DisableAllStateTogglesExcept ( i ) ;
// Display all the transitions in the state
foreach ( var transition in transitions )
foreach ( var transition in transitions ) // Display all the transitions in the state
if ( transition . Display ( ref stateRect ) )
if ( transition . Display ( ref stateRect ) ) // Return if there were changes
{
EditorGUI . EndChangeCheck ( ) ;
EndFoldoutHeaderGroup ( ) ;
EndVertical ( ) ;
EndHorizontal ( ) ;
}
Separator ( ) ;
}
if ( EditorGUI . EndChangeCheck ( ) )
EndFoldoutHeaderGroup ( ) ;
EndVertical ( ) ;
//GUILayout.HorizontalSlider(0, 0, 0);
Separator ( ) ;
}
EndHorizontal ( ) ;
}
internal void DisplayStateEditor ( Object state )
{
if ( _cachedStateEditor = = null )
_cachedStateEditor = CreateEditor ( state , typeof ( StateEditor ) ) ;
else
CreateCachedEditor ( state , typeof ( StateEditor ) , ref _cachedStateEditor ) ;
_displayStateEditor = true ;
}
/// <returns>True if changes were made and returning is required. Otherwise false.</returns>
internal bool ReorderState ( int index , bool up )
internal void ReorderState ( int index , bool up )
if ( ( up & & index = = 0 ) | | ( ! up & & index = = _fromStates . Count - 1 ) )
return false ;
var toggledState = _toggledIndex > - 1 ? _fromStates [ _toggledIndex ] : null ;
// Moving a state up is easier than moving it down. So when moving a state down, we instead move the next state up.
MoveStateUp ( up ? index : index + 1 ) ;
return true ;
}
if ( ! up )
index + + ;
private void MoveStateUp ( int index )
{
serializedObject . ApplyModifiedProperties ( ) ;
Reset ( ) ;
ApplyModifications ( $"Moved {_fromStates[index].name} State {(up ? " up " : " down ")}" ) ;
if ( toggledState )
_toggledIndex = _fromStates . IndexOf ( toggledState ) ;
}
/// <summary>
CopyConditions ( transition . Conditions , source . Conditions ) ;
serializedObject . ApplyModifiedProperties ( ) ;
Reset ( ) ;
ApplyModifications ( $"Added transition from {transition.FromState} to {transition.ToState}" ) ;
_toggles [ fromIndex > = 0 ? fromIndex : _toggles . Length - 1 ] = true ;
_toggledIndex = fromIndex > = 0 ? fromIndex : _fromStates . Count - 1 ;
}
/// <summary>
/// <param name="up">Move up(true) or down(false)</param>
/// <returns>True if changes were made and returning is required. Otherwise false.</returns>
internal bool ReorderTransition ( SerializedTransition serializedTransition , bool up )
internal void ReorderTransition ( SerializedTransition serializedTransition , bool up )
int targetIndex = - 1 ;
int fromId = serializedTransition . FromState . objectReferenceInstanceIDValue ;
SerializedTransition st ;
for ( int i = 0 ; i < _transitions . arraySize ; i + + )
{
if ( up & & i > = serializedTransition . Index )
break ;
if ( ! up & & i < = serializedTransition . Index )
continue ;
st = new SerializedTransition ( _transitions , i ) ;
if ( st . FromState . objectReferenceInstanceIDValue ! = fromId )
continue ;
targetIndex = i ;
if ( ! up )
break ;
}
int stateIndex = _fromStates . IndexOf ( serializedTransition . FromState . objectReferenceValue ) ;
var stateTransitions = _transitionsByFromStates [ stateIndex ] ;
int index = stateTransitions . FindIndex ( t = > t . SerializedTransition . Index = = serializedTransition . Index ) ;
if ( targetIndex = = - 1 )
return false ;
( int currentIndex , int targetIndex ) = up ?
( serializedTransition . Index , stateTransitions [ index - 1 ] . SerializedTransition . Index ) :
( stateTransitions [ index + 1 ] . SerializedTransition . Index , serializedTransition . Index ) ;
_transitions . MoveArrayElement ( serializedTransition . Index , targetIndex ) ;
serializedObject . ApplyModifiedProperties ( ) ;
Reset ( ) ;
_transitions . MoveArrayElement ( currentIndex , targetIndex ) ;
_toggles [
_fromStates . IndexOf (
_transitions . GetArrayElementAtIndex ( targetIndex )
. FindPropertyRelative ( "FromState" )
. objectReferenceValue ) ] = true ;
ApplyModifications ( $"Moved transition to {serializedTransition.ToState.objectReferenceValue.name} {(up ? " up " : " down ")}" ) ;
return true ;
_toggledIndex = stateIndex ;
/// Remove a transition by index.
/// Remove a transition.
/// <param name="index">Index of the transition in the transition table</param>
internal void RemoveTransition ( int index )
/// <param name="serializedTransition">Transition to delete.</param>
internal void RemoveTransition ( SerializedTransition serializedTransition )
var state = _transitions . GetArrayElementAtIndex ( index ) . FindPropertyRelative ( "FromState" ) . objectReferenceValue ;
_transitions . DeleteArrayElementAtIndex ( index ) ;
bool toggle = DoToggleAndSort ( state , index ) ;
serializedObject . ApplyModifiedProperties ( ) ;
Reset ( ) ;
int stateIndex = _fromStates . IndexOf ( serializedTransition . FromState . objectReferenceValue ) ;
var stateTransitions = _transitionsByFromStates [ stateIndex ] ;
int count = stateTransitions . Count ;
int index = stateTransitions . FindIndex ( t = > t . SerializedTransition . Index = = serializedTransition . Index ) ;
int deleteIndex = serializedTransition . Index ;
if ( index = = 0 & & count > 1 )
_transitions . MoveArrayElement ( stateTransitions [ 1 ] . SerializedTransition . Index , deleteIndex + + ) ;
if ( toggle )
{
int i = _fromStates . IndexOf ( state ) ;
if ( i > = 0 )
_toggles [ i ] = true ;
}
}
_transitions . DeleteArrayElementAtIndex ( deleteIndex ) ;
private bool DoToggleAndSort ( Object state , int index )
{
bool ret = false ;
for ( int i = 0 ; i < _transitions . arraySize ; i + + )
{
if ( _transitions . GetArrayElementAtIndex ( i ) . FindPropertyRelative ( "FromState" ) . objectReferenceValue = = state )
{
ret = true ;
if ( i > index )
{
_transitions . MoveArrayElement ( i , index ) ;
break ;
}
}
}
ApplyModifications ( $"Deleted transition from {serializedTransition.FromState.objectReferenceValue.name} " +
"to {serializedTransition.ToState.objectReferenceValue.name}" ) ;
return ret ;
if ( count > 1 )
_toggledIndex = stateIndex ;
private void DisableAllStateTogglesExcept ( int index )
internal List < SerializedTransition > GetStateTransitions ( Object state )
for ( int i = 0 ; i < _toggles . Length ; i + + )
if ( i ! = index )
_toggles [ i ] = false ;
return _transitionsByFromStates [ _fromStates . IndexOf ( state ) ] . Select ( t = > t . SerializedTransition ) . ToList ( ) ;
}
private void CopyConditions ( SerializedProperty copyTo , SerializedProperty copyFrom )
private bool TryGetExistingTransition ( SerializedProperty from , SerializedProperty to , out int fromIndex , out int toIndex )
{
fromIndex = _fromStates . IndexOf ( from . objectReferenceValue ) ;
toIndex = - 1 ;
{
toIndex = - 1 ;
}
}
private void ResetIfRequired ( )
{
if ( serializedObject . UpdateIfRequiredOrScript ( ) )
Reset ( ) ;
}
private void GroupByFromState ( )
{
Debug . LogError ( "Transition with invalid \"From State\" found in table " + serializedObject . targetObject . name + ", deleting..." ) ;
_transitions . DeleteArrayElementAtIndex ( i ) ;
serializedObject . ApplyModifiedProperties ( ) ;
Reset ( ) ;
ApplyModifications ( "Invalid transition deleted" ) ;
return ;
}
if ( serializedTransition . ToState . objectReferenceValue = = null )
serializedObject . ApplyModifiedProperties ( ) ;
Reset ( ) ;
ApplyModifications ( "Invalid transition deleted" ) ;
return ;
}
groupedProps . Add ( new TransitionDisplayHelper ( serializedTransition , this ) ) ;
}
_fromStates = groupedTransitions . Keys . Distinct ( ) . ToList ( ) ;
_fromStates = groupedTransitions . Keys . ToList ( ) ;
}
private void ApplyModifications ( string msg )
{
Undo . RecordObject ( serializedObject . targetObject , msg ) ;
serializedObject . ApplyModifiedProperties ( ) ;
Reset ( ) ;
}
}
}