345 lines
10 KiB
C#
345 lines
10 KiB
C#
![]() |
using System;
|
||
|
using System.Collections.Generic;
|
||
|
using System.Linq;
|
||
|
using AlicizaX.AnimationFlow.Runtime;
|
||
|
using UnityEditor;
|
||
|
using UnityEngine;
|
||
|
using UnityEngine.UIElements;
|
||
|
|
||
|
namespace AlicizaX.AnimationFlow.Editor
|
||
|
{
|
||
|
public class GraphWindow : EditorWindow
|
||
|
{
|
||
|
private GraphInspector graphInspector;
|
||
|
private GraphView graphContentView;
|
||
|
private Runtime.AnimationFlow animationFlowComponent;
|
||
|
private float editorPreviousTime;
|
||
|
|
||
|
private bool _subScribeEditorEventState;
|
||
|
|
||
|
[MenuItem("Window/AnimationGraph")]
|
||
|
public static void Open()
|
||
|
{
|
||
|
GetWindow<GraphWindow>("AnimationFlow");
|
||
|
}
|
||
|
|
||
|
private void Update()
|
||
|
{
|
||
|
if (graphContentView == null || animationFlowComponent == null || Application.isPlaying)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (animationFlowComponent.InPlaying)
|
||
|
{
|
||
|
float delta = (Time.realtimeSinceStartup - editorPreviousTime) * Time.timeScale;
|
||
|
editorPreviousTime = Time.realtimeSinceStartup;
|
||
|
animationFlowComponent.Tick(delta);
|
||
|
}
|
||
|
|
||
|
List<ActionNode> nodes = new List<ActionNode>();
|
||
|
foreach (var VARIABLE in animationFlowComponent.AnimationNodes)
|
||
|
{
|
||
|
nodes.Add(VARIABLE);
|
||
|
}
|
||
|
|
||
|
UpdateNodeState(nodes);
|
||
|
}
|
||
|
|
||
|
private void UpdateNodeState(List<ActionNode> nodes)
|
||
|
{
|
||
|
foreach (var node in nodes)
|
||
|
{
|
||
|
switch (node.State)
|
||
|
{
|
||
|
case EState.Enter:
|
||
|
graphContentView?.OnNodeEnter(node);
|
||
|
break;
|
||
|
case EState.Running:
|
||
|
graphContentView?.OnNodeUpdate(node);
|
||
|
break;
|
||
|
case EState.Exit:
|
||
|
graphContentView?.OnNodeExit(node);
|
||
|
break;
|
||
|
;
|
||
|
}
|
||
|
|
||
|
if (node.Childs.Count > 0)
|
||
|
{
|
||
|
UpdateNodeState(node.Childs);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
private void OnEnable()
|
||
|
{
|
||
|
animationFlowComponent = null;
|
||
|
BuildView();
|
||
|
CheckSelectionAnimationFlow();
|
||
|
Selection.selectionChanged -= OnSelectionChanged;
|
||
|
Selection.selectionChanged += OnSelectionChanged;
|
||
|
Undo.undoRedoPerformed -= UndoRedoPerformed;
|
||
|
Undo.undoRedoPerformed += UndoRedoPerformed;
|
||
|
EditorApplication.playModeStateChanged -= OnModeStateChanged;
|
||
|
EditorApplication.playModeStateChanged += OnModeStateChanged;
|
||
|
}
|
||
|
|
||
|
|
||
|
private void OnDisable()
|
||
|
{
|
||
|
Selection.selectionChanged -= OnSelectionChanged;
|
||
|
Undo.undoRedoPerformed -= UndoRedoPerformed;
|
||
|
EditorApplication.playModeStateChanged -= OnModeStateChanged;
|
||
|
if (animationFlowComponent != null)
|
||
|
{
|
||
|
animationFlowComponent.ResetNode();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// 生成View
|
||
|
/// </summary>
|
||
|
private void BuildView()
|
||
|
{
|
||
|
if (!EditorResourceTool.LocateEditorAssets())
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
Texture2D btnPlayIcon = EditorGUIUtility.FindTexture("d_PlayButton");
|
||
|
Texture2D btnPauseIcon = EditorGUIUtility.FindTexture("d_PauseButton");
|
||
|
Texture2D btnStepIcon = EditorGUIUtility.FindTexture("d_StepButton");
|
||
|
Texture2D btnSaveIcon = EditorGUIUtility.FindTexture("SaveActive");
|
||
|
|
||
|
VisualTreeAsset visual_tree = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(EditorResourceTool.editorAssets + "/UIGraphWindow.uxml");
|
||
|
visual_tree.CloneTree(rootVisualElement);
|
||
|
graphInspector = new GraphInspector();
|
||
|
rootVisualElement.Q<VisualElement>("Inspector").Add(graphInspector);
|
||
|
|
||
|
VisualElement graph_parent = rootVisualElement.Q<VisualElement>("Graph");
|
||
|
|
||
|
|
||
|
GUIStyle gui_style = new();
|
||
|
Texture2D texture = new(1, 1);
|
||
|
texture.SetPixel(0, 0, new Color(0.25f, 0.25f, 0.25f, 1f));
|
||
|
texture.Apply();
|
||
|
gui_style.normal.background = texture;
|
||
|
gui_style.padding = new RectOffset(5, 5, 4, 4);
|
||
|
int index_name = 0;
|
||
|
IMGUIContainer imgui_container = new(() =>
|
||
|
{
|
||
|
if (animationFlowComponent == null)
|
||
|
{
|
||
|
graphContentView?.ClearGraph();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
GUILayout.BeginHorizontal(gui_style);
|
||
|
|
||
|
List<string> keys = new();
|
||
|
foreach (ActionNode baseData in animationFlowComponent.AnimationNodes)
|
||
|
{
|
||
|
if (baseData is EntryNode root && !string.IsNullOrEmpty(root.Name))
|
||
|
{
|
||
|
keys.Add(root.Name);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
GUI.color = !animationFlowComponent.InPlaying && keys.Count > 0 ? Color.white : Color.gray;
|
||
|
index_name = EditorGUILayout.Popup(index_name, keys.ToArray(), GUILayout.Width(100));
|
||
|
if (GUILayout.Button(btnPlayIcon))
|
||
|
{
|
||
|
if (keys.Count > 0)
|
||
|
{
|
||
|
if (animationFlowComponent.InPlaying)
|
||
|
{
|
||
|
animationFlowComponent.Stop();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
OnBtnClickPlay(keys[index_name]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
GUI.color = animationFlowComponent.InPlaying ? Color.white : Color.gray;
|
||
|
if (GUILayout.Button(btnPauseIcon))
|
||
|
{
|
||
|
OnBtnClickPause();
|
||
|
}
|
||
|
|
||
|
GUI.color = animationFlowComponent.InPlaying || animationFlowComponent.InPause ? Color.white : Color.gray;
|
||
|
if (GUILayout.Button(btnStepIcon))
|
||
|
{
|
||
|
OnBtnClickStep();
|
||
|
}
|
||
|
|
||
|
|
||
|
GUI.color = Color.white;
|
||
|
GUILayout.EndHorizontal();
|
||
|
});
|
||
|
graph_parent.Add(imgui_container);
|
||
|
imgui_container.style.position = Position.Absolute;
|
||
|
imgui_container.style.bottom = 0;
|
||
|
imgui_container.style.alignSelf = Align.Center;
|
||
|
imgui_container.style.height = 40;
|
||
|
|
||
|
graphContentView = new(this);
|
||
|
graph_parent.Insert(0, graphContentView);
|
||
|
}
|
||
|
|
||
|
private void OnFocus()
|
||
|
{
|
||
|
CheckSelectionAnimationFlow();
|
||
|
}
|
||
|
|
||
|
private void OnSelectionChanged()
|
||
|
{
|
||
|
CheckSelectionAnimationFlow();
|
||
|
}
|
||
|
|
||
|
private void UndoRedoPerformed()
|
||
|
{
|
||
|
RePaintGraph();
|
||
|
}
|
||
|
|
||
|
private void OnModeStateChanged(PlayModeStateChange _)
|
||
|
{
|
||
|
if (TryGetPlayerFromSelection())
|
||
|
{
|
||
|
RePaintGraph();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
graphInspector.Clear();
|
||
|
graphContentView.ClearGraph();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/// <summary>
|
||
|
/// 检查当前选中的物体是否存在AnimationFlow组件
|
||
|
/// </summary>
|
||
|
private void CheckSelectionAnimationFlow()
|
||
|
{
|
||
|
if (TryGetPlayerFromSelection())
|
||
|
{
|
||
|
RePaintGraph();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
private void RePaintGraph()
|
||
|
{
|
||
|
List<ActionNode> nodes = new List<ActionNode>();
|
||
|
foreach (var VARIABLE in animationFlowComponent.AnimationNodes)
|
||
|
{
|
||
|
nodes.Add(VARIABLE);
|
||
|
}
|
||
|
|
||
|
CheckEmptyNode(nodes);
|
||
|
graphInspector.Clear();
|
||
|
graphContentView.ClearGraph();
|
||
|
graphContentView.SetAnimationFlow(animationFlowComponent);
|
||
|
}
|
||
|
|
||
|
private void CheckEmptyNode(List<ActionNode> nodes)
|
||
|
{
|
||
|
for (int i = nodes.Count - 1; i >= 0; i--)
|
||
|
{
|
||
|
if (nodes[i] == null)
|
||
|
{
|
||
|
nodes.RemoveAt(i);
|
||
|
}
|
||
|
|
||
|
if (nodes[i].Childs.Count > 0)
|
||
|
{
|
||
|
CheckEmptyNode(nodes[i].Childs);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
private bool TryGetPlayerFromSelection()
|
||
|
{
|
||
|
if (Selection.activeGameObject != null)
|
||
|
{
|
||
|
Runtime.AnimationFlow new_animation_flow = Selection.activeGameObject.GetComponent<Runtime.AnimationFlow>();
|
||
|
if (new_animation_flow != null && (animationFlowComponent == null || new_animation_flow != animationFlowComponent))
|
||
|
{
|
||
|
if (animationFlowComponent != null)
|
||
|
{
|
||
|
// DeleteAction();
|
||
|
}
|
||
|
|
||
|
animationFlowComponent = new_animation_flow;
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
#region 行为树底部菜单响应事件
|
||
|
|
||
|
private void OnBtnClickPlay(string name)
|
||
|
{
|
||
|
if (graphContentView == null || animationFlowComponent == null)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (animationFlowComponent.AnimationNodes.Exists(v => !v.Valid()))
|
||
|
{
|
||
|
EditorUtility.DisplayDialog("Warning", "Please make sure the required parameters are set.", "ok");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
editorPreviousTime = Time.realtimeSinceStartup;
|
||
|
ResetView();
|
||
|
animationFlowComponent.Play(name);
|
||
|
}
|
||
|
|
||
|
private void OnBtnClickPause()
|
||
|
{
|
||
|
if (graphContentView == null || animationFlowComponent == null || !animationFlowComponent.InPlaying)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
animationFlowComponent.AnimationFlowPlayebleType = EAnimationFlowPlayebleType.Pause;
|
||
|
}
|
||
|
|
||
|
private void OnBtnClickStep()
|
||
|
{
|
||
|
if (graphContentView == null || animationFlowComponent == null || !animationFlowComponent.InPlaying || !animationFlowComponent.InPause)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
|
||
|
animationFlowComponent.Tick(0.016f);
|
||
|
}
|
||
|
|
||
|
#endregion
|
||
|
|
||
|
|
||
|
public void OnSelect(NodeView node_view)
|
||
|
{
|
||
|
graphInspector.DrawNode(node_view);
|
||
|
}
|
||
|
|
||
|
public void OnNodeRemove()
|
||
|
{
|
||
|
graphInspector.Clear();
|
||
|
}
|
||
|
|
||
|
|
||
|
private void ResetView()
|
||
|
{
|
||
|
graphContentView?.ResetView();
|
||
|
}
|
||
|
}
|
||
|
}
|