com.alicizax.unity.animatio.../Editor/Graph/GraphView.cs
陈思海 11e5744b46 init
2025-02-07 16:05:13 +08:00

367 lines
13 KiB
C#

using System;
using System.Collections.Generic;
using AlicizaX.AnimationFlow.Runtime;
using UnityEditor;
using UnityEditor.Experimental.GraphView;
using UnityEngine;
using UnityEngine.UIElements;
namespace AlicizaX.AnimationFlow.Editor
{
public class GraphView : UnityEditor.Experimental.GraphView.GraphView
{
private readonly SearchWindowProvider searchWindowProvider;
private readonly GraphWindow graphWindow;
private readonly Dictionary<string, NodeView> dicNodeView = new();
private Runtime.AnimationFlow animationFlow;
private const string applicationFlag = "@AnimationFlow";
private readonly Dictionary<string, ActionNode> pendingNodes = new();
public GraphView(GraphWindow graphWindow) : base()
{
this.graphWindow = graphWindow;
InitView();
SetupZoom(ContentZoomer.DefaultMinScale, ContentZoomer.DefaultMaxScale);
this.AddManipulator(new SelectionDragger());
this.AddManipulator(new RectangleSelector());
this.AddManipulator(new ContentDragger());
searchWindowProvider = ScriptableObject.CreateInstance<SearchWindowProvider>();
searchWindowProvider.Initialize(graphWindow, this);
SetCopyAndPaste();
graphViewChanged += OnGraphViewChanged;
}
private void InitView()
{
this.StretchToParentSize();
Insert(0, new GridBackground());
AddStyles();
MiniMap miniMap = new();
Add(miniMap);
miniMap.style.backgroundColor = new Color(0.12f, 0.12f, 0.12f, 1f);
miniMap.style.borderBottomColor = new Color(0.2f, 0.2f, 0.2f, 1f);
miniMap.style.borderRightColor = new Color(0.2f, 0.2f, 0.2f, 1f);
miniMap.style.borderTopColor = new Color(0.2f, 0.2f, 0.2f, 1f);
miniMap.style.borderLeftColor = new Color(0.2f, 0.2f, 0.2f, 1f);
}
private void SetCopyAndPaste()
{
serializeGraphElements += (IEnumerable<GraphElement> elements) =>
{
AnimationFlowSerialize serialize = new();
serialize.flag = applicationFlag;
foreach (var ele in elements)
{
if (ele is NodeView nodeView)
{
serialize.nodes.Add(nodeView.actionNode);
}
}
return JsonUtility.ToJson(serialize);
};
canPasteSerializedData += (string data) => { return data.Contains(applicationFlag); };
unserializeAndPaste += (string operationName, string data) =>
{
ClearSelection();
Undo.RecordObject(animationFlow, operationName);
Vector2 mousePos = contentViewContainer.WorldToLocal(contentRect.center);
try
{
AnimationFlowSerialize serialize = JsonUtility.FromJson<AnimationFlowSerialize>(data);
List<ActionNode> newDatas = new();
Dictionary<string, ActionNode> dicUid = new();
foreach (ActionNode node in serialize.nodes)
{
node.nodePos = node.nodePos + mousePos;
string uuid = node.uuid;
node.uuid = Guid.NewGuid().ToString();
NodeView nodeView = AddNodeView(node);
AddToSelection(nodeView);
newDatas.Add(node);
dicUid.Add(uuid, node);
}
foreach (ActionNode copyNode in newDatas)
{
for (int i = copyNode.Childs.Count - 1; i >= 0; i--)
{
if (dicUid.TryGetValue(copyNode.Childs[i].uuid, out ActionNode newNode))
{
copyNode.Childs[i] = newNode;
if (dicNodeView.TryGetValue(copyNode.uuid, out NodeView inNode) && dicNodeView.TryGetValue(newNode.uuid, out NodeView outNode))
{
AddElement(inNode.portOut.ConnectTo(outNode.portIn));
}
}
else
{
copyNode.Childs.RemoveAt(i);
}
}
}
}
catch (Exception)
{
}
};
}
private void ResetEdge()
{
List<ActionNode> nodes = new List<ActionNode>();
foreach (var item in animationFlow.AnimationNodes)
{
nodes.Add(item);
}
List<ActionNode> allNodes = new List<ActionNode>();
LoadAllChildNode(nodes, allNodes);
foreach (ActionNode data in allNodes)
{
foreach (ActionNode node in allNodes)
{
if (data.Childs.Count > 0 && data.Childs.Find(a => a.uuid == node.uuid) != null)
{
if (dicNodeView.TryGetValue(data.uuid, out NodeView inNode) && dicNodeView.TryGetValue(node.uuid, out NodeView outNode))
{
AddElement(inNode.portOut.ConnectTo(outNode.portIn));
}
}
}
}
}
private NodeView ResetNodeView(ActionNode data)
{
NodeView nodeView = new(data, graphWindow);
AddElement(nodeView);
nodeView.SetPosition(new Rect(data.nodePos, Vector2.zero));
dicNodeView.Add(data.uuid, nodeView);
return nodeView;
}
private void RemoveNodeView(NodeView nodeView)
{
dicNodeView.Remove(nodeView.actionNode.uuid);
pendingNodes.Remove(nodeView.actionNode.uuid);
if (nodeView.actionNode is EntryNode)
{
animationFlow.AnimationNodes.Remove(nodeView.actionNode as EntryNode);
}
}
private void AddStyles()
{
StyleSheet styleSheet = AssetDatabase.LoadAssetAtPath<StyleSheet>(EditorResourceTool.editorAssets + "/AnimationFlowStyles.uss");
styleSheets.Add(styleSheet);
}
private GraphViewChange OnGraphViewChanged(GraphViewChange change)
{
change.edgesToCreate?.ForEach(edge =>
{
if (animationFlow == null)
{
ClearGraph();
return;
}
Undo.RecordObject(animationFlow, "Create Edge");
NodeView nodeIn = edge.input.node as NodeView;
NodeView nodeOut = edge.output.node as NodeView;
nodeOut.actionNode.Childs.Add(nodeIn.actionNode);
EditorUtility.SetDirty(animationFlow);
});
change.elementsToRemove?.ForEach(elem =>
{
if (animationFlow == null)
{
ClearGraph();
return;
}
if (elem is Edge edge)
{
Undo.RecordObject(animationFlow, "Rmove Edge");
NodeView nodeIn = edge.input.node as NodeView;
NodeView nodeOut = edge.output.node as NodeView;
nodeOut.actionNode.Childs.Remove(nodeIn.actionNode);
}
if (elem is NodeView node)
{
Undo.RecordObject(animationFlow, "Remove Node");
RemoveNodeView(node);
graphWindow.OnNodeRemove();
}
EditorUtility.SetDirty(animationFlow);
});
change.movedElements?.ForEach(elem =>
{
if (animationFlow == null)
{
ClearGraph();
return;
}
if (elem is NodeView nodeView)
{
Undo.RecordObject(animationFlow, "Move Node");
nodeView.actionNode.nodePos = nodeView.GetPosition().position;
}
EditorUtility.SetDirty(animationFlow);
});
return change;
}
public override void BuildContextualMenu(ContextualMenuPopulateEvent evt)
{
SearchWindow.Open(new SearchWindowContext(GUIUtility.GUIToScreenPoint(evt.mousePosition)), searchWindowProvider);
}
public override List<Port> GetCompatiblePorts(Port startPort, NodeAdapter nodeAdapter)
{
List<Port> compatiblePorts = new();
ports.ForEach(port =>
{
if (startPort == port)
{
return;
}
if (startPort.node == port.node)
{
return;
}
if (startPort.direction == port.direction)
{
return;
}
if (startPort.portType != port.portType)
{
return;
}
compatiblePorts.Add(port);
});
return compatiblePorts;
}
protected override bool canDeleteSelection => base.canDeleteSelection && !Application.isPlaying && animationFlow != null && !animationFlow.InPlaying;
protected override bool canCopySelection => base.canCopySelection && !Application.isPlaying && animationFlow != null && !animationFlow.InPlaying;
protected override bool canCutSelection => base.canCutSelection && !Application.isPlaying && animationFlow != null && !animationFlow.InPlaying;
protected override bool canDuplicateSelection => base.canDuplicateSelection && !Application.isPlaying && animationFlow != null && !animationFlow.InPlaying;
protected override bool canPaste => base.canPaste && !Application.isPlaying && animationFlow != null;
public NodeView AddNodeView(ActionNode node)
{
if (animationFlow == null)
{
return null;
}
Undo.RecordObject(animationFlow, "Create Node");
if (node is EntryNode)
{
animationFlow.AnimationNodes.Add(node as EntryNode);
}
else
{
pendingNodes.Add(node.uuid, node);
}
return ResetNodeView(node);
}
public void ClearGraph()
{
graphElements.ForEach(g => RemoveElement(g));
dicNodeView.Clear();
animationFlow = null;
}
public void SetAnimationFlow(Runtime.AnimationFlow animationFlow)
{
this.animationFlow = animationFlow;
List<ActionNode> nodes = new List<ActionNode>();
foreach (var VARIABLE in animationFlow.AnimationNodes)
{
nodes.Add(VARIABLE);
}
List<ActionNode> allNodes = new List<ActionNode>();
LoadAllChildNode(nodes, allNodes);
foreach (ActionNode data in allNodes)
{
ResetNodeView(data);
}
ResetEdge();
}
private void LoadAllChildNode(List<ActionNode> nodes, List<ActionNode> collector)
{
foreach (var item in nodes)
{
collector.Add(item);
if (item.Childs.Count > 0) LoadAllChildNode(item.Childs, collector);
}
}
public void ResetView()
{
foreach (NodeView nodeView in dicNodeView.Values)
{
nodeView.RemoveProgress();
nodeView.RemoveComplete();
}
}
public void OnNodeEnter(ActionNode actionNode)
{
if (dicNodeView.TryGetValue(actionNode.uuid, out NodeView nodeView))
{
nodeView.AddProgress();
}
}
public void OnNodeExit(ActionNode actionNode)
{
if (dicNodeView.TryGetValue(actionNode.uuid, out NodeView nodeView))
{
nodeView.RemoveProgress();
nodeView.AddComplete();
}
}
public void OnNodeUpdate(ActionNode actionNode)
{
if (dicNodeView.TryGetValue(actionNode.uuid, out NodeView nodeView))
{
nodeView.UpdateProgress();
}
}
}
}