From e637eac65b7721da433ca78e3587ecfce5a09bb3 Mon Sep 17 00:00:00 2001 From: Xevion Date: Tue, 24 Nov 2020 17:21:54 -0600 Subject: [PATCH] implement ChangeController optimizations (switch off from GridState) --- Paths/Assets/Scripts/Algorithms/AStar.cs | 25 ++-- Paths/Assets/Scripts/Algorithms/NodeGrid.cs | 12 ++ Paths/Assets/Scripts/Change.cs | 15 +++ Paths/Assets/Scripts/Change.cs.meta | 3 + Paths/Assets/Scripts/ChangeController.cs | 107 ++++++++++++++++++ Paths/Assets/Scripts/ChangeController.cs.meta | 3 + Paths/Assets/Scripts/GridController.cs | 10 +- Paths/Assets/Scripts/GridState.cs | 2 +- Paths/Assets/Scripts/IPathfinding.cs | 1 + Paths/Assets/Scripts/Manager.cs | 22 ++-- 10 files changed, 173 insertions(+), 27 deletions(-) create mode 100644 Paths/Assets/Scripts/Change.cs create mode 100644 Paths/Assets/Scripts/Change.cs.meta create mode 100644 Paths/Assets/Scripts/ChangeController.cs create mode 100644 Paths/Assets/Scripts/ChangeController.cs.meta diff --git a/Paths/Assets/Scripts/Algorithms/AStar.cs b/Paths/Assets/Scripts/Algorithms/AStar.cs index b4a1aea..d5dd385 100644 --- a/Paths/Assets/Scripts/Algorithms/AStar.cs +++ b/Paths/Assets/Scripts/Algorithms/AStar.cs @@ -10,6 +10,7 @@ namespace Algorithms { private List _openList; private List _closedList; private List _states; + public ChangeController ChangeController { get; private set; } public Vector2Int Start { get; private set; } public Vector2Int End { get; private set; } @@ -17,11 +18,16 @@ namespace Algorithms { public AStar(NodeGrid nodeGrid) { this._nodeGrid = nodeGrid; _states = new List(); + ChangeController = new ChangeController(nodeGrid.RenderNodeTypes()); } public Stack FindPath(Vector2Int start, Vector2Int end) { this.Start = start; this.End = end; + + ChangeController.AddChange(new Change(start.x, start.y, GridNodeType.Start, GridNodeType.Empty)); + ChangeController.AddChange(new Change(end.x, end.y, GridNodeType.End, GridNodeType.Empty)); + var startNode = new Node(start, true); var endNode = new Node(end, true); @@ -42,11 +48,14 @@ namespace Algorithms { _openList.Remove(current); current.State = NodeState.Closed; + ChangeController.AddChange(new Change( + current.Position.x, current.Position.y, + GridNodeType.Expanded, GridNodeType.Seen)); _closedList.Add(current); - + if (current.Position == endNode.Position) break; - + RecordState(); Node[] adjacentNodes = this._nodeGrid.GetAdjacentNodesArray(current); @@ -57,16 +66,14 @@ namespace Algorithms { node.Parent = current; node.DistanceToTarget = NodeGrid.Manhattan(node, endNode); node.Cost = node.Weight + node.Parent.Cost; - - // Set to open and add to open list (sorted) - node.State = NodeState.Open; - // _openList.Add(node); + node.State = NodeState.Open; + ChangeController.AddChange(new Change(node.Position.x, node.Position.y, GridNodeType.Seen, GridNodeType.Empty)); + + // Insert the new node into the sorted open list in ascending order int index = _openList.BinarySearch(node); if (index < 0) index = ~index; _openList.Insert(index, node); - // _openList = _openList.OrderBy(n => n.F).ToList(); - // _openList.Sort((n, o) => n.F.CompareTo(o.F)); } } } @@ -83,7 +90,7 @@ namespace Algorithms { _path.Push(temp); RecordState(); temp = temp.Parent; - } while (temp != startNode && temp != null); + } while (temp != null && !temp.Equals(startNode)); return _path; } diff --git a/Paths/Assets/Scripts/Algorithms/NodeGrid.cs b/Paths/Assets/Scripts/Algorithms/NodeGrid.cs index 25600fe..fdb059d 100644 --- a/Paths/Assets/Scripts/Algorithms/NodeGrid.cs +++ b/Paths/Assets/Scripts/Algorithms/NodeGrid.cs @@ -138,5 +138,17 @@ namespace Algorithms { Grid[(int) x, (int) y].Walkable = true; } } + + public GridNodeType[,] RenderNodeTypes() { + GridNodeType[,] nodeTypeGrid = new GridNodeType[Grid.GetLength(0), Grid.GetLength(1)]; + + for (int x = 0; x < Grid.GetLength(0); x++) { + for (int y = 0; y < Grid.GetLength(1); y++) { + nodeTypeGrid[x, y] = Grid[x, y].Walkable ? GridNodeType.Empty : GridNodeType.Wall; + } + } + + return nodeTypeGrid; + } } } \ No newline at end of file diff --git a/Paths/Assets/Scripts/Change.cs b/Paths/Assets/Scripts/Change.cs new file mode 100644 index 0000000..ffc122b --- /dev/null +++ b/Paths/Assets/Scripts/Change.cs @@ -0,0 +1,15 @@ +public readonly struct Change { + public readonly int X; + public readonly int Y; + public readonly GridNodeType New; + public readonly GridNodeType Old; + public readonly float Time; + + public Change(int x, int y, GridNodeType newType, GridNodeType oldType) { + this.X = x; + this.Y = y; + this.New = newType; + this.Old = oldType; + this.Time = UnityEngine.Time.realtimeSinceStartup; + } +} \ No newline at end of file diff --git a/Paths/Assets/Scripts/Change.cs.meta b/Paths/Assets/Scripts/Change.cs.meta new file mode 100644 index 0000000..aa31369 --- /dev/null +++ b/Paths/Assets/Scripts/Change.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: ce434b5fefcc4447bc8adc1ab11dd964 +timeCreated: 1606250638 \ No newline at end of file diff --git a/Paths/Assets/Scripts/ChangeController.cs b/Paths/Assets/Scripts/ChangeController.cs new file mode 100644 index 0000000..5f3b244 --- /dev/null +++ b/Paths/Assets/Scripts/ChangeController.cs @@ -0,0 +1,107 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +/// +/// Helps manage Change value types by rendering forward/backward movements to a Enum array. +/// +public class ChangeController { + private readonly GridNodeType[,] _initial; + public GridNodeType[,] Current { get; private set; } + + public int Index { get; private set; } + private readonly List _changes; + public int Count => _changes.Count; + public Change CurrentChange => _changes[Index]; + + public ChangeController(GridNodeType[,] initial) { + _initial = initial; + Current = initial; + Index = 0; + _changes = new List(); + } + + /// + /// Resets the ChangeController back to the initial state. + /// + public void Reset() { + Current = _initial; + } + + /// + /// Adds a new change to the list. + /// + /// A valid Change value type. + public void AddChange(Change change) { + _changes.Add(change); + } + + + /// + /// Move the ChangeController's current index forward by index. + /// Positive values only, wil only result in forward movement (if any). + /// + /// The number of times to move forward. + public void Forward(int n = 1) { + if (n < 0) + throw new ArgumentOutOfRangeException(nameof(n)); + + for (int i = 0; i < n; i++) { + Change cur = _changes[++Index]; + Current[cur.X, cur.Y] = cur.New; + } + } + + /// + /// Move the ChangeController's current index backward by index. + /// Positive values only, will only result in backward movement (if any). + /// + /// The number of times to move backward. + public void Reverse(int n = 1) { + if (n < 0) + throw new ArgumentOutOfRangeException(nameof(n)); + + if (n > 5 && Index - n == 0) + Reset(); // resetting by copying values instead of mutating might be easier. + else { + for (int i = 0; i < n; i++) { + Change cur = _changes[--Index]; + Current[cur.X, cur.Y] = cur.Old; + } + } + } + + /// + /// Move the ChangeController's current index by index, forward or backward. + /// Values can be positive or negative to determine movement. Best used for calculating movement via difference. + /// + /// The number of times to move. Sign determines direction. + public void Move(int n) { + if (n > 0) + Forward(n); + else if (n < 0) + Reverse(-n); + } + + /// + /// Move to this specific index in the ChangeController's states. + /// All changes will be applied to get here, either forward or reverse. + /// + /// The new index to move to. + /// When the index is invalid. + public void MoveTo(int index) { + // check that index is valid at least + if (index >= _changes.Count || index < 0) + throw new ArgumentOutOfRangeException(nameof(index), + $"Cannot move to change index {index}. Only indexes from 0 to {_changes.Count - 1} are valid."); + + // diff & move to + int diff = index - Index; + if (diff != 0) + // prefer resetting if index is 0 and it needs to move at least some. + if (index == 0 && diff > 5) + Reset(); + else + Move(diff); + } +} \ No newline at end of file diff --git a/Paths/Assets/Scripts/ChangeController.cs.meta b/Paths/Assets/Scripts/ChangeController.cs.meta new file mode 100644 index 0000000..0c33a14 --- /dev/null +++ b/Paths/Assets/Scripts/ChangeController.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f90bfa4d7a4f45968a29175338225e65 +timeCreated: 1606251343 \ No newline at end of file diff --git a/Paths/Assets/Scripts/GridController.cs b/Paths/Assets/Scripts/GridController.cs index 5589169..3a3fa82 100644 --- a/Paths/Assets/Scripts/GridController.cs +++ b/Paths/Assets/Scripts/GridController.cs @@ -68,12 +68,12 @@ public class GridController : MonoBehaviour { /// /// Loads a GridState into the Grid Shader's StructuredBuffer /// - /// - public void LoadGridState(GridState gridState) { + /// + public void LoadGridState(GridNodeType[,] state) { // Loop over matrix and set values via cast Enum to int - for (int x = 0; x < gridState.Grid.GetLength(0); x++) { - for (int y = 0; y < gridState.Grid.GetLength(1); y++) - this.SetValue(x, y, (int) gridState.Grid[x, y]); + for (int x = 0; x < state.GetLength(0); x++) { + for (int y = 0; y < state.GetLength(1); y++) + this.SetValue(x, y, (int) state[x, y]); } UpdateShader(PropertyName.Values); diff --git a/Paths/Assets/Scripts/GridState.cs b/Paths/Assets/Scripts/GridState.cs index 0d034a0..7bc7ceb 100644 --- a/Paths/Assets/Scripts/GridState.cs +++ b/Paths/Assets/Scripts/GridState.cs @@ -3,7 +3,7 @@ using System.Linq; using Algorithms; using UnityEngine; -public class GridState { +public struct GridState { public readonly GridNodeType[,] Grid; public readonly float Time; diff --git a/Paths/Assets/Scripts/IPathfinding.cs b/Paths/Assets/Scripts/IPathfinding.cs index 6c77b23..e8c7b5f 100644 --- a/Paths/Assets/Scripts/IPathfinding.cs +++ b/Paths/Assets/Scripts/IPathfinding.cs @@ -22,4 +22,5 @@ public interface IPathfinding { List GetStates(); Vector2Int Start { get; } Vector2Int End { get; } + ChangeController ChangeController { get; } } \ No newline at end of file diff --git a/Paths/Assets/Scripts/Manager.cs b/Paths/Assets/Scripts/Manager.cs index f97dad1..326978a 100644 --- a/Paths/Assets/Scripts/Manager.cs +++ b/Paths/Assets/Scripts/Manager.cs @@ -10,7 +10,7 @@ using UnityEngine; /// public class Manager : MonoBehaviour { private IPathfinding _algorithm; - private List _states; + private ChangeController _state; private int _curIndex; private Stack _path; private float _lastStart; @@ -29,7 +29,6 @@ public class Manager : MonoBehaviour { } public void Start() { - _states = new List(); GeneratePath(); Resize(); } @@ -42,10 +41,10 @@ public class Manager : MonoBehaviour { public void Update() { var increment = Time.deltaTime * speed; if (clampIncrement > 0) - increment = Mathf.Clamp(increment, 0, _states.Count * Time.deltaTime / clampIncrement); + increment = Mathf.Clamp(increment, 0, _state.Count * Time.deltaTime / clampIncrement); _runtime += increment; - if (CurrentIndex < _states.Count) + if (CurrentIndex < _state.Count) this.LoadNextState(); else { try { @@ -61,30 +60,29 @@ public class Manager : MonoBehaviour { private void GeneratePath() { var nodeGrid = new NodeGrid(gridController.width, gridController.height); - _algorithm = new AStar(nodeGrid); Vector2Int start = nodeGrid.RandomPosition(); // Vector2Int start = new Vector2Int(30, 30); Vector2Int end = nodeGrid.RandomPosition(); - nodeGrid.ApplyGenerator(new RandomPlacement(0.20f, true, true)); + nodeGrid.ApplyGenerator(new RandomPlacement(0.3f, true, true)); nodeGrid.GetNode(start).Walkable = true; nodeGrid.GetNode(end).Walkable = true; + _algorithm = new AStar(nodeGrid); _path = _algorithm.FindPath(start, end); - - _states = _algorithm.GetStates(); + _state = _algorithm.ChangeController; } private void LoadNextState() { - GridState state = _states[CurrentIndex]; - gridController.LoadGridState(state); + _state.MoveTo(CurrentIndex); + gridController.LoadGridState(_state.Current); - float change = state.Time - _lastStart; + float change = _state.CurrentChange.Time - _lastStart; string pathCount = _path != null ? $"{_path.Count}" : "N/A"; debugText.text = $"{change * 1000.0:F1}ms\n" + - $"{this.CurrentIndex:000} / {this._states.Count:000}\n" + + $"{this.CurrentIndex:000} / {_state.Count:000}\n" + $"Path: {pathCount} tiles"; }