You've already forked taptap2024_GJ_chidouren
init
This commit is contained in:
259
Packages/com.xfkj.xffsm@357f537fea/Editor/GUI/Context.cs
Normal file
259
Packages/com.xfkj.xffsm@357f537fea/Editor/GUI/Context.cs
Normal file
@@ -0,0 +1,259 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace XFFSM
|
||||
{
|
||||
public class Context
|
||||
{
|
||||
|
||||
#region 字段
|
||||
|
||||
private RuntimeFSMController _runtimeFSMController;
|
||||
|
||||
public List<FSMStateNodeData> SelectNodes = new List<FSMStateNodeData>();
|
||||
|
||||
public FSMTransitionData selectTransition;
|
||||
|
||||
public bool isPreviewTransition = false;
|
||||
public FSMStateNodeData fromState = null;
|
||||
public FSMStateNodeData hoverState = null;
|
||||
|
||||
private FSMController _FSMController;
|
||||
|
||||
//private LayoutMatrix4x4Info _matrixInfo;
|
||||
|
||||
private static Context _instance;
|
||||
|
||||
//private List<FSMStateNodeData> current_show_state_node_data = new List<FSMStateNodeData>();
|
||||
//private List<FSMTransitionData> current_show_transition_data = new List<FSMTransitionData>();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Layers
|
||||
|
||||
private List<string> empty_layers = new List<string>();
|
||||
|
||||
public List<string> Layers
|
||||
{
|
||||
get {
|
||||
|
||||
if(RuntimeFSMController != null)
|
||||
return RuntimeFSMController.Layers;
|
||||
|
||||
return empty_layers;
|
||||
}
|
||||
}
|
||||
|
||||
public string LayerParent
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Layers.Count == 0)
|
||||
return string.Empty;
|
||||
return Layers[Layers.Count - 1];
|
||||
}
|
||||
}
|
||||
|
||||
public void AddLayer(string layer)
|
||||
{
|
||||
if (RuntimeFSMController == null) return;
|
||||
RuntimeFSMController.AddLayer(layer);
|
||||
}
|
||||
|
||||
public void RemoveLayer(int index, int count)
|
||||
{
|
||||
if (RuntimeFSMController == null) return;
|
||||
RuntimeFSMController.RemoveLayer(index,count);
|
||||
}
|
||||
|
||||
public void RemoveLast(string name)
|
||||
{
|
||||
if (RuntimeFSMController == null) return;
|
||||
int index = RuntimeFSMController.Layers.Count - 1;
|
||||
if (index >= 0 && index < RuntimeFSMController.Layers.Count)
|
||||
{
|
||||
string last = RuntimeFSMController.Layers[index];
|
||||
if (!last.Equals(name))
|
||||
return;
|
||||
RuntimeFSMController.RemoveLayer(RuntimeFSMController.Layers.Count - 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 属性
|
||||
|
||||
// 这个逻辑修改为如果只有一个,逻辑不变,
|
||||
// 如果有多个,判断当前显示的是不是在多个中,如果在不做处理,如果不在设置为第一个
|
||||
public RuntimeFSMController RuntimeFSMController {
|
||||
get
|
||||
{
|
||||
if ( RuntimeFSMControllers != null && RuntimeFSMControllers.Count != 0)
|
||||
{
|
||||
if (FSMControllerIndex < 0 || FSMControllerIndex >= RuntimeFSMControllers.Count)
|
||||
FSMControllerIndex = 0;
|
||||
|
||||
RuntimeFSMController controller = RuntimeFSMControllers[FSMControllerIndex];
|
||||
|
||||
if (controller != null && !string.IsNullOrEmpty(controller.originGUID) && _runtimeFSMControllerGUID != controller.originGUID)
|
||||
{
|
||||
PlayerPrefs.SetString("XFFSMRuntimeFSMControllerGUID", controller.originGUID);
|
||||
_runtimeFSMControllerGUID = controller.originGUID;
|
||||
//Debug.LogFormat("保存的GUID:{0}", _runtimeFSMControllerGUID);
|
||||
}
|
||||
|
||||
return controller;
|
||||
}
|
||||
|
||||
if (_runtimeFSMController == null)
|
||||
{
|
||||
string path = AssetDatabase.GUIDToAssetPath(RuntimeFSMControllerGUID);
|
||||
_runtimeFSMController = AssetDatabase.LoadAssetAtPath<RuntimeFSMController>(path);
|
||||
}
|
||||
|
||||
return _runtimeFSMController;
|
||||
}
|
||||
internal set
|
||||
{
|
||||
if (_runtimeFSMController == value) return;
|
||||
_runtimeFSMController = value;
|
||||
}
|
||||
}
|
||||
|
||||
public FSMController FSMController {
|
||||
get {
|
||||
|
||||
if (_FSMController == null)
|
||||
{
|
||||
GameObject gameObj = EditorUtility.InstanceIDToObject(FSMControllerInstanceID) as GameObject;
|
||||
if (gameObj == null) return null;
|
||||
_FSMController = gameObj.GetComponent<FSMController>();
|
||||
}
|
||||
|
||||
return _FSMController;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private List<RuntimeFSMController> currentFSMControllers = new List<RuntimeFSMController> ();
|
||||
|
||||
public List<RuntimeFSMController> RuntimeFSMControllers
|
||||
{
|
||||
get
|
||||
{
|
||||
if (FSMController != null)
|
||||
return FSMController.RuntimeFSMController;
|
||||
currentFSMControllers.Clear();
|
||||
|
||||
if (_runtimeFSMController == null)
|
||||
{
|
||||
string path = AssetDatabase.GUIDToAssetPath(RuntimeFSMControllerGUID);
|
||||
_runtimeFSMController = AssetDatabase.LoadAssetAtPath<RuntimeFSMController>(path);
|
||||
}
|
||||
|
||||
if (_runtimeFSMController != null)
|
||||
currentFSMControllers.Add(_runtimeFSMController);
|
||||
return currentFSMControllers;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static Context Instance
|
||||
{
|
||||
get {
|
||||
if(_instance == null)
|
||||
_instance = new Context();
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
internal int FSMControllerInstanceID
|
||||
{
|
||||
get
|
||||
{
|
||||
return PlayerPrefs.GetInt("XFFSMControllerInstanceID",0);
|
||||
}
|
||||
set {
|
||||
PlayerPrefs.SetInt("XFFSMControllerInstanceID", value);
|
||||
Object obj = EditorUtility.InstanceIDToObject(value);
|
||||
GameObject gameObj = obj as GameObject;
|
||||
if (gameObj != null && gameObj.GetComponent<FSMController>() != null) {
|
||||
_FSMController = gameObj.GetComponent<FSMController>();
|
||||
}
|
||||
else
|
||||
_FSMController = null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private string _runtimeFSMControllerGUID;
|
||||
|
||||
internal string RuntimeFSMControllerGUID
|
||||
{
|
||||
get
|
||||
{
|
||||
return PlayerPrefs.GetString("XFFSMRuntimeFSMControllerGUID",string.Empty);
|
||||
}
|
||||
set
|
||||
{
|
||||
//Debug.LogFormat("修改GUID:{0}",value);
|
||||
PlayerPrefs.SetString("XFFSMRuntimeFSMControllerGUID", value);
|
||||
string path = AssetDatabase.GUIDToAssetPath(value);
|
||||
RuntimeFSMController = AssetDatabase.LoadAssetAtPath<RuntimeFSMController>(path);
|
||||
}
|
||||
}
|
||||
|
||||
internal int FSMControllerIndex
|
||||
{
|
||||
get {
|
||||
return PlayerPrefs.GetInt("FSMControllerIndex", 0);
|
||||
}
|
||||
set
|
||||
{
|
||||
if (FSMControllerIndex == value) return;
|
||||
PlayerPrefs.SetInt("FSMControllerIndex", value);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 方法
|
||||
|
||||
private Context() {
|
||||
}
|
||||
|
||||
public void ClearSelections()
|
||||
{
|
||||
// 如果不是 InspectorWindow 此时清空 Inspector
|
||||
this.SelectNodes.Clear();
|
||||
selectTransition = null;
|
||||
Selection.activeObject = null;
|
||||
//Selection.activeObject = this.RuntimeFSMController;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前显示状态节点
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public List<FSMStateNodeData> GetCurrentShowStateNodeData() {
|
||||
|
||||
if (RuntimeFSMController != null)
|
||||
return RuntimeFSMController.GetCurrentShowStateNodeData(LayerParent);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<FSMTransitionData> GetCurrentShowTransitionData()
|
||||
{
|
||||
if (RuntimeFSMController != null)
|
||||
return RuntimeFSMController.GetCurrentShowTransitionData(LayerParent);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dc4e8d1fd5489124bbd0e11d631762fc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8c7fdc94ab675d543b15b12578676825
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,34 @@
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace XFFSM
|
||||
{
|
||||
|
||||
public class FSMBoolConditionInspector : FSMConditionInspector
|
||||
{
|
||||
|
||||
public override void OnGUI(Rect rect, FSMConditionData condition, RuntimeFSMController controller)
|
||||
{
|
||||
string text = condition.targetValue == 1 ? "True" : "False";
|
||||
if (EditorGUI.DropdownButton(rect, new GUIContent(text), FocusType.Keyboard)) {
|
||||
|
||||
GenericMenu menu = new GenericMenu();
|
||||
|
||||
menu.AddItem(new GUIContent("True"), condition.targetValue == 1, () => {
|
||||
condition.targetValue = 1;
|
||||
controller.Save();
|
||||
});
|
||||
|
||||
//tempContent.text = "False";
|
||||
menu.AddItem(new GUIContent("False"), condition.targetValue == 0, () => {
|
||||
condition.targetValue = 0;
|
||||
controller.Save();
|
||||
});
|
||||
|
||||
menu.ShowAsContext();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ce9f38bc4f6646845b15aa6f152bb7a6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,13 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace XFFSM
|
||||
{
|
||||
public class FSMConditionInspector
|
||||
{
|
||||
public virtual void OnGUI(Rect rect, FSMConditionData condtion, RuntimeFSMController controller) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 64ef6b93dc886854a899900b205641c2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace XFFSM
|
||||
{
|
||||
public class FSMFloatConditionInspector : FSMConditionInspector
|
||||
{
|
||||
private Rect leftRect;
|
||||
private Rect rightRect;
|
||||
|
||||
//private GUIContent tempContent = new GUIContent();
|
||||
|
||||
public override void OnGUI(Rect rect, FSMConditionData condition, RuntimeFSMController controller)
|
||||
{
|
||||
|
||||
leftRect.Set(rect.x, rect.y, rect.width / 2, rect.height);
|
||||
rightRect.Set(rect.x + rect.width / 2, rect.y, rect.width / 2, rect.height);
|
||||
|
||||
//tempContent.text = condition.compareType.ToString();
|
||||
if (EditorGUI.DropdownButton(leftRect, new GUIContent(condition.compareType.ToString()), FocusType.Keyboard)) {
|
||||
|
||||
GenericMenu menu = new GenericMenu();
|
||||
|
||||
for (int i = 0; i < Enum.GetValues(typeof(CompareType)).Length; i++)
|
||||
{
|
||||
CompareType v = (CompareType)Enum.GetValues(typeof(CompareType)).GetValue(i);
|
||||
|
||||
if (v == CompareType.Equal || v == CompareType.NotEqual) continue;
|
||||
|
||||
menu.AddItem(new GUIContent(v.ToString()), condition.compareType == v, () => {
|
||||
condition.compareType = v;
|
||||
controller.Save();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
menu.ShowAsContext();
|
||||
|
||||
}
|
||||
|
||||
condition.targetValue = EditorGUI.FloatField(rightRect, condition.targetValue);
|
||||
EditorUtility.SetDirty(controller);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0769ca1535aa530498501eeea743587f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace XFFSM
|
||||
{
|
||||
public class FSMIntConditionInspector : FSMConditionInspector
|
||||
{
|
||||
|
||||
private Rect leftRect;
|
||||
private Rect rightRect;
|
||||
|
||||
//private GUIContent tempContent = new GUIContent();
|
||||
|
||||
public override void OnGUI(Rect rect, FSMConditionData condition, RuntimeFSMController controller)
|
||||
{
|
||||
leftRect.Set(rect.x, rect.y, rect.width / 2, rect.height);
|
||||
rightRect.Set(rect.x + rect.width / 2, rect.y, rect.width / 2, rect.height);
|
||||
|
||||
//tempContent.text = condition.compareType.ToString();
|
||||
if (EditorGUI.DropdownButton(leftRect, new GUIContent(condition.compareType.ToString()), FocusType.Keyboard))
|
||||
{
|
||||
|
||||
GenericMenu menu = new GenericMenu();
|
||||
|
||||
for (int i = 0; i < Enum.GetValues(typeof(CompareType)).Length; i++)
|
||||
{
|
||||
CompareType v = (CompareType)Enum.GetValues(typeof(CompareType)).GetValue(i);
|
||||
|
||||
//if (v == CompareType.Equal || v == CompareType.NotEqual) continue;
|
||||
|
||||
//tempContent.text = v.ToString();
|
||||
menu.AddItem(new GUIContent(v.ToString()), condition.compareType == v, () => {
|
||||
condition.compareType = v;
|
||||
controller.Save();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
menu.ShowAsContext();
|
||||
|
||||
}
|
||||
|
||||
condition.targetValue = EditorGUI.IntField(rightRect, (int)condition.targetValue);
|
||||
EditorUtility.SetDirty(controller);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8a227da536ccef04d96788e51bad55fc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,266 @@
|
||||
using System;
|
||||
using UnityEditor;
|
||||
using UnityEditorInternal;
|
||||
using UnityEngine;
|
||||
|
||||
namespace XFFSM
|
||||
{
|
||||
[CustomEditor(typeof(FSMStateInspectorHelper))]
|
||||
public class FSMStateInspector : Editor
|
||||
{
|
||||
//private string stateName;
|
||||
|
||||
private ReorderableList reorderableList;
|
||||
|
||||
|
||||
private Rect popupRect;
|
||||
|
||||
private GUIContent btn_add_state_script = new GUIContent("Add State Script");
|
||||
|
||||
private GUIStyle ProjectBrowserHeaderBgMiddle = null;
|
||||
|
||||
private GUIStyle DD_HeaderStyle = null;
|
||||
|
||||
private GUIStyle PrefixLabel = null;
|
||||
|
||||
private GUIContent script_gui_content = new GUIContent();
|
||||
|
||||
private Vector2 scroll;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
FSMStateInspectorHelper helper = target as FSMStateInspectorHelper;
|
||||
if (helper == null) { return; }
|
||||
|
||||
//reorderableList = new ReorderableList(helper.node.StateScripts, typeof(FSMStateScriptInfo), true, true, true, true);
|
||||
//reorderableList.drawHeaderCallback += OnDrawHeaderCallback;
|
||||
//reorderableList.onAddCallback += this.OnAddCallback;
|
||||
//reorderableList.onRemoveCallback += this.OnRemoveCallback;
|
||||
//reorderableList.drawElementCallback += this.DrawItem;
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
//base.OnInspectorGUI();
|
||||
|
||||
if (ProjectBrowserHeaderBgMiddle == null)
|
||||
ProjectBrowserHeaderBgMiddle = new GUIStyle("AC BoldHeader");
|
||||
|
||||
if (DD_HeaderStyle == null)
|
||||
{
|
||||
DD_HeaderStyle = new GUIStyle("IconButton");
|
||||
}
|
||||
|
||||
if (PrefixLabel == null)
|
||||
{
|
||||
PrefixLabel = new GUIStyle("PrefixLabel");
|
||||
PrefixLabel.richText = true;
|
||||
}
|
||||
|
||||
FSMStateInspectorHelper helper = target as FSMStateInspectorHelper;
|
||||
if (helper == null) return;
|
||||
|
||||
bool disabled = EditorApplication.isPlaying || helper.node.IsAnyState || helper.node.IsEntryState || helper.node.IsUpState;
|
||||
|
||||
EditorGUI.BeginDisabledGroup(disabled);
|
||||
|
||||
//GUILayout.Space(-10);
|
||||
//scroll = GUILayout.BeginScrollView(scroll);
|
||||
Vector2 mousePosition = Event.current.mousePosition;
|
||||
|
||||
foreach (var item in helper.node.StateScripts)
|
||||
{
|
||||
|
||||
// 刷新一下
|
||||
if (string.IsNullOrEmpty(item.guid) && !string.IsNullOrEmpty(item.className))
|
||||
helper.node.RefreshStateScripts(helper.controller);
|
||||
|
||||
// 根据guid加载到脚本信息
|
||||
string path = AssetDatabase.GUIDToAssetPath(item.guid);
|
||||
MonoScript script = AssetDatabase.LoadAssetAtPath<MonoScript>(path);
|
||||
if (script == null) continue;
|
||||
Type type = script.GetClass();
|
||||
if (type == null) continue;
|
||||
|
||||
var r = EditorGUILayout.BeginHorizontal(GUILayout.Height(25));
|
||||
r.x = 0;
|
||||
r.width += 30;
|
||||
GUI.Box(r, string.Empty, ProjectBrowserHeaderBgMiddle);
|
||||
GUILayout.Space(-10);
|
||||
GUILayout.Label(EditorGUIUtility.IconContent("d_cs Script Icon"), GUILayout.Width(20), GUILayout.Height(20));
|
||||
|
||||
string displayName = string.Empty;
|
||||
|
||||
if (type.IsSubclassOf(typeof(FSMState)))
|
||||
{
|
||||
displayName = type.Name;
|
||||
script_gui_content.tooltip = string.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
displayName = string.Format("{0}<color=yellow>(Script Missing)</color>", type.Name);
|
||||
script_gui_content.tooltip = "脚本丢失,请检查该脚本是否继承自FSMState!";
|
||||
}
|
||||
|
||||
script_gui_content.text = displayName;
|
||||
|
||||
|
||||
GUILayout.Label(script_gui_content, PrefixLabel, GUILayout.Height(20));
|
||||
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
GUILayout.BeginVertical();
|
||||
|
||||
GUILayout.Space(5);
|
||||
|
||||
if ( GUILayout.Button(EditorGUIUtility.IconContent("d__Menu"), DD_HeaderStyle, GUILayout.Width(25), GUILayout.Height(25)))
|
||||
{
|
||||
ShowMenu(script);
|
||||
}
|
||||
|
||||
GUILayout.EndVertical();
|
||||
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
if ( Event.current.type == EventType.MouseUp && Event.current.button == 1 && r.Contains(mousePosition))
|
||||
{
|
||||
ShowMenu(script);
|
||||
Event.current.Use();
|
||||
}
|
||||
|
||||
if ( Event.current.type == EventType.MouseUp && Event.current.button == 0 && r.Contains(mousePosition)) {
|
||||
|
||||
EditorGUIUtility.PingObject(script);
|
||||
Event.current.Use();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GUILayout.Space(30);
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
GUILayout.FlexibleSpace();
|
||||
if (GUILayout.Button(btn_add_state_script, GUILayout.Width(260), GUILayout.Height(25)))
|
||||
{
|
||||
popupRect.height = 300;
|
||||
popupRect.y -= popupRect.height;
|
||||
if (popupRect.y > 300)
|
||||
popupRect.y -= popupRect.height + 25;
|
||||
|
||||
PopupWindow.Show(popupRect, new FSMSelectStateWindow(popupRect, helper.controller, helper.node));
|
||||
}
|
||||
GUILayout.FlexibleSpace();
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
var rect = GUILayoutUtility.GetRect(255, 0);
|
||||
if (rect.width > 10)
|
||||
popupRect = rect;
|
||||
|
||||
GUILayout.FlexibleSpace();
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
if(helper.controller != null)
|
||||
helper.controller.Save();
|
||||
|
||||
//GUILayout.EndScrollView();
|
||||
|
||||
EditorGUI.EndDisabledGroup();
|
||||
}
|
||||
|
||||
protected override void OnHeaderGUI()
|
||||
{
|
||||
//base.OnHeaderGUI();
|
||||
FSMStateInspectorHelper helper = target as FSMStateInspectorHelper;
|
||||
if (helper == null) return;
|
||||
|
||||
|
||||
string name = null;
|
||||
EditorGUI.BeginChangeCheck();
|
||||
{
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
|
||||
GUILayout.Label(EditorGUIUtility.IconContent("icons/processed/unityeditor/animations/animatorstate icon.asset"), GUILayout.Width(30), GUILayout.Height(30));
|
||||
|
||||
EditorGUILayout.LabelField("Name:", GUILayout.Width(60));
|
||||
|
||||
bool disabled = EditorApplication.isPlaying
|
||||
|| helper.node.IsAnyState || helper.node.IsEntryState || helper.node.IsUpState;
|
||||
|
||||
EditorGUI.BeginDisabledGroup(disabled);
|
||||
name = EditorGUILayout.DelayedTextField(helper.node.DisplayName);
|
||||
EditorGUI.EndDisabledGroup();
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
string oldName = helper.node.name;
|
||||
bool success = FSMStateNodeFactory.Rename(helper.controller, helper.node, name);
|
||||
if (success)
|
||||
helper.grap.RenameState(oldName, name);
|
||||
}
|
||||
EditorGUILayout.Space();
|
||||
var rect = EditorGUILayout.BeginHorizontal();
|
||||
|
||||
Handles.color = Color.black;
|
||||
Handles.DrawLine(new Vector2(rect.x, rect.y), new Vector3(rect.x + rect.width, rect.y));
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
EditorGUILayout.Space();
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void ShowMenu(MonoScript script)
|
||||
{
|
||||
FSMStateInspectorHelper helper = target as FSMStateInspectorHelper;
|
||||
if (helper == null) return;
|
||||
|
||||
var genricMenu = new GenericMenu();
|
||||
|
||||
genricMenu.AddItem(new GUIContent("Remove Script"), false, () =>
|
||||
{
|
||||
helper.node.RemoveStateScript(script);
|
||||
helper.controller.Save();
|
||||
});
|
||||
|
||||
genricMenu.AddItem(new GUIContent("Edit Script"), false, () =>
|
||||
{
|
||||
AssetDatabase.OpenAsset(script);
|
||||
});
|
||||
|
||||
genricMenu.ShowAsContext();
|
||||
}
|
||||
|
||||
|
||||
|
||||
//private void DrawItem(Rect rect, int index, bool isActive, bool isFocused)
|
||||
//{
|
||||
|
||||
|
||||
// if (index < 0 || index >= reorderableList.list.Count) return;
|
||||
// FSMStateScriptInfo info = reorderableList.list[index] as FSMStateScriptInfo;
|
||||
// if (info == null) return;
|
||||
// info.className = GUI.TextField(rect, info.className);
|
||||
// if (string.IsNullOrEmpty(info.className))
|
||||
// {
|
||||
// EditorGUI.BeginDisabledGroup(true);
|
||||
// GUI.Label(rect, "请输入类的全名(含命名空间)!");
|
||||
// EditorGUI.EndDisabledGroup();
|
||||
// }
|
||||
//}
|
||||
|
||||
//private void OnDrawHeaderCallback(Rect rect)
|
||||
//{
|
||||
// GUI.Label(rect, "State Scripts");
|
||||
//}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2304151d2a6c07d429a8183e483319c2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,41 @@
|
||||
|
||||
using UnityEditor;
|
||||
|
||||
namespace XFFSM
|
||||
{
|
||||
public class FSMStateInspectorHelper : ScriptableObjectSingleton<FSMStateInspectorHelper>
|
||||
{
|
||||
//private static FSMStateInspectorHelper _instance;
|
||||
//public static FSMStateInspectorHelper Instance {
|
||||
// get {
|
||||
// if (_instance == null) {
|
||||
// _instance = ScriptableObject.CreateInstance<FSMStateInspectorHelper>();
|
||||
// }
|
||||
// return _instance;
|
||||
// }
|
||||
//}
|
||||
|
||||
public FSMStateNodeData node;
|
||||
public RuntimeFSMController controller;
|
||||
|
||||
public FSMStateGraphView grap;
|
||||
|
||||
public void Inspect(RuntimeFSMController controller, FSMStateNodeData node, FSMStateGraphView grap) {
|
||||
|
||||
if (node == null)
|
||||
{
|
||||
Selection.activeObject = null;
|
||||
return;
|
||||
}
|
||||
|
||||
this.node = node;
|
||||
this.controller = controller;
|
||||
this.grap = grap;
|
||||
Selection.activeObject = this;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9bff4756b18720a4c92ff5f539a260f4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,304 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEditorInternal;
|
||||
using UnityEngine;
|
||||
|
||||
namespace XFFSM
|
||||
{
|
||||
|
||||
[CustomEditor(typeof(FSMTransitionInspectorHelper))]
|
||||
public class FSMTransitionInspector : Editor
|
||||
{
|
||||
|
||||
private ReorderableList reorderableList;
|
||||
|
||||
private Rect condition_left_rect;
|
||||
private Rect condition_right_rect;
|
||||
private Rect popRect;
|
||||
|
||||
private GUIContent add_a_sets_of_conditions = null;
|
||||
|
||||
private GUIContent auto_switch = null;
|
||||
|
||||
private static Dictionary<ParameterType, FSMConditionInspector> conditionInspector = new Dictionary<ParameterType, FSMConditionInspector>();
|
||||
|
||||
private Dictionary<int, ReorderableList> reorderables = new Dictionary<int, ReorderableList>();
|
||||
|
||||
private bool autoSwitch;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
FSMTransitionInspectorHelper helper = target as FSMTransitionInspectorHelper;
|
||||
if (helper == null) { return; }
|
||||
|
||||
reorderableList = new ReorderableList(helper.transition.conditions, typeof(FSMConditionData), true, true, true, true);
|
||||
reorderableList.drawHeaderCallback += OnDrawHeaderCallback;
|
||||
reorderableList.onAddCallback += this.OnAddCallback;
|
||||
reorderableList.onRemoveCallback += this.OnRemoveCallback;
|
||||
reorderableList.drawElementCallback += this.DrawItem;
|
||||
|
||||
autoSwitch = helper.transition.AutoSwtich;
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
FSMTransitionInspectorHelper helper = target as FSMTransitionInspectorHelper;
|
||||
if (helper == null) { return; }
|
||||
|
||||
EditorGUI.BeginDisabledGroup(EditorApplication.isPlaying);
|
||||
|
||||
if (auto_switch == null)
|
||||
auto_switch = new GUIContent("AutoSwitch", "当条件为空时,是否自动切换?当前过渡的条件为空时可用!");
|
||||
|
||||
|
||||
|
||||
|
||||
EditorGUI.BeginDisabledGroup(!helper.transition.Empty);
|
||||
Rect rect = GUILayoutUtility.GetRect(0f, 20, GUILayout.ExpandWidth(expand: true));
|
||||
GUI.Label(rect, auto_switch);
|
||||
rect.Set(rect.width - 20, rect.y, 20, 20);
|
||||
if (helper.transition.Empty)
|
||||
{
|
||||
helper.transition.AutoSwtich = GUI.Toggle(rect, helper.transition.AutoSwtich, string.Empty);
|
||||
}
|
||||
else {
|
||||
GUI.Toggle(rect,false, string.Empty);
|
||||
}
|
||||
|
||||
if (autoSwitch != helper.transition.AutoSwtich)
|
||||
{
|
||||
helper.controller.Save();
|
||||
autoSwitch = helper.transition.AutoSwtich;
|
||||
}
|
||||
GUILayout.Space(10);
|
||||
EditorGUI.EndDisabledGroup();
|
||||
|
||||
reorderableList.list = helper.transition.conditions;
|
||||
reorderableList.DoLayoutList();
|
||||
|
||||
GUILayout.Space(10);
|
||||
|
||||
for (int i = 0; i < helper.transition.group_conditions.Count; i++)
|
||||
{
|
||||
GroupCondition condition = helper.transition.group_conditions[i];
|
||||
if (condition == null) continue;
|
||||
ReorderableList list = GetReorderableList(condition);
|
||||
if (list != null)
|
||||
{
|
||||
list.list = condition.conditions;
|
||||
list.DoLayoutList();
|
||||
GUILayout.Space(10);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
if (add_a_sets_of_conditions == null)
|
||||
add_a_sets_of_conditions = new GUIContent("Add a set of conditions","添加一组条件");
|
||||
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.FlexibleSpace();
|
||||
if (helper.transition.group_conditions.Count > 0)
|
||||
{
|
||||
GUILayout.Label("注:当有多组条件时,其中一组满足,状态就会切换!", "CN StatusWarn");
|
||||
}
|
||||
GUILayout.FlexibleSpace();
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
if (GUILayout.Button(add_a_sets_of_conditions, GUILayout.Width(260), GUILayout.Height(25)))
|
||||
{
|
||||
helper.transition.group_conditions.Add(new GroupCondition());
|
||||
helper.controller.Save();
|
||||
}
|
||||
|
||||
GUILayout.FlexibleSpace();
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
EditorGUI.EndDisabledGroup();
|
||||
|
||||
}
|
||||
|
||||
protected override void OnHeaderGUI()
|
||||
{
|
||||
//base.OnHeaderGUI();
|
||||
FSMTransitionInspectorHelper helper = target as FSMTransitionInspectorHelper;
|
||||
if (helper == null) { return; }
|
||||
|
||||
GUILayout.BeginHorizontal();
|
||||
|
||||
GUILayout.Label(EditorGUIUtility.IconContent("icons/processed/unityeditor/animations/animatorstatetransition icon.asset"),GUILayout.Width(30),GUILayout.Height(30));
|
||||
GUILayout.Label(string.Format("{0} -> {1}", helper.transition.fromStateName, helper.transition.toStateName));
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
// 画一条 分隔的线
|
||||
var rect = EditorGUILayout.BeginHorizontal();
|
||||
|
||||
Handles.color = Color.black;
|
||||
Handles.DrawLine(new Vector2(rect.x, rect.y), new Vector3(rect.x + rect.width, rect.y));
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
EditorGUILayout.Space();
|
||||
|
||||
}
|
||||
|
||||
|
||||
private static void InitConditionInspectors() {
|
||||
|
||||
if (conditionInspector.Count == 0) {
|
||||
conditionInspector.Add(ParameterType.Bool, new FSMBoolConditionInspector());
|
||||
conditionInspector.Add(ParameterType.Float, new FSMFloatConditionInspector());
|
||||
conditionInspector.Add(ParameterType.Int, new FSMIntConditionInspector());
|
||||
conditionInspector.Add(ParameterType.Trigger, new FSMConditionInspector()); // 不需要做任何绘制
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void OnAddCallback(ReorderableList list) {
|
||||
|
||||
FSMTransitionInspectorHelper helper = target as FSMTransitionInspectorHelper;
|
||||
if (helper == null) { return; }
|
||||
//Debug.Log("添加条件");
|
||||
FSMConditionFactory.CreateCondition(helper.controller, helper.transition);
|
||||
}
|
||||
|
||||
private void OnRemoveCallback(ReorderableList list)
|
||||
{
|
||||
|
||||
FSMTransitionInspectorHelper helper = target as FSMTransitionInspectorHelper;
|
||||
if (helper == null) return;
|
||||
|
||||
FSMConditionFactory.DeleteCondition(helper.controller, helper.transition,list.index);
|
||||
}
|
||||
|
||||
private void DrawItem(Rect rect, int index, bool isActive, bool isFocused) {
|
||||
|
||||
FSMTransitionInspectorHelper helper = target as FSMTransitionInspectorHelper;
|
||||
if (helper == null) { return; }
|
||||
|
||||
var conditon = helper.transition.conditions[index];
|
||||
|
||||
DrawItemExcute(rect, conditon);
|
||||
}
|
||||
|
||||
private void DrawItemExcute(Rect rect, FSMConditionData condition)
|
||||
{
|
||||
FSMTransitionInspectorHelper helper = target as FSMTransitionInspectorHelper;
|
||||
if (helper == null) return;
|
||||
|
||||
condition_left_rect.Set(rect.x, rect.y, rect.width / 2, rect.height);
|
||||
condition_right_rect.Set(rect.x + rect.width / 2, rect.y, rect.width / 2, rect.height);
|
||||
|
||||
if (helper.controller.parameters.Count > 0)
|
||||
{
|
||||
//tempContent.text = conditon.parameterName;
|
||||
if (EditorGUI.DropdownButton(condition_left_rect, new GUIContent(condition.parameterName), FocusType.Keyboard))
|
||||
{
|
||||
// 弹出选择参数的弹框 TODO
|
||||
popRect.Set(rect.x, rect.y + 2, rect.width / 2, rect.height);
|
||||
PopupWindow.Show(popRect, new FSMSelectParamWindow(rect.width / 2, condition, helper.controller));
|
||||
}
|
||||
}
|
||||
|
||||
InitConditionInspectors();
|
||||
|
||||
FSMParameterData parameter = helper.controller.GetParameterData(condition.parameterName);
|
||||
|
||||
if (parameter == null)
|
||||
{
|
||||
EditorGUI.LabelField(condition_right_rect, "缺少参数!");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
// 根据不同的参数类型绘制不同的内容
|
||||
if (conditionInspector.ContainsKey(parameter.parameterType))
|
||||
// 进行绘制
|
||||
if (conditionInspector[parameter.parameterType] != null)
|
||||
conditionInspector[parameter.parameterType].OnGUI(condition_right_rect, condition, helper.controller);
|
||||
else
|
||||
Debug.LogErrorFormat("未查询到对应的绘制方式:{0}", parameter.parameterType);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void OnDrawHeaderCallback(Rect rect)
|
||||
{
|
||||
GUI.Label(rect, "Conditions");
|
||||
}
|
||||
|
||||
public ReorderableList GetReorderableList(GroupCondition condition)
|
||||
{
|
||||
if (condition == null)
|
||||
return null;
|
||||
|
||||
FSMTransitionInspectorHelper helper = target as FSMTransitionInspectorHelper;
|
||||
if (helper == null)
|
||||
return null;
|
||||
|
||||
int key = condition.GetHashCode();
|
||||
if (!reorderables.ContainsKey(key))
|
||||
{
|
||||
|
||||
ReorderableList list = new ReorderableList(condition.conditions, typeof(FSMConditionData), true, true, true, true);
|
||||
list.drawHeaderCallback += (rect)=> {
|
||||
|
||||
GUI.Label(rect, "Conditions");
|
||||
|
||||
rect.Set(rect.width - 10, rect.y + 1, 25, 25);
|
||||
|
||||
if (GUI.Button(rect,EditorGUIUtility.IconContent("d__Menu"), "IconButton"))
|
||||
{
|
||||
ShowConditionMenu(condition);
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
list.onAddCallback += (a) => {
|
||||
FSMConditionData data = FSMConditionFactory.CreateCondition(helper.controller);
|
||||
condition.conditions.Add(data);
|
||||
helper.controller.Save();
|
||||
};
|
||||
|
||||
list.onRemoveCallback += (a) =>
|
||||
{
|
||||
if(a.index >= 0 && a.index < condition.conditions.Count)
|
||||
condition.conditions.RemoveAt(a.index);
|
||||
helper.controller.Save();
|
||||
};
|
||||
|
||||
list.drawElementCallback += (rect,index,c,d) =>
|
||||
{
|
||||
DrawItemExcute(rect, condition.conditions[index]);
|
||||
};
|
||||
reorderables.Add(key, list);
|
||||
}
|
||||
|
||||
return reorderables[key];
|
||||
}
|
||||
|
||||
|
||||
private void ShowConditionMenu(GroupCondition condition)
|
||||
{
|
||||
FSMTransitionInspectorHelper helper = target as FSMTransitionInspectorHelper;
|
||||
if (helper == null) { return; }
|
||||
|
||||
var genricMenu = new GenericMenu();
|
||||
|
||||
genricMenu.AddItem(new GUIContent("Remove"), false, () =>
|
||||
{
|
||||
// 移除一组条件
|
||||
helper.transition.group_conditions.Remove(condition);
|
||||
helper.controller.Save();
|
||||
});
|
||||
|
||||
genricMenu.ShowAsContext();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 35e608d082dfb634986b77bdcb9a59cf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,30 @@
|
||||
|
||||
using UnityEditor;
|
||||
|
||||
namespace XFFSM
|
||||
{
|
||||
public class FSMTransitionInspectorHelper : ScriptableObjectSingleton<FSMTransitionInspectorHelper>
|
||||
{
|
||||
|
||||
public FSMTransitionData transition;
|
||||
public RuntimeFSMController controller;
|
||||
|
||||
public void Inspect(RuntimeFSMController controller, FSMTransitionData transition)
|
||||
{
|
||||
if (transition == null)
|
||||
{
|
||||
Selection.activeObject = null;
|
||||
return;
|
||||
}
|
||||
|
||||
this.transition = transition;
|
||||
this.controller = controller;
|
||||
|
||||
Selection.activeObject = this;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d83e4df9515f6834289221614fd2fd62
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,29 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace XFFSM
|
||||
{
|
||||
public class ScriptableObjectSingleton<T> : ScriptableObject where T : ScriptableObjectSingleton<T>
|
||||
{
|
||||
private static T _instance;
|
||||
|
||||
public static T Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_instance == null)
|
||||
{
|
||||
_instance = CreateInstance<T>();
|
||||
}
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
_instance = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f161a7ba0c72a7249971e4d18ab4a249
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4f1e1e098fe279240872b9a9e6cf099c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,56 @@
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace XFFSM
|
||||
{
|
||||
public class GraphLayer
|
||||
{
|
||||
|
||||
#region 字段
|
||||
|
||||
protected Rect position;
|
||||
|
||||
#endregion
|
||||
|
||||
#region 属性
|
||||
|
||||
public EditorWindow EditorWindow { get; private set; }
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region 方法
|
||||
|
||||
public GraphLayer(EditorWindow editorWindow) {
|
||||
this.EditorWindow = editorWindow;
|
||||
}
|
||||
|
||||
public virtual void OnGUI(Rect rect) {
|
||||
position = rect;
|
||||
UpdateTransformationMatrix();
|
||||
}
|
||||
|
||||
public virtual void ProcessEvents() {
|
||||
|
||||
}
|
||||
|
||||
public virtual void Update() { }
|
||||
|
||||
private void UpdateTransformationMatrix()
|
||||
{
|
||||
//this.transormMatrix = Matrix4x4.TRS(position.center + this.Context.DragOffset , Quaternion.identity,Vector3.one * this.Context.ZoomFactor);
|
||||
}
|
||||
|
||||
public virtual void OnLostFocus()
|
||||
{
|
||||
if (UnityEditor.EditorWindow.mouseOverWindow != null && UnityEditor.EditorWindow.mouseOverWindow.GetType().ToString().Equals("UnityEditor.InspectorWindow"))
|
||||
return;
|
||||
Context.Instance.ClearSelections();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0afc6de6e08d3ee479093608ac1302e9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,350 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEditorInternal;
|
||||
using UnityEngine;
|
||||
|
||||
namespace XFFSM
|
||||
{
|
||||
public class ParamLayer : GraphLayer
|
||||
{
|
||||
|
||||
#region 字段
|
||||
private ReorderableList reorderableList;
|
||||
private ReorderableList reorderableListStates;
|
||||
private Vector2 scrollView;
|
||||
|
||||
// 参数区域
|
||||
//private Rect paramLabelRect;
|
||||
//private Rect paramValueRect;
|
||||
//const float param_value_width = 50f;
|
||||
//const float param_value_padding = 2f;
|
||||
|
||||
private bool isRenaming = false;
|
||||
private string newName;
|
||||
private List<FSMParameterData> EmptyList = new List<FSMParameterData>();
|
||||
|
||||
private Rect headerRect = new Rect();
|
||||
|
||||
private string[] toolbars = new string[] { "Controllers", "Paramters" };
|
||||
private int select = 1;
|
||||
|
||||
private int index = 0;
|
||||
|
||||
//private Vector3[] lines = new Vector3[2];
|
||||
#endregion
|
||||
|
||||
#region 重写方法
|
||||
|
||||
public override void OnGUI(Rect rect)
|
||||
{
|
||||
rect.Set(rect.x + 2,rect.y,rect.width,rect.height);
|
||||
|
||||
base.OnGUI(rect);
|
||||
|
||||
headerRect.Set(rect.x, rect.y, rect.width, 20);
|
||||
|
||||
//EditorGUI.DrawRect(headerRect, ColorConst.ParaBackground);
|
||||
|
||||
headerRect.Set(headerRect.x + 5, headerRect.y, 150, headerRect.height);
|
||||
select = GUI.Toolbar(headerRect, select, toolbars, "toolbarbuttonLeft");
|
||||
|
||||
//Handles.color = Color.black;
|
||||
//Handles.DrawLine(new Vector2(rect.x, rect.y + headerRect.height), new Vector2(rect.x + rect.width, rect.y + headerRect.height));
|
||||
//Handles.DrawLine(new Vector2(rect.x + rect.width, rect.y), new Vector2(rect.x + rect.width, rect.y + rect.height));
|
||||
// 偏移20, 用来绘制 States 和 Parmaters
|
||||
rect.Set(rect.x, rect.y + 20, rect.width, rect.height);
|
||||
|
||||
//EditorGUI.DrawRect(rect, ColorConst.ParaBackground);
|
||||
//GUI.Box(rect, string.Empty, GUI.skin.GetStyle("CN Box"));
|
||||
|
||||
if (select == 1)
|
||||
{
|
||||
DrawParamters(rect);
|
||||
}
|
||||
else {
|
||||
DrawStates(rect);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void DrawParamters(Rect rect)
|
||||
{
|
||||
if (reorderableList == null)
|
||||
{
|
||||
if (Context.Instance.RuntimeFSMController != null)
|
||||
{
|
||||
reorderableList = new ReorderableList(Context.Instance.RuntimeFSMController.parameters, typeof(FSMParameterData), true, true, true, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
reorderableList = new ReorderableList(EmptyList, typeof(FSMParameterData), true, true, true, true);
|
||||
}
|
||||
|
||||
reorderableList.drawHeaderCallback += HeaderCallbackDelegate;
|
||||
reorderableList.onAddDropdownCallback += OnAddDropdownCallback;
|
||||
reorderableList.onRemoveCallback += OnRemoveCallback;
|
||||
reorderableList.drawElementCallback += DrawElementCallback;
|
||||
|
||||
reorderableList.onCanRemoveCallback += onCanRemoveCallback;
|
||||
reorderableList.onCanAddCallback += onCanRemoveCallback;
|
||||
}
|
||||
|
||||
if (Context.Instance.RuntimeFSMController != null)
|
||||
reorderableList.list = Context.Instance.RuntimeFSMController.parameters;
|
||||
else
|
||||
reorderableList.list = EmptyList;
|
||||
|
||||
EditorGUI.BeginDisabledGroup(Context.Instance.RuntimeFSMController == null);
|
||||
|
||||
GUILayout.BeginArea(rect);
|
||||
scrollView = GUILayout.BeginScrollView(scrollView);
|
||||
reorderableList.DoLayoutList();
|
||||
GUILayout.EndScrollView();
|
||||
GUILayout.EndArea();
|
||||
|
||||
EditorGUI.EndDisabledGroup();
|
||||
}
|
||||
|
||||
private void DrawStates(Rect rect)
|
||||
{
|
||||
if (reorderableListStates == null)
|
||||
{
|
||||
|
||||
reorderableListStates = new ReorderableList(Context.Instance.RuntimeFSMControllers, typeof(RuntimeFSMController), false, true, false, false);
|
||||
|
||||
reorderableListStates.headerHeight = 0;
|
||||
reorderableListStates.drawElementCallback += DrawStateElementCallback;
|
||||
reorderableListStates.onSelectCallback += OnStateChanged;
|
||||
}
|
||||
|
||||
for (int i = 0; i < Context.Instance.RuntimeFSMControllers.Count; i++)
|
||||
{
|
||||
if (Context.Instance.RuntimeFSMControllers[i] == null) {
|
||||
Context.Instance.RuntimeFSMControllers.RemoveAt(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
reorderableListStates.list = Context.Instance.RuntimeFSMControllers;
|
||||
|
||||
GUILayout.BeginArea(rect);
|
||||
scrollView = GUILayout.BeginScrollView(scrollView);
|
||||
reorderableListStates.DoLayoutList();
|
||||
GUILayout.EndScrollView();
|
||||
GUILayout.EndArea();
|
||||
}
|
||||
|
||||
public override void ProcessEvents()
|
||||
{
|
||||
base.ProcessEvents();
|
||||
if (Event.current.type == EventType.MouseDown && Event.current.button == 0 &&
|
||||
position.Contains(Event.current.mousePosition)
|
||||
)
|
||||
{
|
||||
Context.Instance.ClearSelections();
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnLostFocus()
|
||||
{
|
||||
base.OnLostFocus();
|
||||
isRenaming = false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 方法
|
||||
public ParamLayer(EditorWindow editorWindow) : base(editorWindow)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// 添加
|
||||
private void OnAddDropdownCallback(Rect buttonRect, ReorderableList list)
|
||||
{
|
||||
GenericMenu menu = new GenericMenu();
|
||||
|
||||
for (int i = 0; i < Enum.GetValues(typeof(ParameterType)).Length; i++)
|
||||
{
|
||||
ParameterType v = (ParameterType)Enum.GetValues(typeof(ParameterType)).GetValue(i);
|
||||
menu.AddItem(new GUIContent(v.ToString()), false, () =>
|
||||
{
|
||||
|
||||
FSMParamterFactory.CreateParamter(Context.Instance.RuntimeFSMController, v);
|
||||
});
|
||||
}
|
||||
|
||||
menu.ShowAsContext();
|
||||
}
|
||||
|
||||
// 移除
|
||||
private void OnRemoveCallback(ReorderableList list)
|
||||
{
|
||||
FSMParamterFactory.RemoveParamter(Context.Instance.RuntimeFSMController, list.index);
|
||||
}
|
||||
|
||||
// 绘制每一条数据
|
||||
private void DrawElementCallback(Rect rect, int index, bool isActive, bool isFocused)
|
||||
{
|
||||
if (Context.Instance.RuntimeFSMController == null) return;
|
||||
if (index < 0 || index >= Context.Instance.RuntimeFSMController.parameters.Count) return;
|
||||
|
||||
FSMParameterData parameter = Context.Instance.RuntimeFSMController.parameters[index];
|
||||
|
||||
if (parameter == null) return;
|
||||
|
||||
rect.width *= 0.6f;
|
||||
|
||||
if (Event.current.type == EventType.MouseDown && Event.current.button == 0 && rect.Contains(Event.current.mousePosition) && isFocused )
|
||||
{
|
||||
isRenaming = true;
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
if (Event.current.type == EventType.MouseDown && Event.current.button == 0 &&
|
||||
!rect.Contains(Event.current.mousePosition) && index == reorderableList.index)
|
||||
{
|
||||
EditorApplication.delayCall += () =>
|
||||
{
|
||||
isRenaming = false;
|
||||
};
|
||||
|
||||
GUI.FocusControl(null);
|
||||
}
|
||||
|
||||
// 按下回车键的时候 也需要取消重命名
|
||||
if (Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Return)
|
||||
{
|
||||
EditorApplication.delayCall += () =>
|
||||
{
|
||||
isRenaming = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
if (isRenaming && index == reorderableList.index)
|
||||
{
|
||||
// 绘制输入框
|
||||
EditorGUI.BeginChangeCheck();
|
||||
newName = EditorGUI.DelayedTextField(rect, parameter.name);
|
||||
if (EditorGUI.EndChangeCheck() && this.index == index)
|
||||
{
|
||||
isRenaming = false;
|
||||
if (parameter.name.Equals(newName)) return;
|
||||
FSMParamterFactory.RenameParamter(Context.Instance.RuntimeFSMController, parameter, newName);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GUI.Label(rect, parameter.name);
|
||||
}
|
||||
|
||||
rect.x += rect.width;
|
||||
rect.width /= 3;
|
||||
EditorGUI.BeginDisabledGroup(true);
|
||||
GUI.Label(rect, GetParameterType(parameter));
|
||||
EditorGUI.EndDisabledGroup();
|
||||
rect.x += rect.width;
|
||||
|
||||
switch (parameter.parameterType)
|
||||
{
|
||||
case ParameterType.Float:
|
||||
parameter.Value = EditorGUI.FloatField(rect, parameter.Value);
|
||||
break;
|
||||
case ParameterType.Int:
|
||||
parameter.Value = EditorGUI.IntField(rect, (int)parameter.Value);
|
||||
break;
|
||||
case ParameterType.Bool:
|
||||
parameter.Value = EditorGUI.Toggle(rect, parameter.Value == 1) ? 1 : 0;
|
||||
break;
|
||||
case ParameterType.Trigger:
|
||||
parameter.Value = EditorGUI.Toggle(rect, parameter.Value == 1, GUI.skin.GetStyle("Radio")) ? 1 : 0;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private string GetParameterType(FSMParameterData parameter)
|
||||
{
|
||||
switch (parameter.parameterType)
|
||||
{
|
||||
case ParameterType.Float:
|
||||
return "Float";
|
||||
case ParameterType.Int:
|
||||
return "Int";
|
||||
case ParameterType.Bool:
|
||||
return "Bool";
|
||||
case ParameterType.Trigger:
|
||||
return "Trigger";
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
private void DrawStateElementCallback(Rect rect, int index, bool isActive, bool isFocused)
|
||||
{
|
||||
if (index < 0 || index >= Context.Instance.RuntimeFSMControllers.Count) return;
|
||||
|
||||
RuntimeFSMController controller = Context.Instance.RuntimeFSMControllers[index];
|
||||
|
||||
//if (controller == null) return;
|
||||
|
||||
try
|
||||
{
|
||||
GUIContent content = EditorGUIUtility.IconContent("AnimatorController Icon");
|
||||
|
||||
if (controller == Context.Instance.RuntimeFSMController)
|
||||
GUI.Label(new Rect(rect.x, rect.y, rect.height, rect.height), "✓");
|
||||
|
||||
GUI.Label(new Rect(rect.x + rect.height, rect.y, rect.height, rect.height), content);
|
||||
rect.Set(rect.x + rect.height * 2,rect.y,rect.width - rect.height,rect.height);
|
||||
GUI.Label(rect,controller == null ? "None" : controller.name);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private bool onCanRemoveCallback(ReorderableList list)
|
||||
{
|
||||
return Application.isPlaying == false;
|
||||
}
|
||||
|
||||
|
||||
private void OnStateChanged(ReorderableList list)
|
||||
{
|
||||
if (Context.Instance.RuntimeFSMControllers == null || Context.Instance.RuntimeFSMControllers.Count == 0) return;
|
||||
|
||||
if (Context.Instance.RuntimeFSMController == Context.Instance.RuntimeFSMControllers[list.index]) return;
|
||||
|
||||
Context.Instance.FSMControllerIndex = list.index;
|
||||
}
|
||||
|
||||
|
||||
private void HeaderCallbackDelegate(Rect rect)
|
||||
{
|
||||
rect.width *= 0.6f;
|
||||
rect.x += 15;
|
||||
GUI.Label(rect,"名称");
|
||||
rect.x += rect.width;
|
||||
rect.width /= 3;
|
||||
rect.x -= 10;
|
||||
GUI.Label(rect, "类型");
|
||||
rect.x += rect.width;
|
||||
rect.x -= 5;
|
||||
GUI.Label(rect, "值");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a5e4cbef482e9bd428dc3d594927279e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e334cab2f8105f94a8bde697f6a370de
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,97 @@
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace XFFSM
|
||||
{
|
||||
public class PackageInfo
|
||||
{
|
||||
public string version;
|
||||
}
|
||||
|
||||
public class FSMAboutWindow : EditorWindow
|
||||
{
|
||||
|
||||
Rect textureRect = new Rect(0, 10, 291 * 0.7F, 96 * 0.7F);
|
||||
Texture logo;
|
||||
private GUIStyle style;
|
||||
|
||||
private string version;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
logo = AssetDatabase.LoadAssetAtPath<Texture>("Packages/com.xfkj.xffsm/Editor/Texture/logo_web.png");
|
||||
|
||||
TextAsset p = AssetDatabase.LoadAssetAtPath<TextAsset>("Packages/com.xfkj.xffsm/package.json");
|
||||
if(p != null )
|
||||
{
|
||||
PackageInfo info = JsonUtility.FromJson<PackageInfo>(p.text);
|
||||
version = string.Format("Version {0}", info.version);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void ConfigStyle()
|
||||
{
|
||||
style = new GUIStyle(GUI.skin.label);
|
||||
style.richText = true;
|
||||
style.normal.textColor = new Color(0.03f, 0.4f, 0.9f, 1);
|
||||
style.onHover.textColor = Color.white;
|
||||
style.alignment = TextAnchor.MiddleLeft;
|
||||
style.fontStyle = FontStyle.Italic;
|
||||
//style.onFocused.textColor = Color.red;
|
||||
}
|
||||
|
||||
// 每秒10帧更新
|
||||
void OnInspectorUpdate()
|
||||
{
|
||||
//开启窗口的重绘,不然窗口信息不会刷新
|
||||
Repaint();
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
if(logo!=null)
|
||||
GUI.DrawTexture(textureRect, logo);
|
||||
GUILayout.Space(textureRect.height + 20);
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Space(130);
|
||||
GUILayout.Label(version);
|
||||
GUILayout.EndHorizontal();
|
||||
GUILayout.Space(10);
|
||||
|
||||
GUILayout.Label("欢迎使用XFFSM!");
|
||||
GUILayout.Label("XFFSM 是一款可视化有限状态机插件,XFFSM 能够帮助您加速开发流程,简化状态设计!");
|
||||
GUILayout.Label("如果您在使用的过程碰到任何问题 或 错误,请通过下面的QQ交流群联系到我们!");
|
||||
GUILayout.Label("感谢您的支持!");
|
||||
//GUILayout.Label("更多信息可通过点击下方教程链接获取!");
|
||||
GUILayout.Space(20);
|
||||
if (style == null)
|
||||
{
|
||||
ConfigStyle();
|
||||
}
|
||||
|
||||
//DrawLink("更多教程:", "https://space.bilibili.com/258939476");
|
||||
DrawLink("插件源码:", "https://gitee.com/xianfengkeji/xffsm");
|
||||
GUILayout.Space(20);
|
||||
GUILayout.Label("XFFSM 交流群:644685781");
|
||||
|
||||
//GUILayout.Space(20);
|
||||
GUILayout.Label("*弦风课堂制作");
|
||||
}
|
||||
|
||||
private void DrawLink(string title, string url)
|
||||
{
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Label(title, GUILayout.Width(60));
|
||||
|
||||
if (GUILayout.Button(url, style))
|
||||
{
|
||||
Application.OpenURL(url);
|
||||
}
|
||||
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 608488b1e91e1564aa4c2d5df6758377
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,107 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor.IMGUI.Controls;
|
||||
using UnityEngine;
|
||||
|
||||
namespace XFFSM
|
||||
{
|
||||
public class FSMParamListTree : TreeView
|
||||
{
|
||||
|
||||
#region 字段
|
||||
|
||||
private RuntimeFSMController controller;
|
||||
private FSMConditionData condition;
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
|
||||
#region 重写方法
|
||||
protected override TreeViewItem BuildRoot()
|
||||
{
|
||||
|
||||
TreeViewItem root = new TreeViewItem(-1, -1);
|
||||
|
||||
if (controller != null)
|
||||
{
|
||||
for (int i = 0; i < controller.parameters.Count; i++)
|
||||
{
|
||||
root.AddChild(new TreeViewItem(i, 0, controller.parameters[i].name));
|
||||
}
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
protected override IList<TreeViewItem> BuildRows(TreeViewItem root)
|
||||
{
|
||||
return base.BuildRows(root);
|
||||
}
|
||||
|
||||
protected override void SingleClickedItem(int id)
|
||||
{
|
||||
base.SingleClickedItem(id);
|
||||
|
||||
string paramterName = FindItem(id, rootItem).displayName;
|
||||
|
||||
FSMParameterData p = controller.GetParameterData(paramterName);
|
||||
|
||||
if (p != null)
|
||||
{
|
||||
|
||||
condition.parameterName = paramterName;
|
||||
|
||||
switch (p.parameterType)
|
||||
{
|
||||
case ParameterType.Float:
|
||||
case ParameterType.Int:
|
||||
condition.compareType = CompareType.Greater;
|
||||
break;
|
||||
case ParameterType.Bool:
|
||||
condition.compareType = CompareType.Equal;
|
||||
break;
|
||||
case ParameterType.Trigger:
|
||||
condition.compareType = CompareType.Equal;
|
||||
condition.targetValue = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
Debug.LogErrorFormat("参数查询失败:{0}", paramterName);
|
||||
}
|
||||
|
||||
// 保存
|
||||
controller.Save();
|
||||
}
|
||||
|
||||
protected override void RowGUI(RowGUIArgs args)
|
||||
{
|
||||
base.RowGUI(args);
|
||||
|
||||
if ( args.label.Equals(condition.parameterName) ) {
|
||||
GUI.Label(args.rowRect, "√");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 方法
|
||||
public FSMParamListTree(TreeViewState state, RuntimeFSMController controller, FSMConditionData condition) : base(state)
|
||||
{
|
||||
this.controller = controller;
|
||||
this.condition = condition;
|
||||
|
||||
showBorder = true;
|
||||
showAlternatingRowBackgrounds = true;
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ea5fdc77b6cf3dd4ea75f74a03b069ec
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,83 @@
|
||||
using UnityEditor;
|
||||
using UnityEditor.IMGUI.Controls;
|
||||
using UnityEngine;
|
||||
|
||||
namespace XFFSM
|
||||
{
|
||||
public class FSMSelectParamWindow : PopupWindowContent
|
||||
{
|
||||
|
||||
#region 字段
|
||||
|
||||
private float width;
|
||||
private FSMConditionData condition;
|
||||
private RuntimeFSMController controller;
|
||||
|
||||
// 搜索框
|
||||
private SearchField searchField;
|
||||
private Rect searchRect;
|
||||
const float searchHeight = 25f;
|
||||
|
||||
// 标签
|
||||
private Rect labelRect;
|
||||
const float labelHeight = 30f;
|
||||
|
||||
// 参数列表
|
||||
private FSMParamListTree paramTree;
|
||||
private TreeViewState paramState;
|
||||
private Rect paramRect;
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
public FSMSelectParamWindow(float width,FSMConditionData condition,RuntimeFSMController controller)
|
||||
{
|
||||
this.width = width;
|
||||
this.condition = condition;
|
||||
this.controller = controller;
|
||||
}
|
||||
|
||||
|
||||
public override Vector2 GetWindowSize()
|
||||
{
|
||||
return new Vector2(this.width,120);
|
||||
}
|
||||
|
||||
public override void OnGUI(Rect rect)
|
||||
{
|
||||
|
||||
if (paramTree == null)
|
||||
{
|
||||
if (paramState == null)
|
||||
{
|
||||
paramState = new TreeViewState();
|
||||
}
|
||||
|
||||
paramTree = new FSMParamListTree(paramState, controller, condition);
|
||||
paramTree.Reload();
|
||||
}
|
||||
|
||||
// 搜索框
|
||||
if (searchField == null) {
|
||||
searchField = new SearchField();
|
||||
}
|
||||
searchRect.Set(rect.x + 5, rect.y + 5, rect.width - 10, searchHeight);
|
||||
paramTree.searchString = searchField.OnGUI(searchRect, paramTree.searchString);
|
||||
|
||||
// 标签
|
||||
labelRect.Set(rect.x, rect.y + searchHeight, rect.width, labelHeight);
|
||||
EditorGUI.LabelField(labelRect, condition.parameterName, GUI.skin.GetStyle("AC BoldHeader"));
|
||||
|
||||
// 参数列表
|
||||
|
||||
|
||||
paramRect.Set(rect.x, rect.y + searchHeight + labelHeight - 5, rect.width, rect.height - searchHeight - labelHeight);
|
||||
paramTree.OnGUI(paramRect);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ae14d1697e621a64bb729306e2d34ab6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,226 @@
|
||||
using UnityEditor;
|
||||
using UnityEditor.IMGUI.Controls;
|
||||
using UnityEngine;
|
||||
|
||||
namespace XFFSM
|
||||
{
|
||||
|
||||
|
||||
|
||||
public class FSMSelectStateWindow : PopupWindowContent
|
||||
{
|
||||
|
||||
|
||||
// Fix编码
|
||||
#region 字段
|
||||
|
||||
private RuntimeFSMController controller;
|
||||
|
||||
// 搜索框
|
||||
private SearchField searchField;
|
||||
private Rect searchRect;
|
||||
const float searchHeight = 25f;
|
||||
|
||||
// 标签
|
||||
private Rect labelRect;
|
||||
const float labelHeight = 30f;
|
||||
|
||||
// 参数列表
|
||||
private FSMStateListTree stateTree;
|
||||
private TreeViewState stateState;
|
||||
private Rect stateRect;
|
||||
|
||||
private Rect rect;
|
||||
|
||||
private FSMStateNodeData nodeData;
|
||||
|
||||
private bool showCreateScriptGUI = false;
|
||||
private string scriptName = string.Empty;
|
||||
private string scriptName2;
|
||||
#endregion
|
||||
|
||||
|
||||
public FSMSelectStateWindow(Rect rect,RuntimeFSMController controller,FSMStateNodeData nodeData)
|
||||
{
|
||||
//this.width = width;
|
||||
this.controller = controller;
|
||||
this.rect = rect;
|
||||
this.nodeData = nodeData;
|
||||
this.showCreateScriptGUI = false;
|
||||
|
||||
EditorApplication.update -= Update;
|
||||
EditorApplication.update += Update;
|
||||
}
|
||||
|
||||
|
||||
public override Vector2 GetWindowSize()
|
||||
{
|
||||
return new Vector2(this.rect.width, this.rect.height);
|
||||
}
|
||||
|
||||
public override void OnGUI(Rect rect)
|
||||
{
|
||||
if (!showCreateScriptGUI)
|
||||
{
|
||||
OnGUISearchScripts(rect);
|
||||
}
|
||||
else {
|
||||
OnGUICreateScripts(rect);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void OnGUISearchScripts(Rect rect)
|
||||
{
|
||||
if (stateTree == null)
|
||||
{
|
||||
if (stateState == null)
|
||||
{
|
||||
stateState = new TreeViewState();
|
||||
}
|
||||
|
||||
stateTree = new FSMStateListTree(stateState, controller, this.nodeData,editorWindow);
|
||||
stateTree.Reload();
|
||||
}
|
||||
|
||||
// 搜索框
|
||||
if (searchField == null)
|
||||
{
|
||||
searchField = new SearchField();
|
||||
}
|
||||
searchRect.Set(rect.x + 5, rect.y + 5, rect.width - 5, searchHeight);
|
||||
stateTree.searchString = searchField.OnGUI(searchRect, stateTree.searchString);
|
||||
|
||||
// 标签
|
||||
labelRect.Set(rect.x, rect.y + searchHeight, rect.width, labelHeight);
|
||||
EditorGUI.LabelField(labelRect, "FSMStates", GUI.skin.GetStyle("AC BoldHeader"));
|
||||
|
||||
// 参数列表
|
||||
|
||||
stateRect.Set(rect.x, rect.y + searchHeight + labelHeight - 5, rect.width, rect.height - searchHeight - labelHeight - 20);
|
||||
stateTree.OnGUI(stateRect);
|
||||
|
||||
stateRect.Set(rect.x, stateRect.y + stateRect.height, rect.width, 23);
|
||||
if (GUI.Button(stateRect, "New Scripts", "AppToolbarButtonMid")) {
|
||||
showCreateScriptGUI = true;
|
||||
scriptName = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGUICreateScripts(Rect rect) {
|
||||
|
||||
EditorGUI.BeginDisabledGroup(true);
|
||||
|
||||
// 搜索框
|
||||
if (searchField == null)
|
||||
{
|
||||
searchField = new SearchField();
|
||||
}
|
||||
searchRect.Set(rect.x + 5, rect.y + 5, rect.width - 5, searchHeight);
|
||||
stateTree.searchString = searchField.OnGUI(searchRect, stateTree.searchString);
|
||||
|
||||
EditorGUI.EndDisabledGroup();
|
||||
|
||||
// 标签
|
||||
labelRect.Set(rect.x, rect.y + searchHeight, rect.width, labelHeight);
|
||||
|
||||
if (GUI.Button(labelRect, "New Script", "AC BoldHeader"))
|
||||
{
|
||||
showCreateScriptGUI = false;
|
||||
}
|
||||
labelRect.y -= 3;
|
||||
GUI.Label(labelRect, EditorGUIUtility.IconContent("ArrowNavigationLeft"));
|
||||
|
||||
// 参数列表
|
||||
|
||||
stateRect.Set(rect.x, rect.y + searchHeight + labelHeight - 5, rect.width, rect.height - searchHeight - labelHeight - 20);
|
||||
|
||||
GUILayout.BeginArea(stateRect);
|
||||
GUILayout.Space(10);
|
||||
GUILayout.Label("Name");
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
scriptName2 = EditorGUILayout.DelayedTextField(scriptName);
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
scriptName = scriptName2;
|
||||
EditorApplication.delayCall += CreateAndAddScript;
|
||||
}
|
||||
//GUILayout.Label(string.Format("scriptName:{0} scriptName2:{1}", scriptName, scriptName2));
|
||||
GUILayout.EndArea();
|
||||
|
||||
stateRect.Set(rect.x,rect.height - 23, rect.width, 23);
|
||||
if (GUI.Button(stateRect, "Create And Add", "AppToolbarButtonMid"))
|
||||
{
|
||||
OnButtonClick();
|
||||
}
|
||||
|
||||
if (Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Return )
|
||||
{
|
||||
EditorApplication.delayCall += CreateAndAddScript;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void Update() {
|
||||
|
||||
if(!showCreateScriptGUI && editorWindow != null)
|
||||
editorWindow.Repaint();
|
||||
|
||||
if (EditorWindow.focusedWindow != editorWindow)
|
||||
{
|
||||
EditorApplication.update -= Update;
|
||||
}
|
||||
}
|
||||
|
||||
public void Close() {
|
||||
EditorApplication.update -= Update;
|
||||
if (editorWindow == null) {
|
||||
Debug.Log("host view is null!");
|
||||
return;
|
||||
}
|
||||
editorWindow.Close();
|
||||
}
|
||||
|
||||
|
||||
private void CreateAndAddScript() {
|
||||
|
||||
if (EditorWindow.focusedWindow != editorWindow)
|
||||
return;
|
||||
|
||||
if (string.IsNullOrEmpty(scriptName2))
|
||||
return;
|
||||
|
||||
FSMStateGraphView.SavePrefsSelection(this.nodeData.name);
|
||||
|
||||
string dir = System.IO.Path.GetDirectoryName(AssetDatabase.GetAssetPath(controller));
|
||||
string path = string.Format("{0}/{1}.cs", dir, scriptName2);
|
||||
bool isSuccess = FSMStateCreator.CreateFSMState(path, false);
|
||||
|
||||
if (!isSuccess)
|
||||
{
|
||||
GUIContent content = new GUIContent(string.Format("名称:{0}不可用,请修改后重试!", scriptName));
|
||||
this.editorWindow.ShowNotification(content);
|
||||
return;
|
||||
}
|
||||
|
||||
EditorApplication.delayCall += () =>
|
||||
{
|
||||
MonoScript script = AssetDatabase.LoadAssetAtPath<MonoScript>(path);
|
||||
EditorGUIUtility.PingObject(script);
|
||||
this.nodeData.AddStateScript(script);
|
||||
controller.Save();
|
||||
AssetDatabase.SaveAssets();
|
||||
};
|
||||
//Close();
|
||||
}
|
||||
|
||||
|
||||
private void OnButtonClick()
|
||||
{
|
||||
GUI.FocusControl(null);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: da033b5f5b03f8542805a10cd3d73485
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,177 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEditor.IMGUI.Controls;
|
||||
using UnityEngine;
|
||||
|
||||
namespace XFFSM
|
||||
{
|
||||
public class FSMStateTreeViewItem : TreeViewItem
|
||||
{
|
||||
|
||||
public MonoScript MonoScript { get; private set; }
|
||||
|
||||
|
||||
private Type _type = null;
|
||||
|
||||
public Type Type
|
||||
{
|
||||
get
|
||||
{
|
||||
|
||||
if (_type == null)
|
||||
_type = MonoScript.GetClass();
|
||||
return _type;
|
||||
}
|
||||
}
|
||||
|
||||
public FSMStateTreeViewItem(int id, int depth, string displayName, MonoScript script) : base(id, depth, displayName)
|
||||
{
|
||||
MonoScript = script;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
public class FSMStateListTree : TreeView
|
||||
{
|
||||
|
||||
// Fix编码
|
||||
#region 字段
|
||||
|
||||
private RuntimeFSMController controller;
|
||||
//private FSMConditionData condition;
|
||||
private GUIStyle style = new GUIStyle("label");
|
||||
|
||||
private FSMStateNodeData nodeData = null;
|
||||
|
||||
private EditorWindow editorWindow = null;
|
||||
|
||||
private List<int> selections = new List<int>();
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
|
||||
#region 重写方法
|
||||
protected override TreeViewItem BuildRoot()
|
||||
{
|
||||
|
||||
|
||||
|
||||
TreeViewItem root = new TreeViewItem(-1, -1);
|
||||
|
||||
List<MonoScript> scripts = AssemblyTools.GetAllStatesType();
|
||||
|
||||
for (int i = 0; i < scripts.Count; i++)
|
||||
{
|
||||
|
||||
Type type = scripts[i].GetClass();
|
||||
|
||||
if (type == null) continue;
|
||||
string displayName = type.Name;
|
||||
FSMStateTreeViewItem item = new FSMStateTreeViewItem(i, 0, displayName, scripts[i]);
|
||||
root.AddChild(item);
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
protected override IList<TreeViewItem> BuildRows(TreeViewItem root)
|
||||
{
|
||||
return base.BuildRows(root);
|
||||
}
|
||||
|
||||
protected override void SingleClickedItem(int id)
|
||||
{
|
||||
base.SingleClickedItem(id);
|
||||
|
||||
FSMStateTreeViewItem item = FindItem(id, rootItem) as FSMStateTreeViewItem;
|
||||
|
||||
if (item != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
nodeData.AddStateScript(item.MonoScript);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
editorWindow.ShowNotification(new GUIContent(e.Message));
|
||||
return;
|
||||
}
|
||||
}
|
||||
// 保存
|
||||
controller.Save();
|
||||
editorWindow.Close();
|
||||
}
|
||||
|
||||
protected override void RowGUI(RowGUIArgs args)
|
||||
{
|
||||
//base.RowGUI(args);
|
||||
|
||||
FSMStateTreeViewItem item = args.item as FSMStateTreeViewItem;
|
||||
if (item == null) return;
|
||||
Type type = item.Type;
|
||||
if (type == null) return;
|
||||
|
||||
if (args.rowRect.Contains(Event.current.mousePosition))
|
||||
{
|
||||
selections.Clear();
|
||||
selections.Add(item.id);
|
||||
this.SetSelection(selections);
|
||||
}
|
||||
|
||||
|
||||
Rect rect = new Rect();
|
||||
rect.Set(args.rowRect.x + 20,args.rowRect.y,args.rowRect.width - 20, args.rowRect.height);
|
||||
|
||||
GUI.Label(new Rect(0, args.rowRect.y - 2, 20, 20), EditorGUIUtility.IconContent("d_cs Script Icon"));
|
||||
|
||||
if (string.IsNullOrEmpty(type.Namespace)) {
|
||||
GUI.Label(rect, type.Name);
|
||||
}
|
||||
else
|
||||
{
|
||||
style.richText = true;
|
||||
GUI.Label(rect, string.Format("{0}<color=#A4A4A4>({1})</color>", type.Name, type.Namespace), style);
|
||||
}
|
||||
|
||||
//EditorGUIUtility.IconContent("d_cs Script Icon");
|
||||
}
|
||||
|
||||
//protected override void ContextClicked()
|
||||
//{
|
||||
// base.ContextClicked();
|
||||
//}
|
||||
|
||||
protected override void SelectionChanged(IList<int> selectedIds)
|
||||
{
|
||||
base.SelectionChanged(selectedIds);
|
||||
}
|
||||
|
||||
protected override bool CanMultiSelect(TreeViewItem item)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 方法
|
||||
public FSMStateListTree(TreeViewState state, RuntimeFSMController controller, FSMStateNodeData nodeData,EditorWindow editorWindow) : base(state)
|
||||
{
|
||||
this.controller = controller;
|
||||
|
||||
showBorder = true;
|
||||
showAlternatingRowBackgrounds = true;
|
||||
this.nodeData = nodeData;
|
||||
this.editorWindow = editorWindow;
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4507727dfbbf331478ad4841522086f1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 45e08d70a1e332e43a9f9cb144a75845
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,337 @@
|
||||
|
||||
using UnityEditor;
|
||||
using UnityEditor.Graphs;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace XFFSM
|
||||
{
|
||||
|
||||
public class FSMEditorWindow : EditorWindow
|
||||
{
|
||||
|
||||
#region 字段
|
||||
|
||||
private static FSMEditorWindow _instance;
|
||||
|
||||
private float percent_of_param = 0.3f; // 参数所占用的比例
|
||||
|
||||
private VisualElement left = null;
|
||||
private VisualElement middle = null;
|
||||
private VisualElement right = null;
|
||||
|
||||
private Rect paramResizeArea;
|
||||
|
||||
const float ResizeWidth = 5;
|
||||
|
||||
private bool isResizingParamArea = false; // 是否正在调整参数区域
|
||||
|
||||
private Rect stateRect;
|
||||
|
||||
private FSMStateGraphView graphGUI = null;
|
||||
|
||||
private ParamLayer paramLayer = null;
|
||||
|
||||
private IMGUIContainer state_top_container;
|
||||
private IMGUIContainer state_bottom_container;
|
||||
|
||||
private Vector3 splitLineStart;
|
||||
private Vector3 splitLineEnd;
|
||||
|
||||
private string parentLayer;
|
||||
|
||||
#endregion
|
||||
|
||||
// Fix编码
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
try
|
||||
{
|
||||
this.titleContent = new GUIContent("XFFSM", EditorGUIUtility.IconContent("AnimatorController Icon").image);
|
||||
}
|
||||
catch (System.Exception)
|
||||
{
|
||||
this.titleContent = new GUIContent("XFFSM");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void InitGUI()
|
||||
{
|
||||
rootVisualElement.Clear();
|
||||
|
||||
ClearInspetor();
|
||||
|
||||
rootVisualElement.style.flexDirection = FlexDirection.Row;
|
||||
|
||||
left = new VisualElement();
|
||||
|
||||
IMGUIContainer param_container = new IMGUIContainer();
|
||||
param_container.onGUIHandler += () =>
|
||||
{
|
||||
if (paramLayer == null)
|
||||
{
|
||||
paramLayer = new ParamLayer(this);
|
||||
}
|
||||
|
||||
paramLayer.OnGUI(param_container.layout);
|
||||
paramLayer.ProcessEvents();
|
||||
};
|
||||
param_container.StretchToParentSize();
|
||||
left.Add(param_container);
|
||||
|
||||
middle = new VisualElement();
|
||||
right = new VisualElement();
|
||||
|
||||
|
||||
state_top_container = new IMGUIContainer();
|
||||
state_top_container.onGUIHandler += () =>
|
||||
{
|
||||
DoGraphToolbar(state_top_container.layout);
|
||||
};
|
||||
right.Add(state_top_container);
|
||||
|
||||
graphGUI = new FSMStateGraphView();
|
||||
//graphGUI.StretchToParentSize();
|
||||
right.Add(graphGUI);
|
||||
|
||||
state_bottom_container = new IMGUIContainer();
|
||||
state_bottom_container.onGUIHandler += () =>
|
||||
{
|
||||
DrawButtom(state_bottom_container.layout);
|
||||
};
|
||||
right.Add(state_bottom_container);
|
||||
|
||||
rootVisualElement.Add(left);
|
||||
rootVisualElement.Add(middle);
|
||||
rootVisualElement.Add(right);
|
||||
|
||||
|
||||
middle.RegisterCallback<MouseDownEvent>((e) =>
|
||||
{
|
||||
if (e != null && e.button == 0)
|
||||
{
|
||||
isResizingParamArea = true;
|
||||
}
|
||||
});
|
||||
|
||||
rootVisualElement.RegisterCallback<MouseUpEvent>(OnMouseUpEvent);
|
||||
|
||||
}
|
||||
|
||||
private void ClearInspetor()
|
||||
{
|
||||
// 清空当前的Inspector
|
||||
if (Selection.activeObject == null)
|
||||
return;
|
||||
|
||||
if (Selection.activeObject.GetType() == typeof(FSMStateInspectorHelper) ||
|
||||
Selection.activeObject.GetType() == typeof(FSMTransitionInspectorHelper))
|
||||
{
|
||||
Selection.activeObject = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
Repaint();
|
||||
if (graphGUI != null)
|
||||
graphGUI.Update();
|
||||
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
wantsMouseEnterLeaveWindow = true;
|
||||
wantsMouseMove = true;
|
||||
|
||||
stateRect.Set(this.position.width * percent_of_param + 2, 20, this.position.width * (1 - percent_of_param), position.height - 15);
|
||||
if (Event.current.type == EventType.Repaint)
|
||||
Styles.graphBackground.Draw(stateRect, isHover: false, isActive: false, on: false, hasKeyboardFocus: false);
|
||||
|
||||
if (left == null)
|
||||
{
|
||||
InitGUI();
|
||||
}
|
||||
|
||||
ParamAreaResize();
|
||||
|
||||
left.style.width = this.position.width * percent_of_param - ResizeWidth / 2;
|
||||
left.style.height = this.position.height;
|
||||
|
||||
middle.style.width = ResizeWidth;
|
||||
middle.style.height = this.position.height;
|
||||
|
||||
right.style.width = this.position.width * (1 - percent_of_param) - ResizeWidth / 2;
|
||||
right.style.height = this.position.height;
|
||||
|
||||
|
||||
state_top_container.style.width = right.style.width;
|
||||
state_top_container.style.height = 20;
|
||||
|
||||
graphGUI.style.width = right.style.width;
|
||||
graphGUI.style.height = this.position.height - 35;
|
||||
|
||||
|
||||
state_bottom_container.style.width = right.style.width;
|
||||
state_bottom_container.style.height = 15;
|
||||
|
||||
splitLineStart.Set(this.position.width * percent_of_param + 2, 0, 0);
|
||||
splitLineEnd.Set(this.position.width * percent_of_param + 2, this.position.height, 0);
|
||||
Handles.color = Color.black;
|
||||
Handles.DrawAAPolyLine(2, splitLineStart, splitLineEnd);
|
||||
|
||||
|
||||
graphGUI.OnGUI();
|
||||
|
||||
|
||||
// 取消拖拽
|
||||
if (Event.current.type == EventType.MouseLeaveWindow && Event.current.button == 0)
|
||||
{
|
||||
isResizingParamArea = false;
|
||||
}
|
||||
|
||||
|
||||
_instance = this;
|
||||
}
|
||||
|
||||
|
||||
private void ParamAreaResize()
|
||||
{
|
||||
paramResizeArea.Set(this.position.width * percent_of_param - ResizeWidth / 2, 0, ResizeWidth, position.height);
|
||||
|
||||
EditorGUIUtility.AddCursorRect(paramResizeArea, MouseCursor.ResizeHorizontal);
|
||||
|
||||
if (paramResizeArea.Contains(Event.current.mousePosition))
|
||||
{
|
||||
Vector3 start = paramResizeArea.center - new Vector2(-1, paramResizeArea.height / 2);
|
||||
Vector3 end = paramResizeArea.center + new Vector2(1, paramResizeArea.height / 2);
|
||||
Handles.color = isResizingParamArea ? Color.green : Color.white;
|
||||
Handles.DrawLine(start, end);
|
||||
}
|
||||
|
||||
if (isResizingParamArea)
|
||||
{
|
||||
percent_of_param = Mathf.Clamp(Event.current.mousePosition.x / position.width, 0.1f, 0.5f);
|
||||
if (this.position.width * percent_of_param < 150)
|
||||
percent_of_param = 150 / this.position.width;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void DoGraphToolbar(Rect toolbarRect)
|
||||
{
|
||||
Handles.color = new Color(0, 0, 0, 0.4f);
|
||||
Handles.DrawLine(new Vector3(toolbarRect.x, 0), new Vector3(toolbarRect.x + toolbarRect.width, 0));
|
||||
|
||||
GUILayout.BeginArea(toolbarRect);
|
||||
GUILayout.BeginHorizontal(EditorStyles.toolbar);
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
BreadCrumb(0, Context.Instance.Layers.Count + 1, "Base Layer");
|
||||
if (EditorGUI.EndChangeCheck() && Context.Instance.Layers.Count > 0)
|
||||
{
|
||||
Context.Instance.RemoveLayer(0, Context.Instance.Layers.Count);
|
||||
}
|
||||
|
||||
for (int i = 0; i < Context.Instance.Layers.Count; i++)
|
||||
{
|
||||
EditorGUI.BeginChangeCheck();
|
||||
BreadCrumb(i + 1, Context.Instance.Layers.Count + 1, Context.Instance.Layers[i]);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
Context.Instance.RemoveLayer(i + 1, Context.Instance.Layers.Count - 1 - i);
|
||||
}
|
||||
}
|
||||
|
||||
if (parentLayer != Context.Instance.LayerParent && graphGUI != null)
|
||||
{
|
||||
graphGUI.RefreshNodes();
|
||||
parentLayer = Context.Instance.LayerParent;
|
||||
}
|
||||
|
||||
GUILayout.FlexibleSpace();
|
||||
GUILayout.EndHorizontal();
|
||||
GUILayout.EndArea();
|
||||
}
|
||||
|
||||
private static Rect GetBreadcrumbLayoutRect(GUIContent content, GUIStyle style)
|
||||
{
|
||||
Texture image = content.image;
|
||||
content.image = null;
|
||||
Vector2 vector = style.CalcSize(content);
|
||||
content.image = image;
|
||||
if (image != null)
|
||||
{
|
||||
vector.x += vector.y;
|
||||
}
|
||||
|
||||
return GUILayoutUtility.GetRect(content, style, GUILayout.MaxWidth(vector.x));
|
||||
}
|
||||
|
||||
private void BreadCrumb(int index, int maxCount, string name)
|
||||
{
|
||||
GUIStyle style = index == 0 ? "GUIEditor.BreadcrumbLeft" : "GUIEditor.BreadcrumbMid";
|
||||
GUIStyle gUIStyle = ((index == 0) ? "GUIEditor.BreadcrumbLeftBackground" : "GUIEditor.BreadcrumbMidBackground");
|
||||
GUIContent content = new GUIContent(name);
|
||||
Rect breadcrumbLayoutRect = GetBreadcrumbLayoutRect(content, style);
|
||||
if (Event.current.type == EventType.Repaint)
|
||||
{
|
||||
gUIStyle.Draw(breadcrumbLayoutRect, GUIContent.none, 0);
|
||||
}
|
||||
|
||||
GUI.Toggle(breadcrumbLayoutRect, index == maxCount - 1, content, style);
|
||||
}
|
||||
|
||||
|
||||
private void DrawButtom(Rect rect)
|
||||
{
|
||||
rect.Set(0, 0, rect.width, rect.height);
|
||||
GUILayout.BeginArea(rect);
|
||||
GUILayout.BeginHorizontal();
|
||||
if (Context.Instance.FSMController != null)
|
||||
{
|
||||
if (GUILayout.Button(Context.Instance.FSMController.name, "ShurikenLabel"))
|
||||
EditorGUIUtility.PingObject(Context.Instance.FSMController.gameObject);
|
||||
}
|
||||
|
||||
GUILayout.FlexibleSpace();
|
||||
if (GUILayout.Button(AssetDatabase.GetAssetPath(Context.Instance.RuntimeFSMController), "ShurikenLabel"))
|
||||
EditorGUIUtility.PingObject(Context.Instance.RuntimeFSMController);
|
||||
|
||||
GUILayout.EndHorizontal();
|
||||
GUILayout.EndArea();
|
||||
}
|
||||
|
||||
|
||||
private void OnMouseUpEvent(MouseUpEvent e)
|
||||
{
|
||||
if (e != null && e.button == 0)
|
||||
{
|
||||
isResizingParamArea = false;
|
||||
}
|
||||
|
||||
if (graphGUI != null && e.button == 0 )
|
||||
{
|
||||
graphGUI.selector.CancelDrag();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
ClearInspetor();
|
||||
_instance = null;
|
||||
}
|
||||
|
||||
public static void ShowNotification(string message)
|
||||
{
|
||||
if (_instance == null)
|
||||
return;
|
||||
_instance.ShowNotification(new GUIContent(message));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f1501c152d2567346a25199472a3355e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,167 @@
|
||||
using System;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Graphs;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace XFFSM
|
||||
{
|
||||
|
||||
|
||||
|
||||
public class FSMGridBackground : ImmediateModeElement
|
||||
{
|
||||
private VisualElement m_Container;
|
||||
|
||||
private Vector3 point_left_top;
|
||||
private Vector3 point_right_bottom;
|
||||
|
||||
private Rect renderRect;
|
||||
|
||||
private static readonly Color LineColorDark = new Color(0f, 0f, 0f, 0.45f);
|
||||
private static readonly Color LineColorLight = new Color(0f, 0f, 0f, 0.15f);
|
||||
|
||||
private float gridSpace = 20;
|
||||
private float scale = 1;
|
||||
private static Color LineColor
|
||||
{
|
||||
get
|
||||
{
|
||||
if (EditorGUIUtility.isProSkin)
|
||||
{
|
||||
return LineColorDark;
|
||||
}
|
||||
return LineColorLight;
|
||||
}
|
||||
}
|
||||
|
||||
private Rect backgroundRect;
|
||||
|
||||
// Fix编码
|
||||
protected override void ImmediateRepaint()
|
||||
{
|
||||
|
||||
if (Event.current.type != EventType.Repaint)
|
||||
return;
|
||||
|
||||
VisualElement visualElement = base.parent;
|
||||
FSMStateGraphView graphView = visualElement as FSMStateGraphView;
|
||||
if (graphView == null)
|
||||
{
|
||||
throw new InvalidOperationException("GridBackground can only be added to a GraphView");
|
||||
}
|
||||
|
||||
backgroundRect.Set(0, 0, graphView.layout.width, graphView.layout.height);
|
||||
|
||||
Styles.graphBackground.Draw(backgroundRect, isHover: false, isActive: false, on: false, hasKeyboardFocus: false);
|
||||
|
||||
m_Container = graphView.contentViewContainer;
|
||||
|
||||
Matrix4x4 inverse = m_Container.transform.matrix.inverse;
|
||||
Rect clipRect = graphView.layout;
|
||||
|
||||
//Debug.Log(clipRect.ToString());
|
||||
|
||||
point_left_top.Set(clipRect.xMin, 0, 0);
|
||||
point_right_bottom.Set(clipRect.xMax, clipRect.yMax, 0);
|
||||
|
||||
point_left_top = inverse.MultiplyPoint(point_left_top);
|
||||
point_right_bottom = inverse.MultiplyPoint(point_right_bottom);
|
||||
|
||||
float width = Mathf.Abs(point_right_bottom.x - point_left_top.x);
|
||||
float height = Mathf.Abs(point_right_bottom.y - point_left_top.y);
|
||||
|
||||
renderRect.Set(point_left_top.x, point_left_top.y, width, height);
|
||||
|
||||
Color color = Color.white;
|
||||
|
||||
if (graphView.scale < scale)
|
||||
{
|
||||
color.a = Mathf.InverseLerp(scale * 0.1f, scale, graphView.scale);
|
||||
}
|
||||
else
|
||||
{
|
||||
color.a = Mathf.InverseLerp(scale * 10, scale, graphView.scale);
|
||||
}
|
||||
|
||||
DrawGrid(gridSpace, LineColor * color, renderRect);
|
||||
DrawGrid(gridSpace * 10, LineColor, renderRect);
|
||||
|
||||
//GUI.Label(new Rect(0, 0, 300, 100), graphView.scale.ToString());
|
||||
UpdateSpace(graphView.scale);
|
||||
}
|
||||
|
||||
|
||||
public void DrawGrid(float gridSpacing, Color gridColor, Rect rect)
|
||||
{
|
||||
|
||||
if (gridSpacing == 0) return;
|
||||
|
||||
if (Mathf.Abs(gridSpacing) > rect.width || Mathf.Abs(gridSpacing) > rect.height) return;
|
||||
|
||||
Vector2 point_start = new Vector2();
|
||||
Vector2 point_end = new Vector2();
|
||||
|
||||
|
||||
float offset_x = rect.x - (int)(rect.x / gridSpacing) * gridSpacing;
|
||||
float offset_y = rect.y - (int)(rect.y / gridSpacing) * gridSpacing;
|
||||
|
||||
|
||||
for (float i = rect.x - offset_x; i < rect.x + rect.width; i += gridSpacing)
|
||||
{
|
||||
point_start.Set(i, rect.y);
|
||||
point_start = m_Container.transform.matrix.MultiplyPoint(point_start);
|
||||
|
||||
|
||||
point_end.Set(i, rect.y + rect.height);
|
||||
point_end = m_Container.transform.matrix.MultiplyPoint(point_end);
|
||||
DrawLine(point_start, point_end, gridColor);
|
||||
|
||||
int x = (int)(i / gridSpacing);
|
||||
}
|
||||
|
||||
for (float i = rect.y - offset_y; i < rect.y + rect.height; i += gridSpacing)
|
||||
{
|
||||
point_start.Set(rect.x, i);
|
||||
point_start = m_Container.transform.matrix.MultiplyPoint(point_start);
|
||||
|
||||
point_end.Set(rect.x + rect.width, i);
|
||||
point_end = m_Container.transform.matrix.MultiplyPoint(point_end);
|
||||
|
||||
|
||||
DrawLine(point_start, point_end, gridColor);
|
||||
|
||||
int y = (int)(i / gridSpacing);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static void DrawLine(Vector2 start, Vector2 end, Color lineColor)
|
||||
{
|
||||
Handles.color = lineColor;
|
||||
Handles.DrawLine(start, end);
|
||||
}
|
||||
|
||||
private void UpdateSpace(float currentScale)
|
||||
{
|
||||
|
||||
if (currentScale / scale >= 2)
|
||||
{
|
||||
// 放大了10倍
|
||||
gridSpace /= 10;
|
||||
scale *= 10;
|
||||
}
|
||||
else if (currentScale / scale <= 0.2f)
|
||||
{
|
||||
// 缩小了10倍
|
||||
|
||||
gridSpace *= 10;
|
||||
scale *= 0.1f;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9341f9108c6a27042b024b63a7ab63ff
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,295 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Experimental.GraphView;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace XFFSM
|
||||
{
|
||||
|
||||
public class FSMNodeView : Node
|
||||
{
|
||||
|
||||
public FSMStateNodeData Data { get;private set; }
|
||||
|
||||
|
||||
private FSMStateGraphView graphView = null;
|
||||
|
||||
private GUIStyle labelStyle = null;
|
||||
|
||||
private float runingTimer;
|
||||
|
||||
|
||||
public FSMNodeView(FSMStateNodeData data, FSMStateGraphView graphView)
|
||||
{
|
||||
this.Data = data;
|
||||
this.graphView = graphView;
|
||||
Clear();
|
||||
|
||||
VisualElement image = new VisualElement();
|
||||
image.style.width = 200;
|
||||
image.style.height = 40;
|
||||
IMGUIContainer container = new IMGUIContainer();
|
||||
container.onGUIHandler += () =>
|
||||
{
|
||||
|
||||
if (Context.Instance.RuntimeFSMController == null)
|
||||
return;
|
||||
|
||||
if (labelStyle == null)
|
||||
{
|
||||
labelStyle = new GUIStyle(GUI.skin.label);
|
||||
labelStyle.alignment = TextAnchor.MiddleCenter;
|
||||
labelStyle.fontSize = 15;
|
||||
labelStyle.padding.top = -5;
|
||||
}
|
||||
|
||||
GUI.Label(container.layout,string.Empty, GetStateStyle(selected));
|
||||
GUI.Label(container.layout, Data.DisplayName, labelStyle);
|
||||
|
||||
DrawRuning(container.layout);
|
||||
};
|
||||
|
||||
container.StretchToParentSize();
|
||||
|
||||
//image.name = "node-border";
|
||||
image.style.borderRightWidth = 0;
|
||||
image.style.borderLeftWidth = 0;
|
||||
image.style.borderTopWidth = 0;
|
||||
image.style.borderBottomWidth = 0;
|
||||
|
||||
image.Add(container);
|
||||
Add(image);
|
||||
|
||||
RegisterCallback<MouseDownEvent>(OnMouseDownEvent);
|
||||
}
|
||||
|
||||
public override void SetPosition(Rect newPos)
|
||||
{
|
||||
|
||||
if (Vector2.Distance(newPos.position, GetPosition().position) < 10)
|
||||
return;
|
||||
|
||||
// 实际位置
|
||||
newPos.Set((int)(newPos.x / 20) * 20 , (int)(newPos.y / 20) * 20,FSMConst.StateNodeWith, FSMConst.StateNodeHeight);
|
||||
|
||||
// 显示位置
|
||||
base.SetPosition(new Rect(newPos.x - 2,newPos.y - 2 ,newPos.width,newPos.height));
|
||||
|
||||
Data.rect = newPos;
|
||||
if (Context.Instance.RuntimeFSMController != null)
|
||||
Context.Instance.RuntimeFSMController.Save();
|
||||
|
||||
}
|
||||
|
||||
|
||||
public void ShowMenu(ContextualMenuPopulateEvent evt)
|
||||
{
|
||||
|
||||
bool is_any = Data.IsAnyState;
|
||||
bool is_entry = Data.IsEntryState;
|
||||
|
||||
if (!is_entry && !Data.IsUpState)
|
||||
{
|
||||
evt.menu.AppendAction( "Make Transition",(a) =>
|
||||
{
|
||||
graphView.StartMakeTransition(this);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
evt.menu.AppendAction( "Make Transition", null, DropdownMenuAction.Status.Disabled);
|
||||
}
|
||||
|
||||
if (!is_entry && !is_any && !Data.IsUpState)
|
||||
{
|
||||
|
||||
if (Data.defaultState)
|
||||
{
|
||||
evt.menu.AppendAction("Set as Layer Default State",null, DropdownMenuAction.Status.Disabled);
|
||||
}
|
||||
else
|
||||
{
|
||||
evt.menu.AppendAction( "Set as Layer Default State", (a) =>
|
||||
{
|
||||
//SetDefaultState(node);
|
||||
SetDefaultState();
|
||||
graphView.RefreshNodes();
|
||||
});
|
||||
}
|
||||
|
||||
evt.menu.AppendAction( "Delete",(a) =>
|
||||
{
|
||||
DeleteStates();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
private void DeleteStates()
|
||||
{
|
||||
|
||||
RuntimeFSMController controller = Context.Instance.RuntimeFSMController;
|
||||
if (controller == null)
|
||||
return;
|
||||
foreach (var item in graphView.StateNodes.Values)
|
||||
{
|
||||
if (item.selected)
|
||||
FSMStateNodeFactory.DeleteState(controller, item.Data);
|
||||
}
|
||||
|
||||
graphView.RefreshNodes();
|
||||
}
|
||||
|
||||
private void SetDefaultState()
|
||||
{
|
||||
List<FSMStateNodeData> states = Context.Instance.GetCurrentShowStateNodeData();
|
||||
if (states != null)
|
||||
{
|
||||
foreach (var item in states)
|
||||
{
|
||||
item.defaultState = false;
|
||||
}
|
||||
}
|
||||
this.Data.defaultState = true;
|
||||
Context.Instance.RuntimeFSMController.Save();
|
||||
}
|
||||
|
||||
|
||||
private GUIStyle GetStateStyle(bool selected)
|
||||
{
|
||||
if(Data.defaultState)
|
||||
{
|
||||
|
||||
if (Data.isSubStateMachine)
|
||||
return StateStyles.Get(selected ? Style.OrangeOnHEX : Style.OrangeHEX);
|
||||
|
||||
return StateStyles.Get(selected ? Style.OrangeOn : Style.Orange);
|
||||
}
|
||||
else if (Data.IsEntryState)
|
||||
{
|
||||
if (Data.isSubStateMachine)
|
||||
return StateStyles.Get(selected ? Style.GreenOnHEX : Style.GreenHEX);
|
||||
|
||||
return StateStyles.Get(selected ? Style.GreenOn : Style.Green);
|
||||
}
|
||||
else if (Data.IsAnyState)
|
||||
{
|
||||
if (Data.isSubStateMachine)
|
||||
return StateStyles.Get(selected ? Style.MintOnHEX : Style.MintHEX);
|
||||
|
||||
return StateStyles.Get(selected ? Style.MintOn : Style.Mint);
|
||||
}
|
||||
else if (Data.IsUpState)
|
||||
{
|
||||
return StateStyles.Get(selected ? Style.RedOnHEX : Style.RedHEX);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
if (Data.isSubStateMachine)
|
||||
return StateStyles.Get(selected ? Style.NormalOnHEX : Style.NormalHEX);
|
||||
|
||||
return StateStyles.Get(selected ? Style.NormalOn : Style.Normal);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public override void OnSelected()
|
||||
{
|
||||
base.OnSelected();
|
||||
EditorApplication.delayCall -= DelayCallSelection;
|
||||
EditorApplication.delayCall += DelayCallSelection;
|
||||
}
|
||||
|
||||
private void DelayCallSelection() {
|
||||
FSMStateInspectorHelper.Instance.Inspect(Context.Instance.RuntimeFSMController, Data, graphView);
|
||||
}
|
||||
|
||||
public override void OnUnselected()
|
||||
{
|
||||
base.OnUnselected();
|
||||
FSMStateInspectorHelper.Instance.Inspect(Context.Instance.RuntimeFSMController, null, graphView);
|
||||
}
|
||||
|
||||
|
||||
private void DrawRuning(Rect rect)
|
||||
{
|
||||
|
||||
FSMStateNode currentState = GetCurrentState();
|
||||
|
||||
if (currentState == null) return;
|
||||
|
||||
if (Event.current.type != EventType.Repaint || (!(currentState.data.name == Data.name)))
|
||||
return;
|
||||
|
||||
rect.Set(15, 28, rect.width - 30, 20);
|
||||
|
||||
runingTimer += Time.unscaledDeltaTime;
|
||||
if (runingTimer >= 1)
|
||||
runingTimer = 0;
|
||||
|
||||
GUIStyle gUIStyle = "MeLivePlayBackground";
|
||||
GUIStyle gUIStyle2 = "MeLivePlayBar";
|
||||
rect = gUIStyle.margin.Remove(rect);
|
||||
Rect rect2 = gUIStyle.padding.Remove(rect);
|
||||
|
||||
rect2.width *= runingTimer;
|
||||
|
||||
gUIStyle2.Draw(rect2, isHover: false, isActive: false, on: false, hasKeyboardFocus: false);
|
||||
gUIStyle.Draw(rect, isHover: false, isActive: false, on: false, hasKeyboardFocus: false);
|
||||
}
|
||||
|
||||
private FSMStateNode GetCurrentState()
|
||||
{
|
||||
if (!Application.isPlaying)
|
||||
return null;
|
||||
|
||||
if (Context.Instance.FSMController == null) return null;
|
||||
if (Context.Instance.FSMController.RuntimeFSMController == null) return null;
|
||||
if (Context.Instance.RuntimeFSMController == null) return null;
|
||||
|
||||
// 如果在运行时 正在运行的状态是orange
|
||||
int index = Context.Instance.FSMController.RuntimeFSMController.IndexOf(Context.Instance.RuntimeFSMController);
|
||||
FSMStateNode current_state_node = Context.Instance.FSMController.GetCurrentStateInfo(index, Context.Instance.LayerParent);
|
||||
|
||||
return current_state_node;
|
||||
|
||||
}
|
||||
|
||||
|
||||
public void OnMouseDownEvent(MouseDownEvent evt)
|
||||
{
|
||||
if (evt != null && evt.pressedButtons == 1 && evt.clickCount == 1)
|
||||
{
|
||||
|
||||
if (graphView.isMakeTransition)
|
||||
graphView.StopMakeTransition(this);
|
||||
|
||||
//graphView.selector.DragStateNodeStart();
|
||||
DelayCallSelection();
|
||||
}
|
||||
|
||||
if (evt != null && evt.pressedButtons == 1 && evt.clickCount == 2)
|
||||
{
|
||||
if (Data.isSubStateMachine)
|
||||
{
|
||||
Context.Instance.AddLayer(Data.name);
|
||||
graphView.RefreshNodes();
|
||||
}
|
||||
|
||||
if (Data.IsUpState)
|
||||
{
|
||||
Context.Instance.RemoveLast(Data.Parent);
|
||||
graphView.RefreshNodes();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f6dea5420da549a429b796343a68a47b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,206 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor.Graphs;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace XFFSM
|
||||
{
|
||||
public class FSMRectangleSelector : ImmediateModeElement
|
||||
{
|
||||
private Vector3 dragStartPoint;
|
||||
|
||||
public bool Dragging { get; private set; } = false;
|
||||
|
||||
private Vector3 currentMousePosition;
|
||||
|
||||
private VisualElement m_Container;
|
||||
|
||||
private FSMStateGraphView graphView;
|
||||
|
||||
// Fix编码
|
||||
internal static Rect FromToRect(Vector2 start, Vector2 end)
|
||||
{
|
||||
Rect result = new Rect(start.x, start.y, end.x - start.x, end.y - start.y);
|
||||
if (result.width < 0f)
|
||||
{
|
||||
result.x += result.width;
|
||||
result.width = 0f - result.width;
|
||||
}
|
||||
|
||||
if (result.height < 0f)
|
||||
{
|
||||
result.y += result.height;
|
||||
result.height = 0f - result.height;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private Dictionary<string, Vector3> node_postion_offset = new Dictionary<string, Vector3>();
|
||||
|
||||
protected override void ImmediateRepaint()
|
||||
{
|
||||
|
||||
VisualElement visualElement = base.parent;
|
||||
graphView = visualElement as FSMStateGraphView;
|
||||
if (graphView == null)
|
||||
{
|
||||
throw new InvalidOperationException("FSMRectangleSelector can only be added to a GraphView");
|
||||
}
|
||||
|
||||
currentMousePosition = Event.current.mousePosition;
|
||||
if (Event.current.type == EventType.Repaint && Dragging)
|
||||
Styles.selectionRect.Draw(FromToRect(dragStartPoint, Event.current.mousePosition), isHover: false, isActive: false, on: false, hasKeyboardFocus: false);
|
||||
|
||||
m_Container = graphView.contentViewContainer;
|
||||
Matrix4x4 inverse = m_Container.transform.matrix.inverse;
|
||||
|
||||
if (Dragging)
|
||||
{
|
||||
Vector3 start = inverse.MultiplyPoint(dragStartPoint);
|
||||
Vector3 end = inverse.MultiplyPoint(currentMousePosition);
|
||||
Rect dragRect = FromToRect(start, end);
|
||||
|
||||
graphView.graphElements.ForEach((e) =>
|
||||
{
|
||||
//Debug.LogFormat("name:{0} position:{1}", e.name, e.GetPosition());
|
||||
if (dragRect.Overlaps(e.GetPosition()))
|
||||
{
|
||||
graphView.AddToSelection(e);
|
||||
}
|
||||
else {
|
||||
graphView.RemoveFromSelection(e);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
UpdateHoverState();
|
||||
|
||||
}
|
||||
|
||||
|
||||
public void ExecuteAction(EventBase evt)
|
||||
{
|
||||
VisualElement visualElement = base.parent;
|
||||
graphView = visualElement as FSMStateGraphView;
|
||||
if (graphView == null)
|
||||
{
|
||||
throw new InvalidOperationException("FSMRectangleSelector can only be added to a GraphView");
|
||||
}
|
||||
|
||||
MouseDownEvent mouseDownEvent = evt as MouseDownEvent;
|
||||
|
||||
if (mouseDownEvent != null && mouseDownEvent.button == 0 && graphView.HoverNode == null)
|
||||
{
|
||||
dragStartPoint = currentMousePosition;
|
||||
Dragging = true;
|
||||
}
|
||||
|
||||
MouseUpEvent mouseUpEvent = evt as MouseUpEvent;
|
||||
if (mouseUpEvent != null && Event.current.button == 0)
|
||||
{
|
||||
Dragging = false;
|
||||
}
|
||||
|
||||
MouseLeaveWindowEvent windowEvent = evt as MouseLeaveWindowEvent;
|
||||
if (windowEvent != null && windowEvent.button == 0)
|
||||
{
|
||||
Dragging = false;
|
||||
}
|
||||
|
||||
//DragStateNode(evt);
|
||||
|
||||
}
|
||||
|
||||
public void CancelDrag() {
|
||||
Dragging = false;
|
||||
}
|
||||
|
||||
private void UpdateHoverState()
|
||||
{
|
||||
if (graphView == null)
|
||||
return;
|
||||
|
||||
m_Container = graphView.contentViewContainer;
|
||||
Matrix4x4 inverse = m_Container.transform.matrix.inverse;
|
||||
Vector3 point = inverse.MultiplyPoint(currentMousePosition);
|
||||
|
||||
bool isHover = false;
|
||||
|
||||
foreach (var item in graphView.StateNodes.Values)
|
||||
{
|
||||
if (item.GetPosition().Contains(point))
|
||||
{
|
||||
graphView.transition.CloseTransition = null;
|
||||
graphView.HoverNode = item;
|
||||
isHover = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isHover)
|
||||
{
|
||||
graphView.HoverNode = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//internal void DragStateNodeStart()
|
||||
//{
|
||||
// if (graphView == null)
|
||||
// return;
|
||||
// m_Container = graphView.contentViewContainer;
|
||||
// Matrix4x4 inverse = m_Container.transform.matrix.inverse;
|
||||
// Vector3 point = inverse.MultiplyPoint(currentMousePosition);
|
||||
|
||||
// node_postion_offset.Clear();
|
||||
|
||||
// foreach (var item in graphView.StateNodes.Keys)
|
||||
// {
|
||||
// FSMNodeView node = graphView.StateNodes[item];
|
||||
// if (node == null) continue;
|
||||
// node_postion_offset.Add(item, node.GetPosition().center - (Vector2)point);
|
||||
// }
|
||||
//}
|
||||
|
||||
//private void DragStateNode(EventBase evt)
|
||||
//{
|
||||
// if (graphView == null)
|
||||
// return;
|
||||
|
||||
|
||||
// m_Container = graphView.contentViewContainer;
|
||||
// Matrix4x4 inverse = m_Container.transform.matrix.inverse;
|
||||
// Vector3 point = inverse.MultiplyPoint(currentMousePosition);
|
||||
|
||||
// MouseMoveEvent mouseMoveEvent = evt as MouseMoveEvent;
|
||||
// if (mouseMoveEvent != null && mouseMoveEvent.pressedButtons == 1)
|
||||
// {
|
||||
// //Debug.LogFormat("鼠标移动:{0} detal:{1}", mouseMoveEvent.pressedButtons, mouseMoveEvent.mouseDelta);
|
||||
// foreach (var item in graphView.StateNodes.Keys)
|
||||
// {
|
||||
// FSMNodeView node = graphView.StateNodes[item];
|
||||
// if(node == null) continue;
|
||||
// if (!node.selected) continue;
|
||||
// if(!node_postion_offset.ContainsKey(item)) continue;
|
||||
|
||||
// Debug.LogFormat("修改位置:{0}",item);
|
||||
|
||||
// Rect position = node.GetPosition();
|
||||
// position.center = point + node_postion_offset[item];
|
||||
// node.SetPosition(position);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
//internal void DragStateNodeEnd() {
|
||||
// node_postion_offset.Clear();
|
||||
//}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 29d9cd01fd2ffc44d952b1ab2386a7b4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,544 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEditor.Experimental.GraphView;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace XFFSM
|
||||
{
|
||||
public class FSMSelectionDragger : Dragger
|
||||
{
|
||||
private class OriginalPos
|
||||
{
|
||||
public Rect pos;
|
||||
|
||||
public Scope scope;
|
||||
|
||||
public StackNode stack;
|
||||
|
||||
public int stackIndex;
|
||||
|
||||
public bool dragStarted;
|
||||
}
|
||||
|
||||
private IDropTarget m_PrevDropTarget;
|
||||
|
||||
private GraphViewChange m_GraphViewChange;
|
||||
|
||||
private List<GraphElement> m_MovedElements;
|
||||
|
||||
private List<VisualElement> m_DropTargetPickList = new List<VisualElement>();
|
||||
|
||||
private GraphView m_GraphView;
|
||||
|
||||
private Dictionary<GraphElement, OriginalPos> m_OriginalPos;
|
||||
|
||||
private Vector2 m_originalMouse;
|
||||
|
||||
internal const int k_PanAreaWidth = 100;
|
||||
|
||||
internal const int k_PanSpeed = 4;
|
||||
|
||||
internal const int k_PanInterval = 10;
|
||||
|
||||
internal const float k_MinSpeedFactor = 0.5f;
|
||||
|
||||
internal const float k_MaxSpeedFactor = 2.5f;
|
||||
|
||||
internal const float k_MaxPanSpeed = 10f;
|
||||
|
||||
private IVisualElementScheduledItem m_PanSchedule;
|
||||
|
||||
private Vector3 m_PanDiff = Vector3.zero;
|
||||
|
||||
private Vector3 m_ItemPanDiff = Vector3.zero;
|
||||
|
||||
private Vector2 m_MouseDiff = Vector2.zero;
|
||||
|
||||
private GraphElement selectedElement { get; set; }
|
||||
|
||||
private GraphElement clickedElement { get; set; }
|
||||
|
||||
private IDropTarget GetDropTargetAt(Vector2 mousePosition, IEnumerable<VisualElement> exclusionList)
|
||||
{
|
||||
List<VisualElement> dropTargetPickList = m_DropTargetPickList;
|
||||
dropTargetPickList.Clear();
|
||||
base.target.panel.PickAll(mousePosition, dropTargetPickList);
|
||||
IDropTarget dropTarget = null;
|
||||
for (int i = 0; i < dropTargetPickList.Count; i++)
|
||||
{
|
||||
if (dropTargetPickList[i] == base.target && base.target != m_GraphView)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
VisualElement visualElement = dropTargetPickList[i];
|
||||
dropTarget = visualElement as IDropTarget;
|
||||
if (dropTarget != null)
|
||||
{
|
||||
if (!exclusionList.Contains(visualElement))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
dropTarget = null;
|
||||
}
|
||||
}
|
||||
|
||||
return dropTarget;
|
||||
}
|
||||
|
||||
//
|
||||
// 摘要:
|
||||
// SelectionDragger's constructor.
|
||||
public FSMSelectionDragger()
|
||||
{
|
||||
base.activators.Add(new ManipulatorActivationFilter
|
||||
{
|
||||
button = MouseButton.LeftMouse
|
||||
});
|
||||
base.activators.Add(new ManipulatorActivationFilter
|
||||
{
|
||||
button = MouseButton.LeftMouse,
|
||||
modifiers = EventModifiers.Shift
|
||||
});
|
||||
if (Application.platform == RuntimePlatform.OSXEditor || Application.platform == RuntimePlatform.OSXPlayer)
|
||||
{
|
||||
base.activators.Add(new ManipulatorActivationFilter
|
||||
{
|
||||
button = MouseButton.LeftMouse,
|
||||
modifiers = EventModifiers.Command
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
base.activators.Add(new ManipulatorActivationFilter
|
||||
{
|
||||
button = MouseButton.LeftMouse,
|
||||
modifiers = EventModifiers.Control
|
||||
});
|
||||
}
|
||||
|
||||
base.panSpeed = new Vector2(1f, 1f);
|
||||
base.clampToParentEdges = false;
|
||||
m_MovedElements = new List<GraphElement>();
|
||||
m_GraphViewChange.movedElements = m_MovedElements;
|
||||
}
|
||||
|
||||
//
|
||||
// 摘要:
|
||||
// Called to register click event callbacks on the target element.
|
||||
protected override void RegisterCallbacksOnTarget()
|
||||
{
|
||||
ISelection selection = base.target as ISelection;
|
||||
if (selection == null)
|
||||
{
|
||||
throw new InvalidOperationException("Manipulator can only be added to a control that supports selection");
|
||||
}
|
||||
|
||||
base.target.RegisterCallback<MouseDownEvent>(OnMouseDown);
|
||||
base.target.RegisterCallback<MouseMoveEvent>(OnMouseMove);
|
||||
base.target.RegisterCallback<MouseUpEvent>(OnMouseUp);
|
||||
base.target.RegisterCallback<KeyDownEvent>(OnKeyDown);
|
||||
base.target.RegisterCallback<MouseCaptureOutEvent>(OnMouseCaptureOutEvent);
|
||||
}
|
||||
|
||||
//
|
||||
// 摘要:
|
||||
// Called to unregister event callbacks from the target element.
|
||||
protected override void UnregisterCallbacksFromTarget()
|
||||
{
|
||||
base.target.UnregisterCallback<MouseDownEvent>(OnMouseDown);
|
||||
base.target.UnregisterCallback<MouseMoveEvent>(OnMouseMove);
|
||||
base.target.UnregisterCallback<MouseUpEvent>(OnMouseUp);
|
||||
base.target.UnregisterCallback<KeyDownEvent>(OnKeyDown);
|
||||
base.target.UnregisterCallback<MouseCaptureOutEvent>(OnMouseCaptureOutEvent);
|
||||
}
|
||||
|
||||
private static void SendDragAndDropEvent(IDragAndDropEvent evt, List<ISelectable> selection, IDropTarget dropTarget, ISelection dragSource)
|
||||
{
|
||||
if (dropTarget == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
EventBase eventBase = evt as EventBase;
|
||||
if (eventBase.eventTypeId == EventBase<DragExitedEvent>.TypeId())
|
||||
{
|
||||
dropTarget.DragExited();
|
||||
}
|
||||
else if (eventBase.eventTypeId == EventBase<DragEnterEvent>.TypeId())
|
||||
{
|
||||
dropTarget.DragEnter(evt as DragEnterEvent, selection, dropTarget, dragSource);
|
||||
}
|
||||
else if (eventBase.eventTypeId == EventBase<DragLeaveEvent>.TypeId())
|
||||
{
|
||||
dropTarget.DragLeave(evt as DragLeaveEvent, selection, dropTarget, dragSource);
|
||||
}
|
||||
|
||||
if (dropTarget.CanAcceptDrop(selection))
|
||||
{
|
||||
if (eventBase.eventTypeId == EventBase<DragPerformEvent>.TypeId())
|
||||
{
|
||||
dropTarget.DragPerform(evt as DragPerformEvent, selection, dropTarget, dragSource);
|
||||
}
|
||||
else if (eventBase.eventTypeId == EventBase<DragUpdatedEvent>.TypeId())
|
||||
{
|
||||
dropTarget.DragUpdated(evt as DragUpdatedEvent, selection, dropTarget, dragSource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMouseCaptureOutEvent(MouseCaptureOutEvent e)
|
||||
{
|
||||
if (m_Active)
|
||||
{
|
||||
if (m_PrevDropTarget != null && m_GraphView != null && m_PrevDropTarget.CanAcceptDrop(m_GraphView.selection))
|
||||
{
|
||||
m_PrevDropTarget.DragExited();
|
||||
}
|
||||
|
||||
selectedElement = null;
|
||||
m_PrevDropTarget = null;
|
||||
m_Active = false;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// 摘要:
|
||||
// Called on mouse down event.
|
||||
//
|
||||
// 参数:
|
||||
// e:
|
||||
// The event.
|
||||
protected new void OnMouseDown(MouseDownEvent e)
|
||||
{
|
||||
if (m_Active)
|
||||
{
|
||||
e.StopImmediatePropagation();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!CanStartManipulation(e))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_GraphView = base.target as GraphView;
|
||||
if (m_GraphView == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
selectedElement = null;
|
||||
clickedElement = e.target as GraphElement;
|
||||
if (clickedElement == null)
|
||||
{
|
||||
VisualElement visualElement = e.target as VisualElement;
|
||||
clickedElement = visualElement.GetFirstAncestorOfType<GraphElement>();
|
||||
if (clickedElement == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!clickedElement.IsMovable() || !clickedElement.HitTest(clickedElement.WorldToLocal(e.mousePosition)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_GraphView.selection.Contains(clickedElement))
|
||||
{
|
||||
e.StopImmediatePropagation();
|
||||
return;
|
||||
}
|
||||
|
||||
selectedElement = clickedElement;
|
||||
m_OriginalPos = new Dictionary<GraphElement, OriginalPos>();
|
||||
foreach (ISelectable item in m_GraphView.selection)
|
||||
{
|
||||
GraphElement graphElement = item as GraphElement;
|
||||
if (graphElement == null || !graphElement.IsMovable())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
StackNode stackNode = null;
|
||||
if (graphElement.parent is StackNode)
|
||||
{
|
||||
stackNode = graphElement.parent as StackNode;
|
||||
if (stackNode.IsSelected(m_GraphView))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
Rect position = graphElement.GetPosition();
|
||||
Rect pos = graphElement.hierarchy.parent.ChangeCoordinatesTo(m_GraphView.contentViewContainer, position);
|
||||
m_OriginalPos[graphElement] = new OriginalPos
|
||||
{
|
||||
pos = pos,
|
||||
scope = graphElement.GetContainingScope(),
|
||||
stack = stackNode,
|
||||
stackIndex = (stackNode?.IndexOf(graphElement) ?? (-1))
|
||||
};
|
||||
}
|
||||
|
||||
m_originalMouse = e.mousePosition;
|
||||
m_ItemPanDiff = Vector3.zero;
|
||||
if (m_PanSchedule == null)
|
||||
{
|
||||
m_PanSchedule = m_GraphView.schedule.Execute(Pan).Every(10L).StartingIn(10L);
|
||||
m_PanSchedule.Pause();
|
||||
}
|
||||
|
||||
m_Active = true;
|
||||
base.target.CaptureMouse();
|
||||
e.StopImmediatePropagation();
|
||||
}
|
||||
}
|
||||
|
||||
internal Vector2 GetEffectivePanSpeed(Vector2 mousePos)
|
||||
{
|
||||
Vector2 vector = Vector2.zero;
|
||||
if (mousePos.x <= 100f)
|
||||
{
|
||||
vector.x = (0f - ((100f - mousePos.x) / 100f + 0.5f)) * 4f;
|
||||
}
|
||||
else if (mousePos.x >= m_GraphView.contentContainer.layout.width - 100f)
|
||||
{
|
||||
vector.x = ((mousePos.x - (m_GraphView.contentContainer.layout.width - 100f)) / 100f + 0.5f) * 4f;
|
||||
}
|
||||
|
||||
if (mousePos.y <= 100f)
|
||||
{
|
||||
vector.y = (0f - ((100f - mousePos.y) / 100f + 0.5f)) * 4f;
|
||||
}
|
||||
else if (mousePos.y >= m_GraphView.contentContainer.layout.height - 100f)
|
||||
{
|
||||
vector.y = ((mousePos.y - (m_GraphView.contentContainer.layout.height - 100f)) / 100f + 0.5f) * 4f;
|
||||
}
|
||||
|
||||
vector = Vector2.ClampMagnitude(vector, 10f);
|
||||
return vector;
|
||||
}
|
||||
|
||||
//
|
||||
// 摘要:
|
||||
// Called on mouse move event.
|
||||
//
|
||||
// 参数:
|
||||
// e:
|
||||
// The event.
|
||||
protected new void OnMouseMove(MouseMoveEvent e)
|
||||
{
|
||||
if (!m_Active || m_GraphView == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
VisualElement src = (VisualElement)e.target;
|
||||
Vector2 mousePos = src.ChangeCoordinatesTo(m_GraphView.contentContainer, e.localMousePosition);
|
||||
m_PanDiff = GetEffectivePanSpeed(mousePos);
|
||||
if (m_PanDiff != Vector3.zero)
|
||||
{
|
||||
m_PanSchedule.Resume();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_PanSchedule.Pause();
|
||||
}
|
||||
|
||||
m_MouseDiff = m_originalMouse - e.mousePosition;
|
||||
Dictionary<Group, List<GraphElement>> dictionary = (e.shiftKey ? new Dictionary<Group, List<GraphElement>>() : null);
|
||||
foreach (KeyValuePair<GraphElement, OriginalPos> originalPo in m_OriginalPos)
|
||||
{
|
||||
GraphElement key = originalPo.Key;
|
||||
if (key.hierarchy.parent == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!originalPo.Value.dragStarted)
|
||||
{
|
||||
key.GetFirstAncestorOfType<StackNode>()?.OnStartDragging(key);
|
||||
if (dictionary != null)
|
||||
{
|
||||
Group group = key.GetContainingScope() as Group;
|
||||
if (group != null)
|
||||
{
|
||||
if (!dictionary.ContainsKey(group))
|
||||
{
|
||||
dictionary[group] = new List<GraphElement>();
|
||||
}
|
||||
|
||||
dictionary[group].Add(key);
|
||||
}
|
||||
}
|
||||
|
||||
originalPo.Value.dragStarted = true;
|
||||
}
|
||||
|
||||
MoveElement(key, originalPo.Value.pos);
|
||||
}
|
||||
|
||||
List<ISelectable> selection = m_GraphView.selection;
|
||||
IDropTarget dropTargetAt = GetDropTargetAt(e.mousePosition, selection.OfType<VisualElement>());
|
||||
|
||||
m_PrevDropTarget = dropTargetAt;
|
||||
selectedElement = null;
|
||||
e.StopPropagation();
|
||||
}
|
||||
|
||||
private void Pan(TimerState ts)
|
||||
{
|
||||
//m_GraphView.viewTransform.position -= m_PanDiff;
|
||||
//m_ItemPanDiff += m_PanDiff;
|
||||
//foreach (KeyValuePair<GraphElement, OriginalPos> originalPo in m_OriginalPos)
|
||||
//{
|
||||
// MoveElement(originalPo.Key, originalPo.Value.pos);
|
||||
//}
|
||||
}
|
||||
|
||||
private void MoveElement(GraphElement element, Rect originalPos)
|
||||
{
|
||||
Matrix4x4 worldTransform = element.worldTransform;
|
||||
Vector3 vector = new Vector3(worldTransform.m00, worldTransform.m11, worldTransform.m22);
|
||||
Rect rect = new Rect(0f, 0f, originalPos.width, originalPos.height);
|
||||
rect.x = originalPos.x - (m_MouseDiff.x - m_ItemPanDiff.x) * base.panSpeed.x / vector.x * element.transform.scale.x;
|
||||
rect.y = originalPos.y - (m_MouseDiff.y - m_ItemPanDiff.y) * base.panSpeed.y / vector.y * element.transform.scale.y;
|
||||
element.SetPosition(m_GraphView.contentViewContainer.ChangeCoordinatesTo(element.hierarchy.parent, rect));
|
||||
}
|
||||
|
||||
//
|
||||
// 摘要:
|
||||
// Called on mouse up event.
|
||||
//
|
||||
// 参数:
|
||||
// e:
|
||||
// The event.
|
||||
//
|
||||
// evt:
|
||||
protected new void OnMouseUp(MouseUpEvent evt)
|
||||
{
|
||||
if (m_GraphView == null)
|
||||
{
|
||||
if (m_Active)
|
||||
{
|
||||
base.target.ReleaseMouse();
|
||||
selectedElement = null;
|
||||
m_Active = false;
|
||||
m_PrevDropTarget = null;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
List<ISelectable> selection = m_GraphView.selection;
|
||||
if (!CanStopManipulation(evt))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_Active)
|
||||
{
|
||||
if (selectedElement == null)
|
||||
{
|
||||
m_MovedElements.Clear();
|
||||
foreach (IGrouping<StackNode, GraphElement> item in from v in m_OriginalPos
|
||||
group v.Key by v.Value.stack)
|
||||
{
|
||||
if (item.Key != null && m_GraphView.elementsRemovedFromStackNode != null)
|
||||
{
|
||||
m_GraphView.elementsRemovedFromStackNode(item.Key, item);
|
||||
}
|
||||
|
||||
foreach (GraphElement item2 in item)
|
||||
{
|
||||
item2.UpdatePresenterPosition();
|
||||
}
|
||||
|
||||
m_MovedElements.AddRange(item);
|
||||
}
|
||||
|
||||
GraphView graphView = base.target as GraphView;
|
||||
if (graphView != null && graphView.graphViewChanged != null)
|
||||
{
|
||||
KeyValuePair<GraphElement, OriginalPos> keyValuePair = m_OriginalPos.First();
|
||||
m_GraphViewChange.moveDelta = keyValuePair.Key.GetPosition().position - keyValuePair.Value.pos.position;
|
||||
graphView.graphViewChanged(m_GraphViewChange);
|
||||
}
|
||||
}
|
||||
|
||||
m_PanSchedule.Pause();
|
||||
if (m_ItemPanDiff != Vector3.zero)
|
||||
{
|
||||
Vector3 position = m_GraphView.contentViewContainer.transform.position;
|
||||
Vector3 scale = m_GraphView.contentViewContainer.transform.scale;
|
||||
m_GraphView.UpdateViewTransform(position, scale);
|
||||
}
|
||||
|
||||
base.target.ReleaseMouse();
|
||||
evt.StopPropagation();
|
||||
}
|
||||
|
||||
selectedElement = null;
|
||||
m_Active = false;
|
||||
m_PrevDropTarget = null;
|
||||
}
|
||||
|
||||
private void OnKeyDown(KeyDownEvent e)
|
||||
{
|
||||
if (e.keyCode != KeyCode.Escape || m_GraphView == null || !m_Active)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Dictionary<Scope, List<GraphElement>> dictionary = new Dictionary<Scope, List<GraphElement>>();
|
||||
foreach (KeyValuePair<GraphElement, OriginalPos> originalPo in m_OriginalPos)
|
||||
{
|
||||
OriginalPos value = originalPo.Value;
|
||||
if (value.stack != null)
|
||||
{
|
||||
value.stack.InsertElement(value.stackIndex, originalPo.Key);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (value.scope != null)
|
||||
{
|
||||
if (!dictionary.ContainsKey(value.scope))
|
||||
{
|
||||
dictionary[value.scope] = new List<GraphElement>();
|
||||
}
|
||||
|
||||
dictionary[value.scope].Add(originalPo.Key);
|
||||
}
|
||||
|
||||
originalPo.Key.SetPosition(value.pos);
|
||||
}
|
||||
|
||||
foreach (KeyValuePair<Scope, List<GraphElement>> item in dictionary)
|
||||
{
|
||||
item.Key.AddElements(item.Value);
|
||||
}
|
||||
|
||||
m_PanSchedule.Pause();
|
||||
if (m_ItemPanDiff != Vector3.zero)
|
||||
{
|
||||
Vector3 position = m_GraphView.contentViewContainer.transform.position;
|
||||
Vector3 scale = m_GraphView.contentViewContainer.transform.scale;
|
||||
m_GraphView.UpdateViewTransform(position, scale);
|
||||
}
|
||||
|
||||
using (DragExitedEvent evt = EventBase<DragExitedEvent>.GetPooled())
|
||||
{
|
||||
List<ISelectable> selection = m_GraphView.selection;
|
||||
SendDragAndDropEvent(evt, selection, m_PrevDropTarget, m_GraphView);
|
||||
}
|
||||
|
||||
base.target.ReleaseMouse();
|
||||
e.StopPropagation();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b32807648262cc344b24a40fc99db178
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,379 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Experimental.GraphView;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace XFFSM
|
||||
{
|
||||
public class FSMStateGraphView : GraphView
|
||||
{
|
||||
|
||||
public Dictionary<string, FSMNodeView> StateNodes = new Dictionary<string, FSMNodeView>();
|
||||
|
||||
internal FSMTransitionBackground transition = null;
|
||||
|
||||
internal FSMRectangleSelector selector;
|
||||
|
||||
public bool isMakeTransition = false;
|
||||
|
||||
public FSMNodeView MakeTransitionFrom = null;
|
||||
|
||||
public FSMNodeView HoverNode = null;
|
||||
|
||||
private RuntimeFSMController controller = null;
|
||||
|
||||
//private FSMNodeView selectNode = null;
|
||||
|
||||
public FSMStateGraphView()
|
||||
{
|
||||
FSMGridBackground background = new FSMGridBackground();
|
||||
Insert(0, background);
|
||||
|
||||
transition = new FSMTransitionBackground();
|
||||
Insert(1, transition);
|
||||
|
||||
selector = new FSMRectangleSelector();
|
||||
Insert(3, selector);
|
||||
|
||||
RegisterCallback<KeyDownEvent>(KeyDownControl);
|
||||
RegisterCallback<MouseDownEvent>(MouseDownControl);
|
||||
|
||||
this.AddManipulator(new ContentDragger());
|
||||
this.AddManipulator(new FSMSelectionDragger());
|
||||
ContentZoomer zoomer = new ContentZoomer();
|
||||
zoomer.minScale = 0.1f;
|
||||
zoomer.maxScale = 3;
|
||||
|
||||
this.AddManipulator(zoomer);
|
||||
|
||||
RefreshView();
|
||||
}
|
||||
|
||||
private void RefreshView()
|
||||
{
|
||||
if (Context.Instance.RuntimeFSMController != null)
|
||||
{
|
||||
// 默认位置和缩放
|
||||
viewTransform.position = Context.Instance.RuntimeFSMController.viewPosition;
|
||||
viewTransform.scale = Context.Instance.RuntimeFSMController.viewScale;
|
||||
}
|
||||
controller = Context.Instance.RuntimeFSMController;
|
||||
RefreshNodes();
|
||||
}
|
||||
|
||||
private void SaveScaleAndPosition() {
|
||||
|
||||
if (viewTransform == null || Context.Instance.RuntimeFSMController == null)
|
||||
return;
|
||||
|
||||
|
||||
if (Context.Instance.RuntimeFSMController.viewScale != viewTransform.scale)
|
||||
{
|
||||
Context.Instance.RuntimeFSMController.viewScale = viewTransform.scale;
|
||||
Context.Instance.RuntimeFSMController.Save();
|
||||
}
|
||||
|
||||
|
||||
if (Context.Instance.RuntimeFSMController.viewPosition != viewTransform.position)
|
||||
{
|
||||
Context.Instance.RuntimeFSMController.viewPosition = viewTransform.position;
|
||||
Context.Instance.RuntimeFSMController.Save();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
if (controller != Context.Instance.RuntimeFSMController)
|
||||
{
|
||||
RefreshView();
|
||||
controller = Context.Instance.RuntimeFSMController;
|
||||
}
|
||||
|
||||
SaveScaleAndPosition();
|
||||
}
|
||||
|
||||
public void RefreshNodes()
|
||||
{
|
||||
|
||||
foreach (var item in StateNodes.Values)
|
||||
{
|
||||
RemoveElement(item);
|
||||
}
|
||||
|
||||
StateNodes.Clear();
|
||||
|
||||
List<FSMStateNodeData> datas = Context.Instance.GetCurrentShowStateNodeData();
|
||||
|
||||
if (datas == null || datas.Count == 0)
|
||||
return;
|
||||
|
||||
//selectNode = null;
|
||||
|
||||
foreach (var item in datas)
|
||||
{
|
||||
FSMNodeView node = new FSMNodeView(item,this);
|
||||
node.title = item.DisplayName;
|
||||
node.SetPosition(item.rect);
|
||||
this.AddElement(node);
|
||||
StateNodes.Add(item.name, node);
|
||||
|
||||
if (GetPrefsSelection(item.name))
|
||||
{
|
||||
AddToSelection(node);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public void RenameState(string oldName,string newName)
|
||||
{
|
||||
//Debug.LogFormat("重命名:old:{0} new:{1} cn:{2} co:{3}",oldName,newName, StateNodes.ContainsKey(newName), StateNodes.ContainsKey(oldName));
|
||||
if (StateNodes.ContainsKey(newName))
|
||||
return;
|
||||
if (!StateNodes.ContainsKey(oldName))
|
||||
return;
|
||||
StateNodes.Add(newName, StateNodes[oldName]);
|
||||
StateNodes.Remove(oldName);
|
||||
}
|
||||
|
||||
|
||||
public FSMNodeView GetNode(string name)
|
||||
{
|
||||
if (string.IsNullOrEmpty(name))
|
||||
return null;
|
||||
|
||||
if (StateNodes.ContainsKey(name))
|
||||
return StateNodes[name];
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private void KeyDownControl(KeyDownEvent evt)
|
||||
{
|
||||
if (evt.keyCode == KeyCode.Delete)
|
||||
{
|
||||
RuntimeFSMController controller = Context.Instance.RuntimeFSMController;
|
||||
|
||||
if (controller == null) return;
|
||||
|
||||
// 删除选中的过渡
|
||||
if (transition.Selection != null && controller != null)
|
||||
FSMTransitionFactory.DeleteTransition(controller, transition.Selection);
|
||||
|
||||
RefreshNodes();
|
||||
}
|
||||
}
|
||||
|
||||
private void MouseDownControl(MouseDownEvent evt)
|
||||
{
|
||||
//BehaviourTreeView.TreeWindow.WindowRoot.InspectorView.UpdateInspector();
|
||||
if (evt.button == 0 && evt.clickCount == 1)
|
||||
{
|
||||
// 左键单击
|
||||
transition.OnTransitionSelection(transition.CloseTransition);
|
||||
StopMakeTransition(null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override void BuildContextualMenu(ContextualMenuPopulateEvent evt)
|
||||
{
|
||||
|
||||
FSMNodeView node = evt.target as FSMNodeView;
|
||||
|
||||
if (node != null)
|
||||
transition.OnTransitionSelection(null);
|
||||
|
||||
|
||||
int count = evt.menu.MenuItems().Count;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
evt.menu.RemoveItemAt(0);
|
||||
}
|
||||
|
||||
RuntimeFSMController controller = Context.Instance.RuntimeFSMController;
|
||||
|
||||
bool disable = controller == null || EditorApplication.isPlaying;
|
||||
|
||||
DropdownMenuAction.Status status = disable ? DropdownMenuAction.Status.Disabled : DropdownMenuAction.Status.Normal;
|
||||
|
||||
if (node == null)
|
||||
{
|
||||
|
||||
//Debug.LogFormat("鼠标位置:{0}",Event.current.mousePosition);
|
||||
|
||||
evt.menu.AppendAction("Create State", CreateState, (d) => status, Event.current.mousePosition);
|
||||
evt.menu.AppendAction("Create Sub-State Machine", CreateSubStateMachine, (d) => status, Event.current.mousePosition);
|
||||
}
|
||||
else {
|
||||
node.ShowMenu(evt);
|
||||
}
|
||||
|
||||
//base.BuildContextualMenu
|
||||
|
||||
}
|
||||
|
||||
|
||||
public void CreateState(DropdownMenuAction action)
|
||||
{
|
||||
Vector2 mousePosition = (Vector2)(action.userData);
|
||||
mousePosition.Set(mousePosition.x - parent.layout.x, mousePosition.y - layout.y - 20);
|
||||
Vector2 position = contentViewContainer.transform.matrix.inverse.MultiplyPoint(mousePosition);
|
||||
CreateState(false, position);
|
||||
}
|
||||
|
||||
public void CreateSubStateMachine(DropdownMenuAction action) {
|
||||
Vector2 mousePosition = (Vector2)(action.userData);
|
||||
mousePosition.Set(mousePosition.x - parent.layout.x, mousePosition.y - layout.y - 20);
|
||||
Vector2 position = contentViewContainer.transform.matrix.inverse.MultiplyPoint(mousePosition);
|
||||
CreateState(true, position);
|
||||
}
|
||||
|
||||
|
||||
private void CreateState(bool isSubStateMachine, Vector3 mousePosition)
|
||||
{
|
||||
|
||||
Rect rect = new Rect(0, 0, FSMConst.StateNodeWith, FSMConst.StateNodeHeight);
|
||||
|
||||
rect.center = mousePosition;
|
||||
|
||||
List<FSMStateNodeData> states = Context.Instance.GetCurrentShowStateNodeData();
|
||||
|
||||
bool isDefaultState = states.Count == 2; // 这里是为了兼容旧的配置文件
|
||||
|
||||
if (states != null)
|
||||
{
|
||||
bool isAllBuildIn = true;
|
||||
foreach (var item in states)
|
||||
{
|
||||
if (!item.isBuildInState)
|
||||
{
|
||||
isAllBuildIn = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isAllBuildIn) isDefaultState = true;
|
||||
}
|
||||
|
||||
|
||||
RuntimeFSMController controller = Context.Instance.RuntimeFSMController;
|
||||
|
||||
List<string> parents = new List<string>();
|
||||
parents.AddRange(Context.Instance.Layers);
|
||||
|
||||
FSMStateNodeData state = FSMStateNodeFactory.CreateStateNode(controller, rect, isDefaultState, isSubStateMachine, parents);
|
||||
|
||||
if (isSubStateMachine)
|
||||
{
|
||||
// 给当前子状态机创建 Entry 状态
|
||||
Rect r = new Rect(0, 300, FSMConst.StateNodeWith, FSMConst.StateNodeHeight);
|
||||
CreateSubState(controller, FSMConst.entryState, state, parents, r, FSMConst.entryState);
|
||||
// 给当前子状态机创建 Any 状态
|
||||
r = new Rect(0, 100, FSMConst.StateNodeWith, FSMConst.StateNodeHeight);
|
||||
CreateSubState(controller, FSMConst.anyState, state, parents, r, FSMConst.anyState);
|
||||
// 给当前子状态机创建 Entry 状态
|
||||
r = new Rect(600, 300, FSMConst.StateNodeWith, FSMConst.StateNodeHeight);
|
||||
CreateSubState(controller, FSMConst.up, state, parents, r, FSMConst.up);
|
||||
}
|
||||
|
||||
|
||||
RefreshNodes();
|
||||
}
|
||||
|
||||
private void CreateSubState(RuntimeFSMController controller, string name, FSMStateNodeData state, List<string> parents, Rect rect, string buildInName)
|
||||
{
|
||||
// 创建三个内置状态
|
||||
List<string> parents_temp = new List<string>();
|
||||
parents_temp.AddRange(parents);
|
||||
parents_temp.Add(state.name);
|
||||
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
for (int i = 0; i < parents_temp.Count; i++)
|
||||
{
|
||||
stringBuilder.Append(parents_temp[i]);
|
||||
stringBuilder.Append("/");
|
||||
}
|
||||
stringBuilder.Append(name);
|
||||
FSMStateNodeFactory.CreateStateNode(controller, stringBuilder.ToString(), rect, false, false, parents_temp, true, buildInName);
|
||||
}
|
||||
|
||||
|
||||
public void StartMakeTransition(FSMNodeView node)
|
||||
{
|
||||
isMakeTransition = true;
|
||||
MakeTransitionFrom = node;
|
||||
}
|
||||
|
||||
public void StopMakeTransition(FSMNodeView node)
|
||||
{
|
||||
if (node == MakeTransitionFrom)
|
||||
return;
|
||||
|
||||
isMakeTransition = false;
|
||||
HoverNode = null;
|
||||
|
||||
if (node == null)
|
||||
return;
|
||||
|
||||
RuntimeFSMController controller = Context.Instance.RuntimeFSMController;
|
||||
if (controller == null)
|
||||
return;
|
||||
FSMTransitionFactory.CreateTransition(controller, MakeTransitionFrom.Data.name, node.Data.name);
|
||||
}
|
||||
|
||||
public void OnGUI() {
|
||||
|
||||
// 取消拖拽
|
||||
if (Event.current.type == EventType.MouseLeaveWindow && Event.current.button == 0)
|
||||
{
|
||||
selector.CancelDrag();
|
||||
}
|
||||
|
||||
if (Event.current.type == EventType.MouseUp && Event.current.button == 0)
|
||||
{
|
||||
selector.CancelDrag();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
protected override void ExecuteDefaultAction(EventBase evt)
|
||||
{
|
||||
base.ExecuteDefaultAction(evt);
|
||||
selector.ExecuteAction(evt);
|
||||
}
|
||||
|
||||
internal static bool GetPrefsSelection(string name) {
|
||||
string key = PrefsSelectionKey(name);
|
||||
if (string.IsNullOrEmpty(key))
|
||||
return false;
|
||||
bool v = EditorPrefs.GetBool(key, false);
|
||||
if (v)
|
||||
EditorPrefs.DeleteKey(key);
|
||||
return v;
|
||||
}
|
||||
|
||||
internal static void SavePrefsSelection(string name)
|
||||
{
|
||||
string key = PrefsSelectionKey(name);
|
||||
if (string.IsNullOrEmpty(key))
|
||||
return;
|
||||
EditorPrefs.SetBool(key, true);
|
||||
}
|
||||
|
||||
internal static string PrefsSelectionKey(string name)
|
||||
{
|
||||
RuntimeFSMController controller = Context.Instance.RuntimeFSMController;
|
||||
if (controller == null)
|
||||
return string.Empty;
|
||||
|
||||
return string.Format("XFFSM:{0}:{1}",controller.name,name);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3aacc51bef873224aac65702e7227d17
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,392 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Graphs;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace XFFSM
|
||||
{
|
||||
public class FSMTransitionBackground : ImmediateModeElement
|
||||
{
|
||||
private float edgeDistanceMultiplier => Mathf.Clamp(graphView.scale, 0.1f, 1);
|
||||
|
||||
private float edgeSizeMultiplier => Mathf.Clamp( graphView.scale,0.1f,1);
|
||||
|
||||
private static Slot s_TargetDraggingSlot;
|
||||
|
||||
private FSMStateGraphView graphView;
|
||||
|
||||
private static Vector3 edgeToSelfOffsetVector => new Vector3(0f, 30f, 0f);
|
||||
|
||||
private static Color selectedEdgeColor => new Color(0.42f, 0.7f, 1f, 1f);
|
||||
|
||||
public FSMTransitionData CloseTransition { get;internal set; }
|
||||
|
||||
public FSMTransitionData Selection { get; set; }
|
||||
|
||||
private FSMTransitionData entryToDefault = new FSMTransitionData();
|
||||
|
||||
private Vector3[] vectors = new Vector3[2];
|
||||
|
||||
private Vector3[] points = new Vector3[2];
|
||||
|
||||
private static Color defaultTransitionColor => new Color(0.6f, 0.4f, 0f, 1f);
|
||||
|
||||
private static FSMTransitionData current_change_transition;
|
||||
private float current_change_transition_timer = 0;
|
||||
|
||||
#region 退出当前子状态
|
||||
private FSMStateNode currentState;
|
||||
private bool state_exiting = false;
|
||||
private string exit_state_parent;
|
||||
private string exit_state_name;
|
||||
private string exit_state_to_name;
|
||||
private float exit_state_timer = 0;
|
||||
#endregion
|
||||
|
||||
// Fix编码
|
||||
protected override void ImmediateRepaint()
|
||||
{
|
||||
List<FSMTransitionData> transitions = Context.Instance.GetCurrentShowTransitionData();
|
||||
//if (transitions == null || transitions.Count == 0)
|
||||
// return;
|
||||
|
||||
if (Event.current.type == EventType.Repaint)
|
||||
{
|
||||
VisualElement visualElement = base.parent;
|
||||
graphView = visualElement as FSMStateGraphView;
|
||||
if (graphView == null)
|
||||
throw new InvalidOperationException("FSMTransitionBackground can only be added to a GraphView");
|
||||
|
||||
if (transitions != null)
|
||||
{
|
||||
foreach (var transition in transitions)
|
||||
{
|
||||
DrawTransiton(transition,Color.white);
|
||||
DrawCurrentTransition(transition);
|
||||
}
|
||||
}
|
||||
|
||||
List<FSMStateNodeData> nodes = Context.Instance.GetCurrentShowStateNodeData();
|
||||
if (nodes != null)
|
||||
{
|
||||
entryToDefault.fromStateName = string.Empty;
|
||||
entryToDefault.toStateName = string.Empty;
|
||||
|
||||
foreach (var item in nodes)
|
||||
{
|
||||
if (item.IsEntryState)
|
||||
entryToDefault.fromStateName = item.name;
|
||||
if(item.defaultState)
|
||||
entryToDefault.toStateName = item.name;
|
||||
}
|
||||
|
||||
DrawTransiton(entryToDefault, defaultTransitionColor);
|
||||
}
|
||||
|
||||
DrawMakeTransition();
|
||||
|
||||
DrawStateExitTransition();
|
||||
}
|
||||
|
||||
if(graphView.HoverNode == null)
|
||||
CloseTransition = FindClosestTransition();
|
||||
|
||||
}
|
||||
|
||||
private void DrawTransiton(FSMTransitionData data,Color color )
|
||||
{
|
||||
|
||||
FSMNodeView fromNode = graphView.GetNode(data.fromStateName);
|
||||
FSMNodeView toNode = graphView.GetNode(data.toStateName);
|
||||
|
||||
if (fromNode == null || toNode == null)
|
||||
return;
|
||||
|
||||
|
||||
|
||||
if (data == Selection)
|
||||
color = selectedEdgeColor;
|
||||
|
||||
Vector3[] edgePoints = GetTransitionPoints(data );
|
||||
|
||||
DrawTransiton(edgePoints, color, data == CloseTransition, true);
|
||||
}
|
||||
|
||||
private void DrawTransiton(Vector3[] edgePoints ,Color color,bool bold,bool isDrawArrow)
|
||||
{
|
||||
Texture2D tex = (Texture2D)Styles.connectionTexture.image;
|
||||
float arrowSize = 5f * edgeSizeMultiplier;
|
||||
float outlineWidth = 2f * edgeSizeMultiplier;
|
||||
float arrowLength = 13f * edgeSizeMultiplier;
|
||||
Handles.color = color;
|
||||
float width = 10f * edgeSizeMultiplier;
|
||||
|
||||
if (bold)
|
||||
{
|
||||
arrowSize *= 1.5f;
|
||||
width *= 1.5f;
|
||||
}
|
||||
|
||||
Handles.DrawAAPolyLine(tex, width, edgePoints[0], edgePoints[1]);
|
||||
if (isDrawArrow) {
|
||||
Vector3 cross = Vector3.Cross((edgePoints[0] - edgePoints[1]).normalized, Vector3.forward);
|
||||
DrawArrows(color, cross, edgePoints, isSelf: false, arrowSize, outlineWidth, arrowLength);
|
||||
}
|
||||
}
|
||||
|
||||
private Vector3[] GetTransitionPoints(FSMTransitionData data )
|
||||
{
|
||||
return GetTransitionPoints(data.fromStateName, data.toStateName);
|
||||
}
|
||||
|
||||
private Vector3[] GetTransitionPoints(string fromStateName,string toStateName)
|
||||
{
|
||||
FSMNodeView fromNode = graphView.GetNode(fromStateName);
|
||||
FSMNodeView toNode = graphView.GetNode(toStateName);
|
||||
|
||||
if (fromNode == null || toNode == null)
|
||||
return null;
|
||||
|
||||
float num = 5f * edgeDistanceMultiplier;
|
||||
|
||||
points[0] = GetNodeViewPosition(fromNode);
|
||||
points[1] = GetNodeViewPosition(toNode);
|
||||
|
||||
Vector3 cross = Vector3.Cross((points[0] - points[1]).normalized, Vector3.forward);
|
||||
points[0] += cross * num;
|
||||
points[1] += cross * num;
|
||||
return points;
|
||||
}
|
||||
|
||||
private Vector3 GetNodeViewPosition(FSMNodeView node)
|
||||
{
|
||||
return graphView.contentViewContainer.transform.matrix.MultiplyPoint(node.GetPosition().center);
|
||||
}
|
||||
|
||||
private static void DrawArrows(Color color, Vector3 cross, Vector3[] edgePoints, bool isSelf, float arrowSize, float outlineWidth, float arrowLength)
|
||||
{
|
||||
Vector3 vector = edgePoints[1] - edgePoints[0];
|
||||
Vector3 normalized = vector.normalized;
|
||||
Vector3 vector2 = vector * 0.5f + edgePoints[0];
|
||||
vector2 -= cross * 0.5f;
|
||||
float num = Mathf.Min(arrowLength * 1.5f, (vector2 - edgePoints[0]).magnitude) * 0.66f;
|
||||
int num2 = 1;
|
||||
|
||||
for (int i = 0; i < num2; i++)
|
||||
{
|
||||
Color color2 = color;
|
||||
|
||||
Vector3 center = vector2 + (float)((num2 == 1) ? i : (i - 1)) * num * (isSelf ? cross : normalized);
|
||||
DrawArrow(color2, cross, normalized, center, arrowSize, outlineWidth);
|
||||
}
|
||||
}
|
||||
|
||||
private static void DrawArrow(Color color, Vector3 cross, Vector3 direction, Vector3 center, float arrowSize, float outlineWidth)
|
||||
{
|
||||
if (Event.current.type == EventType.Repaint)
|
||||
{
|
||||
Vector3[] array = new Vector3[4];
|
||||
array[0] = center + direction * arrowSize;
|
||||
array[1] = center - direction * arrowSize + cross * arrowSize;
|
||||
array[2] = center - direction * arrowSize - cross * arrowSize;
|
||||
array[3] = array[0];
|
||||
Shader.SetGlobalColor("_HandleColor", color);
|
||||
Handles.color = color;
|
||||
Handles.DrawAAPolyLine((Texture2D)Styles.connectionTexture.image, outlineWidth, array);
|
||||
Handles.DrawAAConvexPolygon(array);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public FSMTransitionData FindClosestTransition()
|
||||
{
|
||||
FSMTransitionData result = null;
|
||||
float num = float.PositiveInfinity;
|
||||
Vector3 vector = Event.current.mousePosition;
|
||||
|
||||
List<FSMTransitionData> transitions = Context.Instance.GetCurrentShowTransitionData();
|
||||
if (transitions == null || transitions.Count == 0)
|
||||
return null;
|
||||
|
||||
foreach (var transition in transitions)
|
||||
{
|
||||
Vector3[] edgePoints = GetTransitionPoints(transition);
|
||||
|
||||
if(edgePoints == null) continue;
|
||||
|
||||
float num2 = float.PositiveInfinity;
|
||||
num2 = ((!(edgePoints[0] == edgePoints[1])) ? HandleUtility.DistancePointLine(vector, edgePoints[0], edgePoints[1]) : Vector3.Distance(edgeToSelfOffsetVector + edgePoints[0], vector));
|
||||
if (num2 < num && num2 < 10f)
|
||||
{
|
||||
num = num2;
|
||||
result = transition;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void OnTransitionSelection(FSMTransitionData transition)
|
||||
{
|
||||
if (this.Selection != null && transition == null)
|
||||
{
|
||||
FSMTransitionInspectorHelper.Instance.Inspect(Context.Instance.RuntimeFSMController, null);
|
||||
}
|
||||
|
||||
this.Selection = transition;
|
||||
//Debug.Log("OnTransitionSelection");
|
||||
|
||||
|
||||
if(Selection != null)
|
||||
{
|
||||
EditorApplication.delayCall -= DelayCallSelection;
|
||||
EditorApplication.delayCall += DelayCallSelection;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void DrawMakeTransition()
|
||||
{
|
||||
if (!graphView.isMakeTransition)
|
||||
return;
|
||||
|
||||
vectors[0] = GetNodeViewPosition(graphView.MakeTransitionFrom);
|
||||
|
||||
if (graphView.HoverNode != null && !graphView.HoverNode.Data.isBuildInState)
|
||||
vectors[1] = GetNodeViewPosition(graphView.HoverNode);
|
||||
else
|
||||
vectors[1] = Event.current.mousePosition;
|
||||
|
||||
DrawTransiton(vectors , Color.white,false, true);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 绘制当前状态切换的过渡
|
||||
/// </summary>
|
||||
private void DrawCurrentTransition(FSMTransitionData transition )
|
||||
{
|
||||
if (Context.Instance.FSMController == null)
|
||||
return;
|
||||
if (Context.Instance.FSMController.RuntimeFSMController == null)
|
||||
return;
|
||||
if (Context.Instance.RuntimeFSMController == null)
|
||||
return;
|
||||
|
||||
float width = 10f * edgeSizeMultiplier;
|
||||
|
||||
if (CloseTransition == transition)
|
||||
width *= 1.5f;
|
||||
|
||||
Vector3[] edgePoints = GetTransitionPoints(transition);
|
||||
|
||||
int index = Context.Instance.FSMController.RuntimeFSMController.IndexOf(Context.Instance.RuntimeFSMController);
|
||||
|
||||
// 找到当前的过渡
|
||||
|
||||
FSMTransition current_transition = Context.Instance.FSMController.GetCurrentTransition(index);
|
||||
if (current_transition == null) return;
|
||||
if (!transition.Key.Equals(current_transition.Data.Key)) return;
|
||||
|
||||
if (current_change_transition != current_transition.Data)
|
||||
{
|
||||
current_change_transition = current_transition.Data;
|
||||
current_change_transition_timer = 0;
|
||||
}
|
||||
|
||||
if (current_change_transition.Key.Equals(current_transition.Data.Key) && current_change_transition_timer >= 1)
|
||||
return;
|
||||
|
||||
FSMStateNodeData from = Context.Instance.RuntimeFSMController.GetStateNodeData(current_change_transition.fromStateName);
|
||||
|
||||
if (from == null || from.Parent != Context.Instance.LayerParent)
|
||||
{
|
||||
current_change_transition_timer = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
current_change_transition_timer += Time.unscaledDeltaTime * 3;
|
||||
|
||||
if (edgePoints != null && edgePoints.Length >= 2)
|
||||
{
|
||||
Handles.color = selectedEdgeColor;
|
||||
Handles.DrawAAPolyLine(width, edgePoints[0], Vector2.Lerp(edgePoints[0], edgePoints[1], current_change_transition_timer));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void DrawStateExitTransition()
|
||||
{
|
||||
if (Event.current.type != EventType.Repaint)
|
||||
return;
|
||||
|
||||
if (!Application.isPlaying) return;
|
||||
if (Context.Instance.FSMController == null)
|
||||
return;
|
||||
if (Context.Instance.FSMController.RuntimeFSMController == null)
|
||||
return;
|
||||
if (Context.Instance.RuntimeFSMController == null)
|
||||
return;
|
||||
|
||||
float width = 10f * edgeSizeMultiplier;
|
||||
|
||||
int index = Context.Instance.FSMController.RuntimeFSMController.IndexOf(Context.Instance.RuntimeFSMController);
|
||||
FSMStateNode stateNode = Context.Instance.FSMController.GetCurrentStateInfo(index, Context.Instance.LayerParent);
|
||||
|
||||
if (currentState != null && stateNode == null)
|
||||
{
|
||||
state_exiting = true;
|
||||
exit_state_parent = currentState.data.Parent;
|
||||
exit_state_name = currentState.data.name;
|
||||
exit_state_timer = 0;
|
||||
|
||||
List<FSMStateNodeData> nodes = Context.Instance.GetCurrentShowStateNodeData();
|
||||
|
||||
if (nodes != null)
|
||||
{
|
||||
foreach (var item in nodes)
|
||||
{
|
||||
if (item.isBuildInState && item.buildInStateName.Equals(FSMConst.up))
|
||||
{
|
||||
exit_state_to_name = item.name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (state_exiting)
|
||||
{
|
||||
exit_state_timer += Time.unscaledDeltaTime * 3;
|
||||
|
||||
Handles.color = selectedEdgeColor;
|
||||
Vector3[] edgePoints = GetTransitionPoints(exit_state_name, exit_state_to_name);
|
||||
|
||||
if(edgePoints != null)
|
||||
Handles.DrawAAPolyLine(width, edgePoints[0], Vector2.Lerp(edgePoints[0], edgePoints[1], exit_state_timer));
|
||||
|
||||
if (exit_state_timer >= 1 || !exit_state_parent.Equals(Context.Instance.LayerParent))
|
||||
{
|
||||
state_exiting = false;
|
||||
exit_state_parent = null;
|
||||
exit_state_name = null;
|
||||
exit_state_to_name = null;
|
||||
exit_state_timer = 0;
|
||||
}
|
||||
}
|
||||
currentState = stateNode;
|
||||
|
||||
}
|
||||
|
||||
private void DelayCallSelection()
|
||||
{
|
||||
FSMTransitionInspectorHelper.Instance.Inspect(Context.Instance.RuntimeFSMController, Selection);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5669ae347ffc28e4b9ef5ac1c9cc2b36
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user