From ed4e03013690bc7475a20b5576ebdfc8ce73c10e Mon Sep 17 00:00:00 2001 From: Hannes Kuss Date: Sun, 23 Oct 2022 14:00:37 +0200 Subject: [PATCH 1/3] Undo functionality for MarkAction.cs started (needs a bit more testing/debugging) Also changed GetMarkerOfNode(). Now the method is looking if the Node has a Child with the -MARKED suffix --- Assets/SEE/Controls/Actions/MarkAction.cs | 158 ++++++++++++++++++---- Assets/SEE/Net/Actions/MarkNetAction.cs | 32 +++++ 2 files changed, 166 insertions(+), 24 deletions(-) create mode 100644 Assets/SEE/Net/Actions/MarkNetAction.cs diff --git a/Assets/SEE/Controls/Actions/MarkAction.cs b/Assets/SEE/Controls/Actions/MarkAction.cs index e291564986..c33e21794f 100644 --- a/Assets/SEE/Controls/Actions/MarkAction.cs +++ b/Assets/SEE/Controls/Actions/MarkAction.cs @@ -1,6 +1,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Linq; using Dissonance; using SEE.Game; using SEE.GO; @@ -9,51 +10,121 @@ namespace SEE.Controls.Actions { - internal class NodeNotFoundException : Exception { - } - - /// + + /// /// Author: Hannes Kuss - /// + /// /// An action for selecting nodes in a code-city. /// public class MarkAction : AbstractPlayerAction { - public override HashSet GetChangedObjects() - { - throw new System.NotImplementedException(); - } + public override HashSet GetChangedObjects() => + new HashSet(markedNodes.Select(x => x.Item1.ID()).ToList()); - // Tupel (node, markerSphere) + private const string MARKER_NAME_SUFFIX = "-MARKED"; + + // Tuple for marked nodes (node, markerSphere) private List<(GameObject, GameObject)> markedNodes = new List<(GameObject, GameObject)>(); + // A stack with a tupel (node, marked) for keeping track of the last actions/interactions with nodes. + // marked is true, when a node was marked in that action and false if unmarked + private Stack<(GameObject, bool)> undoMarkers = new Stack<(GameObject, bool)>(); + + // A stack with a tupel (node, marked) + // marked is true, when a node was marked in that action and false if unmarked + private Stack<(GameObject, bool)> redoMarkers = new Stack<(GameObject, bool)>(); + + // Is set, when the undo/redo stack should be cleaned up next time + private bool doCleanUpUndoNextTime; public static MarkAction CreateMarkAction() => new MarkAction(); - public override void Undo() + public static ReversibleAction CreateReversibleAction() { - base.Undo(); + return new MarkAction(); } - public override void Redo() + public override void Undo() { - base.Redo(); - } + //base.Undo(); + doCleanUpUndoNextTime = true; + var lastAction = undoMarkers.Pop(); - public override ActionStateType GetActionStateType() - { - throw new System.NotImplementedException(); + // When the last action was, to mark a node, then the node should be unmarked + if (lastAction.Item2) + { + GameObject node = lastAction.Item1; + GameObject marker = GetMarkerOfNode(node) ?? throw new ArgumentNullException("GetMarkerOfNode(node)"); + // Destroy marker + Destroyer.DestroyGameObject(marker); + + // probably redundant but just to make sure. + // TODO: Remove this later + if (IsNodeMarked(node)) + { + RemoveNodeFromMarked(node); + // Add the node to the redo list as removed + redoMarkers.Push((node, false)); + } + } + // When the last action was, to unmark a node, then the node should be marked again + else + { + GameObject node = lastAction.Item1; + string sphereTag = node.tag += MARKER_NAME_SUFFIX; + GameObject marker = GameNodeMarker.CreateMarker(node); + marker.name = sphereTag; + markedNodes.Add((node, marker)); + // Add the node to the redo list as added + redoMarkers.Push((node, true)); + } } - public override ReversibleAction NewInstance() + public override void Redo() { - throw new System.NotImplementedException(); + var lastAction = undoMarkers.Pop(); + + // Properly also redundant but also just to make sure. + doCleanUpUndoNextTime = true; + + // When the last action was, to mark a node, then the node should be unmarked + if (lastAction.Item2) + { + GameObject node = lastAction.Item1; + GameObject marker = GetMarkerOfNode(node) ?? throw new ArgumentNullException("GetMarkerOfNode(node)"); + // Destroy marker + Destroyer.DestroyGameObject(marker); + + // probably redundant but just to make sure. + // TODO: Remove this later + if (IsNodeMarked(node)) + { + RemoveNodeFromMarked(node); + // Add the node to the redo list as removed + undoMarkers.Push((node, false)); + } + } + // When the last action was, to unmark a node, then the node should be marked again + else + { + GameObject node = lastAction.Item1; + string sphereTag = node.tag += MARKER_NAME_SUFFIX; + GameObject marker = GameNodeMarker.CreateMarker(node); + marker.name = sphereTag; + markedNodes.Add((node, marker)); + // Add the node to the redo list as added + undoMarkers.Push((node, true)); + } } - + + public override ActionStateType GetActionStateType() => ActionStateType.MarkNode; + + public override ReversibleAction NewInstance() => new MarkAction(); + /// /// Checks if a node is marked /// @@ -72,6 +143,12 @@ private bool IsNodeMarked(GameObject node) return false; } + /// + /// Removes a node from the marked list if the node exists and was marked. + /// + /// The marker sphere is untouched in this method. + /// + /// The node you want to remove private void RemoveNodeFromMarked(GameObject node) { foreach (var i in markedNodes) @@ -83,15 +160,28 @@ private void RemoveNodeFromMarked(GameObject node) } } + /// + /// Returns the Marker sphere of a node + /// + /// + /// private GameObject GetMarkerOfNode(GameObject node) { + for (int i = 0; i < node.transform.childCount; i++) + { + if (node.transform.GetChild(i).name.EndsWith("MARKED")) + { + return node.transform.GetChild(i).gameObject; + } + } + /* foreach (var i in markedNodes) { if (i.Item1 == node) { return i.Item2; } - } + }*/ return null; } @@ -100,6 +190,16 @@ private GameObject GetMarkerOfNode(GameObject node) public override bool Update() { var ret = true; + if (Input.GetKeyDown(KeyCode.U)) + { + if (Input.GetKeyDown(KeyCode.LeftShift)) + { + Redo(); + } + + Undo(); + } + // When the user clicks the left mouse button and is pointing to a node if (Input.GetMouseButtonDown(0) && Raycasting.RaycastGraphElement(out RaycastHit raycastHit, out GraphElementRef _) == @@ -107,17 +207,27 @@ public override bool Update() { GameObject cnode = raycastHit.collider.gameObject; + if (doCleanUpUndoNextTime) + { + undoMarkers.Clear(); + redoMarkers.Clear(); + doCleanUpUndoNextTime = false; + } + if (!IsNodeMarked(cnode)) { // Extract the code city node. - string sphereTag = cnode.tag += "-MARKED"; + string sphereTag = cnode.tag += MARKER_NAME_SUFFIX; GameObject marker = GameNodeMarker.CreateMarker(cnode); marker.name = sphereTag; markedNodes.Add((cnode, marker)); + + undoMarkers.Push((marker, true)); } else { - GameObject marker = GetMarkerOfNode(cnode) ?? throw new ArgumentNullException("GetMarkerOfNode(cnode)"); + GameObject marker = GetMarkerOfNode(cnode) ?? + throw new ArgumentNullException("GetMarkerOfNode(cnode)"); Destroyer.DestroyGameObject(marker); RemoveNodeFromMarked(cnode); } diff --git a/Assets/SEE/Net/Actions/MarkNetAction.cs b/Assets/SEE/Net/Actions/MarkNetAction.cs new file mode 100644 index 0000000000..e12e49435d --- /dev/null +++ b/Assets/SEE/Net/Actions/MarkNetAction.cs @@ -0,0 +1,32 @@ +using SEE.Game; +using UnityEngine; + +namespace SEE.Net.Actions +{ + public class MarkNetAction : AbstractNetAction + { + + public GameObject Node { get; } + public bool Added { get; } + + public MarkNetAction(GameObject node, bool added) + { + Node = node; + Added = added; + } + + protected override void ExecuteOnServer() + { + throw new System.NotImplementedException(); + } + + protected override void ExecuteOnClient() + { + if (Added) + { + GameNodeMarker.CreateMarker(Node); + } + throw new System.NotImplementedException(); + } + } +} \ No newline at end of file From 82d78b16fed8961049d1a9e8d07004c4a88c13d0 Mon Sep 17 00:00:00 2001 From: Hannes Kuss Date: Sun, 23 Oct 2022 15:45:16 +0200 Subject: [PATCH 2/3] Fixed unmarking of node --- Assets/SEE/Controls/Actions/MarkAction.cs | 28 ++++++++++------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/Assets/SEE/Controls/Actions/MarkAction.cs b/Assets/SEE/Controls/Actions/MarkAction.cs index c33e21794f..1da4aa028f 100644 --- a/Assets/SEE/Controls/Actions/MarkAction.cs +++ b/Assets/SEE/Controls/Actions/MarkAction.cs @@ -25,8 +25,8 @@ public class MarkAction : AbstractPlayerAction public override HashSet GetChangedObjects() => new HashSet(markedNodes.Select(x => x.Item1.ID()).ToList()); - private const string MARKER_NAME_SUFFIX = "-MARKED"; - + internal static string MARKER_NAME_SUFFIX = "-MARKED"; + // Tuple for marked nodes (node, markerSphere) private List<(GameObject, GameObject)> markedNodes = new List<(GameObject, GameObject)>(); @@ -87,7 +87,7 @@ public override void Undo() public override void Redo() { var lastAction = undoMarkers.Pop(); - + // Properly also redundant but also just to make sure. doCleanUpUndoNextTime = true; @@ -112,7 +112,7 @@ public override void Redo() else { GameObject node = lastAction.Item1; - string sphereTag = node.tag += MARKER_NAME_SUFFIX; + string sphereTag = node.name += MARKER_NAME_SUFFIX; GameObject marker = GameNodeMarker.CreateMarker(node); marker.name = sphereTag; markedNodes.Add((node, marker)); @@ -132,9 +132,10 @@ public override void Redo() /// private bool IsNodeMarked(GameObject node) { - foreach (var i in markedNodes) + for (int i = 0; i < node.transform.childCount; i++) { - if (i.Item1 == node) + // When a the child has the tag -MARKED + if (node.transform.GetChild(i).name.EndsWith(MARKER_NAME_SUFFIX)) { return true; } @@ -164,7 +165,8 @@ private void RemoveNodeFromMarked(GameObject node) /// Returns the Marker sphere of a node /// /// - /// + /// The marker object of the node. + /// Can be null private GameObject GetMarkerOfNode(GameObject node) { for (int i = 0; i < node.transform.childCount; i++) @@ -174,14 +176,6 @@ private GameObject GetMarkerOfNode(GameObject node) return node.transform.GetChild(i).gameObject; } } - /* - foreach (var i in markedNodes) - { - if (i.Item1 == node) - { - return i.Item2; - } - }*/ return null; } @@ -214,16 +208,18 @@ public override bool Update() doCleanUpUndoNextTime = false; } + // When the clicked node wasn't marked until now if (!IsNodeMarked(cnode)) { // Extract the code city node. - string sphereTag = cnode.tag += MARKER_NAME_SUFFIX; + string sphereTag = cnode.name += MARKER_NAME_SUFFIX; GameObject marker = GameNodeMarker.CreateMarker(cnode); marker.name = sphereTag; markedNodes.Add((cnode, marker)); undoMarkers.Push((marker, true)); } + // When the clicked node was already marked else { GameObject marker = GetMarkerOfNode(cnode) ?? From a0a574ee454cd932a0d2ecd915b8d8ec3cfc99cf Mon Sep 17 00:00:00 2001 From: Hannes Kuss Date: Mon, 24 Oct 2022 07:05:55 +0200 Subject: [PATCH 3/3] MarkNetAction now also works when a node was unmarked --- Assets/SEE/Controls/Actions/MarkAction.cs | 21 +++++++++++++++++---- Assets/SEE/Net/Actions/MarkNetAction.cs | 5 ++++- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/Assets/SEE/Controls/Actions/MarkAction.cs b/Assets/SEE/Controls/Actions/MarkAction.cs index 1da4aa028f..a554d34dd6 100644 --- a/Assets/SEE/Controls/Actions/MarkAction.cs +++ b/Assets/SEE/Controls/Actions/MarkAction.cs @@ -18,13 +18,22 @@ internal class NodeNotFoundException : Exception /// /// Author: Hannes Kuss /// - /// An action for selecting nodes in a code-city. + /// An action for selecting nodes in a code-city. + /// + /// The selected node are marked with a unity-primitive node. + /// The marked nodes can also be deselected with another click. + /// + /// + /// The Action should also keep full track of the users interactions. + /// So all actions can be undone/redone /// public class MarkAction : AbstractPlayerAction { public override HashSet GetChangedObjects() => new HashSet(markedNodes.Select(x => x.Item1.ID()).ToList()); + // Internal visibility because GameNodeMarker also uses it. + // This suffix is appended to all node markers GameObject names internal static string MARKER_NAME_SUFFIX = "-MARKED"; // Tuple for marked nodes (node, markerSphere) @@ -190,8 +199,10 @@ public override bool Update() { Redo(); } - - Undo(); + else + { + Undo(); + } } // When the user clicks the left mouse button and is pointing to a node @@ -217,7 +228,7 @@ public override bool Update() marker.name = sphereTag; markedNodes.Add((cnode, marker)); - undoMarkers.Push((marker, true)); + undoMarkers.Push((cnode, true)); } // When the clicked node was already marked else @@ -226,6 +237,8 @@ public override bool Update() throw new ArgumentNullException("GetMarkerOfNode(cnode)"); Destroyer.DestroyGameObject(marker); RemoveNodeFromMarked(cnode); + + undoMarkers.Push((cnode, false)); } } diff --git a/Assets/SEE/Net/Actions/MarkNetAction.cs b/Assets/SEE/Net/Actions/MarkNetAction.cs index e12e49435d..1799dbbdd7 100644 --- a/Assets/SEE/Net/Actions/MarkNetAction.cs +++ b/Assets/SEE/Net/Actions/MarkNetAction.cs @@ -26,7 +26,10 @@ protected override void ExecuteOnClient() { GameNodeMarker.CreateMarker(Node); } - throw new System.NotImplementedException(); + else + { + GameNodeMarker.RemoveMarker(Node); + } } } } \ No newline at end of file