浏览代码
Replaced StateTransitionSO with TransitionTableSO + Editor (#114)
Replaced StateTransitionSO with TransitionTableSO + Editor (#114)
* Initial commit. Replaced StateTransitionSO with TransitionTableSO. * [Bot] Automated dotnet-format update * Deleted old transitions assets. * Editor Created TransitionTableEditor and StateEditor. - TransitionTableEditor allows you to fully configure the transition table in an easy way. - A list of the states in the table is displayed, with the initial state at the top. - States can be sorted manually to change the initial state or just for ordering. - The order of the states bellow the initial shouldn't affect the behaviour of the state machine. - Added buttons to easily switch back and forth between state editor and transition table editor. - The state editor is a simple reorderable list with all the action in the state. - Clicking on a state will display all of its transitions, along with their conditions. - Transitions can be sorted to change the order in which checks are executed. - Transit.../main
GitHub
4 年前
当前提交
96e3b3f0
共有 23 个文件被更改,包括 1131 次插入 和 30 次删除
-
2UOP1_Project/Assets/Prefabs/Pig.prefab
-
5UOP1_Project/Assets/Scripts/StateMachine/Core/StateMachine.cs
-
19UOP1_Project/Assets/Scripts/StateMachine/ScriptableObjects/StateSO.cs
-
99UOP1_Project/Assets/ScriptableObjects/Protagonist/CharacterTransitionTable.asset
-
8UOP1_Project/Assets/ScriptableObjects/Protagonist/CharacterTransitionTable.asset.meta
-
8UOP1_Project/Assets/Scripts/StateMachine/Editor.meta
-
95UOP1_Project/Assets/Scripts/StateMachine/ScriptableObjects/TransitionTableSO.cs
-
11UOP1_Project/Assets/Scripts/StateMachine/ScriptableObjects/TransitionTableSO.cs.meta
-
171UOP1_Project/Assets/Scripts/StateMachine/Editor/AddTransitionHelper.cs
-
11UOP1_Project/Assets/Scripts/StateMachine/Editor/AddTransitionHelper.cs.meta
-
50UOP1_Project/Assets/Scripts/StateMachine/Editor/ContentStyle.cs
-
11UOP1_Project/Assets/Scripts/StateMachine/Editor/ContentStyle.cs.meta
-
47UOP1_Project/Assets/Scripts/StateMachine/Editor/SerializedTransition.cs
-
11UOP1_Project/Assets/Scripts/StateMachine/Editor/SerializedTransition.cs.meta
-
83UOP1_Project/Assets/Scripts/StateMachine/Editor/StateEditor.cs
-
11UOP1_Project/Assets/Scripts/StateMachine/Editor/StateEditor.cs.meta
-
110UOP1_Project/Assets/Scripts/StateMachine/Editor/TransitionDisplayHelper.cs
-
11UOP1_Project/Assets/Scripts/StateMachine/Editor/TransitionDisplayHelper.cs.meta
-
379UOP1_Project/Assets/Scripts/StateMachine/Editor/TransitionTableEditor.cs
-
11UOP1_Project/Assets/Scripts/StateMachine/Editor/TransitionTableEditor.cs.meta
-
8UOP1_Project/Assets/ScriptableObjects/Protagonist/Transitions.meta
|
|||
%YAML 1.1 |
|||
%TAG !u! tag:unity3d.com,2011: |
|||
--- !u!114 &11400000 |
|||
MonoBehaviour: |
|||
m_ObjectHideFlags: 0 |
|||
m_CorrespondingSourceObject: {fileID: 0} |
|||
m_PrefabInstance: {fileID: 0} |
|||
m_PrefabAsset: {fileID: 0} |
|||
m_GameObject: {fileID: 0} |
|||
m_Enabled: 1 |
|||
m_EditorHideFlags: 0 |
|||
m_Script: {fileID: 11500000, guid: 872cbaa965d1f6e4e98365d74e2060df, type: 3} |
|||
m_Name: CharacterTransitionTable |
|||
m_EditorClassIdentifier: |
|||
_transitions: |
|||
- FromState: {fileID: 11400000, guid: e128814ff6dbf63449bbc4dc8b6dc066, type: 2} |
|||
ToState: {fileID: 11400000, guid: 027d32476800b3543b2f5446a59054c8, type: 2} |
|||
Conditions: |
|||
- ExpectedResult: 0 |
|||
Condition: {fileID: 11400000, guid: a79b812272ab8314aa305b39f9a2740a, type: 2} |
|||
Operator: 0 |
|||
- FromState: {fileID: 11400000, guid: 0aa906b71b1b4504d950f5ee8e1f06e8, type: 2} |
|||
ToState: {fileID: 11400000, guid: c57ed1dc57a890943b2648ca8a581075, type: 2} |
|||
Conditions: |
|||
- ExpectedResult: 0 |
|||
Condition: {fileID: 11400000, guid: bf6a2f4642738ac4f8e84bcdf1cb5f93, type: 2} |
|||
Operator: 1 |
|||
- ExpectedResult: 1 |
|||
Condition: {fileID: 11400000, guid: 620f4efd93744084a8ac9ba272f093dc, type: 2} |
|||
Operator: 1 |
|||
- ExpectedResult: 0 |
|||
Condition: {fileID: 11400000, guid: 41dbff2af0b6f7141a32bb464309ba69, type: 2} |
|||
Operator: 0 |
|||
- FromState: {fileID: 11400000, guid: c57ed1dc57a890943b2648ca8a581075, type: 2} |
|||
ToState: {fileID: 11400000, guid: 027d32476800b3543b2f5446a59054c8, type: 2} |
|||
Conditions: |
|||
- ExpectedResult: 0 |
|||
Condition: {fileID: 11400000, guid: c7258bdb2558a214f916c8b5b3c0fa1f, type: 2} |
|||
Operator: 0 |
|||
- ExpectedResult: 0 |
|||
Condition: {fileID: 11400000, guid: a79b812272ab8314aa305b39f9a2740a, type: 2} |
|||
Operator: 0 |
|||
- FromState: {fileID: 11400000, guid: c57ed1dc57a890943b2648ca8a581075, type: 2} |
|||
ToState: {fileID: 11400000, guid: e128814ff6dbf63449bbc4dc8b6dc066, type: 2} |
|||
Conditions: |
|||
- ExpectedResult: 0 |
|||
Condition: {fileID: 11400000, guid: c7258bdb2558a214f916c8b5b3c0fa1f, type: 2} |
|||
Operator: 0 |
|||
- ExpectedResult: 1 |
|||
Condition: {fileID: 11400000, guid: a79b812272ab8314aa305b39f9a2740a, type: 2} |
|||
Operator: 0 |
|||
- FromState: {fileID: 11400000, guid: 027d32476800b3543b2f5446a59054c8, type: 2} |
|||
ToState: {fileID: 11400000, guid: e128814ff6dbf63449bbc4dc8b6dc066, type: 2} |
|||
Conditions: |
|||
- ExpectedResult: 1 |
|||
Condition: {fileID: 11400000, guid: a79b812272ab8314aa305b39f9a2740a, type: 2} |
|||
Operator: 0 |
|||
- FromState: {fileID: 11400000, guid: 027d32476800b3543b2f5446a59054c8, type: 2} |
|||
ToState: {fileID: 11400000, guid: 0aa906b71b1b4504d950f5ee8e1f06e8, type: 2} |
|||
Conditions: |
|||
- ExpectedResult: 0 |
|||
Condition: {fileID: 11400000, guid: 620f4efd93744084a8ac9ba272f093dc, type: 2} |
|||
Operator: 0 |
|||
- FromState: {fileID: 11400000, guid: 027d32476800b3543b2f5446a59054c8, type: 2} |
|||
ToState: {fileID: 11400000, guid: 4c2c0dc4f7ee62b4d95b20ac3dce281e, type: 2} |
|||
Conditions: |
|||
- ExpectedResult: 0 |
|||
Condition: {fileID: 11400000, guid: c5090b215e4f1594f9d1ef6680cd15bb, type: 2} |
|||
Operator: 0 |
|||
- FromState: {fileID: 11400000, guid: 027d32476800b3543b2f5446a59054c8, type: 2} |
|||
ToState: {fileID: 11400000, guid: c57ed1dc57a890943b2648ca8a581075, type: 2} |
|||
Conditions: |
|||
- ExpectedResult: 1 |
|||
Condition: {fileID: 11400000, guid: c7258bdb2558a214f916c8b5b3c0fa1f, type: 2} |
|||
Operator: 0 |
|||
- FromState: {fileID: 11400000, guid: e128814ff6dbf63449bbc4dc8b6dc066, type: 2} |
|||
ToState: {fileID: 11400000, guid: 4c2c0dc4f7ee62b4d95b20ac3dce281e, type: 2} |
|||
Conditions: |
|||
- ExpectedResult: 0 |
|||
Condition: {fileID: 11400000, guid: c5090b215e4f1594f9d1ef6680cd15bb, type: 2} |
|||
Operator: 0 |
|||
- FromState: {fileID: 11400000, guid: e128814ff6dbf63449bbc4dc8b6dc066, type: 2} |
|||
ToState: {fileID: 11400000, guid: 0aa906b71b1b4504d950f5ee8e1f06e8, type: 2} |
|||
Conditions: |
|||
- ExpectedResult: 0 |
|||
Condition: {fileID: 11400000, guid: 620f4efd93744084a8ac9ba272f093dc, type: 2} |
|||
Operator: 0 |
|||
- FromState: {fileID: 11400000, guid: e128814ff6dbf63449bbc4dc8b6dc066, type: 2} |
|||
ToState: {fileID: 11400000, guid: c57ed1dc57a890943b2648ca8a581075, type: 2} |
|||
Conditions: |
|||
- ExpectedResult: 1 |
|||
Condition: {fileID: 11400000, guid: c7258bdb2558a214f916c8b5b3c0fa1f, type: 2} |
|||
Operator: 0 |
|||
- FromState: {fileID: 11400000, guid: 4c2c0dc4f7ee62b4d95b20ac3dce281e, type: 2} |
|||
ToState: {fileID: 11400000, guid: e128814ff6dbf63449bbc4dc8b6dc066, type: 2} |
|||
Conditions: |
|||
- ExpectedResult: 1 |
|||
Condition: {fileID: 11400000, guid: c5090b215e4f1594f9d1ef6680cd15bb, type: 2} |
|||
Operator: 0 |
|
|||
fileFormatVersion: 2 |
|||
guid: f0baa4ca2bfa24e4cb6d1efe9fa81ea3 |
|||
NativeFormatImporter: |
|||
externalObjects: {} |
|||
mainObjectFileID: 0 |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: 3596ae92061fc0d4d9cd18d55258b236 |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using UnityEngine; |
|||
|
|||
namespace UOP1.StateMachine.ScriptableObjects |
|||
{ |
|||
[CreateAssetMenu(fileName = "New Transition Table", menuName = "State Machines/Transition Table")] |
|||
public class TransitionTableSO : ScriptableObject |
|||
{ |
|||
[SerializeField] private TransitionItem[] _transitions = default; |
|||
|
|||
public State GetInitialState(StateMachine stateMachine) |
|||
{ |
|||
var states = new List<State>(); |
|||
var transitions = new List<StateTransition>(); |
|||
var createdInstances = new Dictionary<ScriptableObject, object>(); |
|||
|
|||
var fromStates = _transitions.GroupBy(transition => transition.FromState); |
|||
|
|||
foreach (var fromState in fromStates) |
|||
{ |
|||
if (fromState.Key == null) |
|||
throw new ArgumentNullException(nameof(fromState.Key), $"TransitionTable: {name}"); |
|||
|
|||
var state = fromState.Key.GetState(stateMachine, createdInstances); |
|||
states.Add(state); |
|||
|
|||
transitions.Clear(); |
|||
foreach (var transitionItem in fromState) |
|||
{ |
|||
if (transitionItem.ToState == null) |
|||
throw new ArgumentNullException(nameof(transitionItem.ToState), $"TransitionTable: {name}, From State: {fromState.Key.name}"); |
|||
|
|||
var toState = transitionItem.ToState.GetState(stateMachine, createdInstances); |
|||
ProcessConditionUsages(stateMachine, transitionItem.Conditions, createdInstances, out var conditions, out var resultGroups); |
|||
transitions.Add(new StateTransition(toState, conditions, resultGroups)); |
|||
} |
|||
|
|||
state._transitions = transitions.ToArray(); |
|||
} |
|||
|
|||
return states.Count > 0 ? states[0] |
|||
: throw new InvalidOperationException($"TransitionTable {name} is empty."); |
|||
} |
|||
|
|||
private static void ProcessConditionUsages( |
|||
StateMachine stateMachine, |
|||
ConditionUsage[] conditionUsages, |
|||
Dictionary<ScriptableObject, object> createdInstances, |
|||
out StateCondition[] conditions, |
|||
out int[] resultGroups) |
|||
{ |
|||
int count = conditionUsages.Length; |
|||
conditions = new StateCondition[count]; |
|||
for (int i = 0; i < count; i++) |
|||
conditions[i] = conditionUsages[i].Condition.GetCondition( |
|||
stateMachine, conditionUsages[i].ExpectedResult == Result.True, createdInstances); |
|||
|
|||
|
|||
List<int> resultGroupsList = new List<int>(); |
|||
for (int i = 0; i < count; i++) |
|||
{ |
|||
int idx = resultGroupsList.Count; |
|||
resultGroupsList.Add(1); |
|||
while (i < count - 1 && conditionUsages[i].Operator == Operator.And) |
|||
{ |
|||
i++; |
|||
resultGroupsList[idx]++; |
|||
} |
|||
} |
|||
|
|||
resultGroups = resultGroupsList.ToArray(); |
|||
} |
|||
|
|||
[Serializable] |
|||
public struct TransitionItem |
|||
{ |
|||
public StateSO FromState; |
|||
public StateSO ToState; |
|||
public ConditionUsage[] Conditions; |
|||
} |
|||
|
|||
[Serializable] |
|||
public struct ConditionUsage |
|||
{ |
|||
public Result ExpectedResult; |
|||
public StateConditionSO Condition; |
|||
public Operator Operator; |
|||
} |
|||
|
|||
public enum Result { True, False } |
|||
public enum Operator { And, Or } |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 872cbaa965d1f6e4e98365d74e2060df |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System; |
|||
using UnityEditor; |
|||
using UnityEditorInternal; |
|||
using UnityEngine; |
|||
using UOP1.StateMachine.ScriptableObjects; |
|||
using static UnityEditor.EditorGUILayout; |
|||
|
|||
namespace UOP1.StateMachine.Editor |
|||
{ |
|||
internal class AddTransitionHelper : IDisposable |
|||
{ |
|||
internal SerializedTransition SerializedTransition { get; } |
|||
private readonly SerializedObject _transition; |
|||
private readonly ReorderableList _list; |
|||
private readonly TransitionTableEditor _editor; |
|||
private bool _toggle = false; |
|||
|
|||
internal AddTransitionHelper(TransitionTableEditor editor) |
|||
{ |
|||
_editor = editor; |
|||
_transition = new SerializedObject(ScriptableObject.CreateInstance<TransitionItemSO>()); |
|||
SerializedTransition = new SerializedTransition(_transition.FindProperty("Item")); |
|||
_list = new ReorderableList(_transition, SerializedTransition.Conditions); |
|||
SetupConditionsList(_list); |
|||
} |
|||
|
|||
internal void Display() |
|||
{ |
|||
// Display add button only if not already adding a transition
|
|||
if (!_toggle) |
|||
{ |
|||
if (GUILayout.Button(EditorGUIUtility.IconContent("Toolbar Plus"), GUILayout.Width(35))) |
|||
{ |
|||
_toggle = true; |
|||
SerializedTransition.ClearProperties(); |
|||
} |
|||
|
|||
if (!_toggle) |
|||
return; |
|||
} |
|||
|
|||
var rect = BeginVertical(); |
|||
rect.x += 45; |
|||
rect.width -= 40; |
|||
EditorGUI.DrawRect(rect, ContentStyle.LightGray); |
|||
Separator(); |
|||
|
|||
// State Fields
|
|||
BeginHorizontal(); |
|||
{ |
|||
Space(50, false); |
|||
|
|||
StatePropField("From", SerializedTransition.FromState); |
|||
Space(10, false); |
|||
StatePropField("To", SerializedTransition.ToState); |
|||
} |
|||
EndHorizontal(); |
|||
|
|||
// Conditions List
|
|||
BeginHorizontal(); |
|||
{ |
|||
Space(50, false); |
|||
BeginVertical(); |
|||
_list.DoLayoutList(); |
|||
EndVertical(); |
|||
} |
|||
EndHorizontal(); |
|||
|
|||
Separator(); |
|||
|
|||
// Add and cancel buttons
|
|||
BeginHorizontal(); |
|||
{ |
|||
Space(50, false); |
|||
|
|||
if (GUILayout.Button("Add Transition")) |
|||
{ |
|||
if (SerializedTransition.FromState.objectReferenceValue == null) |
|||
Debug.LogException(new ArgumentNullException("FromState")); |
|||
else if (SerializedTransition.ToState.objectReferenceValue == null) |
|||
Debug.LogException(new ArgumentNullException("ToState")); |
|||
else if (SerializedTransition.FromState.objectReferenceValue == SerializedTransition.ToState.objectReferenceValue) |
|||
Debug.LogException(new InvalidOperationException("FromState and ToState are the same.")); |
|||
else |
|||
{ |
|||
_editor.AddTransition(SerializedTransition); |
|||
_toggle = false; |
|||
} |
|||
} |
|||
else if (GUILayout.Button("Cancel")) |
|||
{ |
|||
_toggle = false; |
|||
} |
|||
} |
|||
EndHorizontal(); |
|||
EndVertical(); |
|||
|
|||
void StatePropField(string label, SerializedProperty prop) |
|||
{ |
|||
BeginVertical(); |
|||
LabelField(label); |
|||
BeginHorizontal(); |
|||
Space(20, false); |
|||
PropertyField(prop, GUIContent.none, GUILayout.MaxWidth(180)); |
|||
EndHorizontal(); |
|||
EndVertical(); |
|||
} |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
UnityEngine.Object.DestroyImmediate(_transition.targetObject); |
|||
_transition.Dispose(); |
|||
GC.SuppressFinalize(this); |
|||
} |
|||
|
|||
private static void SetupConditionsList(ReorderableList reorderableList) |
|||
{ |
|||
reorderableList.elementHeight *= 2.3f; |
|||
reorderableList.drawHeaderCallback += rect => GUI.Label(rect, "Conditions"); |
|||
reorderableList.onAddCallback += list => |
|||
{ |
|||
int count = list.count; |
|||
list.serializedProperty.InsertArrayElementAtIndex(count); |
|||
var prop = list.serializedProperty.GetArrayElementAtIndex(count); |
|||
prop.FindPropertyRelative("Condition").objectReferenceValue = null; |
|||
prop.FindPropertyRelative("ExpectedResult").enumValueIndex = 0; |
|||
prop.FindPropertyRelative("Operator").enumValueIndex = 0; |
|||
}; |
|||
|
|||
reorderableList.drawElementCallback += (Rect rect, int index, bool isActive, bool isFocused) => |
|||
{ |
|||
var prop = reorderableList.serializedProperty.GetArrayElementAtIndex(index); |
|||
rect = new Rect(rect.x, rect.y + 2.5f, rect.width, EditorGUIUtility.singleLineHeight); |
|||
var condition = prop.FindPropertyRelative("Condition"); |
|||
if (condition.objectReferenceValue != null) |
|||
{ |
|||
string label = condition.objectReferenceValue.name; |
|||
GUI.Label(rect, "If"); |
|||
GUI.Label(new Rect(rect.x + 20, rect.y, rect.width, rect.height), label, EditorStyles.boldLabel); |
|||
EditorGUI.PropertyField(new Rect(rect.x + rect.width - 180, rect.y, 20, rect.height), condition, GUIContent.none); |
|||
} |
|||
else |
|||
{ |
|||
EditorGUI.PropertyField(new Rect(rect.x, rect.y, 150, rect.height), condition, GUIContent.none); |
|||
} |
|||
EditorGUI.LabelField(new Rect(rect.x + rect.width - 120, rect.y, 20, rect.height), "Is"); |
|||
EditorGUI.PropertyField(new Rect(rect.x + rect.width - 60, rect.y, 60, rect.height), prop.FindPropertyRelative("ExpectedResult"), GUIContent.none); |
|||
EditorGUI.PropertyField(new Rect(rect.x + 20, rect.y + EditorGUIUtility.singleLineHeight + 5, 60, rect.height), prop.FindPropertyRelative("Operator"), GUIContent.none); |
|||
}; |
|||
|
|||
reorderableList.onChangedCallback += list => reorderableList.serializedProperty.serializedObject.ApplyModifiedProperties(); |
|||
reorderableList.drawElementBackgroundCallback += (Rect rect, int index, bool isActive, bool isFocused) => |
|||
{ |
|||
if (isFocused) |
|||
EditorGUI.DrawRect(rect, ContentStyle.Focused); |
|||
|
|||
if (index % 2 != 0) |
|||
EditorGUI.DrawRect(rect, ContentStyle.ZebraDark); |
|||
else |
|||
EditorGUI.DrawRect(rect, ContentStyle.ZebraLight); |
|||
}; |
|||
} |
|||
|
|||
// SO to serialize a TransitionItem
|
|||
internal class TransitionItemSO : ScriptableObject |
|||
{ |
|||
public TransitionTableSO.TransitionItem Item = default; |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 00a48f7c82ea3bd46a36c3f64d759837 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using UnityEditor; |
|||
using UnityEngine; |
|||
|
|||
namespace UOP1.StateMachine.Editor |
|||
{ |
|||
internal static class ContentStyle |
|||
{ |
|||
internal static Color DarkGray { get; private set; } |
|||
internal static Color LightGray { get; private set; } |
|||
internal static Color Focused { get; private set; } |
|||
internal static Color ZebraDark { get; private set; } |
|||
internal static Color ZebraLight { get; private set; } |
|||
internal static RectOffset Padding { get; private set; } |
|||
internal static RectOffset Margin { get; private set; } |
|||
internal static GUIStyle BoldCentered { get; private set; } |
|||
internal static GUIStyle StateListStyle { get; private set; } |
|||
internal static GUIStyle WithPadding { get; private set; } |
|||
internal static GUIStyle WithPaddingAndMargins { get; private set; } |
|||
|
|||
private static bool _initialised = false; |
|||
|
|||
[InitializeOnLoadMethod] |
|||
internal static void Initialize() |
|||
{ |
|||
if (_initialised) |
|||
return; |
|||
|
|||
_initialised = true; |
|||
|
|||
DarkGray = new Color(0.7f, 0.7f, 0.7f); |
|||
LightGray = new Color(0.8f, 0.8f, 0.8f); |
|||
ZebraDark = new Color(0.1f, 0.5f, 0.9f, 0.1f); |
|||
ZebraLight = new Color(0.8f, 0.8f, 0.9f, 0.1f); |
|||
Focused = new Color(0.5f, 0.5f, 0.5f, 0.5f); |
|||
Padding = new RectOffset(5, 5, 5, 5); |
|||
Margin = new RectOffset(8, 8, 8, 8); |
|||
WithPadding = new GUIStyle { padding = Padding }; |
|||
WithPaddingAndMargins = new GUIStyle { padding = Padding, margin = Margin }; |
|||
|
|||
BoldCentered = new GUIStyle { fontStyle = FontStyle.Bold, alignment = TextAnchor.MiddleCenter }; |
|||
StateListStyle = new GUIStyle |
|||
{ |
|||
alignment = TextAnchor.MiddleCenter, |
|||
fontStyle = FontStyle.Bold, |
|||
fontSize = 12, |
|||
margin = Margin |
|||
}; |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: f3ccde57d7ba058488ea2c2a47ce27a9 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using UnityEditor; |
|||
|
|||
namespace UOP1.StateMachine.Editor |
|||
{ |
|||
internal readonly struct SerializedTransition |
|||
{ |
|||
internal readonly SerializedProperty Transition; |
|||
internal readonly SerializedProperty FromState; |
|||
internal readonly SerializedProperty ToState; |
|||
internal readonly SerializedProperty Conditions; |
|||
internal readonly int Index; |
|||
|
|||
internal SerializedTransition(SerializedProperty transition) |
|||
{ |
|||
Transition = transition; |
|||
FromState = Transition.FindPropertyRelative("FromState"); |
|||
ToState = Transition.FindPropertyRelative("ToState"); |
|||
Conditions = Transition.FindPropertyRelative("Conditions"); |
|||
Index = -1; |
|||
} |
|||
|
|||
internal SerializedTransition(SerializedObject transitionTable, int index) |
|||
{ |
|||
Transition = transitionTable.FindProperty("_transitions").GetArrayElementAtIndex(index); |
|||
FromState = Transition.FindPropertyRelative("FromState"); |
|||
ToState = Transition.FindPropertyRelative("ToState"); |
|||
Conditions = Transition.FindPropertyRelative("Conditions"); |
|||
Index = index; |
|||
} |
|||
|
|||
internal SerializedTransition(SerializedProperty transition, int index) |
|||
{ |
|||
Transition = transition.GetArrayElementAtIndex(index); |
|||
FromState = Transition.FindPropertyRelative("FromState"); |
|||
ToState = Transition.FindPropertyRelative("ToState"); |
|||
Conditions = Transition.FindPropertyRelative("Conditions"); |
|||
Index = index; |
|||
} |
|||
|
|||
internal void ClearProperties() |
|||
{ |
|||
FromState.objectReferenceValue = null; |
|||
ToState.objectReferenceValue = null; |
|||
Conditions.ClearArray(); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 6441ed07e21d7e4429c67fddfc22d061 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using UnityEditor; |
|||
using UnityEditorInternal; |
|||
using UnityEngine; |
|||
using UOP1.StateMachine.ScriptableObjects; |
|||
|
|||
namespace UOP1.StateMachine.Editor |
|||
{ |
|||
[CustomEditor(typeof(StateSO))] |
|||
public class StateEditor : UnityEditor.Editor |
|||
{ |
|||
private ReorderableList _list; |
|||
private SerializedProperty _actions; |
|||
|
|||
private void OnEnable() |
|||
{ |
|||
Undo.undoRedoPerformed += DoUndo; |
|||
_actions = serializedObject.FindProperty("_actions"); |
|||
_list = new ReorderableList(serializedObject, _actions, true, true, true, true); |
|||
SetupActionsList(_list); |
|||
} |
|||
|
|||
private void OnDisable() |
|||
{ |
|||
Undo.undoRedoPerformed -= DoUndo; |
|||
} |
|||
|
|||
public override void OnInspectorGUI() |
|||
{ |
|||
_list.DoLayoutList(); |
|||
} |
|||
|
|||
private void DoUndo() |
|||
{ |
|||
serializedObject.UpdateIfRequiredOrScript(); |
|||
} |
|||
|
|||
private static void SetupActionsList(ReorderableList reorderableList) |
|||
{ |
|||
reorderableList.elementHeight *= 1.5f; |
|||
reorderableList.drawHeaderCallback += rect => GUI.Label(rect, "Actions"); |
|||
reorderableList.onAddCallback += list => |
|||
{ |
|||
int count = list.count; |
|||
list.serializedProperty.InsertArrayElementAtIndex(count); |
|||
var prop = list.serializedProperty.GetArrayElementAtIndex(count); |
|||
prop.objectReferenceValue = null; |
|||
}; |
|||
|
|||
reorderableList.drawElementCallback += (Rect rect, int index, bool isActive, bool isFocused) => |
|||
{ |
|||
var r = rect; |
|||
r.height = EditorGUIUtility.singleLineHeight; |
|||
r.y += 5; |
|||
r.x += 5; |
|||
|
|||
var prop = reorderableList.serializedProperty.GetArrayElementAtIndex(index); |
|||
if (prop.objectReferenceValue != null) |
|||
{ |
|||
var label = prop.objectReferenceValue.name; |
|||
r.width = 20; |
|||
EditorGUI.PropertyField(r, prop, GUIContent.none); |
|||
r.width = rect.width - 50; |
|||
r.x += 25; |
|||
GUI.Label(r, label, EditorStyles.boldLabel); |
|||
} |
|||
else |
|||
EditorGUI.PropertyField(r, prop, GUIContent.none); |
|||
}; |
|||
|
|||
reorderableList.onChangedCallback += list => list.serializedProperty.serializedObject.ApplyModifiedProperties(); |
|||
reorderableList.drawElementBackgroundCallback += (Rect rect, int index, bool isActive, bool isFocused) => |
|||
{ |
|||
if (isFocused) |
|||
EditorGUI.DrawRect(rect, ContentStyle.Focused); |
|||
|
|||
if (index % 2 != 0) |
|||
EditorGUI.DrawRect(rect, ContentStyle.ZebraDark); |
|||
else |
|||
EditorGUI.DrawRect(rect, ContentStyle.ZebraLight); |
|||
}; |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 2aa6909dd9f17914ab9fcb1dd55e71a5 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using UnityEditor; |
|||
using UnityEditorInternal; |
|||
using UnityEngine; |
|||
|
|||
namespace UOP1.StateMachine.Editor |
|||
{ |
|||
internal class TransitionDisplayHelper |
|||
{ |
|||
internal SerializedTransition SerializedTransition { get; } |
|||
private readonly ReorderableList _reorderableList; |
|||
private readonly TransitionTableEditor _editor; |
|||
|
|||
internal TransitionDisplayHelper(SerializedTransition serializedTransition, TransitionTableEditor editor) |
|||
{ |
|||
SerializedTransition = serializedTransition; |
|||
_reorderableList = new ReorderableList(SerializedTransition.Transition.serializedObject, SerializedTransition.Conditions, true, false, true, true); |
|||
SetupConditionsList(_reorderableList); |
|||
_editor = editor; |
|||
} |
|||
|
|||
internal bool Display() |
|||
{ |
|||
// Transition Header
|
|||
EditorGUI.DrawRect(EditorGUILayout.BeginHorizontal(), ContentStyle.DarkGray); |
|||
{ |
|||
// Target state
|
|||
EditorGUILayout.LabelField("To", GUILayout.Width(20)); |
|||
EditorGUILayout.LabelField(SerializedTransition.ToState.objectReferenceValue.name, EditorStyles.boldLabel); |
|||
|
|||
// Move transition up
|
|||
if (GUILayout.Button(EditorGUIUtility.IconContent("scrollup"), GUILayout.Width(35), GUILayout.Height(16))) |
|||
{ |
|||
if (_editor.ReorderTransition(SerializedTransition, true)) |
|||
return true; |
|||
} |
|||
// Move transition down
|
|||
if (GUILayout.Button(EditorGUIUtility.IconContent("scrolldown"), GUILayout.Width(35), GUILayout.Height(16))) |
|||
{ |
|||
if (_editor.ReorderTransition(SerializedTransition, false)) |
|||
return true; |
|||
} |
|||
// Remove transition
|
|||
if (GUILayout.Button(EditorGUIUtility.IconContent("Toolbar Minus"), GUILayout.Width(35), GUILayout.Height(16))) |
|||
{ |
|||
_editor.RemoveTransition(SerializedTransition.Index); |
|||
return true; |
|||
} |
|||
} |
|||
EditorGUILayout.EndHorizontal(); |
|||
|
|||
// Conditions
|
|||
_reorderableList.DoLayoutList(); |
|||
|
|||
return false; |
|||
} |
|||
|
|||
private static void SetupConditionsList(ReorderableList reorderableList) |
|||
{ |
|||
reorderableList.elementHeight *= 2.3f; |
|||
reorderableList.headerHeight = 1f; |
|||
reorderableList.onAddCallback += list => |
|||
{ |
|||
int count = list.count; |
|||
list.serializedProperty.InsertArrayElementAtIndex(count); |
|||
var prop = list.serializedProperty.GetArrayElementAtIndex(count); |
|||
prop.FindPropertyRelative("Condition").objectReferenceValue = null; |
|||
prop.FindPropertyRelative("ExpectedResult").enumValueIndex = 0; |
|||
prop.FindPropertyRelative("Operator").enumValueIndex = 0; |
|||
}; |
|||
|
|||
reorderableList.drawElementCallback += (Rect rect, int index, bool isActive, bool isFocused) => |
|||
{ |
|||
var prop = reorderableList.serializedProperty.GetArrayElementAtIndex(index); |
|||
rect = new Rect(rect.x, rect.y + 2.5f, rect.width, EditorGUIUtility.singleLineHeight); |
|||
var condition = prop.FindPropertyRelative("Condition"); |
|||
if (condition.objectReferenceValue != null) |
|||
{ |
|||
string label = condition.objectReferenceValue.name; |
|||
GUI.Label(rect, "If"); |
|||
var r = rect; |
|||
r.x += 20; |
|||
r.width = 20; |
|||
EditorGUI.PropertyField(r, condition, GUIContent.none); |
|||
r.x += 25; |
|||
r.width = rect.width; |
|||
GUI.Label(r, label, EditorStyles.boldLabel); |
|||
} |
|||
else |
|||
{ |
|||
EditorGUI.PropertyField(new Rect(rect.x, rect.y, 150, rect.height), condition, GUIContent.none); |
|||
} |
|||
EditorGUI.LabelField(new Rect(rect.x + rect.width - 120, rect.y, 20, rect.height), "Is"); |
|||
EditorGUI.PropertyField(new Rect(rect.x + rect.width - 60, rect.y, 60, rect.height), prop.FindPropertyRelative("ExpectedResult"), GUIContent.none); |
|||
EditorGUI.PropertyField(new Rect(rect.x + 20, rect.y + EditorGUIUtility.singleLineHeight + 5, 60, rect.height), prop.FindPropertyRelative("Operator"), GUIContent.none); |
|||
}; |
|||
|
|||
reorderableList.onChangedCallback += list => list.serializedProperty.serializedObject.ApplyModifiedProperties(); |
|||
reorderableList.drawElementBackgroundCallback += (Rect rect, int index, bool isActive, bool isFocused) => |
|||
{ |
|||
if (isFocused) |
|||
EditorGUI.DrawRect(rect, ContentStyle.Focused); |
|||
|
|||
if (index % 2 != 0) |
|||
EditorGUI.DrawRect(rect, ContentStyle.ZebraDark); |
|||
else |
|||
EditorGUI.DrawRect(rect, ContentStyle.ZebraLight); |
|||
}; |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 3ec49868c2508ae4199e341bcbba3675 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using UnityEditor; |
|||
using UnityEngine; |
|||
using UOP1.StateMachine.ScriptableObjects; |
|||
using static UnityEditor.EditorGUILayout; |
|||
using Object = UnityEngine.Object; |
|||
|
|||
namespace UOP1.StateMachine.Editor |
|||
{ |
|||
[CustomEditor(typeof(TransitionTableSO))] |
|||
internal class TransitionTableEditor : UnityEditor.Editor |
|||
{ |
|||
// Property with all the transitions.
|
|||
private SerializedProperty _transitions; |
|||
|
|||
// _fromStates and _transitionsByFromStates form an Object->Transitions dictionary.
|
|||
private List<Object> _fromStates; |
|||
private List<List<TransitionDisplayHelper>> _transitionsByFromStates; |
|||
|
|||
// _toggles for the opened states. Only one should be active at a time.
|
|||
private bool[] _toggles; |
|||
|
|||
// Helper class to add new transitions.
|
|||
private AddTransitionHelper _addTransitionHelper; |
|||
|
|||
// Editor to display the StateSO inspector.
|
|||
private UnityEditor.Editor _cachedStateEditor; |
|||
private bool _displayStateEditor; |
|||
|
|||
private void OnEnable() |
|||
{ |
|||
_addTransitionHelper = new AddTransitionHelper(this); |
|||
Undo.undoRedoPerformed += ResetIfRequired; |
|||
Reset(); |
|||
} |
|||
|
|||
private void OnDisable() |
|||
{ |
|||
Undo.undoRedoPerformed -= ResetIfRequired; |
|||
_addTransitionHelper?.Dispose(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Method to fully reset the editor. Used whenever adding, removing and reordering transitions.
|
|||
/// </summary>
|
|||
internal void Reset() |
|||
{ |
|||
_transitions = serializedObject.FindProperty("_transitions"); |
|||
GroupByFromState(); |
|||
_toggles = new bool[_fromStates.Count]; |
|||
} |
|||
|
|||
public override void OnInspectorGUI() |
|||
{ |
|||
if (!_displayStateEditor) |
|||
TransitionTableGUI(); |
|||
else |
|||
StateEditorGUI(); |
|||
} |
|||
|
|||
private void StateEditorGUI() |
|||
{ |
|||
if (GUILayout.Button(EditorGUIUtility.IconContent("scrollleft"), GUILayout.Width(35), GUILayout.Height(20))) |
|||
_displayStateEditor = false; |
|||
|
|||
EditorGUILayout.LabelField(_cachedStateEditor.target.name, EditorStyles.boldLabel); |
|||
_cachedStateEditor.OnInspectorGUI(); |
|||
} |
|||
|
|||
private void TransitionTableGUI() |
|||
{ |
|||
serializedObject.UpdateIfRequiredOrScript(); |
|||
|
|||
// For each fromState
|
|||
for (int i = 0; i < _fromStates.Count; i++) |
|||
{ |
|||
EditorGUI.DrawRect(BeginVertical(ContentStyle.WithPaddingAndMargins), ContentStyle.LightGray); |
|||
|
|||
var transitions = _transitionsByFromStates[i]; |
|||
|
|||
// State Header
|
|||
BeginHorizontal(); |
|||
{ |
|||
BeginVertical(); |
|||
string label = transitions[0].SerializedTransition.FromState.objectReferenceValue.name; |
|||
if (i == 0) |
|||
label += " (Initial State)"; |
|||
|
|||
// State toggle
|
|||
_toggles[i] = BeginFoldoutHeaderGroup( |
|||
foldout: _toggles[i], |
|||
content: label, |
|||
style: ContentStyle.StateListStyle); |
|||
Separator(); |
|||
EndVertical(); |
|||
|
|||
// State Header Buttons
|
|||
{ |
|||
bool Button(string icon) => GUILayout.Button(EditorGUIUtility.IconContent(icon), GUILayout.Width(35), GUILayout.Height(20)); |
|||
|
|||
// Switch to state editor
|
|||
if (Button("SceneViewTools")) |
|||
{ |
|||
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; |
|||
} |
|||
// Move state up
|
|||
if (Button("scrollup")) |
|||
{ |
|||
if (ReorderState(i, true)) |
|||
return; |
|||
} |
|||
// Move state down
|
|||
if (Button("scrolldown")) |
|||
{ |
|||
if (ReorderState(i, false)) |
|||
return; |
|||
} |
|||
} |
|||
} |
|||
EndHorizontal(); |
|||
|
|||
// If state is open
|
|||
if (_toggles[i]) |
|||
{ |
|||
DisableAllStateTogglesExcept(i); |
|||
EditorGUI.BeginChangeCheck(); |
|||
// Display all the transitions in the state
|
|||
foreach (var transition in transitions) |
|||
{ |
|||
if (transition.Display()) |
|||
return; |
|||
Separator(); |
|||
} |
|||
if (EditorGUI.EndChangeCheck()) |
|||
serializedObject.ApplyModifiedProperties(); |
|||
} |
|||
|
|||
EndFoldoutHeaderGroup(); |
|||
EndVertical(); |
|||
GUILayout.HorizontalSlider(0, 0, 0); |
|||
Separator(); |
|||
Separator(); |
|||
} |
|||
|
|||
var rect = BeginHorizontal(); |
|||
Space(rect.width - 55); |
|||
|
|||
// Display add transition button
|
|||
_addTransitionHelper.Display(); |
|||
|
|||
EndHorizontal(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Move a state up or down
|
|||
/// </summary>
|
|||
/// <param name="index">Index of the state in _fromStates</param>
|
|||
/// <param name="up">Moving up(true) or down(true)</param>
|
|||
/// <returns>True if changes were made and returning is required. Otherwise false.</returns>
|
|||
internal bool ReorderState(int index, bool up) |
|||
{ |
|||
if ((up && index == 0) || (!up && index == _fromStates.Count - 1)) |
|||
return false; |
|||
|
|||
// 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; |
|||
} |
|||
|
|||
private void MoveStateUp(int index) |
|||
{ |
|||
var transitions = _transitionsByFromStates[index]; |
|||
int transitionIndex = transitions[0].SerializedTransition.Index; |
|||
int targetIndex = _transitionsByFromStates[index - 1][0].SerializedTransition.Index; |
|||
_transitions.MoveArrayElement(transitionIndex, targetIndex); |
|||
serializedObject.ApplyModifiedProperties(); |
|||
Reset(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Add a new transition. If a transition with the same from and to states is found,
|
|||
/// the conditions in the new transition are added to it.
|
|||
/// </summary>
|
|||
/// <param name="source">Source Transition</param>
|
|||
internal void AddTransition(SerializedTransition source) |
|||
{ |
|||
SerializedTransition transition; |
|||
if (TryGetExistingTransition(source.FromState, source.ToState, out int fromIndex, out int toIndex)) |
|||
{ |
|||
transition = _transitionsByFromStates[fromIndex][toIndex].SerializedTransition; |
|||
} |
|||
else |
|||
{ |
|||
int count = _transitions.arraySize; |
|||
_transitions.InsertArrayElementAtIndex(count); |
|||
transition = new SerializedTransition(_transitions.GetArrayElementAtIndex(count)); |
|||
transition.ClearProperties(); |
|||
transition.FromState.objectReferenceValue = source.FromState.objectReferenceValue; |
|||
transition.ToState.objectReferenceValue = source.ToState.objectReferenceValue; |
|||
} |
|||
|
|||
CopyConditions(transition.Conditions, source.Conditions); |
|||
|
|||
serializedObject.ApplyModifiedProperties(); |
|||
Reset(); |
|||
|
|||
_toggles[fromIndex >= 0 ? fromIndex : _toggles.Length - 1] = true; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Move a transition up or down
|
|||
/// </summary>
|
|||
/// <param name="serializedTransition">The transition to move</param>
|
|||
/// <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) |
|||
{ |
|||
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; |
|||
} |
|||
|
|||
if (targetIndex == -1) |
|||
return false; |
|||
|
|||
_transitions.MoveArrayElement(serializedTransition.Index, targetIndex); |
|||
serializedObject.ApplyModifiedProperties(); |
|||
Reset(); |
|||
|
|||
_toggles[ |
|||
_fromStates.IndexOf( |
|||
_transitions.GetArrayElementAtIndex(targetIndex) |
|||
.FindPropertyRelative("FromState") |
|||
.objectReferenceValue)] = true; |
|||
|
|||
return true; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Remove a transition by index.
|
|||
/// </summary>
|
|||
/// <param name="index">Index of the transition in the transition table</param>
|
|||
internal void RemoveTransition(int index) |
|||
{ |
|||
var state = _transitions.GetArrayElementAtIndex(index).FindPropertyRelative("FromState").objectReferenceValue; |
|||
_transitions.DeleteArrayElementAtIndex(index); |
|||
bool toggle = DoToggleAndSort(state, index); |
|||
serializedObject.ApplyModifiedProperties(); |
|||
Reset(); |
|||
|
|||
if (toggle) |
|||
{ |
|||
int i = _fromStates.IndexOf(state); |
|||
if (i >= 0) |
|||
_toggles[i] = true; |
|||
} |
|||
} |
|||
|
|||
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; |
|||
} |
|||
} |
|||
} |
|||
|
|||
return ret; |
|||
} |
|||
|
|||
private void DisableAllStateTogglesExcept(int index) |
|||
{ |
|||
for (int i = 0; i < _toggles.Length; i++) |
|||
if (i != index) |
|||
_toggles[i] = false; |
|||
} |
|||
|
|||
private void CopyConditions(SerializedProperty copyTo, SerializedProperty copyFrom) |
|||
{ |
|||
for (int i = 0, j = copyTo.arraySize; i < copyFrom.arraySize; i++, j++) |
|||
{ |
|||
copyTo.InsertArrayElementAtIndex(j); |
|||
var cond = copyTo.GetArrayElementAtIndex(j); |
|||
var srcCond = copyFrom.GetArrayElementAtIndex(i); |
|||
cond.FindPropertyRelative("ExpectedResult").enumValueIndex = srcCond.FindPropertyRelative("ExpectedResult").enumValueIndex; |
|||
cond.FindPropertyRelative("Operator").enumValueIndex = srcCond.FindPropertyRelative("Operator").enumValueIndex; |
|||
cond.FindPropertyRelative("Condition").objectReferenceValue = srcCond.FindPropertyRelative("Condition").objectReferenceValue; |
|||
} |
|||
} |
|||
|
|||
private bool TryGetExistingTransition(SerializedProperty from, SerializedProperty to, out int fromIndex, out int toIndex) |
|||
{ |
|||
fromIndex = _fromStates.IndexOf(from.objectReferenceValue); |
|||
if (fromIndex < 0) |
|||
{ |
|||
toIndex = -1; |
|||
return false; |
|||
} |
|||
|
|||
toIndex = _transitionsByFromStates[fromIndex].FindIndex( |
|||
transitionHelper => transitionHelper.SerializedTransition.ToState.objectReferenceValue == to.objectReferenceValue); |
|||
|
|||
return toIndex >= 0; |
|||
} |
|||
|
|||
private void ResetIfRequired() |
|||
{ |
|||
if (serializedObject.UpdateIfRequiredOrScript()) |
|||
Reset(); |
|||
} |
|||
|
|||
private void GroupByFromState() |
|||
{ |
|||
var groupedTransitions = new Dictionary<Object, List<TransitionDisplayHelper>>(); |
|||
int count = _transitions.arraySize; |
|||
for (int i = 0; i < count; i++) |
|||
{ |
|||
var serializedTransition = new SerializedTransition(_transitions, i); |
|||
if (serializedTransition.FromState.objectReferenceValue == null) |
|||
{ |
|||
Debug.LogError("Transition with invalid state found in table " + serializedObject.targetObject.name + ", deleting..."); |
|||
_transitions.DeleteArrayElementAtIndex(i); |
|||
serializedObject.ApplyModifiedProperties(); |
|||
Reset(); |
|||
return; |
|||
} |
|||
if (serializedTransition.FromState.objectReferenceValue == null) |
|||
{ |
|||
Debug.LogError("Transition with invalid state found in table " + serializedObject.targetObject.name + ", deleting..."); |
|||
_transitions.DeleteArrayElementAtIndex(i); |
|||
serializedObject.ApplyModifiedProperties(); |
|||
Reset(); |
|||
return; |
|||
} |
|||
|
|||
if (!groupedTransitions.TryGetValue(serializedTransition.FromState.objectReferenceValue, out var groupedProps)) |
|||
{ |
|||
groupedProps = new List<TransitionDisplayHelper>(); |
|||
groupedTransitions.Add(serializedTransition.FromState.objectReferenceValue, groupedProps); |
|||
} |
|||
groupedProps.Add(new TransitionDisplayHelper(serializedTransition, this)); |
|||
} |
|||
|
|||
_fromStates = groupedTransitions.Keys.Distinct().ToList(); |
|||
_transitionsByFromStates = new List<List<TransitionDisplayHelper>>(); |
|||
foreach (var fromState in _fromStates) |
|||
_transitionsByFromStates.Add(groupedTransitions[fromState]); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 596d4a27cc6cd7f478e7912eb0488033 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: 7273e93cd813a8947b64537b38daed30 |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
撰写
预览
正在加载...
取消
保存
Reference in new issue