367 lines
13 KiB
C#
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();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|