diff options
author | chai <chaifix@163.com> | 2022-06-28 09:40:37 +0800 |
---|---|---|
committer | chai <chaifix@163.com> | 2022-06-28 09:40:37 +0800 |
commit | 49b25e755b70ec412feaaf0b898d6f7e09d2bea6 (patch) | |
tree | 3c5f4260f30d1c2d7196db93153700d7ddec3157 /Other/NodeEditorExamples/Assets/UNEB/Editor/Actions | |
parent | c92269331692feca2c276649f6c4ee8911f1f859 (diff) |
+node example
Diffstat (limited to 'Other/NodeEditorExamples/Assets/UNEB/Editor/Actions')
20 files changed, 1161 insertions, 0 deletions
diff --git a/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/ActionBase.cs b/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/ActionBase.cs new file mode 100644 index 00000000..426c8a50 --- /dev/null +++ b/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/ActionBase.cs @@ -0,0 +1,23 @@ + +namespace UNEB +{ + public abstract class ActionBase + { + + public ActionManager manager; + + /// <summary> + /// Can be used to check if the action is a valid state for furthur execution. + /// For example, we only want to run delete node if a node is selected for deletion. + /// </summary> + /// <returns></returns> + public virtual bool Init() { return true; } + + public abstract void Do(); + + /// <summary> + /// Called when the action is removed from the undo/redo buffers. + /// </summary> + public virtual void OnDestroy() { } + } +}
\ No newline at end of file diff --git a/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/ActionBase.cs.meta b/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/ActionBase.cs.meta new file mode 100644 index 00000000..eb9d005c --- /dev/null +++ b/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/ActionBase.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 5dfa0b77744c2bc4eb973c5114f5de79 +timeCreated: 1501781587 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/ActionManager.cs b/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/ActionManager.cs new file mode 100644 index 00000000..b0450b46 --- /dev/null +++ b/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/ActionManager.cs @@ -0,0 +1,169 @@ + +using System; +using System.Collections.Generic; +using UnityEngine; +using UNEB.Utility; + +namespace UNEB +{ + /// <summary> + /// Handles execution of actions, undo, and redo. + /// </summary> + public class ActionManager + { + private NodeEditorWindow _window; + + private FiniteStack<UndoableAction> _undoStack; + private Stack<UndoableAction> _redoStack; + + // Caches the current multi-stage action that is currently executing. + private MultiStageAction _activeMultiAction = null; + + public event Action OnUndo; + public event Action OnRedo; + + public ActionManager(NodeEditorWindow w) + { + _undoStack = new FiniteStack<UndoableAction>(100); + _redoStack = new Stack<UndoableAction>(); + + _window = w; + + // Makes sure that the action cleans up after itself + // when it is removed from the undo buffer. + _undoStack.OnRemoveBottomItem += (action) => + { + action.OnDestroy(); + }; + } + + public void Update() + { + if (IsRunningAction) { + _activeMultiAction.Do(); + } + } + + public bool IsRunningAction + { + get { return _activeMultiAction != null; } + } + + /// <summary> + /// Runs an action and stores it in the undo stack. + /// </summary> + /// <typeparam name="T"></typeparam> + public void RunUndoableAction<T>() where T : UndoableAction, new() + { + T action = new T(); + action.manager = this; + + if (action.Init()) { + + clearRedoStack(); + _undoStack.Push(action); + action.Do(); + } + } + + /// <summary> + /// Starts a multi stage action but does not record it in the undo stack. + /// </summary> + /// <typeparam name="T"></typeparam> + public void StartMultiStageAction<T>() where T : MultiStageAction, new() + { + // Only run 1 multi-action at a time. + if (_activeMultiAction != null) { + return; + } + + T action = new T(); + action.manager = this; + + if (action.Init()) { + _activeMultiAction = action; + _activeMultiAction.OnActionStart(); + } + } + + /// <summary> + /// Records the multi-stage action in the undo stack. + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="action"></param> + public void FinishMultiStageAction() + { + if (_activeMultiAction == null) { + return; + } + + // We check if the action ended properly so it can be stored in undo. + if (_activeMultiAction.OnActionEnd()) { + + clearRedoStack(); + _undoStack.Push(_activeMultiAction); + } + + // There is no longer an active multi-stage action. + _activeMultiAction = null; + } + + public void UndoAction() + { + if (_undoStack.Count != 0) { + + var action = _undoStack.Pop(); + _redoStack.Push(action); + + action.Undo(); + + if (OnUndo != null) + OnUndo(); + } + } + + public void RedoAction() + { + if (_redoStack.Count != 0) { + + var action = _redoStack.Pop(); + _undoStack.Push(action); + + action.Redo(); + + if (OnRedo != null) + OnRedo(); + } + } + + public void Reset() + { + _activeMultiAction = null; + clearUndoStack(); + clearRedoStack(); + } + + private void clearRedoStack() + { + foreach (var action in _redoStack) { + action.OnDestroy(); + } + + _redoStack.Clear(); + } + + private void clearUndoStack() + { + foreach (var action in _undoStack) { + action.OnDestroy(); + } + + _undoStack.Clear(); + } + + public NodeEditorWindow window + { + get { return _window; } + } + } +}
\ No newline at end of file diff --git a/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/ActionManager.cs.meta b/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/ActionManager.cs.meta new file mode 100644 index 00000000..3cc64c36 --- /dev/null +++ b/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/ActionManager.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 62fe01f31afcc5a4ab446003a7360cb3 +timeCreated: 1501892528 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/ActionTriggerSystem.cs b/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/ActionTriggerSystem.cs new file mode 100644 index 00000000..d5089754 --- /dev/null +++ b/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/ActionTriggerSystem.cs @@ -0,0 +1,456 @@ + +using System; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; +using UNEB.Utility; +using System.Reflection; +using System.Linq; + +namespace UNEB +{ + public class ActionTriggerSystem + { + public List<TriggerMapping> triggers; + + /// <summary> + /// Passive triggers do not interrupt the next possible trigger. + /// </summary> + public List<TriggerMapping> passiveTriggers; + + private ActionManager _manager; + private TriggerMapping _focus; + + public ActionTriggerSystem(ActionManager m) + { + _manager = m; + + triggers = new List<TriggerMapping>(); + passiveTriggers = new List<TriggerMapping>(); + + setupStandardTriggers(); + } + + public void Update() + { + foreach (TriggerMapping tm in triggers) { + if (tm.AllTriggersSatisfied()) { + tm.action(); + return; + } + } + + foreach (TriggerMapping tm in passiveTriggers) { + if (tm.AllTriggersSatisfied()) { + tm.action(); + } + } + + // Block all key inputs from passing through the Unity Editor + if (Event.current.isKey) + Event.current.Use(); + } + + private void setupStandardTriggers() + { + setupImmediateTriggers(); + setupContextTriggers(); + setupMultiStageTriggers(); + } + + private void setupImmediateTriggers() + { + var panInput = Create<InputTrigger>().Mouse(EventType.MouseDrag, InputTrigger.Button.Wheel); + panInput.action = () => + { + window.editor.Pan(Event.current.delta); + window.Repaint(); + }; + + var zoomInput = Create<InputTrigger>().Key(EventType.ScrollWheel, KeyCode.None, false, false); + zoomInput.action = () => + { + window.editor.Zoom(Event.current.delta.y); + window.Repaint(); + }; + + var selectSingle = Create<InputTrigger>().Mouse(EventType.MouseDown, InputTrigger.Button.Left); + selectSingle.action = () => + { + bool bResult = window.editor.OnMouseOverNode(onSingleSelected); + + // If the canvas is clicked then remove focus of GUI elements. + if (!bResult) { + GUI.FocusControl(null); + window.Repaint(); + } + }; + + var undoInput = Create<InputTrigger>().Key(EventType.KeyDown, KeyCode.Z, true, false); + undoInput.action = _manager.UndoAction; + + var redoInput = Create<InputTrigger>().Key(EventType.KeyDown, KeyCode.Y, true, false); + redoInput.action = _manager.RedoAction; + + var recordClick = Create<InputTrigger>().EventOnly(EventType.MouseDown); + recordClick.action = () => { window.state.lastClickedPosition = window.editor.MousePosition(); }; + + var homeView = Create<InputTrigger>().Key(EventType.KeyDown, KeyCode.F, false, false); + homeView.action = window.editor.HomeView; + } + + private void setupContextTriggers() + { + setupNodeCreateMenu(); + + Pair<string, Action>[] nodeContext = + { + ContextItem("Copy Node", () => { Debug.Log("Not Implemented"); }), + ContextItem("Delete Node", _manager.RunUndoableAction<DeleteNodeAction>) + }; + + var nodeTrigger = Create<ContextTrigger>().Build(nodeContext).EventOnly(EventType.ContextClick); + nodeTrigger.triggers.Add(isMouseOverNode); + nodeTrigger.triggers.Add(isGraphValid); + } + + private void setupNodeCreateMenu() + { + //Get all classes deriving from Node via reflection + Type derivedType = typeof(Node); + Assembly assembly = Assembly.GetAssembly(derivedType); + + List<Type> nodeTypes = assembly + .GetTypes() + .Where(t => + t != derivedType && + derivedType.IsAssignableFrom(t) + ).ToList(); + + //Populate canvasContext with entries for all node types + var canvasContext = new Pair<string, Action>[nodeTypes.Count]; + + for (int i = 0; i < nodeTypes.Count; i++) { + + Type nodeType = nodeTypes[i]; + Action createNode = () => + { + _manager.window.state.typeToCreate = nodeType; + _manager.RunUndoableAction<CreateNodeAction>(); + }; + + string name = ObjectNames.NicifyVariableName(nodeType.Name); + canvasContext[i] = ContextItem(name, createNode); + } + + var canvasTrigger = Create<ContextTrigger>().Build(canvasContext).EventOnly(EventType.ContextClick); + canvasTrigger.triggers.Add(isMouseOverCanvas); + canvasTrigger.triggers.Add(isGraphValid); + } + + private void setupMultiStageTriggers() + { + setupNodeDrag(); + setupNodeConnection(); + } + + private void setupNodeDrag() + { + var endDragInput = Create<InputTrigger>(false, true).Mouse(EventType.MouseUp, InputTrigger.Button.Left); + var runningDragInput = Create<InputTrigger>(false, true).EventOnly(EventType.MouseDrag); + var startDragInput = Create<InputTrigger>(false, true).Mouse(EventType.MouseDown, InputTrigger.Button.Left); + + startDragInput.triggers.Add(isMouseOverNode); + startDragInput.action = _manager.StartMultiStageAction<DragNode>; + + endDragInput.action = _manager.FinishMultiStageAction; + + runningDragInput.action = _manager.Update; + runningDragInput.action += window.Repaint; + + new MultiStageInputTrigger(startDragInput, endDragInput, runningDragInput); + } + + private void setupNodeConnection() + { + var endConnInput = Create<InputTrigger>(false, true).Mouse(EventType.MouseUp, InputTrigger.Button.Left); + var runningConnInput = Create<InputTrigger>(false, true).EventOnly(EventType.MouseDrag); + var startConnInput = Create<InputTrigger>(false, true).Mouse(EventType.MouseDown, InputTrigger.Button.Left); + + Func<bool> knobCondition = () => { return isMouseOverOutput() || isMouseOverInputStartConn(); }; + + startConnInput.triggers.Add(knobCondition); + startConnInput.triggers.Add(isOutputSelected); + + startConnInput.action = _manager.StartMultiStageAction<CreateConnection>; + + endConnInput.action = _manager.FinishMultiStageAction; + endConnInput.action += window.Repaint; + + runningConnInput.action = _manager.Update; + runningConnInput.action += window.Repaint; + + new MultiStageInputTrigger(startConnInput, endConnInput, runningConnInput); + } + + /// <summary> + /// Create a trigger mapping and store it in the triggers list. + /// </summary> + /// <typeparam name="T"></typeparam> + /// <returns></returns> + public T Create<T>(bool isPassive = false, bool pushToFront = false) where T : TriggerMapping, new() + { + T mapping = new T(); + + if (isPassive) { + + if (pushToFront && passiveTriggers.Count > 0) passiveTriggers.Insert(0, mapping); + else passiveTriggers.Add(mapping); + } + + else { + + if (pushToFront && triggers.Count > 0) triggers.Insert(0, mapping); + else triggers.Add(mapping); + } + + return mapping; + } + + private void onSingleSelected(Node node) + { + _manager.window.state.selectedNode = node; + _manager.window.graph.PushToEnd(node); + + Selection.activeObject = node; + } + + private void onOutputKnobSelected(NodeOutput output) + { + _manager.window.state.selectedOutput = output; + } + + private bool isMouseOverNode() + { + return window.editor.OnMouseOverNode(onSingleSelected); + } + + private bool isMouseOverCanvas() + { + return !isMouseOverNode(); + } + + private bool isMouseOverOutput() + { + return window.editor.OnMouseOverOutput(onOutputKnobSelected); + } + + private bool isMouseOverInputStartConn() + { + Action<NodeInput> startConnFromInput = (NodeInput input) => + { + window.state.selectedOutput = input.Outputs[0]; + + // Detach this input if we are starting a connection action from the input. + if (window.state.selectedOutput != null) { + + window.state.selectedInput = input; + _manager.RunUndoableAction<RemoveConnection>(); + } + }; + + return window.editor.OnMouseOverInput(startConnFromInput); + } + + private bool isOutputSelected() + { + return window.state.selectedOutput != null; + } + + private NodeEditorWindow window + { + get { return _manager.window; } + } + + private bool isGraphValid() + { + return window.graph != null; + } + + public Pair<string, Action> ContextItem(string label, Action a) + { + return new Pair<string, Action>(label, a); + } + + /// <summary> + /// Maps a conditional trigger with an action. + /// </summary> + public class TriggerMapping + { + public List<Func<bool>> triggers = new List<Func<bool>>(); + public Action action; + + protected TriggerMapping() { } + + public TriggerMapping(Func<bool> trigger, Action action) + { + ; + triggers.Add(trigger); + this.action = action; + } + + public bool AllTriggersSatisfied() + { + foreach (var t in triggers) { + if (!t()) { + return false; + } + } + + return true; + } + } + + /// <summary> + /// Special trigger that uses input as a conditional. + /// </summary> + public class InputTrigger : TriggerMapping + { + public enum Button + { + Left = 0, + Right = 1, + Wheel = 2 + } + + private EventType t; + private KeyCode k; + private int button; + private bool bIsShift, bIsCtrl; + + /// <summary> + /// Initialize the input mapping with a key trigger. + /// </summary> + /// <param name="type"></param> + /// <param name="key"></param> + /// <param name="bShift"></param> + /// <param name="bCtrl"></param> + /// <returns></returns> + public InputTrigger Key(EventType type, KeyCode key, bool bShift, bool bCtrl) + { + t = type; + k = key; + + bIsShift = bShift; + bIsCtrl = bCtrl; + + Func<bool> trigger = () => + { + var e = Event.current; + + return + e.type == t && + e.keyCode == k && + e.shift == bIsShift && + e.control == bIsCtrl; + }; + + triggers.Add(trigger); + return this; + } + + /// <summary> + /// Initialize the input mapping with a mouse button tirgger. + /// </summary> + /// <param name="type"></param> + /// <param name="mButton"></param> + /// <returns></returns> + public InputTrigger Mouse(EventType type, Button mButton) + { + t = type; + button = (int)mButton; + + Func<bool> trigger = () => + { + var e = Event.current; + + return + e.type == t && + e.button == button; + }; + + triggers.Add(trigger); + return this; + } + + /// <summary> + /// Initializes the input mapping with the event. + /// </summary> + /// <param name="type"></param> + /// <returns></returns> + public InputTrigger EventOnly(EventType type) + { + t = type; + Func<bool> trigger = () => { return Event.current.type == t; }; + + triggers.Add(trigger); + return this; + } + } + + /// <summary> + /// Special trigger that uses context menus to execute other actions. + /// </summary> + public class ContextTrigger : InputTrigger + { + private GenericMenu menu; + + public ContextTrigger() + { + menu = new GenericMenu(); + action = menu.ShowAsContext; + } + + public ContextTrigger Build(params Pair<string, Action>[] contents) + { + foreach (var content in contents) { + + string label = content.item1; + Action action = content.item2; + + menu.AddItem(new GUIContent(label), false, () => { action(); }); + } + + return this; + } + } + + public class MultiStageInputTrigger + { + private InputTrigger _startTrigger, _endTrigger, _runningTrigger; + + private bool _bStarted = false; + + public MultiStageInputTrigger(InputTrigger start, InputTrigger end, InputTrigger running) + { + start.action += () => { _bStarted = true; }; + start.triggers.Add(hasNotStarted); + + end.triggers.Add(HasStarted); + end.action += () => { _bStarted = false; }; + + running.triggers.Add(HasStarted); + } + + public bool HasStarted() + { + return _bStarted; + } + + private bool hasNotStarted() + { + return !_bStarted; + } + } + } +}
\ No newline at end of file diff --git a/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/ActionTriggerSystem.cs.meta b/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/ActionTriggerSystem.cs.meta new file mode 100644 index 00000000..6d66a0d4 --- /dev/null +++ b/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/ActionTriggerSystem.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 2b8fd837eb8876e4fa8c9b94d8b977f0 +timeCreated: 1501897267 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/CreateConnection.cs b/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/CreateConnection.cs new file mode 100644 index 00000000..09d3e521 --- /dev/null +++ b/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/CreateConnection.cs @@ -0,0 +1,106 @@ + +using System.Linq; +using System.Collections.Generic; + +using UnityEngine; +using UnityEditor; + +namespace UNEB +{ + public class CreateConnection : MultiStageAction + { + private NodeInput _input; + private NodeOutput _output; + + // The output of the old node it was connected to. + private NodeOutput _oldConnectedOutput; + + // Old inputs of the node. + private List<NodeInput> _oldConnectedInputs; + + public override void Do() + { + manager.window.state.selectedOutput = _output; + } + + public override void Undo() + { + _output.Remove(_input); + reconnectOldConnections(); + } + + public override void Redo() + { + disconnectOldConnections(); + _output.Add(_input); + } + + private void reconnectOldConnections() + { + // Re-connect old connections + if (_oldConnectedOutput != null) { + _oldConnectedOutput.Add(_input); + } + + if (_oldConnectedInputs != null) { + foreach (var input in _oldConnectedInputs) { + _output.Add(input); + } + } + } + + private void disconnectOldConnections() + { + // Remove old connections + if (_oldConnectedOutput != null) { + _oldConnectedOutput.Remove(_input); + } + + if (_oldConnectedInputs != null) { + _output.RemoveAll(); + } + } + + public override void OnActionStart() + { + _output = manager.window.state.selectedOutput; + } + + public override bool OnActionEnd() + { + manager.window.state.selectedOutput = null; + manager.window.editor.OnMouseOverInput((input) => { _input = input; }); + + // Make the connection. + if (_input != null && _output.CanConnectInput(_input)) { + + if (!_output.bCanHaveMultipleConnections) + { + _output.RemoveAll(); + } + + if (!_input.bCanHaveMultipleConnections) { + cacheOldConnections(); + disconnectOldConnections(); + } + + return _output.Add(_input); + } + + return false; + } + + private void cacheOldConnections() + { + // Check if the receiving node was already connected. + if (_input != null && _input.HasOutputConnected()) { + _oldConnectedOutput = _input.Outputs[0]; + } + + // Check if the origin node already had inputs + if (!_output.bCanHaveMultipleConnections && _output.InputCount > 0) { + _oldConnectedInputs = _output.Inputs.ToList(); + } + } + } +}
\ No newline at end of file diff --git a/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/CreateConnection.cs.meta b/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/CreateConnection.cs.meta new file mode 100644 index 00000000..5f6468b1 --- /dev/null +++ b/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/CreateConnection.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 89cf0324fa09d7d4b82a3308a2172117 +timeCreated: 1501807840 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/CreateNodeAction.cs b/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/CreateNodeAction.cs new file mode 100644 index 00000000..522fa0cb --- /dev/null +++ b/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/CreateNodeAction.cs @@ -0,0 +1,55 @@ + +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace UNEB +{ + public class CreateNodeAction : UndoableAction + { + private NodeGraph _graph; + private Node _nodeCreated; + + // The node referenced can only be destroyed if the + // create action has been undone. + private bool _bCanDeleteNode = false; + + public override bool Init() + { + System.Type t = manager.window.state.typeToCreate; + return t != null && typeof(Node).IsAssignableFrom(t); + } + + public override void Do() + { + _graph = manager.window.graph; + + var state = manager.window.state; + + _nodeCreated = SaveManager.CreateNode(state.typeToCreate, _graph); + _nodeCreated.bodyRect.position = manager.window.state.lastClickedPosition; + + // Done with this type creation. + state.typeToCreate = null; + } + + public override void Undo() + { + _graph.Remove(_nodeCreated); + _bCanDeleteNode = true; + } + + public override void Redo() + { + _graph.Add(_nodeCreated); + _bCanDeleteNode = false; + } + + public override void OnDestroy() + { + if (_bCanDeleteNode && _nodeCreated) { + ScriptableObject.DestroyImmediate(_nodeCreated, true); + } + } + } +}
\ No newline at end of file diff --git a/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/CreateNodeAction.cs.meta b/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/CreateNodeAction.cs.meta new file mode 100644 index 00000000..06f04a85 --- /dev/null +++ b/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/CreateNodeAction.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: f1c2e7f15404be3468c08ac729975c13 +timeCreated: 1501781600 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/DeleteNodeAction.cs b/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/DeleteNodeAction.cs new file mode 100644 index 00000000..2e2e6672 --- /dev/null +++ b/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/DeleteNodeAction.cs @@ -0,0 +1,128 @@ + +using System; +using System.Linq; +using System.Collections.Generic; +using UnityEngine; +using UNEB.Utility; + +namespace UNEB +{ + // Each input can have many outputs + using InputToOutputPair = Pair<NodeInput, List<NodeOutput>>; + + // Each output can have many inputs + using OutputToInputsPair = Pair<NodeOutput, List<NodeInput>>; + + public class DeleteNodeAction : UndoableAction + { + private NodeGraph _graph; + private Node _nodeRemoved = null; + + private List<InputToOutputPair> _oldConnectedOutputs; + private List<OutputToInputsPair> _oldConnectedInputs; + + // The node referenced can only be destroyed if the + // delete action has been done or redone. + private bool _bCanDeleteNode = false; + + public DeleteNodeAction() + { + + _oldConnectedOutputs = new List<InputToOutputPair>(); + _oldConnectedInputs = new List<OutputToInputsPair>(); + } + + public override bool Init() + { + return manager.window.state.selectedNode != null; + } + + public override void Do() + { + _graph = manager.window.graph; + _nodeRemoved = manager.window.state.selectedNode; + _graph.Remove(_nodeRemoved); + + // Remember all the old outputs the inputs were connected to. + foreach (var input in _nodeRemoved.Inputs) { + + if (input.HasOutputConnected()) { + _oldConnectedOutputs.Add(new InputToOutputPair(input, input.Outputs.ToList())); + } + } + + // Remember all the old input connections that the outputs were connected to. + foreach (var output in _nodeRemoved.Outputs) { + + if (output.InputCount != 0) { + _oldConnectedInputs.Add(new OutputToInputsPair(output, output.Inputs.ToList())); + } + } + + disconnectOldConnections(); + + _bCanDeleteNode = true; + } + + public override void Undo() + { + _graph.Add(_nodeRemoved); + reconnectOldConnections(); + + _bCanDeleteNode = false; + } + + public override void Redo() + { + _graph.Remove(_nodeRemoved); + disconnectOldConnections(); + + _bCanDeleteNode = true; + } + + private void disconnectOldConnections() + { + // For all the outputs for this node, remove all the connected inputs. + foreach (var output in _nodeRemoved.Outputs) { + output.RemoveAll(); + } + + // For all the inputs for this node, remove all the connected outputs. + foreach (var input in _nodeRemoved.Inputs) { + input.RemoveAll(); + } + } + + private void reconnectOldConnections() + { + // For all the remembered inputs (of this node) to output pairs, reconnect. + foreach (InputToOutputPair inOutPair in _oldConnectedOutputs) { + + NodeInput input = inOutPair.item1; + List<NodeOutput> outputs = inOutPair.item2; + + foreach (var output in outputs) { + output.Add(input); + } + } + + // For all the remembered outputs (of this node) to inputs, reconnect. + foreach (OutputToInputsPair outInsPair in _oldConnectedInputs) { + + NodeOutput output = outInsPair.item1; + List<NodeInput> inputs = outInsPair.item2; + + foreach (var input in inputs) { + output.Add(input); + } + } + } + + public override void OnDestroy() + { + if (_bCanDeleteNode && _nodeRemoved) { + ScriptableObject.DestroyImmediate(_nodeRemoved, true); + } + } + } +}
\ No newline at end of file diff --git a/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/DeleteNodeAction.cs.meta b/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/DeleteNodeAction.cs.meta new file mode 100644 index 00000000..4a62ff87 --- /dev/null +++ b/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/DeleteNodeAction.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 5653c134c34e88b4a971e196d0f42586 +timeCreated: 1501781608 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/DragNode.cs b/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/DragNode.cs new file mode 100644 index 00000000..9e4d89d0 --- /dev/null +++ b/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/DragNode.cs @@ -0,0 +1,42 @@ + +using UnityEngine; + +namespace UNEB +{ + public class DragNode : MultiStageAction + { + private Node _draggingNode; + + private Vector2 _startDragPos, _endDragPos; + + public const float dragSpeed = 1f; + + public override void Undo() + { + _draggingNode.bodyRect.position = _startDragPos; + } + + public override void Redo() + { + _draggingNode.bodyRect.position = _endDragPos; + } + + public override void Do() + { + NodeEditor editor = manager.window.editor; + _draggingNode.bodyRect.position += Event.current.delta * editor.ZoomScale * dragSpeed; + } + + public override void OnActionStart() + { + _draggingNode = manager.window.state.selectedNode; + _startDragPos = _draggingNode.bodyRect.position; + } + + public override bool OnActionEnd() + { + _endDragPos = _draggingNode.bodyRect.position; + return true; + } + } +}
\ No newline at end of file diff --git a/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/DragNode.cs.meta b/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/DragNode.cs.meta new file mode 100644 index 00000000..efbc5101 --- /dev/null +++ b/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/DragNode.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 2075b47cce2d17147a02ec2483f38698 +timeCreated: 1501792802 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/MultiStageAction.cs b/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/MultiStageAction.cs new file mode 100644 index 00000000..b7fd41fb --- /dev/null +++ b/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/MultiStageAction.cs @@ -0,0 +1,18 @@ + +using System.Collections.Generic; +using UnityEngine; + +namespace UNEB +{ + public abstract class MultiStageAction : UndoableAction + { + + public abstract void OnActionStart(); + + /// <summary> + /// Returns true if the action completed succesfully. + /// </summary> + /// <returns></returns> + public abstract bool OnActionEnd(); + } +}
\ No newline at end of file diff --git a/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/MultiStageAction.cs.meta b/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/MultiStageAction.cs.meta new file mode 100644 index 00000000..bc4a1081 --- /dev/null +++ b/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/MultiStageAction.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 211fa78df12a1d440a6aa57ca343d5ab +timeCreated: 1501892778 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/RemoveConnection.cs b/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/RemoveConnection.cs new file mode 100644 index 00000000..341e9f63 --- /dev/null +++ b/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/RemoveConnection.cs @@ -0,0 +1,32 @@ + +using UnityEngine; + +namespace UNEB +{ + public class RemoveConnection : UndoableAction + { + + private NodeOutput _output; + private NodeInput _input; + + public override void Do() + { + _input = manager.window.state.selectedInput; + _output = _input.Outputs[0]; + + _output.Remove(_input); + + manager.window.state.selectedInput = null; + } + + public override void Undo() + { + _output.Add(_input); + } + + public override void Redo() + { + _output.Remove(_input); + } + } +}
\ No newline at end of file diff --git a/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/RemoveConnection.cs.meta b/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/RemoveConnection.cs.meta new file mode 100644 index 00000000..fec8868b --- /dev/null +++ b/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/RemoveConnection.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: d1b449f51ea4820498a2b38021e549d7 +timeCreated: 1501913312 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/UndoableAction.cs b/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/UndoableAction.cs new file mode 100644 index 00000000..53857a9f --- /dev/null +++ b/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/UndoableAction.cs @@ -0,0 +1,12 @@ + +using System.Collections.Generic; +using UnityEngine; + +namespace UNEB +{ + public abstract class UndoableAction : ActionBase + { + public abstract void Undo(); + public abstract void Redo(); + } +}
\ No newline at end of file diff --git a/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/UndoableAction.cs.meta b/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/UndoableAction.cs.meta new file mode 100644 index 00000000..b6e7512b --- /dev/null +++ b/Other/NodeEditorExamples/Assets/UNEB/Editor/Actions/UndoableAction.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 72c02675816f81d4c9d15134303127b4 +timeCreated: 1501892769 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: |