diff --git a/Assets/Scripts/FrameLimiter.cs b/Assets/Scripts/FrameLimiter.cs
index 1f35731..f4dc35b 100644
--- a/Assets/Scripts/FrameLimiter.cs
+++ b/Assets/Scripts/FrameLimiter.cs
@@ -2,12 +2,16 @@ using UnityEngine;
public class FrameLimiter : MonoBehaviour
{
- // Start is called before the first frame update
+ ///
+ /// Melimit frame rate game ke 60 FPS
+ /// Limit dilakukan agar kalkulasi CPU yang digunakan sesuai dengan target FPS
+ ///
+
[SerializeField] private int frameRate = 60;
+ private int vSyncValue = 0;
void Start()
{
- // Set the target frame rate to 60
- QualitySettings.vSyncCount = 0;
+ QualitySettings.vSyncCount = vSyncValue;
Application.targetFrameRate = frameRate;
}
}
diff --git a/Assets/Scripts/GridMap.cs b/Assets/Scripts/GridMap.cs
index 39325d8..f8750dc 100644
--- a/Assets/Scripts/GridMap.cs
+++ b/Assets/Scripts/GridMap.cs
@@ -8,80 +8,64 @@ using Newtonsoft.Json;
/// Kelas ini bertanggung jawab untuk membuat, menampilkan, dan mengelola node-node grid
/// serta memberikan fungsionalitas untuk pathfinding.
///
+
public class GridMap : MonoBehaviour
{
- // Ukuran grid pada sumbu X
[SerializeField]
int numX;
- // Ukuran grid pada sumbu Y
+
[SerializeField]
int numY;
- // Prefab untuk visualisasi node grid
[SerializeField]
GameObject gridNodeViewPrefab;
- // Mengizinkan atau melarang pergerakan diagonal pada pathfinding
[SerializeField]
bool allowDiagonalMovement = false;
- // Properti publik untuk mengakses allowDiagonalMovement dari luar
public bool AllowDiagonalMovement
{
get { return allowDiagonalMovement; }
set { allowDiagonalMovement = value; }
}
- // Warna-warna untuk representasi visual berbagai status node
- public Color COLOR_WALKABLE = new Color(0.4f, 0.4f, 0.8f, 1.0f); // Warna untuk node yang dapat dilalui
- public Color COLOR_NONWALKABLE = Color.black; // Warna untuk node yang tidak dapat dilalui
- public Color COLOR_CURRENT_NODE = Color.cyan; // Warna untuk node yang sedang diproses
- public Color COLOR_ADD_TO_OPENLIST = Color.green; // Warna untuk node yang ditambahkan ke open list
- public Color COLOR_ADD_TO_CLOSEDLIST = Color.grey; // Warna untuk node yang ditambahkan ke closed list
- public Color COLOR_PATH = Color.blue; // Warna untuk node yang menjadi bagian dari jalur final
+ public Color COLOR_WALKABLE = Color.white;
+ public Color COLOR_NONWALKABLE = Color.black;
+ public Color COLOR_CURRENT_NODE = Color.cyan;
+ public Color COLOR_ADD_TO_OPENLIST = Color.green;
+ public Color COLOR_ADD_TO_CLOSEDLIST = Color.grey;
+ public Color COLOR_PATH = Color.blue;
- // Getter untuk ukuran grid
public int NumX { get { return numX; } }
public int NumY { get { return numY; } }
- // Referensi ke NPC yang akan menggunakan path
[SerializeField]
NPC npc;
- // Referensi ke Transform tujuan untuk visualisasi
[SerializeField]
Transform destination;
public Transform Destination { get { return destination; } }
- // Ukuran fisik setiap node dalam grid
float gridNodeWidth = 1.0f;
float gridNodeHeight = 1.0f;
- // Getter untuk ukuran node
public float GridNodeWidth { get { return gridNodeWidth; } }
public float GridNodeHeight { get { return gridNodeHeight; } }
- // Array 2D untuk menyimpan semua GridNodeView
private GridNodeView[,] gridNodeViews = null;
-
- // Posisi mouse terakhir untuk tracking perubahan
- private Vector2 lastMousePosition;
- // Node terakhir yang statusnya diubah
private GridNodeView lastToggledNode = null;
- ///
- /// Inisialisasi grid pada saat permainan dimulai
- ///
+ private Vector2 lastMousePosition;
+
+
void Start()
{
- // Membuat array untuk menyimpan semua node view
gridNodeViews = new GridNodeView[NumX, NumY];
for (int i = 0; i < NumX; i++)
{
for (int j = 0; j < NumY; j++)
{
- // Membuat instance dari prefab node grid di posisi yang sesuai
GameObject obj = Instantiate(
gridNodeViewPrefab,
new Vector3(
@@ -90,61 +74,47 @@ public class GridMap : MonoBehaviour
0.0f),
Quaternion.identity);
- // Memberi nama pada objek grid node untuk identifikasi
obj.name = "GridNode_" + i.ToString() + "_" + j.ToString();
GridNodeView gnv = obj.GetComponent();
gridNodeViews[i, j] = gnv;
gnv.Node = new GridNode(new Vector2Int(i, j), this);
- // Menjadikan node sebagai child dari GridMap
obj.transform.SetParent(transform);
}
}
- // Mengatur posisi kamera agar dapat melihat seluruh grid
SetCameraPosition();
- // Menetapkan referensi grid map pada NPC
+
npc.Map = this;
- // Menetapkan posisi awal NPC pada node (0,0)
npc.SetStartNode(gridNodeViews[0, 0].Node);
}
- ///
- /// Mengatur posisi kamera agar dapat menampilkan seluruh grid
- ///
void SetCameraPosition()
{
- // Calculate the center of the grid
float gridCenterX = ((numX - 1) * GridNodeWidth) / 2;
float gridCenterY = ((numY - 1) * GridNodeHeight) / 2;
- // Calculate dynamic offset based on grid size
float gridWidth = (numX - 1) * GridNodeWidth;
float gridHeight = (numY - 1) * GridNodeHeight;
- // For 20x20 grid: x=5.6, y=10.7, orthoSize=12.4
float baseX = 5.6f;
float baseY = 10.7f;
float baseOrthoSize = 12.4f;
float baseGridSize = 20f;
- // Scale position and ortho size based on current grid size relative to 20x20
float scaleFactor = Mathf.Max(numX, numY) / baseGridSize;
float xPos = baseX * scaleFactor;
float yPos = baseY * scaleFactor;
float orthoSize = baseOrthoSize * scaleFactor;
- // Position camera
Camera.main.transform.position = new Vector3(
xPos,
yPos,
-100.0f);
- // Set orthographic size
Camera.main.orthographicSize = orthoSize;
}
- // Tabel offset arah untuk pathfinding
private static readonly int[,] directions = new int[,] {
{ 0, 1 }, // Atas
{ 1, 0 }, // Kanan
@@ -156,33 +126,24 @@ public class GridMap : MonoBehaviour
{ -1, 1 } // Kiri Atas
};
- ///
- /// Mendapatkan daftar node tetangga yang dapat dilalui dari node yang diberikan
- ///
- /// Node saat ini
- /// Daftar node tetangga yang dapat dilalui
public List> GetNeighbours(PathFinding.Node loc)
{
- // Pre-alokasi dengan kapasitas maksimum untuk menghindari resize
List> neighbours = new List>(8);
int x = loc.Value.x;
int y = loc.Value.y;
- // Periksa arah kardinal terlebih dahulu (lebih cepat dan selalu ada)
for (int dir = 0; dir < 4; dir++)
{
int nx = x + directions[dir, 0];
int ny = y + directions[dir, 1];
- // Periksa bounds dan walkability dalam satu kondisi
if (nx >= 0 && nx < numX && ny >= 0 && ny < numY && gridNodeViews[nx, ny].Node.IsWalkable)
{
neighbours.Add(gridNodeViews[nx, ny].Node);
}
}
- // Jika diagonal movement diizinkan, periksa 4 arah diagonal
if (allowDiagonalMovement)
{
for (int dir = 4; dir < 8; dir++)
@@ -190,40 +151,29 @@ public class GridMap : MonoBehaviour
int nx = x + directions[dir, 0];
int ny = y + directions[dir, 1];
- // Periksa bounds dan walkability dalam satu kondisi
if (nx >= 0 && nx < numX && ny >= 0 && ny < numY && gridNodeViews[nx, ny].Node.IsWalkable)
- {
neighbours.Add(gridNodeViews[nx, ny].Node);
- }
}
}
return neighbours;
}
- ///
- /// Mendeteksi node pada posisi klik mouse dan mengubah status walkable-nya
- ///
public void RayCastAndToggleWalkable()
{
- // Konversi posisi mouse ke koordinat dunia
Vector2 rayPos = new Vector2(
Camera.main.ScreenToWorldPoint(Input.mousePosition).x,
Camera.main.ScreenToWorldPoint(Input.mousePosition).y);
- // Melakukan raycast untuk mendeteksi objek pada posisi mouse
RaycastHit2D hit = Physics2D.Raycast(rayPos, Vector2.zero, 0f);
if (hit)
{
- // Mendapatkan objek yang terkena raycast
GameObject obj = hit.transform.gameObject;
GridNodeView gnv = obj.GetComponent();
- // Memastikan node adalah valid dan belum diubah sebelumnya
if (gnv != null && gnv != lastToggledNode)
{
- //Debug.Log($"Toggling walkable state for node at position: {gnv.Node.Value}");
ToggleWalkable(gnv);
lastToggledNode = gnv;
}
@@ -234,34 +184,26 @@ public class GridMap : MonoBehaviour
}
}
- ///
- /// Mendeteksi node pada posisi klik mouse dan menetapkannya sebagai tujuan
- ///
public void RayCastAndSetDestination()
{
- // Don't allow changing destination during pathfinding, visualization, or movement
- if (npc != null && (npc.pathFinder?.Status == PathFinding.PathFinderStatus.RUNNING ||
- npc.IsVisualizingPath ||
+ if (npc != null && (npc.pathFinder?.Status == PathFinding.PathFinderStatus.RUNNING ||
+ npc.IsVisualizingPath ||
npc.IsMoving))
{
- // Pathfinding, visualization or movement is in progress - ignore position change
return;
}
-
- // Konversi posisi mouse ke koordinat dunia
+
Vector2 rayPos = new Vector2(
Camera.main.ScreenToWorldPoint(Input.mousePosition).x,
Camera.main.ScreenToWorldPoint(Input.mousePosition).y);
- // Melakukan raycast untuk mendeteksi objek pada posisi mouse
+
RaycastHit2D hit = Physics2D.Raycast(rayPos, Vector2.zero, 0f);
if (hit)
{
- // Mendapatkan objek yang terkena raycast
GameObject obj = hit.transform.gameObject;
GridNodeView gnv = obj.GetComponent();
- // Memindahkan objek destination ke posisi node yang dipilih
Vector3 pos = destination.position;
pos.x = gnv.Node.Value.x * gridNodeWidth;
pos.y = gnv.Node.Value.y * gridNodeHeight;
@@ -269,10 +211,6 @@ public class GridMap : MonoBehaviour
}
}
- ///
- /// Mengubah status walkable dari suatu node dan memperbarui warnanya
- ///
- /// GridNodeView yang akan diubah status walkable-nya
public void ToggleWalkable(GridNodeView gnv)
{
if (gnv == null)
@@ -281,10 +219,8 @@ public class GridMap : MonoBehaviour
int x = gnv.Node.Value.x;
int y = gnv.Node.Value.y;
- // Membalik status walkable node
gnv.Node.IsWalkable = !gnv.Node.IsWalkable;
- // Memperbarui warna node berdasarkan status walkable-nya
if (gnv.Node.IsWalkable)
{
gnv.SetInnerColor(COLOR_WALKABLE);
@@ -295,54 +231,41 @@ public class GridMap : MonoBehaviour
}
}
- ///
- /// Mendeteksi node pada posisi klik mouse dan menetapkannya sebagai posisi NPC
- ///
public void RayCastAndSetNPCPosition()
{
- // Don't allow changing NPC position during pathfinding, visualization, or movement
- if (npc != null && (npc.pathFinder?.Status == PathFinding.PathFinderStatus.RUNNING ||
- npc.IsVisualizingPath ||
+ if (npc != null && (npc.pathFinder?.Status == PathFinding.PathFinderStatus.RUNNING ||
+ npc.IsVisualizingPath ||
npc.IsMoving))
{
- // Pathfinding, visualization or movement is in progress - ignore position change
return;
}
-
- // Konversi posisi mouse ke koordinat dunia
+
Vector2 rayPos = new Vector2(
Camera.main.ScreenToWorldPoint(Input.mousePosition).x,
Camera.main.ScreenToWorldPoint(Input.mousePosition).y);
- // Melakukan raycast untuk mendeteksi objek pada posisi mouse
+
RaycastHit2D hit = Physics2D.Raycast(rayPos, Vector2.zero, 0f);
if (hit)
{
- // Mendapatkan objek yang terkena raycast
+
GameObject obj = hit.transform.gameObject;
GridNodeView gnv = obj.GetComponent();
if (gnv != null && gnv.Node.IsWalkable)
{
- // Set posisi NPC ke node yang dipilih
npc.SetStartNode(gnv.Node);
- //Debug.Log($"Setting NPC position to: {gnv.Node.Value}");
}
}
}
- ///
- /// Update dipanggil setiap frame untuk menangani input pengguna
- ///
void Update()
{
- // Check if pathfinding, visualization, or movement is active
- bool isPathfindingActive = npc != null && (npc.pathFinder?.Status == PathFinding.PathFinderStatus.RUNNING ||
- npc.IsVisualizingPath ||
+ bool isPathfindingActive = npc != null && (npc.pathFinder?.Status == PathFinding.PathFinderStatus.RUNNING ||
+ npc.IsVisualizingPath ||
npc.IsMoving);
-
- // Handle camera panning with middle mouse button
- if (Input.GetMouseButton(2)) // Middle mouse button
+ // Middle mouse button
+ if (Input.GetMouseButton(2))
{
float mouseX = Input.GetAxis("Mouse X");
float mouseY = Input.GetAxis("Mouse Y");
@@ -350,7 +273,7 @@ public class GridMap : MonoBehaviour
Camera.main.transform.position += moveDirection;
}
- // Mengubah status walkable node saat Shift + tombol kiri mouse ditekan
+ // Left mouse button
if (Input.GetMouseButton(0))
{
Vector2 currentMousePosition = new Vector2(
@@ -359,23 +282,22 @@ public class GridMap : MonoBehaviour
if (currentMousePosition != lastMousePosition)
{
- // If pathfinding is active, only allow camera movement, no interaction with grid
if (isPathfindingActive)
{
lastMousePosition = currentMousePosition;
return;
}
-
- // Menggambar dinding dengan Shift+Left Click
+
+ // Shift + Left Click
if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift))
{
RayCastAndToggleWalkable();
}
- // Set NPC position dengan Left Click biasa
else
{
RayCastAndSetNPCPosition();
}
+
lastMousePosition = currentMousePosition;
}
}
@@ -384,7 +306,6 @@ public class GridMap : MonoBehaviour
lastToggledNode = null;
}
- // Menetapkan tujuan baru saat tombol kanan mouse ditekan
if (Input.GetMouseButtonDown(1))
{
if (!isPathfindingActive)
@@ -393,63 +314,39 @@ public class GridMap : MonoBehaviour
}
}
- // Menyesuaikan ukuran kamera dengan scroll wheel
float scroll = Input.GetAxis("Mouse ScrollWheel");
if (scroll != 0.0f)
{
- // Increase max zoom out to 200 and keep min zoom in at 1
- Camera.main.orthographicSize = Mathf.Clamp(Camera.main.orthographicSize - scroll * Camera.main.orthographicSize, 1.0f, 200.0f);
+
+ Camera.main.orthographicSize = Mathf.Clamp(
+ Camera.main.orthographicSize - scroll * Camera.main.orthographicSize,
+ 1.0f,
+ 200.0f);
}
}
- ///
- /// Mendapatkan GridNode pada koordinat (x,y)
- ///
- /// Koordinat X
- /// Koordinat Y
- /// GridNode pada koordinat tersebut atau null jika tidak valid
public GridNode GetGridNode(int x, int y)
{
if (x >= 0 && x < numX && y >= 0 && y < numY)
- {
return gridNodeViews[x, y].Node;
- }
+
return null;
}
- ///
- /// Mendapatkan GridNodeView pada koordinat (x,y)
- ///
- /// Koordinat X
- /// Koordinat Y
- /// GridNodeView pada koordinat tersebut atau null jika tidak valid
public GridNodeView GetGridNodeView(int x, int y)
{
if (x >= 0 && x < numX && y >= 0 && y < numY)
- {
return gridNodeViews[x, y];
- }
+
return null;
}
- // Berbagai fungsi penghitungan jarak untuk algoritma pathfinding
-
- ///
- /// Menghitung jarak Manhattan (jarak grid) antara dua titik
- ///
- public static float GetManhattanCost(
- Vector2Int a,
- Vector2Int b)
+ public static float GetManhattanCost(Vector2Int a, Vector2Int b)
{
return Mathf.Abs(a.x - b.x) + Mathf.Abs(a.y - b.y);
}
- ///
- /// Menghitung jarak Euclidean (jarak garis lurus) antara dua titik
- ///
- public static float GetCostBetweenTwoCells(
- Vector2Int a,
- Vector2Int b)
+ public static float GetCostBetweenTwoCells(Vector2Int a, Vector2Int b)
{
return Mathf.Sqrt(
(a.x - b.x) * (a.x - b.x) +
@@ -457,19 +354,11 @@ public class GridMap : MonoBehaviour
);
}
- ///
- /// Alias untuk GetCostBetweenTwoCells (jarak Euclidean)
- ///
- public static float GetEuclideanCost(
- Vector2Int a,
- Vector2Int b)
+ public static float GetEuclideanCost(Vector2Int a, Vector2Int b)
{
return GetCostBetweenTwoCells(a, b);
}
- ///
- /// Callback yang dipanggil saat algoritma pathfinding mengubah current node
- ///
public void OnChangeCurrentNode(PathFinding.PathFinder.PathFinderNode node)
{
int x = node.Location.Value.x;
@@ -478,9 +367,6 @@ public class GridMap : MonoBehaviour
gnv.SetInnerColor(COLOR_CURRENT_NODE);
}
- ///
- /// Callback yang dipanggil saat node ditambahkan ke open list dalam algoritma pathfinding
- ///
public void OnAddToOpenList(PathFinding.PathFinder.PathFinderNode node)
{
int x = node.Location.Value.x;
@@ -489,9 +375,6 @@ public class GridMap : MonoBehaviour
gnv.SetInnerColor(COLOR_ADD_TO_OPENLIST);
}
- ///
- /// Callback yang dipanggil saat node ditambahkan ke closed list dalam algoritma pathfinding
- ///
public void OnAddToClosedList(PathFinding.PathFinder.PathFinderNode node)
{
int x = node.Location.Value.x;
@@ -500,9 +383,6 @@ public class GridMap : MonoBehaviour
gnv.SetInnerColor(COLOR_ADD_TO_CLOSEDLIST);
}
- ///
- /// Mengatur ulang warna semua node grid ke warna default berdasarkan status walkable-nya
- ///
public void ResetGridNodeColours()
{
for (int i = 0; i < numX; ++i)
@@ -522,9 +402,6 @@ public class GridMap : MonoBehaviour
}
}
- ///
- /// Simple class to store grid positions for serialization
- ///
[System.Serializable]
public class SerializablePosition
{
@@ -538,26 +415,18 @@ public class GridMap : MonoBehaviour
}
}
- ///
- /// Kelas untuk menyimpan status grid untuk keperluan save/load
- ///
[System.Serializable]
public class GridState
{
public bool[,] walkableStates;
- public SerializablePosition npcPosition; // Position of NPC
- public SerializablePosition destinationPosition; // Position of destination
+ public SerializablePosition npcPosition;
+ public SerializablePosition destinationPosition;
}
- ///
- /// Menyimpan status walkable dari semua node grid ke file
- ///
- /// Path file untuk menyimpan data
public void SaveGridState(string filePath)
{
try
{
- // Pastikan direktori ada
string directory = Path.GetDirectoryName(filePath);
if (!Directory.Exists(directory) && !string.IsNullOrEmpty(directory))
{
@@ -567,7 +436,6 @@ public class GridMap : MonoBehaviour
GridState gridState = new GridState();
gridState.walkableStates = new bool[numX, numY];
- // Menyimpan status walkable dari setiap node
for (int i = 0; i < numX; i++)
{
for (int j = 0; j < numY; j++)
@@ -576,29 +444,25 @@ public class GridMap : MonoBehaviour
}
}
- // Save NPC position using our serializable class
gridState.npcPosition = new SerializablePosition(
npc.transform.position.x / GridNodeWidth,
npc.transform.position.y / GridNodeHeight
);
- // Save destination position using our serializable class
gridState.destinationPosition = new SerializablePosition(
destination.position.x / GridNodeWidth,
destination.position.y / GridNodeHeight
);
- // Configure serializer settings to ignore Unity-specific circular references
JsonSerializerSettings settings = new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
};
- // Mengkonversi ke JSON dan menyimpan ke file
string json = JsonConvert.SerializeObject(gridState, settings);
File.WriteAllText(filePath, json);
-
Debug.Log($"Grid state saved to {filePath}");
+
}
catch (System.Exception e)
{
@@ -606,10 +470,6 @@ public class GridMap : MonoBehaviour
}
}
- ///
- /// Memuat status walkable dari semua node grid dari file
- ///
- /// Path file untuk memuat data
public void LoadGridState(string filePath)
{
try
@@ -620,18 +480,15 @@ public class GridMap : MonoBehaviour
return;
}
- // Membaca dan mengkonversi data dari file JSON
string json = File.ReadAllText(filePath);
-
- // Configure deserializer settings to handle missing properties (compatibility)
+
JsonSerializerSettings settings = new JsonSerializerSettings
{
MissingMemberHandling = MissingMemberHandling.Ignore
};
-
+
GridState gridState = JsonConvert.DeserializeObject(json, settings);
- // Periksa apakah ukuran grid dalam file sesuai dengan grid saat ini
if (gridState.walkableStates.GetLength(0) != numX ||
gridState.walkableStates.GetLength(1) != numY)
{
@@ -639,7 +496,6 @@ public class GridMap : MonoBehaviour
ResizeGrid(gridState.walkableStates.GetLength(0), gridState.walkableStates.GetLength(1));
}
- // Menerapkan status walkable ke setiap node
for (int i = 0; i < numX; i++)
{
for (int j = 0; j < numY; j++)
@@ -649,53 +505,42 @@ public class GridMap : MonoBehaviour
}
}
- // Check if this is an older save file version (backward compatibility)
bool hasPositionData = gridState.npcPosition != null && gridState.destinationPosition != null;
- // Load NPC position if saved
if (npc != null)
{
if (hasPositionData)
{
- // Find the closest valid grid node position
int npcX = Mathf.Clamp(Mathf.RoundToInt(gridState.npcPosition.x), 0, numX - 1);
int npcY = Mathf.Clamp(Mathf.RoundToInt(gridState.npcPosition.y), 0, numY - 1);
-
- // Make sure the position is walkable
+
GridNode npcNode = GetGridNode(npcX, npcY);
if (npcNode != null && npcNode.IsWalkable)
{
- // Set NPC position
npc.SetStartNode(npcNode);
}
else
{
- // Find a walkable node if the saved position isn't walkable
FindWalkableNodeAndSetNPC();
}
}
else
{
- // For older save files, find a walkable position
FindWalkableNodeAndSetNPC();
}
}
- // Load destination position if saved
if (destination != null)
{
if (hasPositionData)
{
- // Find the closest valid grid node position
int destX = Mathf.Clamp(Mathf.RoundToInt(gridState.destinationPosition.x), 0, numX - 1);
int destY = Mathf.Clamp(Mathf.RoundToInt(gridState.destinationPosition.y), 0, numY - 1);
-
- // Set destination position
+
SetDestination(destX, destY);
}
else
{
- // For older save files, set destination to the opposite corner or a walkable node
FindWalkableNodeAndSetDestination();
}
}
@@ -708,12 +553,8 @@ public class GridMap : MonoBehaviour
}
}
- ///
- /// Find a walkable node and set the NPC position to it
- ///
private void FindWalkableNodeAndSetNPC()
{
- // Find first walkable node
for (int i = 0; i < numX; i++)
{
for (int j = 0; j < numY; j++)
@@ -728,28 +569,23 @@ public class GridMap : MonoBehaviour
}
}
- ///
- /// Resizes the grid to the specified dimensions
- ///
public bool ResizeGrid(int newSizeX, int newSizeY)
{
- // Enforce grid size limits
const int MAX_GRID_SIZE = 200;
const int MIN_GRID_SIZE = 2;
-
+
if (newSizeX > MAX_GRID_SIZE || newSizeY > MAX_GRID_SIZE)
{
Debug.LogWarning($"Attempted to resize grid beyond maximum size of {MAX_GRID_SIZE}x{MAX_GRID_SIZE}. Operation cancelled.");
return false;
}
-
+
if (newSizeX < MIN_GRID_SIZE || newSizeY < MIN_GRID_SIZE)
{
Debug.LogWarning($"Attempted to resize grid below minimum size of {MIN_GRID_SIZE}x{MIN_GRID_SIZE}. Operation cancelled.");
return false;
}
-
- // Clean up existing grid
+
if (gridNodeViews != null)
{
for (int i = 0; i < numX; i++)
@@ -764,11 +600,9 @@ public class GridMap : MonoBehaviour
}
}
- // Update dimensions
numX = newSizeX;
numY = newSizeY;
- // Create new grid
gridNodeViews = new GridNodeView[NumX, NumY];
for (int i = 0; i < NumX; i++)
{
@@ -790,21 +624,16 @@ public class GridMap : MonoBehaviour
}
}
- // Update camera position
SetCameraPosition();
- // Reset NPC position if needed
if (npc != null)
{
npc.SetStartNode(gridNodeViews[0, 0].Node);
}
-
+
return true;
}
- ///
- /// Sets the destination position for pathfinding
- ///
public void SetDestination(int x, int y)
{
if (x >= 0 && x < numX && y >= 0 && y < numY)
@@ -816,16 +645,10 @@ public class GridMap : MonoBehaviour
}
}
- ///
- /// Membuat maze dengan algoritma Recursive Backtracking
- ///
- /// Kepadatan dinding dalam persen (0-100), mempengaruhi rasio jalur terhadap ruang terbuka
public void GenerateRandomMaze(float density = 35f)
{
- // Handle special cases for 0% and 100% density
if (density <= 0f)
{
- // Make all cells walkable (no walls)
for (int x = 0; x < numX; x++)
{
for (int y = 0; y < numY; y++)
@@ -846,7 +669,6 @@ public class GridMap : MonoBehaviour
}
else if (density >= 100f)
{
- // Make all cells non-walkable (all walls)
for (int x = 0; x < numX; x++)
{
for (int y = 0; y < numY; y++)
@@ -863,22 +685,15 @@ public class GridMap : MonoBehaviour
}
}
}
-
- // No path creation - truly 100% blocked
+
return;
}
-
- // Use the recursive backtracking maze generation for normal density values
+
GenerateRecursiveBacktrackingMaze(density);
}
- ///
- /// Membuat maze menggunakan algoritma recursive backtracking dengan kontrol densitas dinding.
- ///
- /// Persentase dinding dalam maze (0-100)
public void GenerateRecursiveBacktrackingMaze(float density = 30f)
{
- // Inisialisasi semua tile sebagai dinding (bukan jalur)
for (int x = 0; x < numX; x++)
{
for (int y = 0; y < numY; y++)
@@ -896,99 +711,76 @@ public class GridMap : MonoBehaviour
}
}
- // Hitung jumlah total tile dan target dinding
int totalTiles = numX * numY;
int targetWallCount = Mathf.RoundToInt((density / 100f) * totalTiles);
- // Buat sistem random
System.Random random = new System.Random();
// Arah: Atas (0), Kanan (1), Bawah (2), Kiri (3)
int[] dx = { 0, 1, 0, -1 };
int[] dy = { 1, 0, -1, 0 };
- // Mulai dari posisi acak
int startX = random.Next(0, numX);
int startY = random.Next(0, numY);
- // Buat cell awal menjadi jalur
MakeCellWalkable(startX, startY);
- // Stack untuk recursive backtracking
Stack stack = new Stack();
stack.Push(new Vector2Int(startX, startY));
// Jalankan recursive backtracking
while (stack.Count > 0)
{
- // Ambil posisi saat ini
Vector2Int current = stack.Peek();
- // Daftar untuk menyimpan arah yang valid (belum dikunjungi)
List directions = new List();
- // Cek semua arah
for (int i = 0; i < 4; i++)
{
- // Periksa tetangga 2 langkah (untuk memastikan kita tidak membuat jalur yang bersebelahan)
int nx = current.x + dx[i] * 2;
int ny = current.y + dy[i] * 2;
- // Jika tetangga dalam batas grid dan belum dikunjungi
if (nx >= 0 && nx < numX && ny >= 0 && ny < numY)
{
GridNode neighborNode = GetGridNode(nx, ny);
if (neighborNode != null && !neighborNode.IsWalkable)
{
- // Tambahkan arah ke daftar valid
directions.Add(i);
}
}
}
- // Jika ada arah yang valid
if (directions.Count > 0)
{
- // Pilih arah secara acak
int direction = directions[random.Next(0, directions.Count)];
- // Hitung posisi tetangga dan dinding di antaranya
int nx = current.x + dx[direction] * 2;
int ny = current.y + dy[direction] * 2;
int wallX = current.x + dx[direction];
int wallY = current.y + dy[direction];
- // Buat jalur di tetangga dan dinding di antaranya
MakeCellWalkable(nx, ny);
MakeCellWalkable(wallX, wallY);
- // Tambahkan tetangga ke stack
stack.Push(new Vector2Int(nx, ny));
}
else
{
- // Tidak ada arah yang valid, backtrack
stack.Pop();
}
}
- // Hitung jumlah dinding saat ini
int currentWallCount = CountNonWalkableCells();
- // Jika kita memiliki terlalu banyak dinding (kepadatan terlalu tinggi)
if (currentWallCount > targetWallCount)
{
- // Hapus dinding secara acak hingga mencapai target
RemoveRandomWalls(currentWallCount - targetWallCount);
}
- // Jika kita memiliki terlalu sedikit dinding (kepadatan terlalu rendah)
else if (currentWallCount < targetWallCount)
{
- // Tambahkan dinding secara acak hingga mencapai target
AddRandomWalls(targetWallCount - currentWallCount);
}
- // Pastikan posisi NPC dan tujuan dapat dilalui
EnsureNodeAndNeighborsWalkable((int)(npc.transform.position.x / gridNodeWidth),
(int)(npc.transform.position.y / gridNodeHeight));
@@ -996,9 +788,6 @@ public class GridMap : MonoBehaviour
(int)(destination.position.y / gridNodeHeight));
}
- ///
- /// Menghitung jumlah sel yang tidak dapat dilalui (dinding) di grid
- ///
private int CountNonWalkableCells()
{
int count = 0;
@@ -1016,9 +805,6 @@ public class GridMap : MonoBehaviour
return count;
}
- ///
- /// Membuat sebuah sel menjadi dapat dilalui (jalur)
- ///
private void MakeCellWalkable(int x, int y)
{
GridNode node = GetGridNode(x, y);
@@ -1033,9 +819,6 @@ public class GridMap : MonoBehaviour
}
}
- ///
- /// Membuat sebuah sel menjadi tidak dapat dilalui (dinding)
- ///
private void MakeCellNonWalkable(int x, int y)
{
GridNode node = GetGridNode(x, y);
@@ -1050,14 +833,10 @@ public class GridMap : MonoBehaviour
}
}
- ///
- /// Menghapus dinding secara acak dari maze
- ///
private void RemoveRandomWalls(int count)
{
System.Random random = new System.Random();
- // Buat daftar semua dinding
List walls = new List();
for (int x = 0; x < numX; x++)
{
@@ -1071,7 +850,6 @@ public class GridMap : MonoBehaviour
}
}
- // Acak daftarnya
for (int i = 0; i < walls.Count; i++)
{
int j = random.Next(i, walls.Count);
@@ -1080,7 +858,6 @@ public class GridMap : MonoBehaviour
walls[j] = temp;
}
- // Hapus dinding hingga mencapai target
for (int i = 0; i < count && i < walls.Count; i++)
{
Vector2Int wall = walls[i];
@@ -1088,14 +865,10 @@ public class GridMap : MonoBehaviour
}
}
- ///
- /// Menambahkan dinding secara acak ke maze
- ///
private void AddRandomWalls(int count)
{
System.Random random = new System.Random();
- // Buat daftar semua jalur
List paths = new List();
for (int x = 0; x < numX; x++)
{
@@ -1104,7 +877,6 @@ public class GridMap : MonoBehaviour
GridNode node = GetGridNode(x, y);
if (node != null && node.IsWalkable)
{
- // Jangan tambahkan dinding di posisi NPC atau tujuan
if ((x != (int)(npc.transform.position.x / gridNodeWidth) ||
y != (int)(npc.transform.position.y / gridNodeHeight)) &&
(x != (int)(destination.position.x / gridNodeWidth) ||
@@ -1116,7 +888,6 @@ public class GridMap : MonoBehaviour
}
}
- // Acak daftarnya
for (int i = 0; i < paths.Count; i++)
{
int j = random.Next(i, paths.Count);
@@ -1125,13 +896,11 @@ public class GridMap : MonoBehaviour
paths[j] = temp;
}
- // Tambahkan dinding hingga mencapai target atau hingga daftar jalur habis
int added = 0;
for (int i = 0; i < paths.Count && added < count; i++)
{
Vector2Int path = paths[i];
- // Pastikan menambahkan dinding tidak memotong jalur penting
if (!IsPathCritical(path.x, path.y))
{
MakeCellNonWalkable(path.x, path.y);
@@ -1140,12 +909,8 @@ public class GridMap : MonoBehaviour
}
}
- ///
- /// Memeriksa apakah sebuah sel merupakan jalur kritis yang tidak boleh diblokir
- ///
private bool IsPathCritical(int x, int y)
{
- // Hindari memblokir jalur satu-satunya
int walkableNeighbors = 0;
// Arah: Atas, Kanan, Bawah, Kiri
@@ -1166,26 +931,18 @@ public class GridMap : MonoBehaviour
}
}
}
-
- // Jika hanya memiliki satu atau dua tetangga yang dapat dilalui, ini mungkin jalur penting
return walkableNeighbors <= 2;
}
- ///
- /// Memastikan bahwa node dan tetangganya dapat dilalui
- ///
private void EnsureNodeAndNeighborsWalkable(int x, int y)
{
- // Pastikan node utama dapat dilalui
MakeCellWalkable(x, y);
- // Pastikan setidaknya satu tetangga dapat dilalui agar tidak terjebak
int[] dx = { 0, 1, 0, -1 };
int[] dy = { 1, 0, -1, 0 };
bool hasWalkableNeighbor = false;
- // Cek jika sudah ada tetangga yang dapat dilalui
for (int i = 0; i < 4; i++)
{
int nx = x + dx[i];
@@ -1202,7 +959,6 @@ public class GridMap : MonoBehaviour
}
}
- // Jika tidak ada tetangga yang dapat dilalui, buat salah satu tetangga dapat dilalui
if (!hasWalkableNeighbor)
{
for (int i = 0; i < 4; i++)
@@ -1219,35 +975,28 @@ public class GridMap : MonoBehaviour
}
}
- ///
- /// Find a walkable node and set the destination position to it
- ///
private void FindWalkableNodeAndSetDestination()
{
- // Try to place destination in the far corner from the NPC
int npcX = (int)(npc.transform.position.x / GridNodeWidth);
int npcY = (int)(npc.transform.position.y / GridNodeHeight);
-
- // Try opposite corner first
+
int destX = numX - 1;
int destY = numY - 1;
-
+
GridNode destinationNode = GetGridNode(destX, destY);
if (destinationNode != null && destinationNode.IsWalkable)
{
SetDestination(destX, destY);
return;
}
-
- // If the opposite corner isn't walkable, find any walkable node that's different from NPC position
+
for (int i = numX - 1; i >= 0; i--)
{
for (int j = numY - 1; j >= 0; j--)
{
- // Skip the node where NPC is
if (i == npcX && j == npcY)
continue;
-
+
GridNode node = GetGridNode(i, j);
if (node != null && node.IsWalkable)
{
@@ -1256,8 +1005,6 @@ public class GridMap : MonoBehaviour
}
}
}
-
- // If no suitable node found, use the NPC node as a fallback
SetDestination(npcX, npcY);
}
}
\ No newline at end of file
diff --git a/Assets/Scripts/GridNode.cs b/Assets/Scripts/GridNode.cs
index 1ff24f4..e3a7f54 100644
--- a/Assets/Scripts/GridNode.cs
+++ b/Assets/Scripts/GridNode.cs
@@ -9,42 +9,18 @@ using UnityEngine;
///
public class GridNode : PathFinding.Node
{
- ///
- /// Menentukan apakah node ini dapat dilalui oleh karakter.
- /// True jika node dapat dilalui, false jika node adalah penghalang.
- ///
- public bool IsWalkable { get; set; }
+ public bool IsWalkable { get; set; }
- ///
- /// Referensi ke GridMap yang mengelola seluruh grid.
- /// Digunakan untuk mendapatkan tetangga dan operasi lain yang berhubungan dengan grid.
- ///
- public GridMap gridMap; // Change to internal or public
+ internal GridMap gridMap;
- ///
- /// Constructor untuk membuat GridNode baru.
- ///
- /// Koordinat Vector2Int yang merepresentasikan posisi node di dalam grid
- /// Referensi ke GridMap yang mengelola grid ini
- public GridNode(Vector2Int value, GridMap gridMap)
- : base(value)
- {
- IsWalkable = true; // Secara default node dapat dilalui
- this.gridMap = gridMap; // Simpan referensi ke GridMap
- }
+ public GridNode(Vector2Int value, GridMap gridMap) : base(value)
+ {
+ IsWalkable = true;
+ this.gridMap = gridMap;
+ }
- ///
- /// Mengimplementasikan metode abstrak dari kelas dasar untuk mendapatkan daftar node tetangga.
- /// Metode ini akan memanggil GridMap.GetNeighbours() untuk mendapatkan semua node tetangga yang dapat dilalui.
- ///
- /// Daftar node tetangga yang dapat dicapai dari node ini
- public override
- List> GetNeighbours()
- {
- // Return an empty list for now.
- // Later we will call gridMap's GetNeighbours
- // function.
- //return new List>();
- return gridMap.GetNeighbours(this);
- }
+ public override List> GetNeighbours()
+ {
+ return gridMap.GetNeighbours(this);
+ }
}
diff --git a/Assets/Scripts/GridNodeView.cs b/Assets/Scripts/GridNodeView.cs
index 1373177..9547188 100644
--- a/Assets/Scripts/GridNodeView.cs
+++ b/Assets/Scripts/GridNodeView.cs
@@ -6,36 +6,19 @@ using UnityEngine;
///
public class GridNodeView : MonoBehaviour
{
- ///
- /// Referensi ke SpriteRenderer untuk bagian dalam node.
- ///
[SerializeField]
SpriteRenderer innerSprite;
- ///
- /// Referensi ke SpriteRenderer untuk bagian luar node.
- ///
[SerializeField]
SpriteRenderer outerSprite;
- ///
- /// Properti yang menyimpan referensi ke objek GridNode yang terkait dengan view ini.
- ///
public GridNode Node { get; set; }
- ///
- /// Mengatur warna sprite bagian dalam dari node.
- ///
- /// Warna yang akan diaplikasikan pada sprite bagian dalam.
public void SetInnerColor(Color col)
{
innerSprite.color = col;
}
- ///
- /// Mengatur warna sprite bagian luar dari node.
- ///
- /// Warna yang akan diaplikasikan pada sprite bagian luar.
public void SetOuterColor(Color col)
{
outerSprite.color = col;
diff --git a/Assets/Scripts/NPC.cs b/Assets/Scripts/NPC.cs
index 1b06128..54fc588 100644
--- a/Assets/Scripts/NPC.cs
+++ b/Assets/Scripts/NPC.cs
@@ -5,19 +5,20 @@ using System.Collections.Generic;
using System.Diagnostics;
using UnityEngine;
+///
+/// Metrik yang digunakan untuk mengukur performa pathfinding.
+/// Dibuat dalam bentuk struct untuk efisiensi memori dan kemudahan penggunaan.
+///
public struct PathfindingMetrics
{
- // Untuk Pengukuran Kinerja
- public float timeTaken; // in milliseconds
+ public float timeTaken; // miliseconds
public int pathLength;
- public int nodesExplored; // number of nodes in path
- public long memoryUsed; // memory used by pathfinding in bytes
+ public int nodesExplored;
+ public long memoryUsed; // bytes
- // Untuk Visualisasi
- public int maxOpenListSize; // maximum size of open list during pathfinding
- public int maxClosedListSize; // maximum size of closed list during pathfinding
+ public int maxOpenListSize;
+ public int maxClosedListSize;
- // Tambahan untuk cost metrics
public float totalGCost; // Total biaya G untuk jalur (jarak sebenarnya)
public float totalHCost; // Total biaya H untuk jalur (heuristik)
public float totalFCost; // Total biaya F untuk jalur (G + H)
@@ -31,16 +32,10 @@ public class NPC : MonoBehaviour
{
public float speed = 2.0f;
public Queue wayPoints = new Queue();
-
- // Event that fires when pathfinding is complete with performance metrics
- public event Action OnPathfindingComplete;
- // Last measured memory usage (for accessing from outside)
+ public event Action OnPathfindingComplete;
public long LastMeasuredMemoryUsage { get; private set; } = 0;
- ///
- /// Enumerasi yang merepresentasikan berbagai algoritma pathfinding yang tersedia.
- ///
public enum PathFinderType
{
ASTAR,
@@ -57,36 +52,21 @@ public class NPC : MonoBehaviour
public GridMap Map { get; set; }
- // List to store all steps for visualization playback
private List visualizationSteps = new List();
private bool isVisualizingPath = false;
private bool isMoving = false;
-
- // Public accessor for visualization state
+
public bool IsVisualizingPath => isVisualizingPath;
-
- // Public accessor for movement state
public bool IsMoving => isMoving;
-
- // Event that fires when visualization is complete
+
public event Action OnVisualizationComplete;
-
- // Event that fires when movement is complete
public event Action OnMovementComplete;
- // Properties to control visualization
- [SerializeField]
+ public float visualizationSpeed = 0.0f;
+ public int visualizationBatch = 1;
- // Visualization speed is time between visualization steps
- public float visualizationSpeed = 0.0f; // Default 0; set higher for slower visualization
+ public bool showVisualization = true;
- // Visualization batch is the number of steps to visualize at once
- public int visualizationBatch = 1; // Default 1; set higher value for faster visualization
-
- [SerializeField]
- public bool showVisualization = true; // Whether to show visualization at all
-
- // Struct to store each step of the pathfinding process for visualization
private struct PathfindingVisualizationStep
{
public enum StepType { CurrentNode, OpenList, ClosedList, FinalPath }
@@ -132,27 +112,25 @@ public class NPC : MonoBehaviour
{
while (wayPoints.Count > 0)
{
- // Set the moving flag when starting to move
if (!isMoving && wayPoints.Count > 0)
{
isMoving = true;
UnityEngine.Debug.Log("NPC movement started");
}
-
+
yield return StartCoroutine(
Coroutine_MoveToPoint(
wayPoints.Dequeue(),
speed));
}
-
- // If we were moving but now have no more waypoints, signal movement completion
+
if (isMoving && wayPoints.Count == 0)
{
isMoving = false;
UnityEngine.Debug.Log("NPC movement complete, invoking OnMovementComplete event");
OnMovementComplete?.Invoke();
}
-
+
yield return null;
}
}
@@ -163,7 +141,6 @@ public class NPC : MonoBehaviour
node.Value.x * Map.GridNodeWidth,
node.Value.y * Map.GridNodeHeight));
- // We set a color to show the path.
GridNodeView gnv = Map.GetGridNodeView(node.Value.x, node.Value.y);
gnv.SetInnerColor(Map.COLOR_PATH);
}
@@ -179,26 +156,20 @@ public class NPC : MonoBehaviour
private void Start()
{
- // Initialize pathfinder based on type
InitializePathFinder();
-
- // Start the movement coroutine
StartCoroutine(Coroutine_MoveTo());
}
private void InitializePathFinder()
{
- // Hitung perkiraan jumlah node dalam grid
int estimatedNodeCount = 0;
if (Map != null)
{
estimatedNodeCount = Map.NumX * Map.NumY;
}
- // Log informasi ukuran grid dan strategi optimisasi
bool isLargeGrid = estimatedNodeCount > 2500;
- // Create new pathfinder instance
switch (pathFinderType)
{
case PathFinderType.ASTAR:
@@ -218,24 +189,20 @@ public class NPC : MonoBehaviour
break;
}
- // Set up callbacks
pathFinder.onSuccess = OnSuccessPathFinding;
pathFinder.onFailure = OnFailurePathFinding;
- // Gunakan setting asli
pathFinder.HeuristicCost = GridMap.GetManhattanCost;
pathFinder.NodeTraversalCost = GridMap.GetEuclideanCost;
}
public void MoveTo(GridNode destination, bool silentMode = false)
{
- // inialisaasi pathfinder jika belum ada
if (pathFinder == null)
{
InitializePathFinder();
}
-
if (pathFinder.Status == PathFinderStatus.RUNNING)
{
return;
@@ -252,7 +219,6 @@ public class NPC : MonoBehaviour
SetStartNode(start);
- // Reset grid colors
if (!silentMode)
{
Map.ResetGridNodeColours();
@@ -261,7 +227,6 @@ public class NPC : MonoBehaviour
visualizationSteps.Clear();
isVisualizingPath = false;
- // jika gagal menginisialisasi pathfinder, tidak perlu melanjutkan
if (!pathFinder.Initialise(start, destination))
{
return;
@@ -274,9 +239,7 @@ public class NPC : MonoBehaviour
{
yield return StartCoroutine(MeasurePerformance(silentMode));
- // Start visualization after calculation is complete regardless of success or failure
- // This allows visualization of the explored area even when no path is found
- if (showVisualization && !silentMode &&
+ if (showVisualization && !silentMode &&
(pathFinder.Status == PathFinderStatus.SUCCESS || pathFinder.Status == PathFinderStatus.FAILURE))
{
yield return StartCoroutine(VisualizePathfinding());
@@ -285,71 +248,54 @@ public class NPC : MonoBehaviour
IEnumerator MeasurePerformance(bool silentMode = false)
{
- // Memory tracking for pathfinding structures - tetap untuk visualisasi
int maxOpenListSize = 0;
int currentOpenListSize = 0;
int maxClosedListSize = 0;
int currentClosedListSize = 0;
- // Pre-allocate visualizationSteps with estimated capacity to avoid reallocations
visualizationSteps = new List(4);
- //GC.Collect(2, GCCollectionMode.Forced, true, true);
- //GC.WaitForPendingFinalizers(); // Tunggu semua finalizers selesai
-
- // ===== MEMORY MEASUREMENT START: Ukur memory sebelum algoritma =====
long memoryBefore = System.GC.GetTotalMemory(false);
- // Setup callbacks before running algorithm
SetupCallbacks(silentMode, ref maxOpenListSize, ref currentOpenListSize,
ref maxClosedListSize, ref currentClosedListSize);
- // ===== STOPWATCH START: Pengukuran waktu algoritma =====
Stopwatch algorithmTimer = Stopwatch.StartNew();
- // Counter untuk jumlah step yang dilakukan algoritma
int stepCount = 0;
- // Execute the pathfinding algorithm synchronously in a single frame without visualization
while (pathFinder.Status == PathFinderStatus.RUNNING)
{
stepCount++;
pathFinder.Step();
}
- // ===== STOPWATCH STOP: Akhir pengukuran waktu algoritma =====
algorithmTimer.Stop();
- // ===== MEMORY MEASUREMENT END: Ukur memory setelah algoritma =====
long memoryAfter = System.GC.GetTotalMemory(false);
long memoryUsed = memoryAfter - memoryBefore;
- // Store the memory usage for external access
LastMeasuredMemoryUsage = memoryUsed > 0 ? memoryUsed : 1024;
-
+
float milliseconds = (algorithmTimer.ElapsedTicks * 1000.0f) / Stopwatch.Frequency;
- // Calculate path length once and reuse
int pathLength = 0;
int nodesExplored = 0;
float totalGCost = 0;
float totalHCost = 0;
float totalFCost = 0;
- // Add memory for path reconstruction (final path)
if (pathFinder.Status == PathFinderStatus.SUCCESS)
{
pathLength = CalculatePathLength();
nodesExplored = pathFinder.ClosedListCount;
- // Hitung total G, H, dan F cost
CalculatePathCosts(out totalGCost, out totalHCost, out totalFCost);
}
- // Create and send metrics - waktu pengukuran algoritma yang tepat
PathfindingMetrics metrics = new PathfindingMetrics
{
- timeTaken = milliseconds, // Waktu algoritma yang diukur dengan stopwatch
+ timeTaken = milliseconds,
pathLength = pathLength,
nodesExplored = nodesExplored,
memoryUsed = memoryUsed,
@@ -360,25 +306,20 @@ public class NPC : MonoBehaviour
totalFCost = totalFCost,
};
- // *** IMPORTANT FIX: Always invoke the event, regardless of silent mode ***
- // Report metrics before visualization
OnPathfindingComplete?.Invoke(metrics);
- // Path visualization and handling
HandlePathFindingResult(silentMode, pathLength);
- // Pastikan untuk mengembalikan nilai di akhir coroutine
yield return null;
}
-
- ///
- /// Setup callbacks for tracking nodes in open/closed lists and visualization
- ///
- private void SetupCallbacks(bool silentMode, ref int maxOpenListSize, ref int currentOpenListSize,
- ref int maxClosedListSize, ref int currentClosedListSize)
+ private void SetupCallbacks(
+ bool silentMode,
+ ref int maxOpenListSize,
+ ref int currentOpenListSize,
+ ref int maxClosedListSize,
+ ref int currentClosedListSize)
{
- // Buat variabel lokal untuk menghindari masalah dengan ref parameter dalam lambda
int localCurrentOpenListSize = currentOpenListSize;
int localMaxOpenListSize = maxOpenListSize;
int localCurrentClosedListSize = currentClosedListSize;
@@ -386,7 +327,6 @@ public class NPC : MonoBehaviour
if (silentMode)
{
- // In silent mode, just set minimal callbacks for metrics
pathFinder.onAddToOpenList = (node) =>
{
localCurrentOpenListSize++;
@@ -399,12 +339,11 @@ public class NPC : MonoBehaviour
localCurrentClosedListSize++;
if (localCurrentClosedListSize > localMaxClosedListSize)
localMaxClosedListSize = localCurrentClosedListSize;
- localCurrentOpenListSize--; // When a node is moved from open to closed list
+ localCurrentOpenListSize--;
};
}
else
{
- // In regular mode, track and prepare for visualization
pathFinder.onAddToOpenList = (node) =>
{
visualizationSteps.Add(new PathfindingVisualizationStep(
@@ -426,7 +365,7 @@ public class NPC : MonoBehaviour
if (localCurrentClosedListSize > localMaxClosedListSize)
localMaxClosedListSize = localCurrentClosedListSize;
- localCurrentOpenListSize--; // When a node is moved from open to closed list
+ localCurrentOpenListSize--;
};
pathFinder.onChangeCurrentNode = (node) =>
@@ -439,38 +378,29 @@ public class NPC : MonoBehaviour
}
-
- // Setelah lambda selesai dijalankan, perbarui variabel ref
maxOpenListSize = localMaxOpenListSize;
currentOpenListSize = localCurrentOpenListSize;
maxClosedListSize = localMaxClosedListSize;
currentClosedListSize = localCurrentClosedListSize;
}
- ///
- /// Handle path finding result (success or failure)
- ///
private void HandlePathFindingResult(bool silentMode, int pathLength)
{
if (pathFinder.Status == PathFinderStatus.SUCCESS)
{
OnSuccessPathFinding();
- // In non-silent mode, prepare visualization data for the path
if (!silentMode && showVisualization)
{
- // Add the path nodes for visualization in efficient batched way
PathFinder.PathFinderNode node = pathFinder.CurrentNode;
- List pathPositions = new List(pathLength); // Pre-allocate with known size
+ List pathPositions = new List(pathLength);
- // Build path in reverse order
while (node != null)
{
pathPositions.Add(node.Location.Value);
node = node.Parent;
}
- // Process path in correct order
for (int i = pathPositions.Count - 1; i >= 0; i--)
{
visualizationSteps.Add(new PathfindingVisualizationStep(
@@ -482,40 +412,19 @@ public class NPC : MonoBehaviour
else if (pathFinder.Status == PathFinderStatus.FAILURE)
{
OnFailurePathFinding();
-
- // For failure case, we don't add any final path visualization steps
- // The exploration steps (open/closed lists) are already added during the search
- // and will be visualized to show what nodes were explored before failure
+
UnityEngine.Debug.Log($"Pathfinding failed - visualization will show {visualizationSteps.Count} exploration steps");
}
}
- ///
- /// Memformat ukuran byte menjadi string yang lebih mudah dibaca
- ///
- private string FormatBytes(long bytes)
- {
- string[] sizes = { "B", "KB", "MB", "GB" };
- int order = 0;
- double size = bytes;
- while (size >= 1024 && order < sizes.Length - 1)
- {
- order++;
- size = size / 1024;
- }
- return $"{size:0.##} {sizes[order]}";
- }
-
void OnSuccessPathFinding()
{
float totalGCost = 0;
float totalHCost = 0;
float totalFCost = 0;
- // Hitung biaya-biaya path menggunakan metode yang sudah ada
CalculatePathCosts(out totalGCost, out totalHCost, out totalFCost);
- // Informasi dasar
int pathLength = CalculatePathLength();
}
@@ -525,12 +434,8 @@ public class NPC : MonoBehaviour
UnityEngine.Debug.Log("Pathfinding failed");
}
- ///
- /// Changes the pathfinding algorithm at runtime
- ///
public void ChangeAlgorithm(PathFinderType newType)
{
- // Don't change if pathfinding is in progress
if (pathFinder != null && pathFinder.Status == PathFinderStatus.RUNNING)
{
UnityEngine.Debug.Log("Cannot change algorithm while pathfinding is running");
@@ -539,14 +444,12 @@ public class NPC : MonoBehaviour
pathFinderType = newType;
- // Hitung perkiraan jumlah node dalam grid
int estimatedNodeCount = 0;
if (Map != null)
{
estimatedNodeCount = Map.NumX * Map.NumY;
}
- // Create new pathfinder instance
switch (pathFinderType)
{
case PathFinderType.ASTAR:
@@ -566,11 +469,9 @@ public class NPC : MonoBehaviour
break;
}
- // Set up callbacks
pathFinder.onSuccess = OnSuccessPathFinding;
pathFinder.onFailure = OnFailurePathFinding;
- // Gunakan setting asli
pathFinder.HeuristicCost = GridMap.GetManhattanCost;
pathFinder.NodeTraversalCost = GridMap.GetEuclideanCost;
}
@@ -594,17 +495,14 @@ public class NPC : MonoBehaviour
UnityEngine.Debug.Log("Path visualization starting");
isVisualizingPath = true;
-
- // First, ensure grid is reset
+
Map.ResetGridNodeColours();
- // Visualize each step with a delay - use batch processing for efficiency
int stepCount = visualizationSteps.Count;
- int batchSize = Mathf.Min(visualizationBatch, stepCount); // set higher value for faster visualization
+ int batchSize = Mathf.Min(visualizationBatch, stepCount);
- // Detect if pathfinding failed - we'll need to know this when processing steps
bool pathfindingFailed = pathFinder.Status == PathFinderStatus.FAILURE;
-
+
if (pathfindingFailed)
{
UnityEngine.Debug.Log($"Visualizing failed pathfinding attempt with {stepCount} steps");
@@ -614,7 +512,6 @@ public class NPC : MonoBehaviour
{
int end = Mathf.Min(i + batchSize, stepCount);
- // Process a batch of steps
for (int j = i; j < end; j++)
{
var step = visualizationSteps[j];
@@ -634,7 +531,7 @@ public class NPC : MonoBehaviour
break;
case PathfindingVisualizationStep.StepType.FinalPath:
gnv.SetInnerColor(Map.COLOR_PATH);
- // Only add waypoints for successful pathfinding
+
if (!pathfindingFailed)
{
GridNode pathNode = Map.GetGridNode(step.position.x, step.position.y);
@@ -644,60 +541,39 @@ public class NPC : MonoBehaviour
}
}
}
-
- // Yield after each batch to prevent frame drops
yield return new WaitForSeconds(visualizationSpeed);
}
isVisualizingPath = false;
UnityEngine.Debug.Log("Path visualization complete, invoking OnVisualizationComplete event");
- // Notify any listeners that visualization is complete
OnVisualizationComplete?.Invoke();
}
- ///
- /// Menghitung biaya G, H, dan F untuk jalur
- ///
private void CalculatePathCosts(out float totalGCost, out float totalHCost, out float totalFCost)
{
- // Inisialisasi nilai awal
totalGCost = 0;
totalHCost = 0;
totalFCost = 0;
- // Jika tidak ada path yang ditemukan, return nilai 0
if (pathFinder.CurrentNode == null)
return;
- // Untuk algoritma yang menggunakan heuristik
bool usesHeuristic = pathFinderType == PathFinderType.ASTAR ||
pathFinderType == PathFinderType.GREEDY;
- // Node final berisi total cost jalur
PathFinder.PathFinderNode finalNode = pathFinder.CurrentNode;
- // G cost adalah biaya sebenarnya dari start ke goal, sudah terakumulasi di node akhir
totalGCost = finalNode.GCost;
- // H cost di node final idealnya 0 (sudah di tujuan),
- // tapi untuk info lengkap, kita dapat path's H cost dari node awal
if (usesHeuristic)
{
- // H cost dari node awal ke tujuan (untuk referensi)
totalHCost = finalNode.HCost;
-
- // F cost adalah G + H di node akhir
totalFCost = finalNode.FCost;
}
else
{
- // Algoritma tanpa heuristik (seperti Dijkstra)
totalFCost = totalGCost;
}
-
- //// Hitung rata-rata biaya per langkah untuk analisis
- //int pathLength = CalculatePathLength();
- //float avgCostPerStep = pathLength > 0 ? totalGCost / pathLength : 0;
}
}
\ No newline at end of file
diff --git a/Assets/Scripts/PathFinder.cs b/Assets/Scripts/PathFinder.cs
index be2bd33..2f251f9 100644
--- a/Assets/Scripts/PathFinder.cs
+++ b/Assets/Scripts/PathFinder.cs
@@ -3,16 +3,18 @@ using System.Collections.Generic;
namespace PathFinding
{
+ #region PathFinderStatus Enumeration
+
///
/// Enumerasi yang merepresentasikan berbagai status dari PathFinder.
/// Digunakan untuk melacak progress dari pencarian jalur (pathfinding).
///
public enum PathFinderStatus
{
- NOT_INITIALISED, // PathFinder belum diinisialisasi
- SUCCESS, // Pencarian jalur berhasil menemukan tujuan
- FAILURE, // Pencarian jalur gagal (tidak ada jalur ditemukan)
- RUNNING, // Proses pencarian jalur sedang berjalan
+ NOT_INITIALISED,
+ SUCCESS,
+ FAILURE,
+ RUNNING,
}
///
@@ -22,25 +24,11 @@ namespace PathFinding
/// Tipe data nilai yang disimpan dalam node
abstract public class Node
{
- ///
- /// Nilai yang disimpan dalam node
- ///
public T Value { get; private set; }
-
- ///
- /// Konstruktor untuk membuat node baru dengan nilai tertentu
- ///
- /// Nilai yang akan disimpan dalam node
public Node(T value)
{
Value = value;
}
-
- ///
- /// Mendapatkan daftar tetangga dari node ini.
- /// Metode ini harus diimplementasikan oleh kelas turunan.
- ///
- /// Daftar tetangga dari node ini
abstract public List> GetNeighbours();
}
@@ -51,31 +39,13 @@ namespace PathFinding
public abstract class PathFinder
{
#region Delegates for Cost Calculation.
- ///
- /// Delegate untuk menghitung biaya perjalanan antara dua node
- ///
- /// Node asal
- /// Node tujuan
- /// Biaya perjalanan dari a ke b
+
public delegate float CostFunction(T a, T b);
-
- ///
- /// Mendapatkan jumlah node dalam daftar tertutup (closedList)
- ///
public int ClosedListCount => closedList.Count;
-
public int OpenListCount => openList.Count;
-
- ///
- /// Fungsi untuk menghitung biaya heuristik antara dua node
- /// Digunakan dalam algoritma informasi seperti A* dan Greedy Best-First
- ///
public CostFunction HeuristicCost { get; set; }
-
- ///
- /// Fungsi untuk menghitung biaya perjalanan antara dua node yang bertetangga
- ///
public CostFunction NodeTraversalCost { get; set; }
+
#endregion
#region PathFinderNode
@@ -86,43 +56,14 @@ namespace PathFinding
///
public class PathFinderNode : System.IComparable
{
- ///
- /// Node induk dalam jalur pencarian
- ///
public PathFinderNode Parent { get; set; }
-
- ///
- /// Lokasi node dalam struktur graph
- ///
public Node Location { get; private set; }
-
- ///
- /// Referensi ke peta grid
- ///
public GridMap Map { get; set; }
- ///
- /// Total biaya (F = G + H)
- ///
public float FCost { get; private set; }
-
- ///
- /// Biaya dari node awal ke node saat ini (cost so far)
- ///
public float GCost { get; private set; }
-
- ///
- /// Biaya heuristik dari node saat ini ke tujuan (estimated cost)
- ///
public float HCost { get; private set; }
- ///
- /// Konstruktor untuk PathFinderNode
- ///
- /// Lokasi node dalam struktur graph
- /// Node induk dalam jalur pencarian
- /// Biaya dari node awal ke node saat ini
- /// Biaya heuristik dari node saat ini ke tujuan
public PathFinderNode(Node location,
PathFinderNode parent,
float gCost,
@@ -134,32 +75,18 @@ namespace PathFinding
SetGCost(gCost);
}
- ///
- /// Mengatur biaya G dan menghitung ulang biaya F
- ///
- /// Nilai baru untuk biaya G
public void SetGCost(float c)
{
GCost = c;
FCost = GCost + HCost;
}
- ///
- /// Mengatur biaya H dan menghitung ulang biaya F
- ///
- /// Nilai baru untuk biaya H
public void SetHCost(float h)
{
HCost = h;
FCost = GCost + HCost;
}
- ///
- /// Membandingkan node berdasarkan biaya F
- /// Digunakan untuk menyortir prioritas node dalam pencarian
- ///
- /// Node lain yang dibandingkan
- /// Hasil perbandingan nilai FCost
public int CompareTo(PathFinderNode other)
{
if (other == null) return 1;
@@ -169,58 +96,27 @@ namespace PathFinding
#endregion
#region Properties
- ///
- /// Status saat ini dari pathfinder
- /// Nilai default-nya adalah NOT_INITIALISED
- ///
+
public PathFinderStatus Status
{
get;
protected set;
} = PathFinderStatus.NOT_INITIALISED;
- ///
- /// Node awal pencarian jalur
- ///
public Node Start { get; protected set; }
-
- ///
- /// Node tujuan pencarian jalur
- ///
public Node Goal { get; protected set; }
-
- ///
- /// Node yang sedang diproses saat ini oleh pathfinder
- ///
public PathFinderNode CurrentNode { get; protected set; }
-
- ///
- /// Referensi ke peta grid yang digunakan untuk pencarian jalur
- ///
public GridMap Map { get; internal set; }
+
#endregion
#region Open and Closed Lists and Associated Functions.
- ///
- /// Daftar node yang belum diperiksa (open list)
- /// Node dalam daftar ini akan diproses di langkah berikutnya
- ///
+
protected List openList =
new List();
- ///
- /// Daftar node yang sudah diperiksa (closed list)
- /// Node dalam daftar ini sudah dievaluasi
- ///
protected List closedList =
new List();
-
- ///
- /// Mendapatkan node dengan biaya terendah dari suatu daftar
- /// Digunakan untuk memilih node berikutnya yang akan diperiksa
- ///
- /// Daftar node yang akan diperiksa
- /// Node dengan biaya terendah
protected PathFinderNode GetLeastCostNode(
List myList)
{
@@ -237,13 +133,6 @@ namespace PathFinding
PathFinderNode n = myList[best_index];
return n;
}
-
- ///
- /// Memeriksa apakah suatu cell (nilai T) ada dalam daftar node
- ///
- /// Daftar node yang akan diperiksa
- /// Cell yang dicari
- /// Indeks cell dalam daftar jika ditemukan, -1 jika tidak
protected int IsInList(List myList, T cell)
{
for (int i = 0; i < myList.Count; i++)
@@ -256,68 +145,28 @@ namespace PathFinding
#endregion
#region Delegates for Action Callbacks
- ///
- /// Delegate untuk menangani event terkait node selama proses pathfinding
- /// Dapat digunakan untuk visualisasi atau debugging
- ///
- /// Node yang terlibat dalam event
+
public delegate void DelegatePathFinderNode(PathFinderNode node);
- ///
- /// Event dipanggil ketika current node berubah
- ///
public DelegatePathFinderNode onChangeCurrentNode;
-
- ///
- /// Event dipanggil ketika node ditambahkan ke open list
- ///
public DelegatePathFinderNode onAddToOpenList;
-
- ///
- /// Event dipanggil ketika node ditambahkan ke closed list
- ///
public DelegatePathFinderNode onAddToClosedList;
-
- ///
- /// Event dipanggil ketika node tujuan ditemukan
- ///
public DelegatePathFinderNode onDestinationFound;
- ///
- /// Delegate untuk menangani event tanpa parameter
- ///
public delegate void DelegateNoArguments();
- ///
- /// Event dipanggil ketika pencarian jalur dimulai
- ///
public DelegateNoArguments onStarted;
-
- ///
- /// Event dipanggil ketika pencarian jalur sedang berjalan
- ///
public DelegateNoArguments onRunning;
-
- ///
- /// Event dipanggil ketika pencarian jalur gagal
- ///
public DelegateNoArguments onFailure;
-
- ///
- /// Event dipanggil ketika pencarian jalur berhasil
- ///
public DelegateNoArguments onSuccess;
+
#endregion
#region Pathfinding Search Related Functions
- ///
- /// Mereset variabel internal untuk pencarian baru
- ///
public virtual void Reset()
{
if (Status == PathFinderStatus.RUNNING)
{
- // Tidak bisa reset karena pathfinding sedang berlangsung
return;
}
@@ -328,11 +177,6 @@ namespace PathFinding
Status = PathFinderStatus.NOT_INITIALISED;
}
- ///
- /// Melakukan satu langkah pencarian jalur
- /// Harus dipanggil berulang kali sampai Status menjadi SUCCESS atau FAILURE
- ///
- /// Status pathfinder setelah langkah ini selesai
public virtual PathFinderStatus Step()
{
closedList.Add(CurrentNode);
@@ -340,20 +184,17 @@ namespace PathFinding
if (openList.Count == 0)
{
- // Pencarian telah selesai tanpa menemukan jalur
Status = PathFinderStatus.FAILURE;
onFailure?.Invoke();
return Status;
}
- // Dapatkan node dengan biaya terendah dari openList
CurrentNode = GetLeastCostNode(openList);
onChangeCurrentNode?.Invoke(CurrentNode);
openList.Remove(CurrentNode);
- // Periksa apakah node ini mengandung cell tujuan
if (EqualityComparer.Default.Equals(
CurrentNode.Location.Value, Goal.Value))
{
@@ -363,10 +204,8 @@ namespace PathFinding
return Status;
}
- // Dapatkan tetangga dari node saat ini
List> neighbours = CurrentNode.Location.GetNeighbours();
- // Proses setiap tetangga untuk kemungkinan ekspansi pencarian
foreach (Node cell in neighbours)
{
AlgorithmSpecificImplementation(cell);
@@ -376,25 +215,11 @@ namespace PathFinding
onRunning?.Invoke();
return Status;
}
-
- ///
- /// Implementasi algoritma spesifik untuk memproses node tetangga
- /// Metode ini akan diimplementasikan berbeda untuk setiap algoritma pathfinding
- ///
- /// Node tetangga yang akan diproses
abstract protected void AlgorithmSpecificImplementation(Node cell);
-
- ///
- /// Inisialisasi pathfinder dengan node awal dan tujuan
- ///
- /// Node awal pencarian
- /// Node tujuan pencarian
- /// True jika inisialisasi berhasil, False jika gagal
public virtual bool Initialise(Node start, Node goal)
{
if (Status == PathFinderStatus.RUNNING)
{
- // Pathfinding sedang berlangsung, tidak bisa diinisialisasi ulang
return false;
}
@@ -403,19 +228,15 @@ namespace PathFinding
Start = start;
Goal = goal;
- // Deteksi dini jika start dan goal sama
if (EqualityComparer.Default.Equals(Start.Value, Goal.Value))
{
- // Pada kasus start=goal, tidak perlu menjalankan algoritma pencarian
- // Cukup atur CurrentNode dan Status secara langsung
+ // Cost set to 0
CurrentNode = new PathFinderNode(Start, null, 0.0f, 0.0f);
- // Panggil callback sesuai urutan normal
onChangeCurrentNode?.Invoke(CurrentNode);
onStarted?.Invoke();
onDestinationFound?.Invoke(CurrentNode);
- // Atur status ke SUCCESS
Status = PathFinderStatus.SUCCESS;
onSuccess?.Invoke();
@@ -441,6 +262,8 @@ namespace PathFinding
#endregion
}
+ #endregion
+
#region Priority Queue
///
/// Memprioritaskan item berdasarkan nilai komparatif mereka
@@ -448,34 +271,16 @@ namespace PathFinding
/// Tipe item dalam antrian prioritas
public class PriorityQueue where T : IComparable
{
- ///
- /// Data yang disimpan dalam priority queue
- ///
private List data;
-
- ///
- /// Pembanding untuk menentukan prioritas item
- ///
private IComparer comparer;
-
- ///
- /// Menyimpan indeks setiap elemen dalam data untuk akses cepat
- ///
private Dictionary elementIndexMap;
// Cache untuk optimasi
private T _lastDequeued;
private int _count;
- ///
- /// Konstruktor default menggunakan pembanding default untuk tipe T
- ///
public PriorityQueue() : this(Comparer.Default) { }
- ///
- /// Konstruktor dengan custom comparer
- ///
- /// Pembanding untuk menentukan prioritas item
public PriorityQueue(IComparer comparer)
{
this.data = new List();
@@ -484,10 +289,6 @@ namespace PathFinding
this._count = 0;
}
- ///
- /// Menambahkan item ke dalam antrian prioritas
- ///
- /// Item yang akan ditambahkan
public void Enqueue(T item)
{
data.Add(item);
@@ -497,10 +298,6 @@ namespace PathFinding
_count = data.Count;
}
- ///
- /// Mengambil dan menghapus item dengan prioritas tertinggi
- ///
- /// Item dengan prioritas tertinggi
public T Dequeue()
{
if (data.Count == 0)
@@ -524,11 +321,6 @@ namespace PathFinding
return frontItem;
}
- ///
- /// Menghapus item tertentu dari antrian
- ///
- /// Item yang akan dihapus
- /// True jika berhasil dihapus, False jika tidak
public bool Remove(T item)
{
if (!elementIndexMap.TryGetValue(item, out int index))
@@ -538,7 +330,6 @@ namespace PathFinding
if (index == lastIndex)
{
- // Item yang dihapus adalah item terakhir
data.RemoveAt(lastIndex);
elementIndexMap.Remove(item);
_count = data.Count;
@@ -553,7 +344,6 @@ namespace PathFinding
{
elementIndexMap[data[index]] = index;
- // Tentukan apakah perlu heapify up atau down
int parentIndex = (index - 1) / 2;
if (index > 0 && comparer.Compare(data[index], data[parentIndex]) < 0)
HeapifyUp(index);
@@ -565,21 +355,14 @@ namespace PathFinding
return true;
}
- ///
- /// Memperbarui prioritas item dalam antrian
- ///
- /// Item yang akan diperbarui
- /// Nilai prioritas baru
public void UpdatePriority(T item, float newPriority)
{
- // Fast check - jika item adalah yang terakhir dihapus, jangan lakukan apa-apa
if (_lastDequeued != null && EqualityComparer.Default.Equals(item, _lastDequeued))
return;
if (!elementIndexMap.TryGetValue(item, out int index))
return;
- // Lakukan heapify - lebih efisien untuk A* pada grid kecil
int parentIndex = (index - 1) / 2;
if (index > 0 && comparer.Compare(data[index], data[parentIndex]) < 0)
HeapifyUp(index);
@@ -587,13 +370,8 @@ namespace PathFinding
HeapifyDown(index);
}
- ///
- /// Menjaga sifat heap dengan pergerakan ke atas
- ///
- /// Indeks node yang akan diperbaiki posisinya
private void HeapifyUp(int index)
{
- // Iteratif lebih cepat daripada rekursif untuk grid kecil
int parentIndex = (index - 1) / 2;
while (index > 0 && comparer.Compare(data[index], data[parentIndex]) < 0)
{
@@ -603,10 +381,6 @@ namespace PathFinding
}
}
- ///
- /// Menjaga sifat heap dengan pergerakan ke bawah
- ///
- /// Indeks node yang akan diperbaiki posisinya
private void HeapifyDown(int index)
{
int lastIndex = data.Count - 1;
@@ -618,7 +392,6 @@ namespace PathFinding
int rightChildIndex = leftChildIndex + 1;
int smallestChildIndex = leftChildIndex;
- // Fast check untuk rightChildIndex
if (rightChildIndex <= lastIndex && comparer.Compare(data[rightChildIndex], data[leftChildIndex]) < 0)
smallestChildIndex = rightChildIndex;
@@ -629,11 +402,6 @@ namespace PathFinding
}
}
- ///
- /// Menukar posisi dua item dalam heap
- ///
- /// Indeks item pertama
- /// Indeks item kedua
private void Swap(int index1, int index2)
{
T tmp = data[index1];
@@ -643,15 +411,8 @@ namespace PathFinding
elementIndexMap[data[index2]] = index2;
}
- ///
- /// Jumlah item dalam antrian
- ///
public int Count => _count;
- ///
- /// Mendapatkan enumerator untuk data
- ///
- /// Enumerator untuk data
public IEnumerator GetEnumerator()
{
return data.GetEnumerator();
@@ -667,58 +428,34 @@ namespace PathFinding
/// Tipe data nilai yang disimpan dalam node
public class DijkstraPathFinder : PathFinder
{
- ///
- /// HashSet untuk pemeriksaan closed list dengan cepat (O(1) complexity)
- ///
private HashSet closedSet;
-
- ///
- /// Dictionary untuk mengakses node di open list dengan cepat berdasarkan nilai mereka
- ///
private Dictionary openListMap;
- // Flag untuk mengontrol level optimisasi berdasarkan ukuran grid
private bool isGridLarge = false;
private int estimatedNodesCount = 0;
- ///
- /// Constructor baru dengan estimasi jumlah node
- ///
public DijkstraPathFinder(int estimatedNodeCount = 0)
{
- // Estimasi ukuran grid untuk optimisasi memory
this.estimatedNodesCount = estimatedNodeCount;
- // Tentukan kapasitas awal berdasarkan ukuran grid
int initialCapacity = estimatedNodesCount > 0 ?
Math.Min(estimatedNodesCount / 4, 256) : 16;
- // Grid dianggap besar jika memiliki > 2500 node (50x50)
isGridLarge = estimatedNodesCount > 2500;
- // Alokasi dengan kapasitas yang sesuai
closedSet = new HashSet(initialCapacity);
openListMap = new Dictionary(initialCapacity);
}
- ///
- /// Implementasi spesifik algoritma Dijkstra untuk memproses node tetangga
- ///
- /// Node tetangga yang akan diproses
protected override void AlgorithmSpecificImplementation(Node cell)
{
- // Pemeriksaan O(1) dengan HashSet
- // Melakukan pemeriksaan apakah node sudah ada di closed list
if (!closedSet.Contains(cell.Value))
{
float G = CurrentNode.GCost + NodeTraversalCost(
CurrentNode.Location.Value, cell.Value);
- // Biaya heuristik untuk Dijkstra adalah 0
float H = 0.0f;
- // Pemeriksaan O(1) dengan Dictionary
- // Melakukan pemeriksaan apakah node sudah ada di open list
if (!openListMap.TryGetValue(cell.Value, out PathFinderNode existingNode))
{
PathFinderNode n = new PathFinderNode(cell, CurrentNode, G, H);
@@ -739,10 +476,6 @@ namespace PathFinding
}
}
- ///
- /// Melakukan satu langkah pencarian jalur dengan algoritma Dijkstra
- ///
- /// Status pathfinder setelah langkah ini selesai
public override PathFinderStatus Step()
{
if (CurrentNode == null)
@@ -760,7 +493,6 @@ namespace PathFinding
onChangeCurrentNode?.Invoke(CurrentNode);
}
- // Pindahkan pemeriksaan duplikasi ke sini
if (!closedSet.Contains(CurrentNode.Location.Value))
{
closedList.Add(CurrentNode);
@@ -798,10 +530,6 @@ namespace PathFinding
onRunning?.Invoke();
return Status;
}
-
- ///
- /// Reset state pathfinder
- ///
public override void Reset()
{
base.Reset();
@@ -819,50 +547,29 @@ namespace PathFinding
/// Tipe data nilai yang disimpan dalam node
public class AStarPathFinder : PathFinder
{
- ///
- /// Open list diimplementasikan sebagai priority queue untuk efisiensi
- ///
private new PriorityQueue openList;
-
- ///
- /// Peta untuk mengakses node dalam open list dengan cepat
- ///
private Dictionary openListMap;
-
- ///
- /// HashSet untuk memeriksa closed list dengan cepat (O(1) complexity)
- ///
private HashSet closedSet;
- // Flag untuk batch processing - hanya diaktifkan untuk grid besar
private bool processingBatch = false;
private List> neighborBatch;
- // Flag untuk mengontrol level optimisasi
private bool isGridLarge = false;
private int estimatedNodesCount = 0;
- ///
- /// Constructor baru dengan estimasi jumlah node
- ///
public AStarPathFinder(int estimatedNodeCount = 0)
{
- // Estimasi ukuran grid untuk optimisasi memory
this.estimatedNodesCount = estimatedNodeCount;
- // Tentukan kapasitas awal berdasarkan ukuran grid
int initialCapacity = estimatedNodesCount > 0 ?
Math.Min(estimatedNodesCount / 4, 256) : 16;
- // Grid dianggap besar jika memiliki > 2500 node (50x50)
isGridLarge = estimatedNodesCount > 2500;
- // Alokasi dengan kapasitas yang sesuai
openList = new PriorityQueue(new FCostComparer());
openListMap = new Dictionary(initialCapacity);
closedSet = new HashSet(initialCapacity);
- // Alokasi neighborBatch hanya jika diperlukan
if (isGridLarge)
{
neighborBatch = new List>(8);
@@ -873,57 +580,39 @@ namespace PathFinding
}
}
- ///
- /// Implementasi spesifik algoritma A* untuk memproses node tetangga
- ///
- /// Node tetangga yang akan diproses
protected override void AlgorithmSpecificImplementation(Node cell)
{
- // Fast-reject: node sudah di closed list
if (closedSet.Contains(cell.Value))
return;
- // Hitung biaya G yang sebenarnya (jarak dari start)
float G = CurrentNode.GCost + NodeTraversalCost(CurrentNode.Location.Value, cell.Value);
- // Periksa apakah node sudah ada di open list
PathFinderNode existingNode = null;
bool nodeExists = openListMap.TryGetValue(cell.Value, out existingNode);
if (!nodeExists)
{
- // Hitung heuristik dengan normal
float H = HeuristicCost(cell.Value, Goal.Value);
- // Buat node baru dan tambahkan ke open list
PathFinderNode n = new PathFinderNode(cell, CurrentNode, G, H);
openList.Enqueue(n);
openListMap[cell.Value] = n;
- // Callback hanya jika tidak dalam batch processing
if (!processingBatch || !isGridLarge)
onAddToOpenList?.Invoke(n);
}
else if (G < existingNode.GCost)
{
- // Path lebih baik ditemukan, update nilai G
existingNode.Parent = CurrentNode;
existingNode.SetGCost(G);
- // Jika kita menggunakan G sebagai tie-breaker, maka perlu update prioritas
- // karena G yang lebih rendah sekarang bisa mempengaruhi urutan priority queue
openList.UpdatePriority(existingNode, existingNode.HCost);
- // Callback untuk UI
if ((!processingBatch || !isGridLarge) && onAddToOpenList != null)
onAddToOpenList.Invoke(existingNode);
}
}
- ///
- /// Melakukan satu langkah pencarian jalur dengan algoritma A*
- ///
- /// Status pathfinder setelah langkah ini selesai
public override PathFinderStatus Step()
{
if (CurrentNode == null)
@@ -949,39 +638,32 @@ namespace PathFinding
}
closedList.Add(CurrentNode);
- closedSet.Add(CurrentNode.Location.Value); // Tambahkan ke hashset untuk pemeriksaan O(1)
+ closedSet.Add(CurrentNode.Location.Value);
onAddToClosedList?.Invoke(CurrentNode);
- // Ambil semua tetangga
List> neighbors;
- // Untuk grid besar, gunakan batch processing
if (isGridLarge)
{
neighborBatch.Clear();
neighborBatch.AddRange(CurrentNode.Location.GetNeighbours());
neighbors = neighborBatch;
- // Gunakan batch hanya jika cukup banyak tetangga dan grid besar
processingBatch = neighbors.Count > 5;
}
else
{
- // Untuk grid kecil, tidak perlu batch processing
neighbors = CurrentNode.Location.GetNeighbours();
processingBatch = false;
}
- // Proses semua tetangga
foreach (Node cell in neighbors)
{
AlgorithmSpecificImplementation(cell);
}
- // Jika dalam batch mode, invoke callback sekali untuk semua tetangga
if (processingBatch && onAddToOpenList != null && isGridLarge)
{
- // Notify UI untuk refresh (optional)
onAddToOpenList.Invoke(CurrentNode);
processingBatch = false;
}
@@ -1002,9 +684,6 @@ namespace PathFinding
return Status;
}
- ///
- /// Reset state pathfinder
- ///
public override void Reset()
{
base.Reset();
@@ -1015,18 +694,8 @@ namespace PathFinding
processingBatch = false;
}
- ///
- /// Pembanding F-Cost untuk priority queue dalam algoritma A*
- ///
private class FCostComparer : IComparer
{
- ///
- /// Membandingkan dua node berdasarkan FCost mereka
- /// Jika FCost sama, gunakan HCost sebagai tie-breaker
- ///
- /// Node pertama
- /// Node kedua
- /// Hasil perbandingan
public int Compare(PathFinderNode x, PathFinderNode y)
{
int result = x.FCost.CompareTo(y.FCost);
@@ -1048,50 +717,28 @@ namespace PathFinding
/// Tipe data nilai yang disimpan dalam node
public class GreedyPathFinder : PathFinder
{
- ///
- /// Open list diimplementasikan sebagai priority queue untuk performa yang lebih baik
- ///
private new PriorityQueue openList = new PriorityQueue(new HeuristicComparer());
-
- ///
- /// Dictionary untuk akses node open list yang cepat
- ///
private Dictionary openSet = new Dictionary(256);
-
- ///
- /// HashSet untuk pemeriksaan closed list O(1)
- ///
private HashSet closedSet = new HashSet(256);
- // Flag untuk batch processing
private bool processingBatch = false;
private List> neighborBatch = new List>(4);
- ///
- /// Implementasi spesifik algoritma Greedy untuk memproses node tetangga
- ///
- /// Node tetangga yang akan diproses
protected override void AlgorithmSpecificImplementation(Node cell)
{
- // Fast-reject: node sudah diproses
if (closedSet.Contains(cell.Value))
return;
- // Hitung biaya G yang sebenarnya (jarak dari start)
float G = CurrentNode.GCost + NodeTraversalCost(CurrentNode.Location.Value, cell.Value);
float H;
- // Periksa apakah node sudah ada di open list
PathFinderNode existingNode = null;
bool nodeExists = openSet.TryGetValue(cell.Value, out existingNode);
if (!nodeExists)
{
- // Hitung heuristik dengan normal
-
if (EqualityComparer.Default.Equals(cell.Value, Goal.Value))
{
- // Langsung ke tujuan - prioritaskan dengan H = 0
H = 0;
}
else
@@ -1099,38 +746,22 @@ namespace PathFinding
H = HeuristicCost(cell.Value, Goal.Value);
}
- // Buat node dan tambahkan ke open list - gunakan G yang benar
PathFinderNode n = new PathFinderNode(cell, CurrentNode, G, H);
openList.Enqueue(n);
onAddToOpenList?.Invoke(n);
openSet[cell.Value] = n;
- //if (!processingBatch && onAddToOpenList != null)
- // onAddToOpenList.Invoke(n);
}
else if (G < existingNode.GCost)
{
- // Path lebih baik ditemukan, update nilai G
- // Meskipun Greedy hanya menggunakan H untuk prioritas,
- // G cost yang benar tetap penting untuk rekonstruksi jalur
existingNode.Parent = CurrentNode;
existingNode.SetGCost(G);
- // Jika kita menggunakan G sebagai tie-breaker, maka perlu update prioritas
- // karena G yang lebih rendah sekarang bisa mempengaruhi urutan priority queue
openList.UpdatePriority(existingNode, existingNode.HCost);
onAddToOpenList.Invoke(existingNode);
-
- // Callback untuk UI
- //if (!processingBatch && onAddToOpenList != null)
-
}
}
- ///
- /// Melakukan satu langkah pencarian jalur dengan algoritma Greedy
- ///
- /// Status pathfinder setelah langkah ini selesai
public override PathFinderStatus Step()
{
if (CurrentNode == null)
@@ -1159,22 +790,16 @@ namespace PathFinding
return Status;
}
- // Dapatkan semua tetangga sekaligus untuk mengurangi panggilan fungsi
neighborBatch.Clear();
neighborBatch.AddRange(CurrentNode.Location.GetNeighbours());
- // Proses tetangga dalam batch untuk mengurangi callback overhead
- // processingBatch = neighborBatch.Count > 5; // Gunakan batch hanya jika cukup banyak tetangga
-
foreach (Node cell in neighborBatch)
{
AlgorithmSpecificImplementation(cell);
}
- // Jika dalam batch mode, invoke callback sekali untuk semua tetangga
if (processingBatch && onAddToOpenList != null)
{
- // Notify UI untuk refresh (optional)
onAddToOpenList.Invoke(CurrentNode);
processingBatch = false;
}
@@ -1195,9 +820,6 @@ namespace PathFinding
return Status;
}
- ///
- /// Reset state pathfinder
- ///
public override void Reset()
{
base.Reset();
@@ -1207,28 +829,13 @@ namespace PathFinding
processingBatch = false;
}
- ///
- /// Pembanding Heuristik untuk priority queue dalam algoritma Greedy
- ///
private class HeuristicComparer : IComparer
{
- ///
- /// Membandingkan dua node berdasarkan nilai heuristik mereka.
- /// PENTING: Ini adalah kunci perbedaan dari algoritma Greedy - prioritas
- /// hanya didasarkan pada nilai H cost (jarak ke tujuan), tidak seperti
- /// A* yang menggunakan FCost (G+H). G cost tetap dilacak untuk
- /// rekonstruksi jalur yang benar, tetapi tidak memengaruhi prioritas.
- ///
- /// Node pertama
- /// Node kedua
- /// Hasil perbandingan
public int Compare(PathFinderNode x, PathFinderNode y)
{
int result = x.HCost.CompareTo(y.HCost);
if (result == 0)
{
- // Jika H cost sama, gunakan G cost sebagai tie-breaker
- // Pilih jalur dengan G cost lebih rendah (lebih dekat dari start)
result = x.GCost.CompareTo(y.GCost);
}
return result;
@@ -1245,51 +852,29 @@ namespace PathFinding
/// Tipe data nilai yang disimpan dalam node
public class BacktrackingPathFinder : PathFinder
{
- ///
- /// Stack untuk menyimpan node yang akan dikunjungi (open list)
- ///
private Stack openStack = new Stack();
-
- ///
- /// HashSet untuk pemeriksaan cepat apakah node tertentu sudah dikunjungi
- ///
private HashSet closedSet = new HashSet();
-
- ///
- /// HashSet untuk pemeriksaan cepat apakah node tertentu sudah ada di open list
- ///
private HashSet openSet = new HashSet();
- ///
- /// Implementasi spesifik algoritma Backtracking untuk memproses node tetangga
- ///
- /// Node tetangga yang akan diproses
protected override void AlgorithmSpecificImplementation(Node cell)
{
if (!closedSet.Contains(cell.Value) && !openSet.Contains(cell.Value))
{
- // Backtracking tidak memperhitungkan cost, tetapi tetap menghitungnya untuk visualisasi
float G = CurrentNode.GCost + NodeTraversalCost(CurrentNode.Location.Value, cell.Value);
float H = 0.0f;
PathFinderNode n = new PathFinderNode(cell, CurrentNode, G, H);
- openList.Add(n); // Tambahkan ke list untuk tujuan kompatibilitas
- openStack.Push(n); // Tambahkan ke stack untuk DFS
+ openList.Add(n);
+ openStack.Push(n);
openSet.Add(cell.Value);
onAddToOpenList?.Invoke(n);
}
}
- ///
- /// Melakukan satu langkah pencarian jalur dengan algoritma Backtracking
- ///
- /// Status pathfinder setelah langkah ini selesai
public override PathFinderStatus Step()
{
- // Jika currentNode belum diinisialisasi
if (CurrentNode == null)
{
- // Jika tidak ada node dalam open stack, berarti pencarian gagal
if (openStack.Count == 0)
{
Status = PathFinderStatus.FAILURE;
@@ -1297,19 +882,16 @@ namespace PathFinding
return Status;
}
- // Ambil node berikutnya dari stack (LIFO - kedalaman pertama)
CurrentNode = openStack.Pop();
- openList.Remove(CurrentNode); // Hapus dari list untuk kompatibilitas
+ openList.Remove(CurrentNode);
openSet.Remove(CurrentNode.Location.Value);
onChangeCurrentNode?.Invoke(CurrentNode);
}
- // Tambahkan node saat ini ke closed list
closedList.Add(CurrentNode);
closedSet.Add(CurrentNode.Location.Value);
onAddToClosedList?.Invoke(CurrentNode);
- // Cek apakah kita telah mencapai tujuan
if (EqualityComparer.Default.Equals(CurrentNode.Location.Value, Goal.Value))
{
Status = PathFinderStatus.SUCCESS;
@@ -1318,14 +900,12 @@ namespace PathFinding
return Status;
}
- // Proses semua tetangga dari node saat ini
List> neighbours = CurrentNode.Location.GetNeighbours();
foreach (Node cell in neighbours)
{
AlgorithmSpecificImplementation(cell);
}
- // Jika tidak ada lagi node yang dapat dikunjungi, berarti pencarian gagal
if (openStack.Count == 0)
{
Status = PathFinderStatus.FAILURE;
@@ -1333,9 +913,8 @@ namespace PathFinding
return Status;
}
- // Ambil node berikutnya dari stack
CurrentNode = openStack.Pop();
- openList.Remove(CurrentNode); // Hapus dari list untuk kompatibilitas
+ openList.Remove(CurrentNode);
openSet.Remove(CurrentNode.Location.Value);
onChangeCurrentNode?.Invoke(CurrentNode);
@@ -1344,9 +923,6 @@ namespace PathFinding
return Status;
}
- ///
- /// Reset state pathfinder
- ///
public override void Reset()
{
base.Reset();
@@ -1366,52 +942,30 @@ namespace PathFinding
/// Tipe data nilai yang disimpan dalam node
public class BFSPathFinder : PathFinder
{
- ///
- /// Queue sebagai open list untuk BFS
- ///
private Queue openQueue = new Queue();
-
- ///
- /// HashSet untuk pemeriksaan nilai node dalam closed list (O(1) complexity)
- ///
private HashSet closedSet = new HashSet();
-
- ///
- /// HashSet untuk pemeriksaan nilai node dalam open list (O(1) complexity)
- ///
private HashSet openSet = new HashSet();
- ///
- /// Implementasi spesifik algoritma BFS untuk memproses node tetangga
- ///
- /// Node tetangga yang akan diproses
protected override void AlgorithmSpecificImplementation(Node cell)
{
if (!closedSet.Contains(cell.Value) && !openSet.Contains(cell.Value))
{
- // BFS tidak memperhitungkan cost, tapi kita tetap menghitungnya untuk visualisasi
float G = CurrentNode.GCost + NodeTraversalCost(
CurrentNode.Location.Value, cell.Value);
float H = 0.0f;
PathFinderNode n = new PathFinderNode(cell, CurrentNode, G, H);
- openList.Add(n); // Tambahkan ke list untuk tujuan kompatibilitas
- openQueue.Enqueue(n); // Tambahkan ke queue untuk BFS
+ openList.Add(n);
+ openQueue.Enqueue(n);
openSet.Add(cell.Value);
onAddToOpenList?.Invoke(n);
}
}
- ///
- /// Melakukan satu langkah pencarian jalur dengan algoritma BFS
- ///
- /// Status pathfinder setelah langkah ini selesai
public override PathFinderStatus Step()
{
- // Jika currentNode belum diinisialisasi
if (CurrentNode == null)
{
- // Jika tidak ada node dalam open queue, berarti pencarian gagal
if (openQueue.Count == 0)
{
Status = PathFinderStatus.FAILURE;
@@ -1419,19 +973,16 @@ namespace PathFinding
return Status;
}
- // Ambil node berikutnya dari queue (FIFO)
CurrentNode = openQueue.Dequeue();
- openList.Remove(CurrentNode); // Hapus dari list untuk kompatibilitas
+ openList.Remove(CurrentNode);
openSet.Remove(CurrentNode.Location.Value);
onChangeCurrentNode?.Invoke(CurrentNode);
}
- // Tambahkan node saat ini ke closed list
closedList.Add(CurrentNode);
closedSet.Add(CurrentNode.Location.Value);
onAddToClosedList?.Invoke(CurrentNode);
- // Cek apakah kita telah mencapai tujuan
if (EqualityComparer.Default.Equals(CurrentNode.Location.Value, Goal.Value))
{
Status = PathFinderStatus.SUCCESS;
@@ -1440,14 +991,12 @@ namespace PathFinding
return Status;
}
- // Proses semua tetangga dari node saat ini
List> neighbours = CurrentNode.Location.GetNeighbours();
foreach (Node cell in neighbours)
{
AlgorithmSpecificImplementation(cell);
}
- // Jika tidak ada lagi node yang dapat dikunjungi, berarti pencarian gagal
if (openQueue.Count == 0)
{
Status = PathFinderStatus.FAILURE;
@@ -1455,9 +1004,8 @@ namespace PathFinding
return Status;
}
- // Ambil node berikutnya dari queue
CurrentNode = openQueue.Dequeue();
- openList.Remove(CurrentNode); // Hapus dari list untuk kompatibilitas
+ openList.Remove(CurrentNode);
openSet.Remove(CurrentNode.Location.Value);
onChangeCurrentNode?.Invoke(CurrentNode);
@@ -1466,9 +1014,6 @@ namespace PathFinding
return Status;
}
- ///
- /// Reset state pathfinder
- ///
public override void Reset()
{
base.Reset();
diff --git a/Assets/Scripts/PathfindingTestAnalyzer.cs b/Assets/Scripts/PathfindingTestAnalyzer.cs
deleted file mode 100644
index b19a3fa..0000000
--- a/Assets/Scripts/PathfindingTestAnalyzer.cs
+++ /dev/null
@@ -1,419 +0,0 @@
-using System.Collections;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using UnityEngine;
-using UnityEngine.UI;
-using TMPro;
-
-///
-/// Utility class to analyze pathfinding test results and display statistics
-///
-public class PathfindingTestAnalyzer : MonoBehaviour
-{
- [Header("UI References")]
- public TMP_Text resultsText;
- public TMP_Dropdown algorithmFilterDropdown;
- public TMP_Dropdown statisticTypeDropdown;
- public RectTransform graphContainer;
- public GameObject barPrefab;
-
- [Header("File Settings")]
- public string resultsFileName = "pathfinding_tests.csv";
-
- // Data structures
- private List allResults = new List();
- private Dictionary algorithmColors = new Dictionary()
- {
- { "ASTAR", Color.blue },
- { "DIJKSTRA", Color.green },
- { "GREEDY", Color.red },
- { "BACKTRACKING", Color.yellow },
- { "BFS", Color.magenta }
- };
-
- // Struct to hold a single test result
- private struct TestResult
- {
- public string algorithm;
- public int gridSizeX;
- public int gridSizeY;
- public float density;
- public bool diagonalMovement;
- public float timeTaken;
- public int pathLength;
- public int nodesExplored;
- public long memoryUsed;
- public bool pathFound;
- public int testIndex;
- }
-
- void Start()
- {
- SetupDropdowns();
- LoadResults();
-
- // Set default visualization
- if (allResults.Count > 0)
- {
- GenerateStatistics();
- }
- }
-
- private void SetupDropdowns()
- {
- // Set up algorithm filter dropdown
- algorithmFilterDropdown.ClearOptions();
- algorithmFilterDropdown.AddOptions(new List {
- "All Algorithms",
- "A*",
- "Dijkstra",
- "Greedy BFS",
- "Backtracking",
- "BFS"
- });
- algorithmFilterDropdown.onValueChanged.AddListener(OnFilterChanged);
-
- // Set up statistic type dropdown
- statisticTypeDropdown.ClearOptions();
- statisticTypeDropdown.AddOptions(new List {
- "Execution Time",
- "Path Length",
- "Nodes Explored",
- "Memory Used",
- "Success Rate"
- });
- statisticTypeDropdown.onValueChanged.AddListener(OnFilterChanged);
- }
-
- private void LoadResults()
- {
- string directory = Path.Combine(Application.persistentDataPath, "TestResults");
- string filePath = Path.Combine(directory, resultsFileName);
-
- if (!File.Exists(filePath))
- {
- Debug.LogError($"Results file not found: {filePath}");
- if (resultsText != null)
- {
- resultsText.text = "No test results found. Run tests first.";
- }
- return;
- }
-
- // Read all lines and skip header
- string[] lines = File.ReadAllLines(filePath);
- if (lines.Length <= 1)
- {
- Debug.LogWarning("Results file is empty or only contains a header");
- return;
- }
-
- // Clear previous results
- allResults.Clear();
-
- // Skip header row and parse data rows
- for (int i = 1; i < lines.Length; i++)
- {
- string line = lines[i];
- if (string.IsNullOrWhiteSpace(line))
- continue;
-
- string[] values = line.Split(',');
- if (values.Length < 10)
- {
- Debug.LogWarning($"Invalid data in line {i}: {line}");
- continue;
- }
-
- TestResult result = new TestResult
- {
- algorithm = values[0],
- gridSizeX = int.Parse(values[1]),
- gridSizeY = int.Parse(values[2]),
- density = float.Parse(values[3]),
- diagonalMovement = bool.Parse(values[4]),
- timeTaken = float.Parse(values[5]),
- pathLength = int.Parse(values[6]),
- nodesExplored = int.Parse(values[7]),
- memoryUsed = long.Parse(values[8]),
- pathFound = bool.Parse(values[9]),
- testIndex = int.Parse(values[10])
- };
-
- allResults.Add(result);
- }
-
- Debug.Log($"Loaded {allResults.Count} test results");
- }
-
- public void OnFilterChanged(int value)
- {
- GenerateStatistics();
- }
-
- private void GenerateStatistics()
- {
- if (allResults.Count == 0)
- return;
-
- // Clear previous graph
- foreach (Transform child in graphContainer)
- {
- Destroy(child.gameObject);
- }
-
- // Get selected algorithm filter
- string algorithmFilter = "All";
- switch (algorithmFilterDropdown.value)
- {
- case 0: algorithmFilter = "All"; break;
- case 1: algorithmFilter = "ASTAR"; break;
- case 2: algorithmFilter = "DIJKSTRA"; break;
- case 3: algorithmFilter = "GREEDY"; break;
- case 4: algorithmFilter = "BACKTRACKING"; break;
- case 5: algorithmFilter = "BFS"; break;
- }
-
- // Filter results by algorithm if not "All"
- List filteredResults = allResults;
- if (algorithmFilter != "All")
- {
- filteredResults = allResults.Where(r => r.algorithm == algorithmFilter).ToList();
- }
-
- if (filteredResults.Count == 0)
- {
- resultsText.text = "No data for selected filters.";
- return;
- }
-
- // Generate statistics based on selected metric
- switch (statisticTypeDropdown.value)
- {
- case 0: // Execution Time
- GenerateTimeStatistics(filteredResults);
- break;
- case 1: // Path Length
- GeneratePathLengthStatistics(filteredResults);
- break;
- case 2: // Nodes Explored
- GenerateNodesExploredStatistics(filteredResults);
- break;
- case 3: // Memory Used
- GenerateMemoryStatistics(filteredResults);
- break;
- case 4: // Success Rate
- GenerateSuccessRateStatistics(filteredResults);
- break;
- }
- }
-
- private void GenerateTimeStatistics(List results)
- {
- var averageTimeByAlgorithm = results
- .GroupBy(r => r.algorithm)
- .Select(g => new {
- Algorithm = g.Key,
- AverageTime = g.Average(r => r.timeTaken)
- })
- .OrderByDescending(x => x.AverageTime)
- .ToList();
-
- // Generate graph bars
- CreateBarsForData(averageTimeByAlgorithm.Select(x => x.Algorithm).ToList(),
- averageTimeByAlgorithm.Select(x => (float)x.AverageTime).ToList(),
- "ms");
-
- // Generate text summary
- resultsText.text = "Average Execution Time by Algorithm:\n\n";
- foreach (var stat in averageTimeByAlgorithm)
- {
- resultsText.text += $"{GetAlgorithmName(stat.Algorithm)}: {stat.AverageTime:F2} ms\n";
- }
- }
-
- private void GeneratePathLengthStatistics(List results)
- {
- var averagePathByAlgorithm = results
- .Where(r => r.pathFound) // Only include successful paths
- .GroupBy(r => r.algorithm)
- .Select(g => new {
- Algorithm = g.Key,
- AveragePath = g.Average(r => r.pathLength)
- })
- .OrderByDescending(x => x.AveragePath)
- .ToList();
-
- // Generate graph bars
- CreateBarsForData(averagePathByAlgorithm.Select(x => x.Algorithm).ToList(),
- averagePathByAlgorithm.Select(x => (float)x.AveragePath).ToList(),
- "nodes");
-
- // Generate text summary
- resultsText.text = "Average Path Length by Algorithm:\n\n";
- foreach (var stat in averagePathByAlgorithm)
- {
- resultsText.text += $"{GetAlgorithmName(stat.Algorithm)}: {stat.AveragePath:F2} nodes\n";
- }
- }
-
- private void GenerateNodesExploredStatistics(List results)
- {
- var averageNodesExploredByAlgorithm = results
- .GroupBy(r => r.algorithm)
- .Select(g => new {
- Algorithm = g.Key,
- AverageNodes = g.Average(r => r.nodesExplored)
- })
- .OrderByDescending(x => x.AverageNodes)
- .ToList();
-
- // Generate graph bars
- CreateBarsForData(averageNodesExploredByAlgorithm.Select(x => x.Algorithm).ToList(),
- averageNodesExploredByAlgorithm.Select(x => (float)x.AverageNodes).ToList(),
- "nodes");
-
- // Generate text summary
- resultsText.text = "Average Nodes Explored by Algorithm:\n\n";
- foreach (var stat in averageNodesExploredByAlgorithm)
- {
- resultsText.text += $"{GetAlgorithmName(stat.Algorithm)}: {stat.AverageNodes:F0} nodes\n";
- }
- }
-
- private void GenerateMemoryStatistics(List results)
- {
- var averageMemoryByAlgorithm = results
- .GroupBy(r => r.algorithm)
- .Select(g => new {
- Algorithm = g.Key,
- AverageMemory = g.Average(r => r.memoryUsed) / 1024.0f // Convert to KB
- })
- .OrderByDescending(x => x.AverageMemory)
- .ToList();
-
- // Generate graph bars
- CreateBarsForData(averageMemoryByAlgorithm.Select(x => x.Algorithm).ToList(),
- averageMemoryByAlgorithm.Select(x => (float)x.AverageMemory).ToList(),
- "KB");
-
- // Generate text summary
- resultsText.text = "Average Memory Usage by Algorithm:\n\n";
- foreach (var stat in averageMemoryByAlgorithm)
- {
- resultsText.text += $"{GetAlgorithmName(stat.Algorithm)}: {stat.AverageMemory:F2} KB\n";
- }
- }
-
- private void GenerateSuccessRateStatistics(List results)
- {
- var successRateByAlgorithm = results
- .GroupBy(r => r.algorithm)
- .Select(g => new {
- Algorithm = g.Key,
- SuccessRate = g.Count(r => r.pathFound) * 100.0f / g.Count()
- })
- .OrderByDescending(x => x.SuccessRate)
- .ToList();
-
- // Generate graph bars
- CreateBarsForData(successRateByAlgorithm.Select(x => x.Algorithm).ToList(),
- successRateByAlgorithm.Select(x => (float)x.SuccessRate).ToList(),
- "%");
-
- // Generate text summary
- resultsText.text = "Success Rate by Algorithm:\n\n";
- foreach (var stat in successRateByAlgorithm)
- {
- resultsText.text += $"{GetAlgorithmName(stat.Algorithm)}: {stat.SuccessRate:F1}%\n";
- }
- }
-
- private void CreateBarsForData(List labels, List values, string unit)
- {
- if (barPrefab == null || graphContainer == null || labels.Count == 0)
- return;
-
- float graphWidth = graphContainer.rect.width;
- float graphHeight = graphContainer.rect.height;
- float maxValue = values.Max();
- float barWidth = graphWidth / (labels.Count + 1);
-
- for (int i = 0; i < labels.Count; i++)
- {
- // Create bar
- GameObject barObj = Instantiate(barPrefab, graphContainer);
- RectTransform barRect = barObj.GetComponent();
- Image barImage = barObj.GetComponent();
-
- // Calculate height based on value
- float normalizedValue = values[i] / maxValue;
- float barHeight = graphHeight * normalizedValue * 0.8f; // 80% of graph height max
-
- // Position and size bar
- barRect.anchoredPosition = new Vector2((i + 0.5f) * barWidth, barHeight / 2);
- barRect.sizeDelta = new Vector2(barWidth * 0.8f, barHeight);
-
- // Set bar color
- string algorithm = labels[i];
- if (algorithmColors.ContainsKey(algorithm))
- barImage.color = algorithmColors[algorithm];
-
- // Add label
- GameObject labelObj = new GameObject($"Label_{labels[i]}");
- labelObj.transform.SetParent(barObj.transform);
-
- TMP_Text labelText = labelObj.AddComponent();
- labelText.text = $"{values[i]:F1}{unit}";
- labelText.fontSize = 12;
- labelText.alignment = TextAlignmentOptions.Center;
- labelText.color = Color.black;
-
- RectTransform labelRect = labelObj.GetComponent();
- labelRect.anchoredPosition = new Vector2(0, barHeight + 10);
- labelRect.sizeDelta = new Vector2(barWidth, 20);
-
- // Add algorithm name label at bottom
- GameObject nameObj = new GameObject($"Name_{labels[i]}");
- nameObj.transform.SetParent(barObj.transform);
-
- TMP_Text nameText = nameObj.AddComponent();
- nameText.text = GetAlgorithmName(labels[i]);
- nameText.fontSize = 10;
- nameText.alignment = TextAlignmentOptions.Center;
- nameText.color = Color.black;
-
- RectTransform nameRect = nameObj.GetComponent();
- nameRect.anchoredPosition = new Vector2(0, -10);
- nameRect.sizeDelta = new Vector2(barWidth, 20);
- }
- }
-
- private string GetAlgorithmName(string algorithm)
- {
- switch (algorithm)
- {
- case "ASTAR": return "A*";
- case "DIJKSTRA": return "Dijkstra";
- case "GREEDY": return "Greedy";
- case "BACKTRACKING": return "Backtracking";
- case "BFS": return "BFS";
- default: return algorithm;
- }
- }
-
- // Utility function to format memory size
- private string FormatBytes(long bytes)
- {
- string[] sizes = { "B", "KB", "MB", "GB" };
- int order = 0;
- double size = bytes;
- while (size >= 1024 && order < sizes.Length - 1)
- {
- order++;
- size = size / 1024;
- }
- return $"{size:0.##} {sizes[order]}";
- }
-}
\ No newline at end of file
diff --git a/Assets/Scripts/PathfindingTestAnalyzer.cs.meta b/Assets/Scripts/PathfindingTestAnalyzer.cs.meta
deleted file mode 100644
index 1709582..0000000
--- a/Assets/Scripts/PathfindingTestAnalyzer.cs.meta
+++ /dev/null
@@ -1,11 +0,0 @@
-fileFormatVersion: 2
-guid: 9fee3f5c29ad21b428f68d801f2377f1
-MonoImporter:
- externalObjects: {}
- serializedVersion: 2
- defaultReferences: []
- executionOrder: 0
- icon: {instanceID: 0}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Scripts/PathfindingTester.cs b/Assets/Scripts/PathfindingTester.cs
deleted file mode 100644
index 328c110..0000000
--- a/Assets/Scripts/PathfindingTester.cs
+++ /dev/null
@@ -1,510 +0,0 @@
-using System.Collections;
-using System.Collections.Generic;
-using System.IO;
-using System.Text;
-using UnityEngine;
-using UnityEngine.UI;
-using TMPro;
-
-public class PathfindingTester : MonoBehaviour
-{
- [Header("References")]
- public GridMap gridMap;
- public NPC npc;
- public Button startTestButton;
- public TMP_Text statusText;
- public Slider progressBar;
-
- [Header("Test Configuration")]
- [Tooltip("Number of times to run each test combination")]
- public int testsPerCombination = 3;
-
- [Tooltip("Delay between tests in seconds")]
- public float delayBetweenTests = 0.5f;
-
- [Tooltip("Whether to save results to a CSV file")]
- public bool saveResultsToFile = true;
-
- [Tooltip("File name for test results (CSV)")]
- public string resultsFileName = "pathfinding_tests.csv";
-
- // Test matrix parameters
- private NPC.PathFinderType[] algorithmsToTest = new NPC.PathFinderType[] {
- NPC.PathFinderType.ASTAR,
- NPC.PathFinderType.DIJKSTRA,
- NPC.PathFinderType.GREEDY,
- NPC.PathFinderType.BACKTRACKING,
- NPC.PathFinderType.BFS
- };
-
- private Vector2Int[] gridSizesToTest = new Vector2Int[] {
- new Vector2Int(20, 20),
- new Vector2Int(35, 35),
- new Vector2Int(50, 50),
- new Vector2Int(40, 25) // Another non-square grid
- };
-
- private float[] mazeDensitiesToTest = new float[] {
- 0f, // Empty (no walls)
- 10f, // Very low
- 30f, // Medium
- 50f, // High
- 100f // Fully blocked (all walls)
- };
-
- private bool[] diagonalMovementOptions = new bool[] {
- true,
- false
- };
-
- // Test state tracking
- private bool isTestingRunning = false;
- private int totalTests;
- private int completedTests;
- private List testResults = new List();
-
- // Current test parameters
- private NPC.PathFinderType currentTestAlgorithm;
- private Vector2Int currentTestGridSize;
- private float currentTestDensity;
- private bool currentTestDiagonal;
- private int currentTestIndex;
-
- // Structure to store test results
- private struct PathfindingTestResult
- {
- public NPC.PathFinderType algorithm;
- public Vector2Int gridSize;
- public float mazeDensity;
- public bool diagonalMovement;
- public float timeTaken;
- public int pathLength;
- public int nodesExplored;
- public long memoryUsed;
- public bool pathFound;
- public int testIndex;
- }
-
- private void Start()
- {
- // Subscribe to NPC's pathfinding completion event
- npc.OnPathfindingComplete += OnPathfindingComplete;
-
- // Set up the button listener
- startTestButton.onClick.AddListener(StartTesting);
-
- // Initial status message
- UpdateStatus("Ready to start testing. Click the Start Tests button.");
- }
-
- private void OnDestroy()
- {
- // Unsubscribe from events
- if (npc != null)
- {
- npc.OnPathfindingComplete -= OnPathfindingComplete;
- }
- }
-
- private void UpdateStatus(string message)
- {
- if (statusText != null)
- {
- statusText.text = message;
- }
- Debug.Log(message);
-
- // Update progress bar if available
- if (progressBar != null && totalTests > 0)
- {
- progressBar.value = (float)completedTests / totalTests;
- }
- }
-
- public void StartTesting()
- {
- if (isTestingRunning)
- {
- Debug.LogWarning("Tests are already running.");
- return;
- }
-
- // Clear previous results
- testResults.Clear();
-
- // Calculate total number of tests
- totalTests = algorithmsToTest.Length *
- gridSizesToTest.Length *
- mazeDensitiesToTest.Length *
- diagonalMovementOptions.Length *
- testsPerCombination;
-
- completedTests = 0;
- isTestingRunning = true;
-
- UpdateStatus($"Starting {totalTests} tests...");
-
- // Disable button during testing
- startTestButton.interactable = false;
-
- // Start the test coroutine
- StartCoroutine(RunTestMatrix());
- }
-
- private IEnumerator RunTestMatrix()
- {
- // Iterate through all test combinations
- foreach (var algorithm in algorithmsToTest)
- {
- foreach (var gridSize in gridSizesToTest)
- {
- foreach (var density in mazeDensitiesToTest)
- {
- foreach (var useDiagonals in diagonalMovementOptions)
- {
- for (int testIndex = 0; testIndex < testsPerCombination; testIndex++)
- {
- yield return StartCoroutine(RunSingleTest(algorithm, gridSize, density, useDiagonals, testIndex));
-
- // Wait between tests
- yield return new WaitForSeconds(delayBetweenTests);
- }
- }
- }
- }
- }
-
- // All tests completed
- isTestingRunning = false;
- startTestButton.interactable = true;
-
- // Save results if enabled
- if (saveResultsToFile)
- {
- SaveResultsToCSV();
- }
-
- UpdateStatus($"Testing complete! {completedTests} tests run. Results saved to {resultsFileName}");
- }
-
- private IEnumerator RunSingleTest(NPC.PathFinderType algorithm, Vector2Int gridSize, float density, bool useDiagonals, int testIndex)
- {
- // Update status with current test info
- UpdateStatus($"Test {completedTests+1}/{totalTests}: {algorithm} - Grid: {gridSize.x}x{gridSize.y} - Density: {density}% - Diagonals: {useDiagonals}");
-
- // Configure the test environment
- yield return StartCoroutine(SetupTestEnvironment(algorithm, gridSize, density, useDiagonals));
-
- // Generate start and destination points
- GridNode startNode = FindValidStartNode();
- GridNode destNode = FindValidDestinationNode(startNode);
-
- if (startNode == null || destNode == null)
- {
- Debug.LogWarning($"Failed to find valid start/destination nodes for test - density: {density}%");
-
- // Record the test as "impossible" with a failed path
- PathfindingTestResult result = new PathfindingTestResult
- {
- algorithm = algorithm,
- gridSize = gridSize,
- mazeDensity = density,
- diagonalMovement = useDiagonals,
- timeTaken = 0f,
- pathLength = 0,
- nodesExplored = 0,
- memoryUsed = 0,
- pathFound = false,
- testIndex = testIndex
- };
-
- testResults.Add(result);
- completedTests++;
- yield break;
- }
-
- // Position NPC at start node
- npc.SetStartNode(startNode);
-
- // Position destination
- gridMap.SetDestination(destNode.Value.x, destNode.Value.y);
-
- // Wait a frame to ensure everything is set up
- yield return null;
-
- // Store the current test parameters
- currentTestAlgorithm = algorithm;
- currentTestGridSize = gridSize;
- currentTestDensity = density;
- currentTestDiagonal = useDiagonals;
- currentTestIndex = testIndex;
-
- // Flag to track if callback was triggered
- bool callbackTriggered = false;
-
- // Setup a temporary callback listener to detect if the event fires
- System.Action tempCallback = (metrics) => { callbackTriggered = true; };
- npc.OnPathfindingComplete += tempCallback;
-
- // Run pathfinding in silent mode - now the event will still fire with our NPC fix
- npc.MoveTo(destNode, true);
-
- // Wait for pathfinding to complete
- float timeout = 10.0f;
- float elapsed = 0f;
-
- while (npc.pathFinder != null && npc.pathFinder.Status == PathFinding.PathFinderStatus.RUNNING && elapsed < timeout)
- {
- yield return null;
- elapsed += Time.deltaTime;
- }
-
- // Wait a bit more to ensure completion
- yield return new WaitForSeconds(0.1f);
-
- // Remove the temporary callback
- npc.OnPathfindingComplete -= tempCallback;
-
- // If callback wasn't triggered but pathfinding is complete, manually record the result
- if (!callbackTriggered && npc.pathFinder != null && npc.pathFinder.Status != PathFinding.PathFinderStatus.RUNNING)
- {
- Debug.LogWarning($"Callback wasn't triggered for test {completedTests+1}. Recording results manually.");
-
- // Create a metrics object with available data
- PathfindingMetrics metrics = new PathfindingMetrics
- {
- timeTaken = elapsed * 1000f, // convert to ms
- pathLength = CalculatePathLength(npc.pathFinder),
- nodesExplored = npc.pathFinder.ClosedListCount,
- memoryUsed = npc.LastMeasuredMemoryUsage // Get the last memory measurement from NPC
- };
-
- // Create a test result entry directly
- PathfindingTestResult result = new PathfindingTestResult
- {
- algorithm = currentTestAlgorithm,
- gridSize = currentTestGridSize,
- mazeDensity = currentTestDensity,
- diagonalMovement = currentTestDiagonal,
- timeTaken = metrics.timeTaken,
- pathLength = metrics.pathLength,
- nodesExplored = metrics.nodesExplored,
- memoryUsed = metrics.memoryUsed,
- pathFound = npc.pathFinder.Status == PathFinding.PathFinderStatus.SUCCESS,
- testIndex = currentTestIndex
- };
-
- // Add to results directly
- testResults.Add(result);
-
- // Log result
- Debug.Log($"Test {completedTests} (manual): {GetAlgorithmName(result.algorithm)} - {result.timeTaken:F2}ms - Path: {result.pathLength} - Nodes: {result.nodesExplored}");
- }
-
- // Increment test counter
- completedTests++;
- }
-
- private int CalculatePathLength(PathFinding.PathFinder pathFinder)
- {
- if (pathFinder == null || pathFinder.CurrentNode == null)
- return 0;
-
- int length = 0;
- var node = pathFinder.CurrentNode;
- while (node != null)
- {
- length++;
- node = node.Parent;
- }
- return length;
- }
-
- private IEnumerator SetupTestEnvironment(NPC.PathFinderType algorithm, Vector2Int gridSize, float density, bool useDiagonals)
- {
- Debug.Log($"Setting up test environment: Algorithm={algorithm}, Grid={gridSize.x}x{gridSize.y}, Density={density}, Diagonals={useDiagonals}");
-
- // Resize grid
- gridMap.ResizeGrid(gridSize.x, gridSize.y);
- yield return null;
-
- // Set algorithm
- npc.ChangeAlgorithm(algorithm);
- yield return null;
-
- // Set diagonal movement
- gridMap.AllowDiagonalMovement = useDiagonals;
- yield return null;
-
- // Generate maze with specified density
- gridMap.GenerateRandomMaze(density);
- yield return null;
-
- // Note: We no longer change visualization settings here
- // This is handled in the RunSingleTest method
-
- yield return null;
- }
-
- private GridNode FindValidStartNode()
- {
- // Find a walkable node for start position, starting from the top left
- for (int x = 0; x < gridMap.NumX; x++)
- {
- for (int y = 0; y < gridMap.NumY; y++)
- {
- GridNode node = gridMap.GetGridNode(x, y);
- if (node != null && node.IsWalkable)
- {
- return node;
- }
- }
- }
- return null;
- }
-
- private GridNode FindValidDestinationNode(GridNode startNode)
- {
- if (startNode == null)
- return null;
-
- // Find a walkable node far from the start position, starting from the bottom right
- int maxDistance = 0;
- GridNode bestNode = null;
-
- // First try to find a node with good distance
- for (int x = gridMap.NumX - 1; x >= 0; x--)
- {
- for (int y = gridMap.NumY - 1; y >= 0; y--)
- {
- GridNode node = gridMap.GetGridNode(x, y);
- if (node != null && node.IsWalkable && node != startNode)
- {
- int distance = Mathf.Abs(x - startNode.Value.x) + Mathf.Abs(y - startNode.Value.y);
- if (distance > maxDistance)
- {
- maxDistance = distance;
- bestNode = node;
- }
- }
- }
- }
-
- // If we found a node and the distance is decent, use it
- if (bestNode != null && maxDistance > gridMap.NumX / 4)
- {
- return bestNode;
- }
-
- // If no good node was found or distance is too small, try harder to find any walkable node
- // different from start node (this helps with very high density mazes)
- for (int x = 0; x < gridMap.NumX; x++)
- {
- for (int y = 0; y < gridMap.NumY; y++)
- {
- GridNode node = gridMap.GetGridNode(x, y);
- if (node != null && node.IsWalkable && node != startNode)
- {
- return node; // Return the first walkable node that isn't the start
- }
- }
- }
-
- // If we get here and bestNode is null, there's only one walkable node in the entire grid
- // In this case, we have to return null and the test should be skipped
- return bestNode;
- }
-
- private void OnPathfindingComplete(PathfindingMetrics metrics)
- {
- if (!isTestingRunning)
- {
- Debug.Log("OnPathfindingComplete called but test is not running - ignoring");
- return;
- }
-
- Debug.Log($"OnPathfindingComplete called for test {completedTests+1} with algorithm {currentTestAlgorithm}");
-
- // Create a test result entry
- PathfindingTestResult result = new PathfindingTestResult
- {
- algorithm = currentTestAlgorithm,
- gridSize = currentTestGridSize,
- mazeDensity = currentTestDensity,
- diagonalMovement = currentTestDiagonal,
- timeTaken = metrics.timeTaken,
- pathLength = metrics.pathLength,
- nodesExplored = metrics.nodesExplored,
- memoryUsed = metrics.memoryUsed,
- pathFound = npc.pathFinder.Status == PathFinding.PathFinderStatus.SUCCESS,
- testIndex = currentTestIndex
- };
-
- // Add to results
- testResults.Add(result);
-
- // Log result
- Debug.Log($"Test {completedTests+1}: {GetAlgorithmName(result.algorithm)} - {result.timeTaken:F2}ms - Path: {result.pathLength} - Nodes: {result.nodesExplored} - Success: {result.pathFound} - Results count: {testResults.Count}");
- }
-
- private void SaveResultsToCSV()
- {
- try
- {
- Debug.Log($"Saving {testResults.Count} test results to CSV...");
-
- if (testResults.Count == 0)
- {
- Debug.LogWarning("No test results to save!");
- return;
- }
-
- // Create directory if it doesn't exist
- string directory = Path.Combine(Application.persistentDataPath, "TestResults");
- if (!Directory.Exists(directory))
- {
- Directory.CreateDirectory(directory);
- }
-
- string filePath = Path.Combine(directory, resultsFileName);
-
- StringBuilder csv = new StringBuilder();
-
- // Write header
- csv.AppendLine("Algorithm,GridSizeX,GridSizeY,Density,DiagonalMovement,TimeTaken,PathLength,NodesExplored,MemoryUsed,PathFound,TestIndex");
-
- // Write each result
- foreach (var result in testResults)
- {
- csv.AppendLine($"{result.algorithm},{result.gridSize.x},{result.gridSize.y},{result.mazeDensity},{result.diagonalMovement}," +
- $"{result.timeTaken},{result.pathLength},{result.nodesExplored},{result.memoryUsed},{result.pathFound},{result.testIndex}");
- }
-
- // Write file
- File.WriteAllText(filePath, csv.ToString());
-
- Debug.Log($"Test results saved to {filePath}");
-
- // Make it easier to find in Windows Explorer
- System.Diagnostics.Process.Start("explorer.exe", $"/select,\"{filePath}\"");
- }
- catch (System.Exception e)
- {
- Debug.LogError($"Error saving test results: {e.Message}");
- }
- }
-
- // Utility method to get algorithm name as a string
- private string GetAlgorithmName(NPC.PathFinderType algorithm)
- {
- switch (algorithm)
- {
- case NPC.PathFinderType.ASTAR: return "A*";
- case NPC.PathFinderType.DIJKSTRA: return "Dijkstra";
- case NPC.PathFinderType.GREEDY: return "Greedy";
- case NPC.PathFinderType.BACKTRACKING: return "Backtracking";
- case NPC.PathFinderType.BFS: return "BFS";
- default: return "Unknown";
- }
- }
-}
\ No newline at end of file
diff --git a/Assets/Scripts/PathfindingTester.cs.meta b/Assets/Scripts/PathfindingTester.cs.meta
deleted file mode 100644
index 2cef5ac..0000000
--- a/Assets/Scripts/PathfindingTester.cs.meta
+++ /dev/null
@@ -1,11 +0,0 @@
-fileFormatVersion: 2
-guid: ed03b279fa9f3194f8caed356ea838dc
-MonoImporter:
- externalObjects: {}
- serializedVersion: 2
- defaultReferences: []
- executionOrder: 0
- icon: {instanceID: 0}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Scripts/PathfindingUIManager.cs b/Assets/Scripts/PathfindingUIManager.cs
index 6f56a07..9064d3d 100644
--- a/Assets/Scripts/PathfindingUIManager.cs
+++ b/Assets/Scripts/PathfindingUIManager.cs
@@ -6,6 +6,11 @@ using UnityEngine.UI;
using TMPro;
using System.IO;
+///
+/// PathfindingUIManager merupakan kelas yang mengelola antarmuka pengguna untuk sistem pathfinding.
+/// pengguna dapat mengatur ukuran grid, memilih algoritma pathfinding, menjalankan pathfinding.
+///
+
public class PathfindingUIManager : MonoBehaviour
{
[Header("References")]
@@ -22,12 +27,11 @@ public class PathfindingUIManager : MonoBehaviour
public TMP_Dropdown algorithmDropdown;
public Button runPathfindingButton;
public Button resetButton;
- public Toggle allowDiagonalToggle; // Toggle untuk diagonal movement
+ public Toggle allowDiagonalToggle;
[Header("Visualization Controls")]
public Slider visualizationSpeedSlider;
public Slider visualizationBatchSlider;
- // public Toggle showVisualizationToggle;
public TMP_Text speedValueText;
public TMP_Text batchValueText;
@@ -36,7 +40,7 @@ public class PathfindingUIManager : MonoBehaviour
public TMP_Text pathLengthText;
public TMP_Text nodesExploredText;
public TMP_Text memoryUsageText;
- public TMP_Text cpuUsageText; // Text untuk menampilkan penggunaan CPU
+ public TMP_Text cpuUsageText;
[Header("Map Save/Load")]
public TMP_InputField mapNameInput;
@@ -44,7 +48,7 @@ public class PathfindingUIManager : MonoBehaviour
public Button loadButton;
[Header("Application Controls")]
- public Button exitButton; // Tombol untuk keluar aplikasi
+ public Button exitButton;
[Header("Optimization")]
[SerializeField] private bool performWarmup = true;
@@ -54,29 +58,16 @@ public class PathfindingUIManager : MonoBehaviour
public TMP_Dropdown mazeSizeDropdown;
public TMP_Dropdown mazeDensityDropdown;
- [Header("Automated Testing")]
- [SerializeField] private GameObject testerPanelPrefab;
- [SerializeField] private Transform testerPanelParent;
- private GameObject testerPanelInstance;
-
- // Konstanta untuk perhitungan CPU usage
private const float TARGET_FRAME_TIME_MS = 16.67f; // 60 FPS = 16.67ms per frame
- // Add a flag to track if pathfinding is running
private bool isPathfindingRunning = false;
-
- // Add a flag to track if visualization is running
private bool isVisualizationRunning = false;
-
- // Add a flag to track if NPC is moving
private bool isNpcMoving = false;
private void Start()
{
- // Initialize UI elements
InitializeUI();
- // Add listeners
applyGridSizeButton.onClick.AddListener(OnApplyGridSize);
runPathfindingButton.onClick.AddListener(OnRunPathfinding);
resetButton.onClick.AddListener(OnResetPathfinding);
@@ -84,56 +75,38 @@ public class PathfindingUIManager : MonoBehaviour
saveButton.onClick.AddListener(OnSaveMap);
loadButton.onClick.AddListener(OnLoadMap);
- // Add exit button listener if the button exists
if (exitButton != null)
exitButton.onClick.AddListener(OnExitApplication);
- // Add listener for maze generator
if (generateMazeButton != null)
generateMazeButton.onClick.AddListener(OnGenerateMaze);
- // Add visualization control listeners
if (visualizationSpeedSlider != null)
visualizationSpeedSlider.onValueChanged.AddListener(OnVisualizationSpeedChanged);
if (visualizationBatchSlider != null)
visualizationBatchSlider.onValueChanged.AddListener(OnVisualizationBatchChanged);
- //if (showVisualizationToggle != null)
- //{
- // showVisualizationToggle.isOn = npc.showVisualization;
- // showVisualizationToggle.onValueChanged.AddListener(OnShowVisualizationChanged);
- //}
-
- // Subscribe to NPC's pathfinding events
npc.OnPathfindingComplete += UpdatePerformanceMetrics;
npc.OnPathfindingComplete += OnPathfindingCompleted;
- // Subscribe to visualization completion event
npc.OnVisualizationComplete += OnVisualizationCompleted;
- // Subscribe to movement completion event
npc.OnMovementComplete += OnMovementCompleted;
- // Initialize performance metrics
ClearPerformanceMetrics();
-
- // Initialize visualization controls
InitializeVisualizationControls();
- // Perform algorithm warmup
if (performWarmup)
{
StartCoroutine(WarmupPathfindingSystem());
}
- // Tampilkan lokasi penyimpanan
ShowSaveLocation();
}
private IEnumerator WarmupPathfindingSystem()
{
- // Wait one frame to ensure everything is initialized
yield return null;
if (showWarmupMessage)
@@ -141,20 +114,16 @@ public class PathfindingUIManager : MonoBehaviour
//Debug.Log("Performing pathfinding warmup...");
}
- // Get current NPC position
Vector3 npcPos = npc.transform.position;
int startX = (int)(npcPos.x / gridMap.GridNodeWidth);
int startY = (int)(npcPos.y / gridMap.GridNodeHeight);
- // Find destination node for warmup (try to use opposite corner)
int destX = gridMap.NumX - 1;
int destY = gridMap.NumY - 1;
- // Ensure destination is walkable
GridNode destNode = gridMap.GetGridNode(destX, destY);
if (destNode == null || !destNode.IsWalkable)
{
- // Find any walkable node for warmup
for (int x = 0; x < gridMap.NumX; x++)
{
for (int y = 0; y < gridMap.NumY; y++)
@@ -173,66 +142,46 @@ public class PathfindingUIManager : MonoBehaviour
}
}
- // Save current destination position
Vector3 originalDestPos = gridMap.Destination.position;
- // Set temporary destination for warmup
gridMap.SetDestination(destX, destY);
- // Run pathfinding quietly (without visualization)
GridNode startNode = gridMap.GetGridNode(startX, startY);
if (startNode != null && destNode != null && destNode.IsWalkable)
{
- // Temporarily disable visualization for warmup
float originalVisualizationSpeed = npc.visualizationSpeed;
npc.visualizationSpeed = 0f;
bool originalShowVisualization = npc.showVisualization;
npc.showVisualization = false;
- // Do warmup for each algorithm type to JIT compile all code paths
foreach (NPC.PathFinderType algoType in System.Enum.GetValues(typeof(NPC.PathFinderType)))
{
- // Save current algorithm
NPC.PathFinderType originalAlgorithm = npc.pathFinderType;
- // Change to this algorithm
npc.ChangeAlgorithm(algoType);
-
- // Run silent pathfinding
npc.MoveTo(destNode, true);
-
- // Wait a bit to ensure completion
yield return new WaitForSeconds(0.05f);
- // Reset back to original algorithm
npc.ChangeAlgorithm(originalAlgorithm);
}
- // Restore visualization settings
npc.visualizationSpeed = originalVisualizationSpeed;
npc.showVisualization = originalShowVisualization;
}
-
- // Restore original destination
gridMap.Destination.position = originalDestPos;
- // Clear metrics from warmup
ClearPerformanceMetrics();
if (showWarmupMessage)
{
//Debug.Log("Pathfinding warmup complete");
}
-
-
- // Reset grid colors
gridMap.ResetGridNodeColours();
}
private void OnDestroy()
{
- // Unsubscribe from events
if (npc != null)
{
npc.OnPathfindingComplete -= UpdatePerformanceMetrics;
@@ -241,7 +190,6 @@ public class PathfindingUIManager : MonoBehaviour
npc.OnMovementComplete -= OnMovementCompleted;
}
- // Unsubscribe from UI events
if (allowDiagonalToggle != null)
{
allowDiagonalToggle.onValueChanged.RemoveListener(OnDiagonalMovementChanged);
@@ -250,19 +198,15 @@ public class PathfindingUIManager : MonoBehaviour
private void InitializeUI()
{
- // Set initial values
gridSizeXInput.text = gridMap.NumX.ToString();
gridSizeYInput.text = gridMap.NumY.ToString();
- // Set input fields to only accept integers
gridSizeXInput.contentType = TMP_InputField.ContentType.IntegerNumber;
gridSizeYInput.contentType = TMP_InputField.ContentType.IntegerNumber;
- // Add input validation events
gridSizeXInput.onValidateInput += ValidateNumberInput;
gridSizeYInput.onValidateInput += ValidateNumberInput;
- // Setup algorithm dropdown
algorithmDropdown.ClearOptions();
algorithmDropdown.AddOptions(new System.Collections.Generic.List {
"A*",
@@ -272,14 +216,12 @@ public class PathfindingUIManager : MonoBehaviour
"BFS"
});
- // Initialize diagonal movement toggle
if (allowDiagonalToggle != null)
{
allowDiagonalToggle.isOn = gridMap.AllowDiagonalMovement;
allowDiagonalToggle.onValueChanged.AddListener(OnDiagonalMovementChanged);
}
- // Setup maze size dropdown
if (mazeSizeDropdown != null)
{
mazeSizeDropdown.ClearOptions();
@@ -292,7 +234,6 @@ public class PathfindingUIManager : MonoBehaviour
});
}
- // Setup maze density dropdown
if (mazeDensityDropdown != null)
{
mazeDensityDropdown.ClearOptions();
@@ -306,17 +247,15 @@ public class PathfindingUIManager : MonoBehaviour
ClearPerformanceMetrics();
}
- // Validation function to only allow numeric input
private char ValidateNumberInput(string text, int charIndex, char addedChar)
{
- // Only allow digits
if (char.IsDigit(addedChar))
{
return addedChar;
}
else
{
- return '\0'; // Return null character to reject the input
+ return '\0';
}
}
@@ -336,7 +275,6 @@ public class PathfindingUIManager : MonoBehaviour
nodesExploredText.text = $"{metrics.nodesExplored} nodes";
memoryUsageText.text = FormatBytes(metrics.memoryUsed);
- // Hitung dan tampilkan CPU usage
if (cpuUsageText != null)
{
float cpuUsagePercentage = (metrics.timeTaken / TARGET_FRAME_TIME_MS) * 100f;
@@ -362,38 +300,29 @@ public class PathfindingUIManager : MonoBehaviour
if (int.TryParse(gridSizeXInput.text, out int newSizeX) &&
int.TryParse(gridSizeYInput.text, out int newSizeY))
{
- // Validate grid size limits
const int MAX_GRID_SIZE = 200;
const int MIN_GRID_SIZE = 2;
if (newSizeX > MAX_GRID_SIZE || newSizeY > MAX_GRID_SIZE)
{
- // Display an error message
Debug.LogWarning($"Grid size cannot exceed {MAX_GRID_SIZE}x{MAX_GRID_SIZE}. Resize operation cancelled.");
- // Revert input fields to current grid size
gridSizeXInput.text = gridMap.NumX.ToString();
gridSizeYInput.text = gridMap.NumY.ToString();
- // Don't proceed with resize
return;
}
- // Check for minimum size
if (newSizeX < MIN_GRID_SIZE || newSizeY < MIN_GRID_SIZE)
{
- // Display an error message
Debug.LogWarning($"Grid size cannot be less than {MIN_GRID_SIZE}x{MIN_GRID_SIZE}. Resize operation cancelled.");
- // Revert input fields to current grid size
gridSizeXInput.text = gridMap.NumX.ToString();
gridSizeYInput.text = gridMap.NumY.ToString();
- // Don't proceed with resize
return;
}
- // Apply the grid size (only if within limits)
if (gridMap.ResizeGrid(newSizeX, newSizeY))
{
ClearPerformanceMetrics();
@@ -403,17 +332,14 @@ public class PathfindingUIManager : MonoBehaviour
private void OnRunPathfinding()
{
- // Get current NPC position
Vector3 npcPos = npc.transform.position;
int startX = (int)(npcPos.x / gridMap.GridNodeWidth);
int startY = (int)(npcPos.y / gridMap.GridNodeHeight);
- // Get destination position
Vector3 destPos = gridMap.Destination.position;
int destX = (int)(destPos.x / gridMap.GridNodeWidth);
int destY = (int)(destPos.y / gridMap.GridNodeHeight);
- // Run pathfinding
GridNode startNode = gridMap.GetGridNode(startX, startY);
GridNode endNode = gridMap.GetGridNode(destX, destY);
@@ -421,10 +347,9 @@ public class PathfindingUIManager : MonoBehaviour
{
ClearPerformanceMetrics();
- // Set flags that pathfinding, visualization, and movement will happen
isPathfindingRunning = true;
isVisualizationRunning = true;
- isNpcMoving = true; // assume movement will happen
+ isNpcMoving = true;
SetUIInteractivity(false);
npc.MoveTo(endNode);
@@ -433,20 +358,15 @@ public class PathfindingUIManager : MonoBehaviour
private void OnResetPathfinding()
{
- // Reset all flags to ensure UI will be enabled after reload
isPathfindingRunning = false;
isVisualizationRunning = false;
isNpcMoving = false;
- // Force enable UI - this ensures buttons will be enabled
- // after reset regardless of editor/build status
SetUIInteractivity(true);
- // Reload the current scene
UnityEngine.SceneManagement.SceneManager.LoadScene(
UnityEngine.SceneManagement.SceneManager.GetActiveScene().name);
- //Debug.Log("Reloading scene...");
}
private void OnAlgorithmChanged(int index)
@@ -460,7 +380,6 @@ public class PathfindingUIManager : MonoBehaviour
ClearPerformanceMetrics();
}
- // Helper method to get the readable name of the algorithm
private string GetAlgorithmName(NPC.PathFinderType type)
{
switch (type)
@@ -488,7 +407,6 @@ public class PathfindingUIManager : MonoBehaviour
return;
}
- // Buat direktori jika belum ada
string saveDirectory = Path.Combine(Application.persistentDataPath, "GridSaves");
if (!Directory.Exists(saveDirectory))
{
@@ -501,20 +419,13 @@ public class PathfindingUIManager : MonoBehaviour
Debug.Log($"Map saved to: {filePath} (includes grid state, NPC position, and destination position)");
}
- ///
- /// Membuka folder penyimpanan di File Explorer
- ///
public void OpenSaveFolder()
{
string saveDirectory = Path.Combine(Application.persistentDataPath, "GridSaves");
-
- // Buat direktori jika belum ada
if (!Directory.Exists(saveDirectory))
{
Directory.CreateDirectory(saveDirectory);
}
-
- // Buka folder di file explorer
System.Diagnostics.Process.Start("explorer.exe", saveDirectory);
}
@@ -530,22 +441,15 @@ public class PathfindingUIManager : MonoBehaviour
if (!File.Exists(filePath))
{
- //Debug.LogWarning($"Map file not found: {filePath}");
return;
}
gridMap.LoadGridState(filePath);
ClearPerformanceMetrics();
-
- //Debug.Log($"Map loaded from: {filePath}");
}
- ///
- /// Generates a random maze with the selected size and density
- ///
private void OnGenerateMaze()
{
- // Get selected maze size
int sizeX = 20;
int sizeY = 20;
bool isLargeGrid = false;
@@ -569,10 +473,8 @@ public class PathfindingUIManager : MonoBehaviour
sizeX = sizeY = 100;
isLargeGrid = true;
break;
- // If more options are added that exceed MAX_GRID_SIZE, they'll be rejected
}
- // Check if size exceeds maximum or is below minimum - reject if it does
if (sizeX > MAX_GRID_SIZE || sizeY > MAX_GRID_SIZE)
{
Debug.LogWarning($"Maze size {sizeX}x{sizeY} exceeds maximum of {MAX_GRID_SIZE}x{MAX_GRID_SIZE}. Operation cancelled.");
@@ -585,22 +487,18 @@ public class PathfindingUIManager : MonoBehaviour
return;
}
- // Resize grid if needed
if (gridMap.NumX != sizeX || gridMap.NumY != sizeY)
{
if (!gridMap.ResizeGrid(sizeX, sizeY))
{
- // Resize failed, abort maze generation
return;
}
- // Update grid size inputs
gridSizeXInput.text = sizeX.ToString();
gridSizeYInput.text = sizeY.ToString();
}
- // Get selected density
- float density = 30f; // Default medium
+ float density = 30f;
switch (mazeDensityDropdown.value)
{
@@ -615,61 +513,40 @@ public class PathfindingUIManager : MonoBehaviour
break;
}
- // Untuk grid besar, nonaktifkan visualisasi sementara untuk performa lebih baik
bool originalShowVisualization = false;
float originalVisualizationSpeed = 0f;
if (isLargeGrid && npc != null)
{
- // Simpan nilai asli
originalShowVisualization = npc.showVisualization;
originalVisualizationSpeed = npc.visualizationSpeed;
- // Nonaktifkan visualisasi untuk grid besar
npc.showVisualization = false;
}
- // Generate the maze with selected density
gridMap.GenerateRandomMaze(density);
- // Kembalikan nilai visualisasi jika diubah
if (isLargeGrid && npc != null)
{
npc.showVisualization = originalShowVisualization;
npc.visualizationSpeed = originalVisualizationSpeed;
}
- // Clear performance metrics
ClearPerformanceMetrics();
-
- // Tampilkan pesan khusus untuk grid besar
- if (isLargeGrid)
- {
- //Debug.Log("Large maze generated. For best performance, consider disabling visualization during pathfinding.");
- }
-
- //Debug.Log($"Generated maze with size {sizeX}x{sizeY} and density {density}%");
}
- ///
- /// Menampilkan lokasi penyimpanan file di konsol
- ///
private void ShowSaveLocation()
{
string saveDirectory = Path.Combine(Application.persistentDataPath, "GridSaves");
}
- ///
- /// Menutup aplikasi saat tombol exit ditekan
- ///
public void OnExitApplication()
{
#if UNITY_EDITOR
- // Jika di Unity Editor
UnityEditor.EditorApplication.isPlaying = false;
#else
- // Jika di build
- Application.Quit();
+ // Jika di build
+ Application.Quit();
#endif
Debug.Log("Application exit requested");
@@ -677,7 +554,6 @@ public class PathfindingUIManager : MonoBehaviour
private void InitializeVisualizationControls()
{
- // Set initial slider values based on NPC settings
if (visualizationSpeedSlider != null)
{
visualizationSpeedSlider.value = npc.visualizationSpeed;
@@ -699,7 +575,6 @@ public class PathfindingUIManager : MonoBehaviour
private void OnVisualizationBatchChanged(float newValue)
{
- // Pastikan nilai batch adalah integer
int batchValue = Mathf.RoundToInt(newValue);
npc.visualizationBatch = batchValue;
UpdateBatchValueText(batchValue);
@@ -725,31 +600,22 @@ public class PathfindingUIManager : MonoBehaviour
batchValueText.text = value.ToString();
}
}
-
- ///
- /// Menangani perubahan toggle diagonal movement
- ///
private void OnDiagonalMovementChanged(bool isOn)
{
gridMap.AllowDiagonalMovement = isOn;
- // Reset performance metrics
ClearPerformanceMetrics();
}
- // New method to handle pathfinding completion
private void OnPathfindingCompleted(PathfindingMetrics metrics)
{
- // Pathfinding is completed, but visualization might still be running
isPathfindingRunning = false;
- // Check if pathfinding failed by looking at metrics or path length
bool pathfindingFailed = (metrics.pathLength == 0) ||
(npc.pathFinder != null &&
npc.pathFinder.Status == PathFinding.PathFinderStatus.FAILURE);
if (pathfindingFailed)
{
- // If pathfinding failed, there won't be any visualization or movement
Debug.Log("Pathfinding failed - re-enabling UI controls immediately");
isVisualizationRunning = false;
isNpcMoving = false;
@@ -765,22 +631,15 @@ public class PathfindingUIManager : MonoBehaviour
return;
}
- // If pathfinding succeeded, continue with normal flow
- // Very important: Keep UI disabled regardless of visualization state to prevent
- // the brief window of interactivity between pathfinding completion and visualization start
+
if (npc.showVisualization)
{
- // If visualization is enabled in settings, assume it will start soon
- // Keep UI disabled by keeping isVisualizationRunning true
isVisualizationRunning = true;
- // Do NOT enable UI here - wait for visualization to complete
}
else
{
- // Only if visualization is completely disabled in settings, enable UI
isVisualizationRunning = false;
- // Only re-enable in editor mode
#if UNITY_EDITOR
SetUIInteractivity(true);
#else
@@ -790,25 +649,18 @@ public class PathfindingUIManager : MonoBehaviour
}
}
- // New method to handle visualization completion
private void OnVisualizationCompleted()
{
- // Visualization is completed, but NPC may start moving
isVisualizationRunning = false;
- // Check if NPC is moving or will move
if (npc.IsMoving || npc.wayPoints.Count > 0)
{
- // Movement is starting or in progress
isNpcMoving = true;
- // Leave UI disabled
}
else
{
- // No movement expected
isNpcMoving = false;
- // Only re-enable in editor mode
#if UNITY_EDITOR
SetUIInteractivity(true);
#else
@@ -818,13 +670,10 @@ public class PathfindingUIManager : MonoBehaviour
}
}
- // New method to handle movement completion
private void OnMovementCompleted()
{
- // Movement is completed, re-enable UI buttons only in editor
isNpcMoving = false;
- // Only re-enable in editor mode
#if UNITY_EDITOR
SetUIInteractivity(true);
#else
@@ -834,13 +683,10 @@ public class PathfindingUIManager : MonoBehaviour
#endif
}
- // Method to enable/disable UI elements based on pathfinding, visualization, and movement state
private void SetUIInteractivity(bool enabled)
{
- // If any process is running, disable controls
bool shouldEnable = enabled && !isPathfindingRunning && !isVisualizationRunning && !isNpcMoving;
- // In builds (not editor), once disabled, buttons stay disabled until reset
#if !UNITY_EDITOR
if (shouldEnable && (isPathfindingRunning || isVisualizationRunning || isNpcMoving))
{
@@ -850,13 +696,10 @@ public class PathfindingUIManager : MonoBehaviour
}
#endif
- // Add debug logging
Debug.Log($"SetUIInteractivity called with enabled={enabled}, pathfinding={isPathfindingRunning}, " +
$"visualization={isVisualizationRunning}, movement={isNpcMoving}, shouldEnable={shouldEnable}, " +
$"inEditor={Application.isEditor}");
- // Keep reset and exit buttons always enabled
- // Disable all other buttons when processes are running
if (applyGridSizeButton != null)
applyGridSizeButton.interactable = shouldEnable;
@@ -899,17 +742,12 @@ public class PathfindingUIManager : MonoBehaviour
if (mapNameInput != null)
mapNameInput.interactable = shouldEnable;
-
- // Reset and exit buttons remain enabled
- // resetButton and exitButton stay interactable
}
private void Update()
{
- // Continuously check for various states - this ensures buttons stay disabled
if (npc != null)
{
- // Check visualization
if (npc.IsVisualizingPath && !isVisualizationRunning)
{
Debug.Log("Detected active visualization - updating UI state");
@@ -917,7 +755,6 @@ public class PathfindingUIManager : MonoBehaviour
SetUIInteractivity(false);
}
- // Check movement
if (npc.IsMoving && !isNpcMoving)
{
Debug.Log("Detected active NPC movement - updating UI state");
diff --git a/Assets/Scripts/PriorityQueue.cs b/Assets/Scripts/PriorityQueue.cs
index da4a56a..f8ad147 100644
--- a/Assets/Scripts/PriorityQueue.cs
+++ b/Assets/Scripts/PriorityQueue.cs
@@ -9,21 +9,10 @@ using System.Collections.Generic;
/// Tipe prioritas yang digunakan, harus implementasi IComparable
public class PriorityQueue where TPriority : IComparable
{
- // Menyimpan pasangan elemen dan prioritasnya dalam struktur heap
private List> elements = new List>();
- // Menyimpan indeks setiap elemen dalam heap untuk akses cepat
private Dictionary elementIndexMap = new Dictionary();
-
- ///
- /// Mendapatkan jumlah elemen dalam antrean prioritas
- ///
public int Count => elements.Count;
- ///
- /// Menambahkan elemen baru ke dalam antrean prioritas dengan prioritas tertentu
- ///
- /// Elemen yang akan ditambahkan
- /// Prioritas dari elemen
public void Enqueue(TElement element, TPriority priority)
{
elements.Add(Tuple.Create(element, priority));
@@ -31,12 +20,6 @@ public class PriorityQueue where TPriority : IComparable
- /// Mengambil dan menghapus elemen dengan prioritas tertinggi (nilai terkecil)
- /// dari antrean prioritas
- ///
- /// Elemen dengan prioritas tertinggi
- /// Dilempar jika antrean kosong
public TElement Dequeue()
{
if (elements.Count == 0)
@@ -48,7 +31,6 @@ public class PriorityQueue where TPriority : IComparable 0)
{
- // Pindahkan elemen terakhir ke root, lalu atur ulang heap
elements[0] = last;
elementIndexMap[last.Item1] = 0;
HeapifyDown(0);
@@ -58,12 +40,6 @@ public class PriorityQueue where TPriority : IComparable
- /// Memperbarui prioritas elemen yang sudah ada dalam antrean
- ///
- /// Elemen yang akan diperbarui prioritasnya
- /// Nilai prioritas baru
- /// Dilempar jika elemen tidak ditemukan
public void UpdatePriority(TElement element, TPriority newPriority)
{
if (!elementIndexMap.ContainsKey(element))
@@ -73,23 +49,16 @@ public class PriorityQueue where TPriority : IComparable
- /// Mempertahankan properti heap dengan memindahkan elemen ke atas jika
- /// prioritasnya lebih tinggi dari parent
- ///
- /// Indeks elemen yang akan dipindahkan ke atas
private void HeapifyUp(int index)
{
var parentIndex = (index - 1) / 2;
@@ -100,18 +69,12 @@ public class PriorityQueue where TPriority : IComparable
- /// Mempertahankan properti heap dengan memindahkan elemen ke bawah jika
- /// prioritasnya lebih rendah dari child
- ///
- /// Indeks elemen yang akan dipindahkan ke bawah
private void HeapifyDown(int index)
{
var leftChildIndex = 2 * index + 1;
var rightChildIndex = 2 * index + 2;
var smallest = index;
- // Cari child dengan prioritas tertinggi (nilai terkecil)
if (leftChildIndex < elements.Count && elements[leftChildIndex].Item2.CompareTo(elements[smallest].Item2) < 0)
{
smallest = leftChildIndex;
@@ -122,7 +85,6 @@ public class PriorityQueue where TPriority : IComparable where TPriority : IComparable
- /// Menukar posisi dua elemen dalam heap dan memperbarui elementIndexMap
- ///
- /// Indeks elemen pertama
- /// Indeks elemen kedua
private void Swap(int i, int j)
{
var temp = elements[i];
elements[i] = elements[j];
elements[j] = temp;
- // Perbarui elementIndexMap untuk mencerminkan posisi baru
elementIndexMap[elements[i].Item1] = i;
elementIndexMap[elements[j].Item1] = j;
}
diff --git a/Assets/Scripts/pathfinding_tests.csv b/Assets/Scripts/pathfinding_tests.csv
deleted file mode 100644
index eb68217..0000000
--- a/Assets/Scripts/pathfinding_tests.csv
+++ /dev/null
@@ -1,271 +0,0 @@
-Algorithm,GridSizeX,GridSizeY,Density,DiagonalMovement,TimeTaken,PathLength,NodesExplored,MemoryUsed,PathFound,TestIndex
-ASTAR,20,20,10,True,1.0282,24,26,4096,True,0
-ASTAR,20,20,10,True,0.1261,22,22,4096,True,1
-ASTAR,20,20,10,True,0.1369,21,20,4096,True,2
-ASTAR,20,20,10,False,0.12,39,38,4096,True,0
-ASTAR,20,20,10,False,0.1708,39,53,8192,True,1
-ASTAR,20,20,10,False,0.2009,39,69,12288,True,2
-ASTAR,20,20,30,True,0.1264,26,27,8192,True,0
-ASTAR,20,20,30,True,0.1366,26,29,8192,True,1
-ASTAR,20,20,30,True,0.1186,24,25,8192,True,2
-ASTAR,20,20,30,False,0.1441,39,45,4096,True,0
-ASTAR,20,20,30,False,0.1042,39,48,12288,True,1
-ASTAR,20,20,30,False,0.2301,39,87,24576,True,2
-ASTAR,20,20,50,True,0.1618,43,89,16384,True,0
-ASTAR,20,20,50,True,0.3753,54,171,8192,True,1
-ASTAR,20,20,50,True,0.2745,48,130,8192,True,2
-ASTAR,20,20,50,False,0.1702,63,104,0,True,0
-ASTAR,20,20,50,False,0.1628,59,98,12288,True,1
-ASTAR,20,20,50,False,0.3159,135,180,40960,True,2
-ASTAR,35,35,10,True,0.2377,36,35,16384,True,0
-ASTAR,35,35,10,True,0.2482,38,39,16384,True,1
-ASTAR,35,35,10,True,0.2412,40,39,16384,True,2
-ASTAR,35,35,10,False,0.2433,69,68,8192,True,0
-ASTAR,35,35,10,False,0.281,69,95,24576,True,1
-ASTAR,35,35,10,False,0.2874,69,93,20480,True,2
-ASTAR,35,35,30,True,0.2388,47,46,20480,True,0
-ASTAR,35,35,30,True,0.3082,45,68,4096,True,1
-ASTAR,35,35,30,True,0.2438,42,44,4096,True,2
-ASTAR,35,35,30,False,0.3319,69,111,4096,True,0
-ASTAR,35,35,30,False,0.4247,69,139,20480,True,1
-ASTAR,35,35,30,False,0.2607,69,86,12288,True,2
-ASTAR,35,35,50,True,0.4856,101,261,36864,True,0
-ASTAR,35,35,50,True,1.0796,219,563,98304,True,1
-ASTAR,35,35,50,True,1.0919,97,529,114688,True,2
-ASTAR,35,35,50,False,0.4922,145,300,69632,True,0
-ASTAR,35,35,50,False,0.2173,97,134,24576,True,1
-ASTAR,35,35,50,False,0.4082,77,191,28672,True,2
-ASTAR,50,50,10,True,0.3457,54,53,40960,True,0
-ASTAR,50,50,10,True,0.3793,54,53,16384,True,1
-ASTAR,50,50,10,True,0.3698,57,56,16384,True,2
-ASTAR,50,50,10,False,0.3816,99,98,20480,True,0
-ASTAR,50,50,10,False,0.3855,99,120,20480,True,1
-ASTAR,50,50,10,False,0.4885,99,158,24576,True,2
-ASTAR,50,50,30,True,0.3388,59,61,16384,True,0
-ASTAR,50,50,30,True,0.3565,57,59,20480,True,1
-ASTAR,50,50,30,True,0.3343,64,67,20480,True,2
-ASTAR,50,50,30,False,1.1702,99,412,102400,True,0
-ASTAR,50,50,30,False,0.4831,99,153,32768,True,1
-ASTAR,50,50,30,False,0.4641,99,200,8192,True,2
-ASTAR,50,50,50,True,2.2106,147,926,73728,True,0
-ASTAR,50,50,50,True,1.8848,162,854,73728,True,1
-ASTAR,50,50,50,True,1.8428,305,1033,196608,True,2
-ASTAR,50,50,50,False,1.3853,259,813,155648,True,0
-ASTAR,50,50,50,False,1.3047,289,809,151552,True,1
-ASTAR,50,50,50,False,0.7769,223,485,77824,True,2
-DIJKSTRA,20,20,10,True,0.5732,21,360,69632,True,0
-DIJKSTRA,20,20,10,True,0.6119,21,360,69632,True,1
-DIJKSTRA,20,20,10,True,0.5995,20,360,81920,True,2
-DIJKSTRA,20,20,10,False,0.4575,39,360,126976,True,0
-DIJKSTRA,20,20,10,False,0.4468,39,359,114688,True,1
-DIJKSTRA,20,20,10,False,0.4462,39,361,32768,True,2
-DIJKSTRA,20,20,30,True,0.4703,23,281,32768,True,0
-DIJKSTRA,20,20,30,True,0.4805,25,281,32768,True,1
-DIJKSTRA,20,20,30,True,0.4821,24,281,61440,True,2
-DIJKSTRA,20,20,30,False,0.3919,39,282,65536,True,0
-DIJKSTRA,20,20,30,False,0.3812,41,280,61440,True,1
-DIJKSTRA,20,20,30,False,0.3457,39,281,61440,True,2
-DIJKSTRA,20,20,50,True,0.2829,44,197,28672,True,0
-DIJKSTRA,20,20,50,True,0.2141,58,144,20480,True,1
-DIJKSTRA,20,20,50,True,0.2861,66,192,32768,True,2
-DIJKSTRA,20,20,50,False,0.2427,59,189,28672,True,0
-DIJKSTRA,20,20,50,False,0.1485,51,115,24576,True,1
-DIJKSTRA,20,20,50,False,0.1985,91,159,45056,True,2
-DIJKSTRA,35,35,10,True,2.1114,37,1103,327680,True,0
-DIJKSTRA,35,35,10,True,2.0576,37,1103,94208,True,1
-DIJKSTRA,35,35,10,True,2.1407,37,1104,94208,True,2
-DIJKSTRA,35,35,10,False,1.5362,69,1103,253952,True,0
-DIJKSTRA,35,35,10,False,1.4893,69,1103,204800,True,1
-DIJKSTRA,35,35,10,False,1.4763,69,1103,200704,True,2
-DIJKSTRA,35,35,30,True,1.5209,42,858,163840,True,0
-DIJKSTRA,35,35,30,True,1.435,42,857,155648,True,1
-DIJKSTRA,35,35,30,True,1.4548,40,857,159744,True,2
-DIJKSTRA,35,35,30,False,1.129,69,856,253952,True,0
-DIJKSTRA,35,35,30,False,1.1949,69,855,253952,True,1
-DIJKSTRA,35,35,30,False,1.1977,69,856,294912,True,2
-DIJKSTRA,35,35,50,True,0.6041,136,400,28672,True,0
-DIJKSTRA,35,35,50,True,0.6758,145,487,28672,True,1
-DIJKSTRA,35,35,50,True,0.6081,137,424,28672,True,2
-DIJKSTRA,35,35,50,False,0.58,0,0,8192,False,0
-DIJKSTRA,35,35,50,False,0.4809,129,362,65536,True,1
-DIJKSTRA,35,35,50,False,0.7123,77,532,98304,True,2
-DIJKSTRA,50,50,10,True,4.5189,51,2250,409600,True,0
-DIJKSTRA,50,50,10,True,4.5321,53,2250,409600,True,1
-DIJKSTRA,50,50,10,True,4.3824,54,2251,573440,True,2
-DIJKSTRA,50,50,10,False,3.1707,99,2250,659456,True,0
-DIJKSTRA,50,50,10,False,3.3113,99,2250,663552,True,1
-DIJKSTRA,50,50,10,False,3.3981,99,2251,188416,True,2
-DIJKSTRA,50,50,30,True,3.2778,58,1750,258048,True,0
-DIJKSTRA,50,50,30,True,3.0818,58,1751,327680,True,1
-DIJKSTRA,50,50,30,True,3.1183,58,1750,323584,True,2
-DIJKSTRA,50,50,30,False,2.4624,99,1749,323584,True,0
-DIJKSTRA,50,50,30,False,2.5493,99,1751,323584,True,1
-DIJKSTRA,50,50,30,False,2.4891,99,1749,319488,True,2
-DIJKSTRA,50,50,50,True,1.2986,301,880,217088,True,0
-DIJKSTRA,50,50,50,True,1.6238,194,1187,344064,True,1
-DIJKSTRA,50,50,50,True,2.5347,174,565,184320,True,2
-DIJKSTRA,50,50,50,False,1.2548,271,896,73728,True,0
-DIJKSTRA,50,50,50,False,1.3304,409,934,90112,True,1
-DIJKSTRA,50,50,50,False,0.7657,357,612,122880,True,2
-GREEDY,20,20,10,True,0.1293,22,22,8192,True,0
-GREEDY,20,20,10,True,0.1508,21,21,8192,True,1
-GREEDY,20,20,10,True,0.1445,21,21,4096,True,2
-GREEDY,20,20,10,False,0.1191,39,39,4096,True,0
-GREEDY,20,20,10,False,0.1347,41,41,4096,True,1
-GREEDY,20,20,10,False,0.1692,43,46,12288,True,2
-GREEDY,20,20,30,True,0.1515,25,27,4096,True,0
-GREEDY,20,20,30,True,0.1471,25,25,8192,True,1
-GREEDY,20,20,30,True,0.1325,28,28,8192,True,2
-GREEDY,20,20,30,False,0.166,43,46,0,True,0
-GREEDY,20,20,30,False,0.1449,39,45,0,True,1
-GREEDY,20,20,30,False,0.1382,41,45,0,True,2
-GREEDY,20,20,50,True,0.2456,47,95,0,True,0
-GREEDY,20,20,50,True,0.3083,60,119,12288,True,1
-GREEDY,20,20,50,True,0.2403,60,80,8192,True,2
-GREEDY,20,20,50,False,0.2485,67,115,12288,True,0
-GREEDY,20,20,50,False,0.173,59,91,20480,True,1
-GREEDY,20,20,50,False,0.3466,79,193,20480,True,2
-GREEDY,35,35,10,True,0.2559,39,39,16384,True,0
-GREEDY,35,35,10,True,0.2487,38,38,16384,True,1
-GREEDY,35,35,10,True,0.2573,40,40,24576,True,2
-GREEDY,35,35,10,False,0.3217,83,95,20480,True,0
-GREEDY,35,35,10,False,0.6061,69,69,32768,True,1
-GREEDY,35,35,10,False,0.281,69,69,4096,True,2
-GREEDY,35,35,30,True,0.2408,43,45,8192,True,0
-GREEDY,35,35,30,True,0.2331,43,43,4096,True,1
-GREEDY,35,35,30,True,0.2392,42,42,12288,True,2
-GREEDY,35,35,30,False,0.225,71,77,8192,True,0
-GREEDY,35,35,30,False,0.2613,83,83,12288,True,1
-GREEDY,35,35,30,False,0.23,71,71,8192,True,2
-GREEDY,35,35,50,True,0.9538,91,365,65536,True,0
-GREEDY,35,35,50,True,0.9843,147,364,81920,True,1
-GREEDY,35,35,50,True,0.7238,197,300,94208,True,2
-GREEDY,35,35,50,False,0.3991,147,194,28672,True,0
-GREEDY,35,35,50,False,0.4047,117,211,49152,True,1
-GREEDY,35,35,50,False,0.3874,183,196,4096,True,2
-GREEDY,50,50,10,True,0.3848,57,58,16384,True,0
-GREEDY,50,50,10,True,0.3706,58,58,20480,True,1
-GREEDY,50,50,10,True,0.3754,54,54,24576,True,2
-GREEDY,50,50,10,False,0.3671,99,99,28672,True,0
-GREEDY,50,50,10,False,0.3749,99,99,12288,True,1
-GREEDY,50,50,10,False,0.3661,101,102,24576,True,2
-GREEDY,50,50,30,True,0.3392,63,63,24576,True,0
-GREEDY,50,50,30,True,0.3247,64,64,20480,True,1
-GREEDY,50,50,30,True,0.3234,68,69,24576,True,2
-GREEDY,50,50,30,False,0.4684,123,147,8192,True,0
-GREEDY,50,50,30,False,0.3809,107,108,4096,True,1
-GREEDY,50,50,30,False,0.3617,101,109,4096,True,2
-GREEDY,50,50,50,True,0.7341,208,268,45056,True,0
-GREEDY,50,50,50,True,0.849,172,316,65536,True,1
-GREEDY,50,50,50,True,0.6744,168,222,32768,True,2
-GREEDY,50,50,50,False,1.0245,211,568,98304,True,0
-GREEDY,50,50,50,False,1.2286,507,721,143360,True,1
-GREEDY,50,50,50,False,1.2725,419,670,172032,True,2
-BACKTRACKING,20,20,10,True,0.3594,121,131,69632,True,0
-BACKTRACKING,20,20,10,True,0.3929,111,164,61440,True,1
-BACKTRACKING,20,20,10,True,0.2945,104,116,65536,True,2
-BACKTRACKING,20,20,10,False,0.4242,183,241,28672,True,0
-BACKTRACKING,20,20,10,False,0.3568,187,205,28672,True,1
-BACKTRACKING,20,20,10,False,0.3895,177,221,28672,True,2
-BACKTRACKING,20,20,30,True,0.3235,75,129,28672,True,0
-BACKTRACKING,20,20,30,True,0.3471,77,167,36864,True,1
-BACKTRACKING,20,20,30,True,0.3286,92,140,36864,True,2
-BACKTRACKING,20,20,30,False,0.3422,147,203,45056,True,0
-BACKTRACKING,20,20,30,False,0.2824,137,191,32768,True,1
-BACKTRACKING,20,20,30,False,0.3286,157,193,36864,True,2
-BACKTRACKING,20,20,50,True,0.2677,30,137,28672,True,0
-BACKTRACKING,20,20,50,True,0.2214,37,130,24576,True,1
-BACKTRACKING,20,20,50,True,0.1721,42,104,20480,True,2
-BACKTRACKING,20,20,50,False,0.1367,71,124,36864,True,0
-BACKTRACKING,20,20,50,False,0.2432,79,179,8192,True,1
-BACKTRACKING,20,20,50,False,0.2324,43,186,8192,True,2
-BACKTRACKING,35,35,10,True,1.465,328,401,106496,True,0
-BACKTRACKING,35,35,10,True,1.3811,323,409,188416,True,1
-BACKTRACKING,35,35,10,True,1.1492,285,349,147456,True,2
-BACKTRACKING,35,35,10,False,1.4213,521,635,188416,True,0
-BACKTRACKING,35,35,10,False,1.4778,527,680,188416,True,1
-BACKTRACKING,35,35,10,False,1.4704,573,649,184320,True,2
-BACKTRACKING,35,35,30,True,1.1103,290,408,110592,True,0
-BACKTRACKING,35,35,30,True,1.0674,241,456,208896,True,1
-BACKTRACKING,35,35,30,True,1.1679,270,445,212992,True,2
-BACKTRACKING,35,35,30,False,0.931,429,556,245760,True,0
-BACKTRACKING,35,35,30,False,1.0727,403,561,69632,True,1
-BACKTRACKING,35,35,30,False,1.5718,401,618,69632,True,2
-BACKTRACKING,35,35,50,True,0.6532,77,428,24576,True,0
-BACKTRACKING,35,35,50,True,0.2979,79,179,28672,True,1
-BACKTRACKING,35,35,50,True,0.4046,125,241,40960,True,2
-BACKTRACKING,35,35,50,False,0.3571,0,0,4096,False,0
-BACKTRACKING,35,35,50,False,0.652,193,506,102400,True,1
-BACKTRACKING,35,35,50,False,0.3799,0,0,16384,False,2
-BACKTRACKING,50,50,10,True,3.1519,636,758,438272,True,0
-BACKTRACKING,50,50,10,True,2.5557,565,614,495616,True,1
-BACKTRACKING,50,50,10,True,3.3404,651,845,610304,True,2
-BACKTRACKING,50,50,10,False,3.7901,1137,1239,241664,True,0
-BACKTRACKING,50,50,10,False,3.601,1099,1299,200704,True,1
-BACKTRACKING,50,50,10,False,3.2003,967,1197,319488,True,2
-BACKTRACKING,50,50,30,True,2.6415,593,733,212992,True,0
-BACKTRACKING,50,50,30,True,2.1968,520,708,217088,True,1
-BACKTRACKING,50,50,30,True,2.4687,557,787,225280,True,2
-BACKTRACKING,50,50,30,False,2.5318,703,1337,294912,True,0
-BACKTRACKING,50,50,30,False,2.1927,687,1132,430080,True,1
-BACKTRACKING,50,50,30,False,2.2509,821,1179,438272,True,2
-BACKTRACKING,50,50,50,True,1.2183,145,849,262144,True,0
-BACKTRACKING,50,50,50,True,0.8606,234,570,204800,True,1
-BACKTRACKING,50,50,50,True,1.8035,150,1043,131072,True,2
-BACKTRACKING,50,50,50,False,1.2391,655,933,98304,True,0
-BACKTRACKING,50,50,50,False,0.9434,335,763,131072,True,1
-BACKTRACKING,50,50,50,False,0.7697,195,577,114688,True,2
-BFS,20,20,10,True,0.4677,20,360,61440,True,0
-BFS,20,20,10,True,0.621,21,361,77824,True,1
-BFS,20,20,10,True,0.5292,21,361,65536,True,2
-BFS,20,20,10,False,0.3891,39,361,65536,True,0
-BFS,20,20,10,False,0.4235,39,361,65536,True,1
-BFS,20,20,10,False,0.4674,39,360,90112,True,2
-BFS,20,20,30,True,0.3917,25,282,73728,True,0
-BFS,20,20,30,True,0.3715,26,281,86016,True,1
-BFS,20,20,30,True,0.449,23,282,24576,True,2
-BFS,20,20,30,False,0.3117,39,255,16384,True,0
-BFS,20,20,30,False,0.3499,39,281,24576,True,1
-BFS,20,20,30,False,0.3472,39,282,57344,True,2
-BFS,20,20,50,True,0.2092,42,144,20480,True,0
-BFS,20,20,50,True,0.2498,41,161,32768,True,1
-BFS,20,20,50,True,0.2399,38,163,24576,True,2
-BFS,20,20,50,False,0.2321,59,155,24576,True,0
-BFS,20,20,50,False,0.2403,71,162,24576,True,1
-BFS,20,20,50,False,0.1412,51,100,16384,True,2
-BFS,35,35,10,True,1.679,38,1104,233472,True,0
-BFS,35,35,10,True,1.6694,36,1103,294912,True,1
-BFS,35,35,10,True,1.7347,39,1104,425984,True,2
-BFS,35,35,10,False,1.4652,69,1103,118784,True,0
-BFS,35,35,10,False,1.3521,69,1103,118784,True,1
-BFS,35,35,10,False,1.3525,69,1103,221184,True,2
-BFS,35,35,30,True,1.2777,40,855,139264,True,0
-BFS,35,35,30,True,1.2761,43,857,143360,True,1
-BFS,35,35,30,True,1.3204,43,857,147456,True,2
-BFS,35,35,30,False,1.1577,69,857,176128,True,0
-BFS,35,35,30,False,1.1088,69,854,143360,True,1
-BFS,35,35,30,False,1.1091,69,857,188416,True,2
-BFS,35,35,50,True,0.6461,53,490,155648,True,0
-BFS,35,35,50,True,0.6363,88,510,155648,True,1
-BFS,35,35,50,True,0.4843,100,350,110592,True,2
-BFS,35,35,50,False,0.9047,79,544,57344,True,0
-BFS,35,35,50,False,0.3727,0,0,4096,False,1
-BFS,35,35,50,False,0.4923,0,0,8192,False,2
-BFS,50,50,10,True,3.7701,53,2250,581632,True,0
-BFS,50,50,10,True,3.4752,52,2250,462848,True,1
-BFS,50,50,10,True,3.5727,52,2250,458752,True,2
-BFS,50,50,10,False,2.7849,99,2251,471040,True,0
-BFS,50,50,10,False,2.7301,99,2251,598016,True,1
-BFS,50,50,10,False,2.6218,99,2251,716800,True,2
-BFS,50,50,30,True,2.346,62,1751,491520,True,0
-BFS,50,50,30,True,2.3452,58,1750,118784,True,1
-BFS,50,50,30,True,2.6295,58,1751,118784,True,2
-BFS,50,50,30,False,2.2686,99,1751,290816,True,0
-BFS,50,50,30,False,2.1289,99,1751,286720,True,1
-BFS,50,50,30,False,2.2602,99,1751,294912,True,2
-BFS,50,50,50,True,1.0863,240,773,139264,True,0
-BFS,50,50,50,True,0.8318,174,592,118784,True,1
-BFS,50,50,50,True,0.7613,160,535,110592,True,2
-BFS,50,50,50,False,1.3722,487,1120,282624,True,0
-BFS,50,50,50,False,0.8369,227,677,200704,True,1
-BFS,50,50,50,False,1.3386,223,996,311296,True,2
diff --git a/Assets/Scripts/pathfinding_tests.csv.meta b/Assets/Scripts/pathfinding_tests.csv.meta
deleted file mode 100644
index ae7f4ef..0000000
--- a/Assets/Scripts/pathfinding_tests.csv.meta
+++ /dev/null
@@ -1,7 +0,0 @@
-fileFormatVersion: 2
-guid: b5f5e061be1cbf946b769794cb0ba604
-TextScriptImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant: