浏览代码

Merge remote-tracking branch 'tech-mkt/state-machine-improvements' into main

/main
Ciro Continisio 4 年前
当前提交
77c86ba9
共有 9 个文件被更改,包括 398 次插入71 次删除
  1. 83
      UOP1_Project/Assets/Scripts/StateMachine/Editor/AddTransitionHelper.cs
  2. 72
      UOP1_Project/Assets/Scripts/StateMachine/Editor/TransitionDisplayHelper.cs
  3. 48
      UOP1_Project/Assets/Scripts/StateMachine/Editor/TransitionTableEditor.cs
  4. 132
      UOP1_Project/Assets/Scripts/StateMachine/Editor/TransitionTableEditorWindow.cs
  5. 11
      UOP1_Project/Assets/Scripts/StateMachine/Editor/TransitionTableEditorWindow.cs.meta
  6. 81
      UOP1_Project/Assets/Scripts/StateMachine/Editor/TransitionTableEditorWindow.uss
  7. 11
      UOP1_Project/Assets/Scripts/StateMachine/Editor/TransitionTableEditorWindow.uss.meta
  8. 21
      UOP1_Project/Assets/Scripts/StateMachine/Editor/TransitionTableEditorWindow.uxml
  9. 10
      UOP1_Project/Assets/Scripts/StateMachine/Editor/TransitionTableEditorWindow.uxml.meta

83
UOP1_Project/Assets/Scripts/StateMachine/Editor/AddTransitionHelper.cs


using UnityEditorInternal;
using UnityEngine;
using UOP1.StateMachine.ScriptableObjects;
using static UnityEditor.EditorGUILayout;
using static UnityEditor.EditorGUI;
namespace UOP1.StateMachine.Editor
{

SetupConditionsList(_list);
}
internal void Display()
internal void Display(Rect position)
var rect = position;
float listHeight = _list.GetHeight();
float singleLineHeight = EditorGUIUtility.singleLineHeight;
if (GUILayout.Button(EditorGUIUtility.IconContent("Toolbar Plus"), GUILayout.Width(35)))
position.height = singleLineHeight;
// Reserve space
GUILayoutUtility.GetRect(position.width, position.height);
if (GUI.Button(position, "Add Transition"))
if (!_toggle)
return;
return;
var rect = BeginVertical();
rect.x += 45;
rect.width -= 40;
EditorGUI.DrawRect(rect, ContentStyle.LightGray);
Separator();
// Background
{
position.height = listHeight + singleLineHeight * 4;
DrawRect(position, ContentStyle.LightGray);
}
// Reserve space
GUILayoutUtility.GetRect(position.width, position.height);
BeginHorizontal();
Space(50, false);
StatePropField("From", SerializedTransition.FromState);
Space(10, false);
StatePropField("To", SerializedTransition.ToState);
position.y += 10;
position.x += 25;
StatePropField(position, "From", SerializedTransition.FromState);
position.x = rect.width / 2 + 25;
StatePropField(position, "To", SerializedTransition.ToState);
EndHorizontal();
BeginHorizontal();
Space(50, false);
BeginVertical();
_list.DoLayoutList();
EndVertical();
position.y += 30;
position.x = rect.x + 10;
position.height = listHeight;
position.width -= 20;
_list.DoList(position);
EndHorizontal();
Separator();
BeginHorizontal();
Space(50, false);
if (GUILayout.Button("Add Transition"))
position.y += position.height + 5;
position.height = singleLineHeight;
position.width = rect.width / 2 - 20;
if (GUI.Button(position, "Add Transition"))
{
if (SerializedTransition.FromState.objectReferenceValue == null)
Debug.LogException(new ArgumentNullException("FromState"));

_toggle = false;
}
}
else if (GUILayout.Button("Cancel"))
position.x += rect.width / 2;
if (GUI.Button(position, "Cancel"))
EndHorizontal();
EndVertical();
void StatePropField(string label, SerializedProperty prop)
void StatePropField(Rect pos, string label, SerializedProperty prop)
BeginVertical();
LabelField(label);
BeginHorizontal();
Space(20, false);
PropertyField(prop, GUIContent.none, GUILayout.MaxWidth(180));
EndHorizontal();
EndVertical();
pos.height = singleLineHeight;
LabelField(pos, label);
pos.x += 40;
pos.width /= 4;
PropertyField(pos, prop, GUIContent.none);
}
}

