diff --git a/Paths/Assets/Scripts/Algorithms/AStar.cs b/Paths/Assets/Scripts/Algorithms/AStar.cs
index 2a289ce..aafc771 100644
--- a/Paths/Assets/Scripts/Algorithms/AStar.cs
+++ b/Paths/Assets/Scripts/Algorithms/AStar.cs
@@ -1,4 +1,5 @@
using System.Collections.Generic;
+using System.Linq;
using UnityEngine;
namespace Algorithms {
@@ -92,5 +93,23 @@ namespace Algorithms {
return _path;
}
+ ///
+ /// Attempts to clean the NodeGrid of all edits made to heuristic values and such, fast.
+ ///
+ public void Cleanup() {
+ while (_openList.Count > 0) {
+ Node node = _openList[0];
+ _openList.RemoveAt(0);
+
+ node.Reset();
+ }
+
+ while (_closedList.Count > 0) {
+ Node node = _closedList[0];
+ _closedList.RemoveAt(0);
+
+ node.Reset();
+ }
+ }
}
}
\ No newline at end of file
diff --git a/Paths/Assets/Scripts/Algorithms/Node.cs b/Paths/Assets/Scripts/Algorithms/Node.cs
index faab057..ae79d90 100644
--- a/Paths/Assets/Scripts/Algorithms/Node.cs
+++ b/Paths/Assets/Scripts/Algorithms/Node.cs
@@ -37,6 +37,16 @@ namespace Algorithms {
Walkable = walkable;
}
+ ///
+ /// Resets this Node back to it's assumed default values.
+ ///
+ public void Reset() {
+ Parent = null;
+ DistanceToTarget = null;
+ Cost = 1;
+ State = NodeState.None;
+ }
+
public override bool Equals(object obj) {
return obj is Node node && Position.Equals(node.Position);
}
diff --git a/Paths/Assets/Scripts/Algorithms/NodeGrid.cs b/Paths/Assets/Scripts/Algorithms/NodeGrid.cs
index b3bf83f..12a0287 100644
--- a/Paths/Assets/Scripts/Algorithms/NodeGrid.cs
+++ b/Paths/Assets/Scripts/Algorithms/NodeGrid.cs
@@ -147,7 +147,7 @@ namespace Algorithms {
}
}
- public GridNodeType[,] RenderNodeTypes() {
+ public GridNodeType[,] RenderNodeTypes(Vector2Int? start = null, Vector2Int? end = null) {
GridNodeType[,] nodeTypeGrid = new GridNodeType[Grid.GetLength(0), Grid.GetLength(1)];
for (int x = 0; x < Grid.GetLength(0); x++) {
@@ -156,6 +156,13 @@ namespace Algorithms {
}
}
+ // Start / End node addition
+ if (start.HasValue)
+ nodeTypeGrid[start.Value.x, start.Value.y] = GridNodeType.Start;
+
+ if (end.HasValue)
+ nodeTypeGrid[end.Value.x, end.Value.y] = GridNodeType.End;
+
return nodeTypeGrid;
}
}
diff --git a/Paths/Assets/Scripts/UIController.cs b/Paths/Assets/Scripts/UIController.cs
index 76dded3..c2111be 100644
--- a/Paths/Assets/Scripts/UIController.cs
+++ b/Paths/Assets/Scripts/UIController.cs
@@ -1,5 +1,7 @@
using System;
+using System.Collections.Generic;
using Algorithms;
+using UnityEditor;
using UnityEngine;
using UnityEngine.UI;
@@ -22,6 +24,7 @@ public enum ClickType {
///
public enum AnimationState {
Stopped,
+ Paused,
Started,
Reloading
}
@@ -31,23 +34,41 @@ public enum AnimationState {
/// All UI elements are referenced and controlled here.
///
public class UIController : MonoBehaviour {
+ // UI & important App references
public Slider progressSlider;
public GridController gridController;
public Manager manager;
+ // Animation State, Click Management
private Vector2Int _lastClickLocation;
private ClickType _modify;
- private AnimationState _animating;
+ private AnimationState _animationState;
+ private AnimationState _previousAnimationState;
+ private bool EditShouldReload =>
+ _animationState == AnimationState.Started || _animationState == AnimationState.Paused;
+
+ // Grid State & Pathfinding
private NodeGrid _grid;
private Vector2Int _start;
private Vector2Int _end;
+ private IPathfinding _algorithm;
+ private Stack _path;
+ private ChangeController _state;
+
+ // Animation speed & indexing
+ public float clampIncrement;
+ private float _runtime;
+ public int CurrentIndex => (int) _runtime;
+ public float speed;
private void Start() {
_grid = new NodeGrid(gridController.width, gridController.height);
- _animating = AnimationState.Stopped;
+ _animationState = AnimationState.Stopped;
+ _previousAnimationState = _animationState;
_start = _grid.RandomPosition();
_end = _grid.RandomPosition();
+ _runtime = 0;
}
private void Update() {
@@ -65,6 +86,8 @@ public class UIController : MonoBehaviour {
Node node = _grid.GetNode(position);
_modify = node.Walkable ? ClickType.Add : ClickType.Remove;
node.Walkable = !node.Walkable;
+ if (EditShouldReload)
+ _animationState = AnimationState.Reloading;
}
_lastClickLocation = position;
@@ -79,25 +102,27 @@ public class UIController : MonoBehaviour {
// Note: Wall toggling instantly reloads, but only real start/end node movement reloads.
case ClickType.Add:
node.Walkable = false;
- _animating = AnimationState.Reloading;
+ if (EditShouldReload)
+ _animationState = AnimationState.Reloading;
break;
case ClickType.Remove:
node.Walkable = true;
- _animating = AnimationState.Reloading;
+ if (EditShouldReload)
+ _animationState = AnimationState.Reloading;
break;
case ClickType.Start:
if (node.Walkable) {
_start = position;
- if (_animating == AnimationState.Started)
- _animating = AnimationState.Reloading;
+ if (EditShouldReload)
+ _animationState = AnimationState.Reloading;
}
break;
case ClickType.End:
if (node.Walkable) {
_end = position;
- if (_animating == AnimationState.Started)
- _animating = AnimationState.Reloading;
+ if (EditShouldReload)
+ _animationState = AnimationState.Reloading;
}
break;
@@ -108,23 +133,130 @@ public class UIController : MonoBehaviour {
}
}
- if (_animating == AnimationState.Reloading) {
- }
- else if (_animating == AnimationState.Started) {
+ // Handle user start/stopping
+ if (Input.GetKeyDown(KeyCode.Space)) {
+ switch (_animationState) {
+ case AnimationState.Stopped:
+ _animationState = AnimationState.Reloading;
+ break;
+ case AnimationState.Paused:
+ _animationState = AnimationState.Started;
+ break;
+ case AnimationState.Started:
+ _animationState = AnimationState.Paused;
+ break;
+ case AnimationState.Reloading:
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
}
- gridController.LoadGridState(_grid.RenderNodeTypes(_start, _end));
+ switch (_animationState) {
+ case AnimationState.Reloading:
+ if (!Input.GetMouseButton(0)) {
+ GeneratePath();
+ LoadNextState();
+ _animationState = AnimationState.Started;
+ }
+ else
+ gridController.LoadGridState(_grid.RenderNodeTypes(_start, _end));
+
+ break;
+ case AnimationState.Started:
+ // Calculate how much to move forward
+ var increment = Time.deltaTime * speed * CurrentMultiplier();
+ if (clampIncrement > 0)
+ increment = Mathf.Clamp(increment, 0, _state.Count * Time.deltaTime / clampIncrement);
+ _runtime += increment;
+
+ if (CurrentIndex < _state.Count)
+ LoadNextState();
+ break;
+ case AnimationState.Stopped:
+ gridController.LoadGridState(_grid.RenderNodeTypes(_start, _end));
+ break;
+ case AnimationState.Paused:
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+
+ if (_animationState != _previousAnimationState) {
+ Debug.Log($"Animation State {_previousAnimationState} -> {_animationState}");
+ _previousAnimationState = _animationState;
+ }
+ }
+
+ private void GeneratePath() {
+ if (_algorithm != null)
+ _algorithm.Cleanup();
+
+ _runtime = 0f;
+ _algorithm = new AStar(_grid);
+ _path = _algorithm.FindPath(_start, _end);
+ // Debug.Log($"{_state.Count} changs made");
+ _state = _algorithm.ChangeController;
+ }
+
+ private void LoadNextState() {
+ // Move to the new calculated index
+ _state.MoveTo(Math.Max(1, CurrentIndex)); // use Math.max to ensure both start/end nodes are always rendered
+ gridController.LoadDirtyGridState(_state.Current, _state.DirtyFlags);
+
+ string pathCount = _path != null ? $"{_path.Count}" : "N/A";
+ manager.debugText.text = $"{_state.CurrentRuntime * 1000.0:F1}ms\n" +
+ $"{CurrentIndex:000} / {_state.Count:000}\n" +
+ $"Path: {pathCount} tiles";
}
///
- /// Generates the path and sets up the UI Controller to begin animation.
+ /// Returns the current time multiplier, based on the latest change in the path.
///
- private void StartAnimation() {
+ /// A positive non-zero float representing how fast the current frame should be processed.
+ private float CurrentMultiplier() {
+ if (_state.Index == -1)
+ return 1;
+
+ switch (_state.CurrentChange.New) {
+ case GridNodeType.Path:
+ return 1 / 5f;
+ case GridNodeType.Empty:
+ break;
+ case GridNodeType.Wall:
+ break;
+ case GridNodeType.Start:
+ break;
+ case GridNodeType.End:
+ break;
+ case GridNodeType.Seen:
+ break;
+ case GridNodeType.Expanded:
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+
+ return 1;
}
- ///
- /// Stops the path animation and readies the UI Controller for grid editing.
- ///
- private void StopAnimation() {
+ public void OnDrawGizmos() {
+ if (!Application.isPlaying) return;
+
+ Vector3 mouse = manager.mainCamera.ScreenToWorldPoint(Input.mousePosition);
+ Vector3 localScale = manager.gridObject.transform.localScale;
+ Vector2Int gridPosition = gridController.GetGridPosition(mouse);
+
+ var style = new GUIStyle();
+ style.normal.textColor = Color.blue;
+ Gizmos.color = Color.blue;
+
+ Gizmos.DrawWireCube(gridController.GetWorldPosition(gridPosition), localScale / (Vector2) gridController.Size);
+ Handles.Label(mouse, String.Format("{0}{1}",
+ gridPosition,
+ _algorithm.NodeGrid.IsValid(gridPosition)
+ ? $"\n{_state.Current[gridPosition.x, gridPosition.y]}"
+ : ""
+ ), style);
}
}
\ No newline at end of file