From a6a40da39f9472eca142d8d971438dcb761263c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zaj=C4=85c?= Date: Thu, 23 Apr 2020 04:03:58 +0100 Subject: [PATCH] Adds comments, cleanup and MiniEngineAO license file --- Assets/Code/ClueHUD.cs | 1 + Assets/Code/ExecutionStatus.cs | 3 +- Assets/Code/Levels/Car.cs | 2 + Assets/Code/Levels/CarProgramController.cs | 14 +++++ Assets/Code/Levels/DoorProgramController.cs | 5 ++ Assets/Code/Levels/Fan.cs | 6 ++ Assets/Code/Levels/FanProgramController.cs | 5 ++ Assets/Code/Levels/GridCell.cs | 3 + .../SampleScene/PlatformProgramController.cs | 30 +++++++-- Assets/Code/Levels/UnlockableDoorWithLock.cs | 2 + Assets/Code/ProgramController.cs | 30 ++++++++- .../Code/ProgramEditor/EditorDraggableNode.cs | 27 +++++--- Assets/Code/ProgramEditor/EditorProgram.cs | 61 +++++++++---------- Assets/Code/ProgramEditor/NodeBase.cs | 22 ++----- .../NodeTypes/CodeBlocks/LogicalBlock.cs | 6 ++ .../NodeTypes/CodeBlocks/WhileLoop.cs | 1 + .../NodeTypes/FunctionCallBase.cs | 47 +++----------- .../NodeTypes/FunctionParameter.cs | 2 +- .../NodeTypes/Operators/AllocateArray.cs | 7 +++ .../Operators/ArithmeticOperationBase.cs | 3 + .../NodeTypes/Operators/AssignValue.cs | 2 +- Assets/Code/TerminalLookAtCamera.cs | 1 + Assets/Code/UnlockableDoor.cs | 1 + LICENSE-MiniEngineAO | 21 +++++++ 24 files changed, 195 insertions(+), 107 deletions(-) create mode 100644 LICENSE-MiniEngineAO diff --git a/Assets/Code/ClueHUD.cs b/Assets/Code/ClueHUD.cs index 3682b22..25b0508 100644 --- a/Assets/Code/ClueHUD.cs +++ b/Assets/Code/ClueHUD.cs @@ -3,6 +3,7 @@ using UnityEngine; using UnityEngine.UI; +// On-screen help prompts, dynamically adapting to the situation public class ClueHUD : MonoBehaviour { public GameObject FPPTerminalPrompts; diff --git a/Assets/Code/ExecutionStatus.cs b/Assets/Code/ExecutionStatus.cs index 56617b5..c4a2f50 100644 --- a/Assets/Code/ExecutionStatus.cs +++ b/Assets/Code/ExecutionStatus.cs @@ -1,4 +1,5 @@ -public class ExecutionStatus +// Status returned after handling a node +public class ExecutionStatus { public bool success; // Was the execution successful? public bool handover; // Should the execution be handed over to the derived class? diff --git a/Assets/Code/Levels/Car.cs b/Assets/Code/Levels/Car.cs index 8b723c6..55e906e 100644 --- a/Assets/Code/Levels/Car.cs +++ b/Assets/Code/Levels/Car.cs @@ -4,9 +4,11 @@ public class Car : Programmable { + // Direction to move in on the grid public int XDir = 0; public int YDir = 1; + // Starting coordinate and current position on the grid public Vector2 StartingCoord = new Vector2(2, 1); public Vector2 CurrentCoord = new Vector2(2, 1); diff --git a/Assets/Code/Levels/CarProgramController.cs b/Assets/Code/Levels/CarProgramController.cs index 7eabee3..d85e2ee 100644 --- a/Assets/Code/Levels/CarProgramController.cs +++ b/Assets/Code/Levels/CarProgramController.cs @@ -5,27 +5,40 @@ public class CarProgramController : ProgramController { + // Controlled car/robot public Car car; + + // The grid object public GameObject grid; + + // The cells on the grid public GridCell[] cells; + + // Controlled door public UnlockableDoorWithLock doorToUnlock; + + // Destination cell (which unlocks the door) GridCell endCell = null; + // Puzzle-specific functions for the user program protected override Dictionary ControllerFunctions() { return new Dictionary { { "moveForward", new Action(MoveForward) }, { "turnLeft", new Action(TurnLeft) }, { "turnRight", new Action(TurnRight) } }; } + // Rotate clockwise private void TurnRight() { car.TurnRight(); } + // Rotate counter-clockwise private void TurnLeft() { car.TurnLeft(); } + // Move the car in the current direction, if that cell isn't blocked private void MoveForward() { bool reachedEnd = false; @@ -72,6 +85,7 @@ protected override void Start() cells = grid.GetComponentsInChildren(); } + // Handle puzzle-specific function calls if ProgramController.ExecuteNode set the handover flag public override ExecutionStatus ExecuteNode(NodeBase node) { ExecutionStatus baseStatus = base.ExecuteNode(node); diff --git a/Assets/Code/Levels/DoorProgramController.cs b/Assets/Code/Levels/DoorProgramController.cs index e32424a..10bd340 100644 --- a/Assets/Code/Levels/DoorProgramController.cs +++ b/Assets/Code/Levels/DoorProgramController.cs @@ -8,10 +8,14 @@ public class DoorProgramController : ProgramController // Controlled door public GameObject doorObject; + // Timer for processing the setLock function node - this is based on the doorOpenDuration of the controlled door float timer = 0.0f; bool timerActive = false; + // Lock type used for this door (BasicLock, FizzBuzz) public UnlockableDoorWithLock.LockType lockType; + + // For FizzBuzz puzzle: for how many numbers should the fizzbuzz game be played? public int fizzBuzzN = 30; protected virtual void SetLock(string state) @@ -28,6 +32,7 @@ protected virtual void SetLock(string state) timerActive = true; } + // Check the FizzBuzz output when ProgramEnd is reached protected override void ProgramEndCallback() { base.ProgramEndCallback(); diff --git a/Assets/Code/Levels/Fan.cs b/Assets/Code/Levels/Fan.cs index a42e2c0..f165dbc 100644 --- a/Assets/Code/Levels/Fan.cs +++ b/Assets/Code/Levels/Fan.cs @@ -4,13 +4,19 @@ public class Fan : Programmable { + // Current angular velocity public double speed = 0.0; + // Is the fan deadly? (ie. does contact cause respawn?) public bool isDeadly = false; + + // Is the fan sentient? (ie. does it spin on its own) public bool isSentient = false; + // Respawn point used if fan is deadly public Transform respawnPoint; + // The fan is made out of multiple meshes, but only features one box collider for checking if player hits it. BoxCollider col; public float speedMult = 1.0f; diff --git a/Assets/Code/Levels/FanProgramController.cs b/Assets/Code/Levels/FanProgramController.cs index 365db66..520e7f5 100644 --- a/Assets/Code/Levels/FanProgramController.cs +++ b/Assets/Code/Levels/FanProgramController.cs @@ -5,13 +5,16 @@ public class FanProgramController : ProgramController { + // Fan in the scene public Fan fan; + // Declare puzzle-specific functions for the user program protected override Dictionary ControllerFunctions() { return new Dictionary { { "speedUp", new Action(SpeedUp) }, { "speedDown", new Action(SpeedDown) } }; } + // Add acceleration to the fan's torque private void SpeedUp(string acceleration) { double accel = 0.0; @@ -24,6 +27,7 @@ private void SpeedUp(string acceleration) fan.speedMult = 1.0f; } + // Slow down the fan torque private void SpeedDown(string deceleration) { double decel = 0.0; @@ -35,6 +39,7 @@ private void SpeedDown(string deceleration) fan.speed = Mathf.Max(0.0f, (float)(fan.speed - decel)); } + // Handle puzzle-specific function calls if ProgramController.ExecuteNode set the handover flag public override ExecutionStatus ExecuteNode(NodeBase node) { ExecutionStatus baseStatus = base.ExecuteNode(node); diff --git a/Assets/Code/Levels/GridCell.cs b/Assets/Code/Levels/GridCell.cs index 059ad41..92c1b30 100644 --- a/Assets/Code/Levels/GridCell.cs +++ b/Assets/Code/Levels/GridCell.cs @@ -4,7 +4,10 @@ public class GridCell : MonoBehaviour { + // Is this cell meant to block the Car? public bool blocked; + + // Grid coordinates of the cell public Vector2 Coords; // Start is called before the first frame update diff --git a/Assets/Code/Levels/SampleScene/PlatformProgramController.cs b/Assets/Code/Levels/SampleScene/PlatformProgramController.cs index dae0b2a..b17e543 100644 --- a/Assets/Code/Levels/SampleScene/PlatformProgramController.cs +++ b/Assets/Code/Levels/SampleScene/PlatformProgramController.cs @@ -5,11 +5,13 @@ public class PlatformProgramController : ProgramController { + // Object in scene that contains Platforms public GameObject PlatformContainer; + // Amount to raise/lower the platform by public float stepHeight = 0.25f; - // Original local positions of the platforms (at the time of calling raisePlatform/lowerPlatform, used to track progress in V3.Lerp) + // Original local positions of the platforms (at the time of calling raisePlatform/lowerPlatform, used to track progress in Vector3.Lerp) public List originalPositions = new List(); // Initial local positions of the platforms (at the time of starting the level, used to cap their elevation) @@ -21,6 +23,7 @@ public class PlatformProgramController : ProgramController // Time taken already to raise/lower current platform (each PlatformProgram can only raise/lower one platform at a tick!) protected float currentRaiseTime = 0.0f; + // Declare puzzle-specific functions protected override Dictionary ControllerFunctions() { return new Dictionary { { "raisePlatform", new Action(RaisePlatform) }, { "lowerPlatform", new Action(LowerPlatform) } }; @@ -29,17 +32,22 @@ protected override Dictionary ControllerFunctions() protected override void Start() { base.Start(); + + // Combine base functions and puzzle functions CombineControllerFunctions(base.ControllerFunctions()); CombineControllerFunctions(ControllerFunctions()); + // Store the initial platform positions as a reference point for interpolation GetPlatformPositions(); + // Set each platform's Computer reference to this terminal foreach(Platform platform in PlatformContainer.GetComponentsInChildren()) { platform.Computer = gameObject; } } + // Store positions of each platform protected void GetPlatformPositions() { Programmable[] platforms = PlatformContainer.GetComponentsInChildren(); @@ -61,8 +69,11 @@ protected void GetPlatformPositions() } } + // Puzzle-specific ExecuteNode implementation. This should only execute if ProgramController.ExecuteNode set the handover flag. public override ExecutionStatus ExecuteNode(NodeBase node) { + // Check if node can be handled by ProgramController first. + // If not, it will return .handover as true ExecutionStatus baseStatus = base.ExecuteNode(node); if (!baseStatus.handover) return baseStatus; @@ -87,20 +98,19 @@ public override ExecutionStatus ExecuteNode(NodeBase node) { Platform platform = GetChildProgrammable(PlatformContainer, int.Parse((string)functionCall.GetRawParameters(symbolTable)[0])).GetComponent(); - // Check elevation boundaries + // Check elevation boundaries (e.g. to prevent platforms going under ground) bool allowElevation = functionCall.functionName == "raisePlatform" && initPositions[platform.index].y + platform.MaxElevation < platform.transform.localPosition.y + stepHeight; bool allowDown = functionCall.functionName == "lowerPlatform" && initPositions[platform.index].y + platform.MinElevation > platform.transform.localPosition.y - stepHeight; bool allowMove = allowElevation || allowDown; if (allowMove) { - // Ignore this call if outside limits + // If outside limits, prevent moving processingDone = true; return new ExecutionStatus { success = true, handover = false }; } // Otherwise continue with raising/lowering the platform - GetPlatformPositions(); currentRaiseTime = 0.0f; processingDone = false; @@ -115,22 +125,30 @@ public override ExecutionStatus ExecuteNode(NodeBase node) return new ExecutionStatus { success = true, handover = false }; } + public override bool ExecuteFrame() { + // Check if ProgramController.ExecuteFrame moved to next node already, in which case this child implementation does not need to be called. if (!base.ExecuteFrame()) return false; + // Is the node still being processed? if(!processingDone) { + // Is it a function? if(CheckNodeType(currentNode) == NodeType.FunctionCallBase) { FunctionCallBase functionCall = currentNode.GetComponent(); + // Is it not a base function? if (ControllerFunctions().ContainsKey(functionCall.functionName)) { + // Platform index int index = -1; string val = (string)functionCall.GetRawParameters(symbolTable)[0]; + // Get the platform index from command node in the user program if (int.TryParse(val, out index) || int.TryParse(symbolTable[val].Value, out index)) { + // If the index value is valid, call raisePlatform/lowerPlatform with the platform index as a parameter. if (index >= 0) { //Logger.Log($"Calling {functionCall.functionName}({index})"); @@ -155,6 +173,7 @@ public override bool ExecuteFrame() } // Finally, check if the timer overran raisingTime - if yes, mark processingDone as true. + // Then, the execution can move to next node. if(currentRaiseTime >= raisingTime) { Logger.Log("Platform raised!"); @@ -165,6 +184,7 @@ public override bool ExecuteFrame() return true; } + // Raise the platform marked by index by one step. protected void RaisePlatform(string index) { int nIndex = -1; @@ -176,6 +196,8 @@ protected void RaisePlatform(string index) Programmable platform = GetChildProgrammable(PlatformContainer, nIndex).GetComponent(); platform.transform.localPosition = Vector3.Lerp(platform.transform.localPosition, originalPositions[nIndex] + new Vector3(0.0f, stepHeight, 0.0f), currentRaiseTime / raisingTime); } + + // Lower the platform marked by index by one step. protected void LowerPlatform(string index) { int nIndex = -1; diff --git a/Assets/Code/Levels/UnlockableDoorWithLock.cs b/Assets/Code/Levels/UnlockableDoorWithLock.cs index 3cf86c3..ca9657e 100644 --- a/Assets/Code/Levels/UnlockableDoorWithLock.cs +++ b/Assets/Code/Levels/UnlockableDoorWithLock.cs @@ -6,8 +6,10 @@ public class UnlockableDoorWithLock : UnlockableDoor { public enum LockType { BasicLock = 0, LogicGate, KeyCode, FizzBuzz }; + // For FizzBuzz puzzle: for how many numbers should the fizzbuzz game be played? public int fizzBuzzCount = 20; + // For BasicLock: the door's current lock state (true = locked) protected bool basicLockState = true; // Start is called before the first frame update diff --git a/Assets/Code/ProgramController.cs b/Assets/Code/ProgramController.cs index 11746ec..c8ea1fe 100644 --- a/Assets/Code/ProgramController.cs +++ b/Assets/Code/ProgramController.cs @@ -10,14 +10,29 @@ public class ProgramController : Interactable { public Transform editorUi; + // Time it takes to move from one command to the next. public double tickTime = 1.0; + // Time taken processing the current node. Can be ignored in child implementations if they require an irregular tick time for some functions. protected double timeSinceTick = 0.0; + + // Reference to EditorProgram held by the editorUi protected EditorProgram program; + + // Currently executed node public NodeBase currentNode; + + // For special cases like if-else statements or while loops, the program flow can be overriden by setting specialNextNode. + // Set to null if execution model should follow regular flow (currentNode to currentNode.nextNode) public NodeBase specialNextNode; + // For puzzles that require the player's program to match a specific text output, it can be set with this variable. public string expectedOutput = ""; + // Actual output + public string outputBuffer = ""; + + // Is the program running? + // ie. was it started by the user, has it reached the end/crashed yet public bool programRunning = false; // This is a sort of a "locking mechanism". If a tick needs more than tickTime to perform its actions (e.g. lerping 3D positions), @@ -32,15 +47,21 @@ public class ProgramController : Interactable // Is set to true when processingDone != processingDoneLastFrame protected bool waitForNextTick = false; + // Is this the first command node being executed? protected bool firstTick = true; // Variables, constants etc. that are present in this program public Dictionary symbolTable; + // For debugging: names and values taken from the symbol table. Shows up in the Unity Inspector. public List SymbolNames; public List SymbolValues; + // Function names and their callbacks public Dictionary functions; + + // Add a function name here to hide it from the user. + // E.g. "create list" is written as a function in the game's backend, but shouldn't be presented to the user as a function call. public List hiddenFunctions = new List { "create list" }; // Prepend symbol names with this if the symbol is only used internally by the game. @@ -49,8 +70,7 @@ public class ProgramController : Interactable // This is used by CheckNodeType public enum NodeType { Unknown, FunctionCallBase, ProgramStart, ArithmeticOperationBase, CodeBlock, AssignValue, ProgramEnd, LogicalBlock, WhileLoop, AllocateArray, Continue, Break, ElseBlock }; - public string outputBuffer = ""; - + // HUD script to update on-screen hints private ClueHUD clueHud; // Returns true if there are any nodes other than Start and End present in the program editor @@ -63,6 +83,7 @@ public bool HasAnyNodes() // Initialises a symbol table public void InitSymTable() { + // To make things easier, keywords are added as symbols. symbolTable = new Dictionary { { "True", new FunctionParameter { Value = "True", Type = "Boolean" } }, { "False", new FunctionParameter { Value = "False", Type = "Boolean" } }, @@ -70,11 +91,15 @@ public void InitSymTable() }; } + // printNewline protected void OutPrintNewline() { outputBuffer += "\n"; } + // Dummy implementation. + // This is just so the AllocateArray node can use the FunctionCallBase UI script. + // The reason for that is to allow users to initialise elements of the list, which requires dynamic node sizing, which only the FunctionCallBase does. public void CreateList(string size, string name) { int nSize = -1; @@ -90,6 +115,7 @@ public void CreateList(string size, string name) } } + // public void OutPrint(string text) { if (text.StartsWith("\"") && text.EndsWith("\"")) diff --git a/Assets/Code/ProgramEditor/EditorDraggableNode.cs b/Assets/Code/ProgramEditor/EditorDraggableNode.cs index e8f5713..1d480ad 100644 --- a/Assets/Code/ProgramEditor/EditorDraggableNode.cs +++ b/Assets/Code/ProgramEditor/EditorDraggableNode.cs @@ -11,25 +11,34 @@ public class EditorDraggableNode : MonoBehaviour protected RectTransform rectTransform; protected Selectable container; + // Is the node being dragged right now? protected bool isDragged = false; + // Location of the cursor on the node's surface when dragging started protected Vector2 anchorPoint; protected Vector2 lastFramePointer; + // Editor UI this node is located in public EditorProgram owner; + // should we allow dragging this node? set to false for START/END nodes public bool allowDrag = true; - // is this clickable the arithmetic operator in the UI? if so, mark as true in prefab inspector + + // is this clickable the arithmetic operator in the UI? if so, mark as true in prefab inspector. public bool isArithmeticOperator = false; + // Tracking double-clicks: two left-clicks need to be detected within 1 second. protected int clickCounter; protected float timeSinceClick; protected const float doubleClickAllowedTime = 1.0f; + // Is some node already being dragged? Prevents nodes on top of each other being dragged together at the same time. public static bool nodeAlreadyDragged = false; + // On-screen help prompts private ClueHUD clueHud; + // Default background colour of the node. The editor highlights an erroneous node as red, so this is just stored to revert to the original colour. public float[] color = null; // Start is called before the first frame update @@ -46,6 +55,7 @@ void Start() clueHud = GameObject.Find("ClueHUD").GetComponent(); + // Find original colour Image image = GetComponent(); if (image) { @@ -78,20 +88,23 @@ public bool IsReference() return !isArithmeticOperator && transform.childCount == 1 && GetComponentInChildren().name == "Text"; } + // Handle a double-click public void DoubleClick() { - // Check if conditional + // Check if clicked a conditional if(transform.name == "Comparison") { DispatchEditingProperty(new Action(LogicalOpEditingFinished), transform.parent.GetComponentInParent().condition.comparison); } + // Check if clicked an arithmetic operator else if (isArithmeticOperator) { DispatchEditingProperty(new Action(ArithmeticOpEditingFinished), GetComponentInParent().operatorStr); } - // Check if function name + // Check if clicked a function name else if (transform.name == "FuncName") { + // Removed: editing function names was causing issues //DispatchEditingProperty(new Action(FunctionNameEditingFinished), GetComponentInParent().functionName); } else if (IsReference() || (transform.name.Contains("Parameter") && name != "Parameter")) @@ -193,10 +206,6 @@ public void ParamEditingFinished(string finalValue) if (transform.name.Contains("Parameter") && name != "Parameter") { paramIndex = System.Convert.ToInt32(transform.name.Substring("Parameter".Length)) - 1; - /*if(GetComponentInParent()) - { - paramIndex = (GetComponentInParent().parameters.Count-1) - paramIndex; - }*/ } if (paramIndex >= 0) { @@ -243,9 +252,6 @@ void Update() UpdateHUD(pointer, nodeRect); - //Logger.Log("Pointer: " + pointer); - //Logger.Log("Node: " + nodeRect); - // Drag if LMB held down and inside the node rectangle if (Input.GetKeyDown(KeyCode.Mouse0) && nodeRect.Contains(pointer) || isDragged) { @@ -258,6 +264,7 @@ void Update() } // Have previewNode follow the mouse + // This is used to show a preview of the node connection as the user is moving the mouse to the next/firstBody node if(name == "previewNode") { rectTransform.SetPositionAndRotation(pointer, Quaternion.identity); diff --git a/Assets/Code/ProgramEditor/EditorProgram.cs b/Assets/Code/ProgramEditor/EditorProgram.cs index 6749f5b..8b9aee2 100644 --- a/Assets/Code/ProgramEditor/EditorProgram.cs +++ b/Assets/Code/ProgramEditor/EditorProgram.cs @@ -4,33 +4,47 @@ using UnityEngine; using UnityEngine.UI; +// The code editor UI public class EditorProgram : MonoBehaviour { + // Canvas and material for rendering the node links public Canvas lineCanvas; public static Material lineMaterial = null; + // Object containing command nodes public GameObject elementContainer; + // START/END nodes public ProgramStart programStart; public ProgramEnd programEnd; + + // Computer Terminal for this editor public ProgramController programController; + // For displaying the generated Python code public GameObject generatedCodeContainer; public Text generatedCodeText; + // Since many objects associated with the editor are triggered on startup, + // the game will disable all editors for 2 frames on startup to ensure that they are by default not active. public int framesToDisable = 2; - int frameCounter = 0; + + // Prefabs to instantiate for each command node type in the UI protected static Dictionary> nodePrefabs = null; + // GUI skin used for IMGUI elements (text entry, adding nodes, etc.) public GUISkin guiSkin; // initial pose for the newly added node // will be set to the mouse cursor's last position before pressing TAB public Vector2 newNodeInitPos; + // On-screen help prompts private ClueHUD clueHud; + // For scaling text to different window sizes + // Scaling is based on window width public const double referenceFontSize = 14.0; public Vector2 referenceScreenSize = new Vector2(1000, 1); @@ -58,6 +72,7 @@ public bool EditorActive { private bool editorActive = false; + // Should this editor be enabled on startup? public bool enableEditorOnStartup = false; // Editor state @@ -78,8 +93,10 @@ public enum LinkingMode { NextNode, FirstBodyNode } public System.Action editingNodeFinishedClb = null; bool editedNodeFocused = false; + // Node that caused an error. The editor will change its background colour to red. public GameObject errorNode; + // Node currently stored in the clipboard public GameObject nodeClipboard; // FlowChart: the default node editor @@ -87,6 +104,7 @@ public enum LinkingMode { NextNode, FirstBodyNode } public enum EditorMode { FlowChart, CodeViewer }; public EditorMode editorMode = EditorMode.FlowChart; + // Toggle between the flowchart editor and the Python code viewer. void SwitchMode(EditorMode mode) { @@ -124,6 +142,7 @@ void SwitchMode(EditorMode mode) } } + // Enables/disables line rendering with LineRenderer. Used to avoid issues with lines being seen in editors they don't belong to. void ToggleLineRendering(bool state) { lineCanvas.worldCamera.enabled = state; @@ -180,11 +199,13 @@ void EnableEditor() editorActive = true; } + // Adds node based on type name public GameObject AddNode(string type = "NodeBase", float x = 0.0f, float y = 0.0f) { return AddNode(nodePrefabs[type].Value, x, y); } + // Adds node based on copy public GameObject AddNode(GameObject copy, float x = 0.0f, float y = 0.0f) { GameObject nodeObject = Instantiate(copy, elementContainer.transform); @@ -194,6 +215,7 @@ public GameObject AddNode(GameObject copy, float x = 0.0f, float y = 0.0f) return nodeObject; } + // Links the first right-clicked node with the second-right clicked node, taking the connector type (next/firstBody) into consideration. public void LinkCurrentlySelectedObjects() { linkingNodes = false; @@ -364,9 +386,6 @@ void Start() nodePrefabs.Add("CreateList", new KeyValuePair("Create List: Initialises a named list with a specific size.", Resources.Load("Prefabs/ProgramEditor/Nodes/Operations/CreateList") as GameObject)); nodePrefabs.Add("FunctionCallBase", new KeyValuePair("Function call: Triggers the specified function. Can pass parameters.", Resources.Load("Prefabs/ProgramEditor/Nodes/Operations/FunctionCall") as GameObject)); - // ArithmeticOperationBase uses ArithmeticAdd as a default. - //nodePrefabs.Add("ArithmeticOperationBase", new KeyValuePair("Arithmetic: Performs arithmetic math operations.", Resources.Load("Prefabs/ProgramEditor/Nodes/Operations/ArithmeticAdd") as GameObject)); - nodePrefabs.Add("LogicalBlock", new KeyValuePair("If Statement: Runs a block of code if a condition is met.", Resources.Load("Prefabs/ProgramEditor/Nodes/IfStatement") as GameObject)); nodePrefabs.Add("ElseBlock", new KeyValuePair("Else: Runs a block of code if its linked If Statement condition is not met. Must appear after an If Statement.", Resources.Load("Prefabs/ProgramEditor/Nodes/ElseBlock") as GameObject)); @@ -388,6 +407,7 @@ void Start() if (!programEnd) programEnd = elementContainer.GetComponentInChildren(); + // linkingPreviewNode is used to visualise the node connections when the user is still selecting the second node to link to linkingPreviewNode = new GameObject("previewNode", new Type[] { typeof(RectTransform), typeof(EditorDraggableNode) }); linkingPreviewNode.transform.parent = elementContainer.transform; linkingPreviewNode.GetComponent().allowDrag = false; @@ -409,6 +429,8 @@ void Update() } DrawNodeLinks(); + + // Only show links if the editor is active and in flow chart mode. ToggleLineRendering(editorActive && editorMode == EditorMode.FlowChart); // Editor creation occurs at startup so we may need a couple of frames to disable it for good measure? @@ -488,11 +510,13 @@ void Update() } } + // Converts from normalised device coordinates to screen-space private Vector2 ndcToScreen(Vector2 ndc) { return new Vector2(ndc.x * Screen.width, ndc.y * Screen.height); } + // Converts from normalised device coordinates to screen-space private Vector2 ndcToScreen(float x, float y) { return ndcToScreen(new Vector2(x, y)); @@ -642,23 +666,6 @@ void DrawNodeLinks() { elements.Add(transform.Find("Canvas").Find("Elements").GetChild(childIndex).gameObject); } - /*LineRenderer renderer = lineCanvas.transform.Find("NextNodeLines").GetComponent(); - renderer.SetPositions(new List().ToArray()); - renderer.positionCount = 0; - if (elements.Count > 2) - { - GameObject currentObject = elements[0]; - { - while (currentObject.GetComponent() != null && currentObject.GetComponent().NextNodeObject != null) - { - renderer.positionCount += 2; - renderer.SetPosition(renderer.positionCount - 2, currentObject.GetComponent().localPosition); - renderer.SetPosition(renderer.positionCount - 1, currentObject.GetComponent().NextNodeObject.GetComponent().localPosition); - - currentObject = currentObject.GetComponent().NextNodeObject; - } - } - }*/ // Names of valid link GameObjects List validLinks = new List(); @@ -771,18 +778,6 @@ void DrawNodeLinks() // This is to prevent culling from other 3D elements in the scene currentRenderer.gameObject.layer = LayerMask.NameToLayer("CodeEditor"); validLinks.Add(currentLink.name); - - /*GameObject currentObject = potentialCodeBlock.FirstBodyNodeObject; - { - while (currentObject.GetComponent() != null && currentObject.GetComponent().NextNodeObject != null) - { - currentRenderer.positionCount += 2; - currentRenderer.SetPosition(renderer.positionCount - 2, currentObject.GetComponent().localPosition); - currentRenderer.SetPosition(renderer.positionCount - 1, currentObject.GetComponent().NextNodeObject.GetComponent().localPosition); - - currentObject = currentObject.GetComponent().NextNodeObject; - } - }*/ } } diff --git a/Assets/Code/ProgramEditor/NodeBase.cs b/Assets/Code/ProgramEditor/NodeBase.cs index c781d37..60a7c9e 100644 --- a/Assets/Code/ProgramEditor/NodeBase.cs +++ b/Assets/Code/ProgramEditor/NodeBase.cs @@ -33,6 +33,7 @@ public abstract class NodeBase : MonoBehaviour, IProgramNode // a conditional statement, like an if statement or a loop, inside which this Node is nested public LogicalBlock ownerLoop; + // Finds previous nodes for all nodes in the program this node belongs to void FindPrevNode() { NodeBase[] nodes = computer.editorUi.GetComponent().elementContainer.GetComponentsInChildren(); @@ -101,26 +102,9 @@ public virtual void Start() // TODO: only initialise if !isInitialised? InitialiseNode(); - // Add the NodeLinker arrowhead (or render node link, if already pre-connected: TODO) - /*if(transform.Find("NodeLinker") == null && nodeLinker == null) - { - // Load necessary resources and "cache" them using static fields - if(nodeLinkerPrefab == null) - { - nodeLinkerPrefab = Resources.Load("Prefabs/ProgramEditor/NodeLinker", typeof(GameObject)) as GameObject; - } - if(lineMaterial == null) - { - lineMaterial = Resources.Load("Materials/LineMaterial") as Material; - } - - nodeLinker = Instantiate(nodeLinkerPrefab) as GameObject; - nodeLinker.name = "NodeLinker"; // prevent (Clone) from appearing in the name - - //RenderNodeLinker(); - }*/ } + // Not used anymore public virtual void RenderNodeLinker() { nodeLinker.transform.SetParent(null, false); @@ -196,6 +180,7 @@ public Transform FindElementContainer() return elements; } + // Delete this node from the program public void DeleteNode() { NodeBase[] nodes = FindElementContainer().GetComponentsInChildren(); @@ -218,6 +203,7 @@ public void DeleteNode() Destroy(gameObject); } + // If the node is inside an if-statement/loop, set all its nextNodes to belong to the same code block (ownerLoop) public void PropagateOwnershipChanges() { NodeBase currentNode = (NodeBase)nextNode; diff --git a/Assets/Code/ProgramEditor/NodeTypes/CodeBlocks/LogicalBlock.cs b/Assets/Code/ProgramEditor/NodeTypes/CodeBlocks/LogicalBlock.cs index 69d595d..0aa843e 100644 --- a/Assets/Code/ProgramEditor/NodeTypes/CodeBlocks/LogicalBlock.cs +++ b/Assets/Code/ProgramEditor/NodeTypes/CodeBlocks/LogicalBlock.cs @@ -3,13 +3,18 @@ using UnityEngine; using UnityEngine.UI; +// A CodeBlock that only triggers if a condition is met. +// In other terms: an if-statement. public class LogicalBlock : CodeBlock { + // The logical condition (leftHand, comparison, rightHand) of this if-statement/loop [SerializeField] public BoolCondition condition; + // UI object holding the conditional parameters public GameObject ConditionalObject; + // Most recent outcome of evaluating the logical condition public bool evaluatedResult = false; public override void InitialiseNode() @@ -61,6 +66,7 @@ public virtual void PropagateOwnership() } // Similar to FunctionCallBase.UpdateFunctionParameters + // Updates the elements inside the command node UI with their respective values public virtual void UpdateUI() { if (ConditionalObject == null) diff --git a/Assets/Code/ProgramEditor/NodeTypes/CodeBlocks/WhileLoop.cs b/Assets/Code/ProgramEditor/NodeTypes/CodeBlocks/WhileLoop.cs index b355716..7b232d4 100644 --- a/Assets/Code/ProgramEditor/NodeTypes/CodeBlocks/WhileLoop.cs +++ b/Assets/Code/ProgramEditor/NodeTypes/CodeBlocks/WhileLoop.cs @@ -7,6 +7,7 @@ public class WhileLoop : LogicalBlock { // Should we break out of the loop? (Every time this is detected as true, it will be reset back to false after breaking, to allow the loop to be ran again later) public bool breakNow = false; + public override string SerializeBlockHeader() { return $"while {condition.leftHand.Serialize()} {condition.comparison} {condition.rightHand.Serialize()}:"; diff --git a/Assets/Code/ProgramEditor/NodeTypes/FunctionCallBase.cs b/Assets/Code/ProgramEditor/NodeTypes/FunctionCallBase.cs index 3478318..461da9a 100644 --- a/Assets/Code/ProgramEditor/NodeTypes/FunctionCallBase.cs +++ b/Assets/Code/ProgramEditor/NodeTypes/FunctionCallBase.cs @@ -13,12 +13,11 @@ public class FunctionCallBase : NodeBase // UI stuff: public GameObject functionNameText; - protected Vector2 firstParamOrigin; protected Rect firstParamRect; protected float firstParamWidth, firstParamHeight; - // Used to instantiate parameters + // Used to instantiate parameters in function call UI on runtime static GameObject ParameterTemplate; // Used for arithmetic operation chain, to feed arithmetic expressions & evaluated results into assignValue righthands/functionCall params @@ -35,6 +34,7 @@ public override void InitialiseNode() bool wasInitialised = isInitialised; base.InitialiseNode(); + // Create a ParameterTemplate, which can then be instantiated for each parameter added to the function call in the UI. // TODO: performance fixes, this can be optimised by storing Parameter in a variable if (transform.Find("Parameter")) { @@ -75,8 +75,6 @@ public override void Start() { base.Start(); - //if (!isInitialised) - //InitialiseNode(); UpdateFunctionProperties(); } @@ -93,23 +91,6 @@ public override void Reset() public virtual void UpdateFunctionProperties() { - // TODO: maybe enable a prompt that asks the user if they want to use the arithmetic chain as a default value for param0 or type their own? - /*if(paramCount == 1 && prevArithmetic != null) - { - if(parameters.Count < 1) - { - parameters.Capacity = 1; - } - if(parameters[0] == null) - { - parameters[0] = new FunctionParameter(); - } - ArithmeticOperationBase arithmetic = prevArithmetic; - parameters[0].Value = result.ToString(); - parameters[0].Expression = arithmetic.Serialize(); - parameters[0].Type = "Int"; - parameters[0].IsReference = false; - }*/ if (functionNameText == null) { @@ -129,22 +110,15 @@ public virtual void UpdateFunctionProperties() } } - //if (parameters.Count < paramsToDestroy.Count) - //{ - foreach (Transform param in paramsToDestroy) + foreach (Transform param in paramsToDestroy) + { + if (Convert.ToInt32(param.name.Replace("Parameter", "")) > parameters.Count) { - if (Convert.ToInt32(param.name.Replace("Parameter", "")) > parameters.Count) - { - Destroy(param.gameObject); - GetComponent().SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, (initHeight == 0.0f ? GetComponent().rect.height : initHeight) - firstParamHeight * (parameters.Count > 2 ? (float)parameters.Count : 1.5f) - margin); - } + Destroy(param.gameObject); + GetComponent().SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, (initHeight == 0.0f ? GetComponent().rect.height : initHeight) - firstParamHeight * (parameters.Count > 2 ? (float)parameters.Count : 1.5f) - margin); } - //} - /*if (needResize) - {*/ - GetComponent().SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, (initHeight == 0.0f ? GetComponent().rect.height : initHeight) + firstParamHeight * (parameters.Count > 2 ? (float)parameters.Count : 1.5f) + margin); - //needResize = false; - //} + } + GetComponent().SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, (initHeight == 0.0f ? GetComponent().rect.height : initHeight) + firstParamHeight * (parameters.Count > 2 ? (float)parameters.Count : 1.5f) + margin); for (ushort i = 0; i < paramCount && parameters != null && i < parameters.Count; i++) { @@ -158,8 +132,7 @@ public virtual void UpdateFunctionProperties() GameObject paramObject = Instantiate(ParameterTemplate, transform); paramObject.SetActive(true); - //paramObject.transform.localPosition = new Vector3(0.0f, (functionNameText.GetComponentInParent().rect.yMin * -i + margin)); - // some really weird maths? + // some really weird maths? it works for arranging the parameter objects in the UI though... float height = paramObject.GetComponent().rect.height; float origin = parameters.Count > 0 ? ((parameters.Count / 2.0f - parameters.Count) + i) * height : 0.0f; paramObject.transform.localPosition = new Vector3(0.0f, origin); diff --git a/Assets/Code/ProgramEditor/NodeTypes/FunctionParameter.cs b/Assets/Code/ProgramEditor/NodeTypes/FunctionParameter.cs index dfb17af..08e3cb8 100644 --- a/Assets/Code/ProgramEditor/NodeTypes/FunctionParameter.cs +++ b/Assets/Code/ProgramEditor/NodeTypes/FunctionParameter.cs @@ -11,7 +11,7 @@ public class FunctionParameter : IProgramNode // Literal value or result of evaluating an arithmetic expression public string Value; - // Used for carrying over arithmetic expressions + // Used for carrying over arithmetic expressions with the arithmetic chain public string Expression; public string Type; diff --git a/Assets/Code/ProgramEditor/NodeTypes/Operators/AllocateArray.cs b/Assets/Code/ProgramEditor/NodeTypes/Operators/AllocateArray.cs index 7e6c7ea..a46dbb8 100644 --- a/Assets/Code/ProgramEditor/NodeTypes/Operators/AllocateArray.cs +++ b/Assets/Code/ProgramEditor/NodeTypes/Operators/AllocateArray.cs @@ -2,8 +2,13 @@ using System.Collections.Generic; using UnityEngine; +// Allows creating a list with a given name and size public class AllocateArray : FunctionCallBase { + // Initialise this node as a function call with two parameters, name and size + // + // If a number literal (ie. raw numerical value) is provided as the size, then the user is able to set all list elements' values in this node. + // In that case, the elements are just added to the FunctionCallBase.parameters list, which means it always has a size of n+2 public override void InitialiseNode() { paramCount = 2; @@ -19,6 +24,7 @@ public override string Serialize() int count = -1; if (int.TryParse(parameters[0].Value, out count) && count > 0) { + // Comma-separated values in the list declaration string csv = ""; for(int i = 0; i < count && i+2 < parameters.Count; i++) { @@ -47,6 +53,7 @@ public override string Serialize() } } + // Update the node's UI in editor, display parameter values etc. public override void UpdateFunctionProperties() { base.UpdateFunctionProperties(); diff --git a/Assets/Code/ProgramEditor/NodeTypes/Operators/ArithmeticOperationBase.cs b/Assets/Code/ProgramEditor/NodeTypes/Operators/ArithmeticOperationBase.cs index 00e2885..f919645 100644 --- a/Assets/Code/ProgramEditor/NodeTypes/Operators/ArithmeticOperationBase.cs +++ b/Assets/Code/ProgramEditor/NodeTypes/Operators/ArithmeticOperationBase.cs @@ -3,6 +3,8 @@ using UnityEngine; using UnityEngine.UI; +// This class used to be for the arithmetic chain concept, which is now deleted. +// However, it provides the GetResult method, which is still used for arithmetic and symbol table lookups. public class ArithmeticOperationBase : FunctionCallBase { public FunctionParameter leftHand, rightHand; @@ -82,6 +84,7 @@ public override string Serialize() return $"{(wrap ? "(" : "")}{(prevArithmetic ? prevArithmetic.Serialize() : leftHand.Value)} {operatorStr} {rightHand.Value}{(wrap ? ")" : "")}"; } + // Evaluate a mathematical expression (or just replace the symbols with their current values) public static string GetResult(string expr, ref Dictionary symbolTable) { Logger.Log($"Arithmetic.GetResult evaluating {expr}"); diff --git a/Assets/Code/ProgramEditor/NodeTypes/Operators/AssignValue.cs b/Assets/Code/ProgramEditor/NodeTypes/Operators/AssignValue.cs index 3c50b3b..f85b68f 100644 --- a/Assets/Code/ProgramEditor/NodeTypes/Operators/AssignValue.cs +++ b/Assets/Code/ProgramEditor/NodeTypes/Operators/AssignValue.cs @@ -3,7 +3,7 @@ using UnityEngine; // Inherits from ArithmeticOperationBase because it's easier to program the UI this way. -// Be careful about order of types with regards to inheritance in ProgramController.CheckNodeTypes() though. +// Be careful about order of types with regards to inheritance in ProgramController.CheckNodeTypes() though - this needs to appear before the parent class. public class AssignValue : ArithmeticOperationBase { public override void InitialiseNode() diff --git a/Assets/Code/TerminalLookAtCamera.cs b/Assets/Code/TerminalLookAtCamera.cs index a08f1a7..8f1e55d 100644 --- a/Assets/Code/TerminalLookAtCamera.cs +++ b/Assets/Code/TerminalLookAtCamera.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using UnityEngine; +// Code for the generated code line showing above the computer terminal. It's meant to appear at the same orientation as the player's viewing frustum. public class TerminalLookAtCamera : MonoBehaviour { // Start is called before the first frame update diff --git a/Assets/Code/UnlockableDoor.cs b/Assets/Code/UnlockableDoor.cs index fea1b93..4a3adff 100644 --- a/Assets/Code/UnlockableDoor.cs +++ b/Assets/Code/UnlockableDoor.cs @@ -13,6 +13,7 @@ public class UnlockableDoor : Unlockable private Transform playerTransform; + // time before door locks private float timeToLock = 0.0f; // Start is called before the first frame update diff --git a/LICENSE-MiniEngineAO b/LICENSE-MiniEngineAO new file mode 100644 index 0000000..49d2166 --- /dev/null +++ b/LICENSE-MiniEngineAO @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Microsoft + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file