-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
#479 A node marking action is implemented.
The user can toggle the marking on any node. A marked node will have a white sphere hovering above it.
- Loading branch information
Showing
8 changed files
with
376 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
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 toggle a marking for a node. | ||
/// The mark is a white sphere, hovering over the node. | ||
/// </summary> | ||
internal class MarkAction : AbstractPlayerAction | ||
{ | ||
/// <summary> | ||
/// If the user clicks with the mouse hitting a game object representing a node, | ||
/// this node gets a mark. | ||
/// <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 is the Node which gets a mark as a child. | ||
GameObject parent = raycastHit.collider.gameObject; | ||
/// the position of the Node is used for the mark. | ||
Vector3 position = parent.transform.position; | ||
/// the scale of the Node is used to make the Sphere fit into the ground space of the node. | ||
Vector3 scale = FindSize(parent); | ||
GameNodeMarker.Mark(parent, position: position, worldSpaceScale: scale); | ||
memento = new Memento(parent, position: position, scale: scale); | ||
new MarkNetAction(parentID: memento.Parent.name, memento.Position, memento.Scale).Execute(); | ||
result = true; | ||
currentState = ReversibleAction.Progress.Completed; | ||
} | ||
return result; | ||
} | ||
|
||
/// <summary> | ||
/// Memento capturing the data necessary to re-do this action. | ||
/// </summary> | ||
private Memento memento; | ||
|
||
/// <summary> | ||
/// The information we need to re-add a mark whose addition was undone. | ||
/// </summary> | ||
private struct Memento | ||
{ | ||
/// <summary> | ||
/// The node which is marked. | ||
/// </summary> | ||
public readonly GameObject Parent; | ||
/// <summary> | ||
/// The position of the mark in world space. | ||
/// </summary> | ||
public readonly Vector3 Position; | ||
/// <summary> | ||
/// The scale of the new mark in world space. | ||
/// </summary> | ||
public readonly Vector3 Scale; | ||
|
||
/// <summary> | ||
/// Constructor setting the information necessary to re-do this action. | ||
/// </summary> | ||
/// <param name="parent">The node which is marked.</param> | ||
/// <param name="position">position of the mark in world space.</param> | ||
/// <param name="scale">scale of the mark in world space.</param> | ||
public Memento(GameObject parent, Vector3 position, Vector3 scale) | ||
{ | ||
Parent = parent; | ||
Position = position; | ||
Scale = scale; | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Returns a scale of a cube that fits into the ground area of <paramref name="parent"/>. | ||
/// </summary> | ||
/// <param name="parent">parent in which ground area to fit the cube.</param> | ||
/// <returns>the scale of a cube that fits into the ground area of <paramref name="parent"/>.</returns> | ||
private static Vector3 FindSize(GameObject parent) | ||
{ | ||
Vector3 result = parent.transform.lossyScale; | ||
/// The ground area of the result must be a square. | ||
if (result.x > result.z) | ||
{ | ||
result.x = result.z; | ||
} | ||
else | ||
{ | ||
result.z = result.x; | ||
} | ||
/// make the square a cube. | ||
result.y = result.z; | ||
return result; | ||
} | ||
|
||
/// <summary> | ||
/// Undoes this MarkAction. | ||
/// </summary> | ||
public override void Undo() | ||
{ | ||
base.Undo(); | ||
|
||
GameNodeMarker.Mark(memento.Parent, position: memento.Position, worldSpaceScale: memento.Scale); | ||
new MarkNetAction(parentID: memento.Parent.name, memento.Position, memento.Scale).Execute(); | ||
|
||
} | ||
|
||
/// <summary> | ||
/// Redoes this MarkAction. | ||
/// </summary> | ||
public override void Redo() | ||
{ | ||
base.Redo(); | ||
GameNodeMarker.Mark(memento.Parent, position: memento.Position, worldSpaceScale: memento.Scale); | ||
new MarkNetAction(parentID: memento.Parent.name, memento.Position, memento.Scale).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.Parent.name | ||
}; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
fileFormatVersion: 2 | ||
guid: 0a8cbb4ba5a85514e95f72f80e62bf06 | ||
MonoImporter: | ||
externalObjects: {} | ||
serializedVersion: 2 | ||
defaultReferences: [] | ||
executionOrder: 0 | ||
icon: {instanceID: 0} | ||
userData: | ||
assetBundleName: | ||
assetBundleVariant: |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
using System; | ||
using SEE.DataModel.DG; | ||
using SEE.Game.City; | ||
using SEE.GO; | ||
using UnityEngine; | ||
|
||
namespace SEE.Game | ||
{ | ||
/// <summary> | ||
/// Toggle a marking for a node. | ||
/// The mark is a white sphere, hovering over the node. | ||
/// </summary> | ||
public static class GameNodeMarker | ||
{ | ||
|
||
/// <summary> | ||
/// Toggles the marking of a node <paramref name="parent"/> at the | ||
/// given <paramref name="position"/> with the given <paramref name="worldSpaceScale"/>. | ||
/// The marking is a new GameObject, added as a child of the Node. | ||
/// The GameObject is represented by a white hovering sphere above the node which is marked. | ||
/// | ||
/// Precondition: <paramref name="parent"/> must have a valid node reference. | ||
/// </summary> | ||
/// <param name="parent">node which marking will be toggled.</param> | ||
/// <param name="position">the position in world space for the center point of the mark.</param> | ||
/// <param name="worldSpaceScale">the scale in world space of the mark.</param> | ||
/// <exception cref="Exception">thrown if <paramref name="parent"/> is not contained in a code city.</exception> | ||
public static void Mark(GameObject parent, Vector3 position, Vector3 worldSpaceScale) | ||
{ | ||
SEECity city = parent.ContainingCity() as SEECity; | ||
if (city != null) | ||
{ | ||
/// Gets the actual GameObject which represents the node. | ||
Node parentNode = parent.GetNode(); | ||
/// Mark if the node is not marked. | ||
if (!parentNode.IsMarked) { | ||
GameObject sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere); | ||
sphere.transform.localScale = worldSpaceScale; | ||
/// The sphere is located at the golden ratio with the smaller part being the distance to the sphere, and the bigger part being the sphere. | ||
/// Mathematical explanation of the position: | ||
/// parent.transform.position.y = middle of node. | ||
/// parent.transform.lossyScale.y/2 = half of the height of the node. | ||
/// worldSpaceScale.y*0.5 = half of the height of the sphere. | ||
/// because the position of the sphere describes the middle of itself, we add all 3 values together, to put the mark ontop of the node. | ||
/// We finally add worldSpaceScale.y*0.38196601125F to get the smaller part of the golden ratio in relation to the size of the sphere as distance to the node. | ||
/// Because "worldSpaceScale.y*0.5 + worldSpaceScale.y*0.38196601125F" is the same result as "worldSpaceScale.y*0.88196601125F", we simplify it. | ||
sphere.transform.position = new Vector3(position.x, parent.transform.position.y + parent.transform.lossyScale.y/2 + worldSpaceScale.y* 0.88196601125F, position.z); | ||
sphere.transform.SetParent(parent.transform); | ||
Portal.SetPortal(city.gameObject, gameObject: sphere); | ||
parentNode.IsMarked = true; | ||
parentNode.Marking = sphere; | ||
return; | ||
} | ||
/// unmark if the node is marked. | ||
else | ||
{ | ||
parentNode.IsMarked = false; | ||
GameObject marking = parentNode.Marking; | ||
GameObject.Destroy(marking); | ||
return; | ||
} | ||
|
||
} | ||
else | ||
{ | ||
throw new Exception($"The node {parent.name} is not contained in a code city."); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
fileFormatVersion: 2 | ||
guid: 9159bf7682c3e9d47ae67bd910089581 | ||
MonoImporter: | ||
externalObjects: {} | ||
serializedVersion: 2 | ||
defaultReferences: [] | ||
executionOrder: 0 | ||
icon: {instanceID: 0} | ||
userData: | ||
assetBundleName: | ||
assetBundleVariant: |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
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 node which is marked. | ||
/// </summary> | ||
public string ParentID; | ||
|
||
/// <summary> | ||
/// The position of the mark in world space. | ||
/// </summary> | ||
public Vector3 Position; | ||
|
||
/// <summary> | ||
/// The scale of the new mark in world space. | ||
/// </summary> | ||
public Vector3 Scale; | ||
|
||
/// <summary> | ||
/// Constructor. | ||
/// </summary> | ||
/// <param name="parentID">unique ID of the Node which will get marked.</param> | ||
/// <param name="position">the position for the mark.</param> | ||
/// <param name="scale">the scale of the mark in world space.</param> | ||
public MarkNetAction | ||
(string parentID, | ||
Vector3 position, | ||
Vector3 scale) | ||
: base() | ||
{ | ||
this.ParentID = parentID; | ||
this.Position = position; | ||
this.Scale = scale; | ||
} | ||
|
||
/// <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> | ||
/// Toggle the marking of the node on each client. | ||
/// </summary> | ||
protected override void ExecuteOnClient() | ||
{ | ||
if (!IsRequester()) | ||
{ | ||
GameObject parent = GraphElementIDMap.Find(ParentID); | ||
if (parent == null) | ||
{ | ||
throw new System.Exception($"There is no node with the ID {ParentID}."); | ||
} | ||
GameNodeMarker.Mark(parent, Position, Scale); | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.