Skip to content

Commit

Permalink
#479 mark node feature
Browse files Browse the repository at this point in the history
all in one: create MarkAction.cs, MarkNetAction.cs and GameNodeMarker.cs, also add the mark action in ActionStateType.cs
  • Loading branch information
SchrammJonas committed Oct 27, 2022
1 parent 240742a commit f719d66
Show file tree
Hide file tree
Showing 49 changed files with 11,843 additions and 1,793 deletions.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Assets/OfficeArchviz.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file not shown.
Binary file not shown.
4 changes: 4 additions & 0 deletions Assets/SEE/Controls/Actions/ActionStateType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ public class ActionStateType
new ActionStateType(9, "Draw", "Draw a line",
Color.magenta.Darker(), "Materials/ModernUIPack/Pencil",
DrawAction.CreateReversibleAction);
public static ActionStateType Mark { get; } =
new ActionStateType(10, "Mark", "Mark a node",
Color.magenta, "Materials/ModernUIPack/Pencil",
MarkAction.CreateReversibleAction);
#endregion

/// <summary>
Expand Down
140 changes: 140 additions & 0 deletions Assets/SEE/Controls/Actions/MarkAction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
using System.Collections.Generic;
using SEE.Game;
using SEE.GO;
using SEE.Net;
using SEE.Utils;
using UnityEngine;

namespace SEE.Controls.Actions
{
/// <summary>
/// Action to visually mark/unmark a node as selected via a sphere floating above it.
/// </summary>
internal class MarkAction : AbstractPlayerAction
{
/// <summary>
/// If the user clicks with the mouse hitting a game object representing a graph node,
/// the node's marking status is toggled. Being marked means a sphere is floating above the node.
/// <see cref="ReversibleAction.Update"/>.
/// </summary>
/// <returns>true if completed</returns>
public override bool Update()
{
bool result = false;

// FIXME: Needs adaptation for VR where no mouse is available.
if (Input.GetMouseButtonDown(0)
&& Raycasting.RaycastGraphElement(out RaycastHit raycastHit, out GraphElementRef _) == HitGraphElement.Node)
{
// the hit object that is either selected or unselected
GameObject targetNode = raycastHit.collider.gameObject;

GameObject markerSphere = GameNodeMarker.TryMarking(targetNode);

memento = new Memento(targetNode);

// propagate the MarkAction to other clients
new MarkNetAction(markerSphere).Execute();

// MarkAction completed successfully
result = true;

currentState = ReversibleAction.Progress.Completed;

}
return result;
}

/// <summary>
/// Memento capturing the data necessary to re-do this marking action.
/// </summary>
private Memento memento;

/// <summary>
/// The information we need to re-add a marker whose addition was undone.
/// </summary>
private struct Memento
{
/// <summary>
/// The node marked by the marker.
/// </summary>
public readonly GameObject MarkedNode;

/// <summary>
/// The node ID for the added node. It must be kept to re-use the
/// original name of the node in Redo().
/// </summary>
public string MarkerID;

/// <summary>
/// Constructor setting the information necessary to re-do this action.
/// </summary>
/// <param name="parent">the node targeted by our MarkAction</param>
public Memento(GameObject markedNode)
{
MarkedNode = markedNode;
MarkerID = markedNode.ID();
}
}

/// <summary>
/// Undoes this MarkAction.
/// </summary>
public override void Undo()
{
base.Undo();
GameNodeMarker.TryMarking(memento.MarkedNode);
new MarkNetAction(memento.MarkedNode).Execute();
}

/// <summary>
/// Redoes this MarkAction.
/// </summary>
public override void Redo()
{
base.Redo();
GameNodeMarker.TryMarking(memento.MarkedNode);
new MarkNetAction(memento.MarkedNode).Execute();
}

/// <summary>
/// Returns a new instance of <see cref="MarkAction"/>.
/// </summary>
/// <returns>new instance</returns>
public static ReversibleAction CreateReversibleAction()
{
return new MarkAction();
}

/// <summary>
/// Returns a new instance of <see cref="MarkAction"/>.
/// </summary>
/// <returns>new instance</returns>
public override ReversibleAction NewInstance()
{
return CreateReversibleAction();
}

/// <summary>
/// Returns the <see cref="ActionStateType"/> of this action.
/// </summary>
/// <returns><see cref="ActionStateType.NewNode"/></returns>
public override ActionStateType GetActionStateType()
{
return ActionStateType.Mark;
}

/// <summary>
/// Returns all IDs of gameObjects manipulated by this action.
/// </summary>
/// <returns>all IDs of gameObjects manipulated by this action</returns>
public override HashSet<string> GetChangedObjects()
{
return new HashSet<string>
{
memento.MarkedNode.name,
memento.MarkerID
};
}
}
}
11 changes: 11 additions & 0 deletions Assets/SEE/Controls/Actions/MarkAction.cs.meta
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c6e496ba734f3a9449f72dc8f07fe2da
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
100 changes: 100 additions & 0 deletions Assets/SEE/Game/GameNodeMarker.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
using System;
using SEE.GO;
using SEE.Utils;
using UnityEngine;