72
UOP1_Project/Assets/Scripts/StateMachine/Editor/TransitionDisplayHelper.cs


using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
using static UnityEditor.EditorGUI;
namespace UOP1.StateMachine.Editor
{

_editor = editor;
}
internal bool Display()
internal bool Display(ref Rect position)
var rect = position;
float listHeight = _reorderableList.GetHeight();
float singleLineHeight = EditorGUIUtility.singleLineHeight;
// Reserve space
{
rect.height = singleLineHeight + 10 + listHeight;
GUILayoutUtility.GetRect(rect.width, rect.height);
position.y += rect.height + 5;
}
// Background
{
rect.x += 5;
rect.width -= 10;
rect.height -= listHeight;
DrawRect(rect, ContentStyle.DarkGray);
}
EditorGUI.DrawRect(EditorGUILayout.BeginHorizontal(GUILayout.Height(24)), ContentStyle.DarkGray);
// Target state
EditorGUILayout.Space(3f, false);
EditorGUILayout.LabelField("To", GUILayout.Width(20));
EditorGUILayout.LabelField(SerializedTransition.ToState.objectReferenceValue.name, EditorStyles.boldLabel);
rect.x += 3;
LabelField(rect, "To");
// TODO: Fix the space in between the labels above and the buttons below
// Right now the buttons disappear to the right if the Inspector is made too narrow
rect.x += 20;
LabelField(rect, SerializedTransition.ToState.objectReferenceValue.name, EditorStyles.boldLabel);
}
// Buttons
{
bool Button(Rect pos, string icon) => GUI.Button(pos, EditorGUIUtility.IconContent(icon));
var buttonRect = new Rect(
x: rect.width - 90,
y: rect.y + 5,
width: 30,
height: 18);
if (GUILayout.Button(EditorGUIUtility.IconContent("scrollup"), GUILayout.Width(30), GUILayout.Height(18)))
{
if (Button(buttonRect, "scrollup"))
}
buttonRect.x += 35;
if (GUILayout.Button(EditorGUIUtility.IconContent("scrolldown"), GUILayout.Width(30), GUILayout.Height(18)))
{
if (Button(buttonRect, "scrolldown"))
}
buttonRect.x += 35;
if (GUILayout.Button(EditorGUIUtility.IconContent("Toolbar Minus"), GUILayout.Width(30), GUILayout.Height(18)))
if (Button(buttonRect, "Toolbar Minus"))
EditorGUILayout.EndHorizontal();
// Conditions
_reorderableList.DoLayoutList();
rect.x = position.x + 5;
rect.y += rect.height;
rect.width = position.width - 10;
rect.height = listHeight;
// Display conditions
_reorderableList.DoList(rect);
return false;
}

48
UOP1_Project/Assets/Scripts/StateMachine/Editor/TransitionTableEditor.cs


