这是第一个 Unity 开放项目的repo,是 Unity 和社区合作创建的一个小型开源游戏演示,第一款游戏是一款名为 Chop Chop 的动作冒险游戏。
您最多选择25个主题 主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 

171 行
5.4 KiB

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;
}
}
}