namespace SEE.Game
{
/// <summary>
/// Creates new game objects representing graph nodes or deleting these again,
/// respectively.
/// </summary>
public static class GameNodeMarker
{

/// <summary>
/// Check the transfered target node whether it was been marked already, and toggle it to the other state.
/// <param name="targetNode">the node that was targeted for marking.</param>
/// </summary>
/// <returns>new instance</returns>
public static GameObject TryMarking(GameObject targetNode)
{

// save the sphere that was used as a marker
GameObject markerSphere = null;

// iterate over all children of the targeted node
foreach (Transform child in targetNode.transform)
{
// exit the loop if a (/the) marker was found
if (child.name == "MarkerSphere" + targetNode.name)
{
markerSphere = child.gameObject;
break;
}
}

if (markerSphere != null)
{
// delete existing marker sphere
Destroyer.DestroyGameObject(markerSphere);
return null;
}
else
{
// create a new marker sphere because there is none currently
GameObject newMarkerSphere = GameObject.CreatePrimitive(PrimitiveType.Sphere);

// the scale of the marker sphere depends on the scale of the marked node
newMarkerSphere.transform.localScale = CalculateMarkerSize(targetNode);

// the position of the marker sphere is above the marked node
newMarkerSphere.transform.position = CalculateMarkerPosition(targetNode);

// FIXME: check back with lead devs whether this consitutes desired behaviour
// ensure markers are not blocking us from unmarking or marking other nodes
newMarkerSphere.GetComponent<SphereCollider>().radius = 0;

// FIXME: marker color could be a configuration option
newMarkerSphere.SetColor(Color.green);

// marker spheres can be recognized by their name-prefix "MarkerSphere"
newMarkerSphere.name = "MarkerSphere" + targetNode.name;

// assign the marker sphere to the node it is marking
newMarkerSphere.transform.SetParent(targetNode.transform);

return newMarkerSphere;
}
}

/// <summary>
/// Returns the scale of the marker sphere based on the area of the <paramref name="targetNode"/>.
/// </summary>
/// <param name="targetNode">The marked node relative to which the marker sphere is positioned and scaled.</param>
/// <returns>The scale of the marker sphere.</returns>
private static Vector3 CalculateMarkerSize(GameObject targetNode)
{
// calculate the maximum ground area
float verticalLength = Math.Min(targetNode.transform.lossyScale.x,
targetNode.transform.lossyScale.z);

// return the marker size as a cube
return Vector3.one * verticalLength;
}

/// <summary>
/// Returns the position of the marker sphere based on the position of the <paramref name="targetNode"/>.
/// </summary>
/// <param name="targetNode">The marked node relative to which the marker sphere is positioned and scaled.</param>
/// <returns>The position of the marker sphere.</returns>
private static Vector3 CalculateMarkerPosition(GameObject targetNode)
{
return new Vector3(targetNode.transform.position.x,
targetNode.transform.position.y
+ targetNode.transform.lossyScale.y / 2
+ CalculateMarkerSize(targetNode).y,
targetNode.transform.position.z);
}
}
}
11 changes: 11 additions & 0 deletions Assets/SEE/Game/GameNodeMarker.cs.meta
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b91a45137cc401b4a94beddcdd11df09
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
51 changes: 51 additions & 0 deletions Assets/SEE/Net/Actions/MarkNetAction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using SEE.Game;
using UnityEngine;

namespace SEE.Net
{
/// <summary>
/// This class is responsible for marking a node via network from one client to all others and
/// to the server.
/// </summary>
public class MarkNetAction : AbstractNetAction
{
// Note: All attributes are made public so that they will be serialized
// for the network transfer.

/// <summary>
/// The GameObject that is targeted for marking.
/// </summary>
public GameObject TargetNode;

/// <summary>
/// Constructor.
/// </summary>
/// <param name="targetNode">the node that was targeted for marking.</param>
public MarkNetAction
(GameObject targetNode)
: base()
{
this.TargetNode = targetNode;
}

/// <summary>
/// Things to execute on the server (none for this class). Necessary because it is abstract
/// in the superclass.
/// </summary>
protected override void ExecuteOnServer()
{
// Intentionally left blank.
}

/// <summary>
/// Tries marking on each client.
/// </summary>
protected override void ExecuteOnClient()
{
if (!IsRequester())
{
GameObject markerSphere = GameNodeMarker.TryMarking(TargetNode);
}
}
}
}
11 changes: 11 additions & 0 deletions Assets/SEE/Net/Actions/MarkNetAction.cs.meta
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 24533459853e8e849a8d32b16b21ede0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
Loading

0 comments on commit f719d66

Please sign in to comment.