// For each fromState
for (int i = 0; i < _fromStates.Count; i++)
{
EditorGUI.DrawRect(BeginVertical(ContentStyle.WithPaddingAndMargins), ContentStyle.LightGray);
var stateRect = BeginVertical(ContentStyle.WithPaddingAndMargins);
EditorGUI.DrawRect(stateRect, ContentStyle.LightGray);
BeginHorizontal();
var headerRect = BeginHorizontal();
{
BeginVertical();
string label = transitions[0].SerializedTransition.FromState.objectReferenceValue.name;

// State toggle
_toggles[i] = BeginFoldoutHeaderGroup(
foldout: _toggles[i],
content: label,
style: ContentStyle.StateListStyle);
headerRect.height = EditorGUIUtility.singleLineHeight;
GUILayoutUtility.GetRect(headerRect.width, headerRect.height);
headerRect.x += 5;
// Toggle
{
var toggleRect = headerRect;
toggleRect.width -= 140;
_toggles[i] = EditorGUI.BeginFoldoutHeaderGroup(toggleRect,
foldout: _toggles[i],
content: label,
style: ContentStyle.StateListStyle);
}
bool Button(string icon) => GUILayout.Button(EditorGUIUtility.IconContent(icon), GUILayout.Width(35), GUILayout.Height(20));
bool Button(Rect position, string icon) => GUI.Button(position, EditorGUIUtility.IconContent(icon));
var buttonRect = new Rect(
x: headerRect.width - 105,
y: headerRect.y,
width: 35,
height: 20);
if (Button("SceneViewTools"))
if (Button(buttonRect, "SceneViewTools"))
{
if (_cachedStateEditor == null)
_cachedStateEditor = CreateEditor(transitions[0].SerializedTransition.FromState.objectReferenceValue, typeof(StateEditor));

_displayStateEditor = true;
return;
}
buttonRect.x += 40;
if (Button("scrollup"))
if (Button(buttonRect, "scrollup"))
buttonRect.x += 40;
if (Button("scrolldown"))
if (Button(buttonRect, "scrolldown"))
{
if (ReorderState(i, false))
return;

{
DisableAllStateTogglesExcept(i);
EditorGUI.BeginChangeCheck();
stateRect.y += EditorGUIUtility.singleLineHeight * 2;
if (transition.Display())
if (transition.Display(ref stateRect))
return;
Separator();
}

Space(rect.width - 55);
// Display add transition button
_addTransitionHelper.Display();
_addTransitionHelper.Display(rect);
EndHorizontal();
}

132
UOP1_Project/Assets/Scripts/StateMachine/Editor/TransitionTableEditorWindow.cs


using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
using UOP1.StateMachine.ScriptableObjects;
namespace UOP1.StateMachine.Editor
{
internal class TransitionTableEditorWindow : EditorWindow
{
private static TransitionTableEditorWindow _window;
private static readonly string _uxmlPath = "Assets/Scripts/StateMachine/Editor/TransitionTableEditorWindow.uxml";
private static readonly string _ussPath = "Assets/Scripts/StateMachine/Editor/TransitionTableEditorWindow.uss";
private bool _doRefresh;
private UnityEditor.Editor _transitionTableEditor;
[MenuItem("Transition Table Editor", menuItem = "ChopChop/State Machine/Transition Table Editor")]
internal static void Display()
{
if (_window == null)
_window = GetWindow<TransitionTableEditorWindow>("Transition Table Editor");
_window.Show();
}
private void OnEnable()
{
var visualTree = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(_uxmlPath);
var styleSheet = AssetDatabase.LoadAssetAtPath<StyleSheet>(_ussPath);
rootVisualElement.Add(visualTree.CloneTree());
string labelClass = $"label-{(EditorGUIUtility.isProSkin ? "pro" : "personal")}";
rootVisualElement.Query<Label>().Build().ForEach(label => label.AddToClassList(labelClass));
rootVisualElement.styleSheets.Add(styleSheet);
minSize = new Vector2(480, 360);
}
/// <summary>
/// Update list every time we gain focus
/// </summary>
private void OnFocus()
{
// Calling CreateListView() from here when the window is docked
// throws a NullReferenceException in UnityEditor.DockArea:OnEnable().
if (_doRefresh == false)
_doRefresh = true;
}
private void Update()
{
if (!_doRefresh)
return;
CreateListView();
_doRefresh = false;
}
private void CreateListView()
{
var assets = FindAssets();
ListView listView = rootVisualElement.Q<ListView>(className: "table-list");
listView.makeItem = null;
listView.bindItem = null;
listView.itemsSource = assets;
listView.itemHeight = 16;
string labelClass = $"label-{(EditorGUIUtility.isProSkin ? "pro" : "personal")}";
listView.makeItem = () =>
{
var label = new Label();
label.AddToClassList(labelClass);
return label;
};
listView.bindItem = (element, i) => ((Label)element).text = assets[i].name;
listView.selectionType = SelectionType.Single;
listView.onSelectionChanged += enumerable =>
{
IMGUIContainer editor = rootVisualElement.Q<IMGUIContainer>(className: "table-editor");
editor.onGUIHandler = null;
if (enumerable.Count == 0)
return;
var table = (TransitionTableSO)enumerable[0];
if (table == null)
return;
if (_transitionTableEditor == null)
_transitionTableEditor = UnityEditor.Editor.CreateEditor(table, typeof(TransitionTableEditor));
else
UnityEditor.Editor.CreateCachedEditor(table, typeof(TransitionTableEditor), ref _transitionTableEditor);
editor.onGUIHandler = () =>
{
if (!_transitionTableEditor.target)
{
editor.onGUIHandler = null;
return;
}
if ((Object)listView.selectedItem != _transitionTableEditor.target)
{
var i = listView.itemsSource.IndexOf(_transitionTableEditor.target);
listView.selectedIndex = i;
if (i < 0)
{
editor.onGUIHandler = null;
return;
}
}
_transitionTableEditor.OnInspectorGUI();
};
};
}
private TransitionTableSO[] FindAssets()
{
var guids = AssetDatabase.FindAssets($"t:{nameof(TransitionTableSO)}");
var assets = new TransitionTableSO[guids.Length];
for (int i = 0; i < guids.Length; i++)
assets[i] = AssetDatabase.LoadAssetAtPath<TransitionTableSO>(AssetDatabase.GUIDToAssetPath(guids[i]));
return assets;
}
}
}

11
UOP1_Project/Assets/Scripts/StateMachine/Editor/TransitionTableEditorWindow.cs.meta


fileFormatVersion: 2
guid: 1206c79203d3312419e6043c944a8f14
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

81
UOP1_Project/Assets/Scripts/StateMachine/Editor/TransitionTableEditorWindow.uss


.root-container {
flex-direction: row;
flex-wrap: nowrap;
flex-basis: auto;
padding: 5px;
width: auto;
height: auto;
min-height: 250px;
flex-grow: 1;
flex-shrink: 1;
}
.table-list-container {
flex-direction: column;
flex-wrap: nowrap;
flex-grow: 1;
justify-content: flex-start;
align-items: stretch;
align-content: stretch;
align-self: stretch;
width: 40%;
height: auto;
-unity-text-align: upper-center;
}
.table-list-title {
color: gray;
flex-grow: 0;
width: auto;
height: auto;
-unity-font-style: bold;
font-size: 16px;
padding: 5px;
}
.table-list {
flex-direction: column;
flex-wrap: nowrap;
flex-grow: 1;
width: auto;
height: auto;
padding: 2px;
}
.label-personal {
color: #565656;
}
.label-pro {
color: #d9d9d9;
}
.table-list-refresh {
width: 20px;
height: 20px;
padding: 1px;
margin: 5px;
align-self: stretch;
justify-content: center;
align-content: center;
}
.table-editor-container {
flex-direction: column;
padding: 5px;
width: 60%;
border-color: rgb(150, 150, 150);
border-left-width: 1px;
}
.table-editor {
padding: 5px;
width: auto;
}
.table-name {
width: auto;
flex-grow: 1;
padding: 10px;
font-size: 20px;
}

11
UOP1_Project/Assets/Scripts/StateMachine/Editor/TransitionTableEditorWindow.uss.meta


fileFormatVersion: 2
guid: 9e86a94cec7a6f549b603658cb974b8e
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0}
disableValidation: 0

21
UOP1_Project/Assets/Scripts/StateMachine/Editor/TransitionTableEditorWindow.uxml


<?xml version="1.0" encoding="utf-8"?>
<engine:UXML
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:engine="UnityEngine.UIElements"
xmlns:editor="UnityEditor.UIElements"
xsi:noNamespaceSchemaLocation="../../../../UIElementsSchema/UIElements.xsd">
<engine:VisualElement class="root-container">
<engine:VisualElement class="table-list-container">
<engine:Label class="table-list-title" text="Transition Tables" />
<engine:ListView class="table-list" />
</engine:VisualElement>
<engine:ScrollView class="table-editor-container">
<engine:IMGUIContainer class="table-editor" />
</engine:ScrollView>
</engine:VisualElement>
</engine:UXML>

10
UOP1_Project/Assets/Scripts/StateMachine/Editor/TransitionTableEditorWindow.uxml.meta


fileFormatVersion: 2
guid: 32d0e869d54306c4a94a0cc1d8176a87
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}
正在加载...
取消
保存