mirror of
https://github.com/BobbyRafael31/Unity-MazeRunner-Pathfinding-Visualizer.git
synced 2025-08-12 08:42:21 +00:00
Fix: Clean up code
Removing unused comment, refactor and remove some unused script
This commit is contained in:
@ -2,12 +2,16 @@ using UnityEngine;
|
|||||||
|
|
||||||
public class FrameLimiter : MonoBehaviour
|
public class FrameLimiter : MonoBehaviour
|
||||||
{
|
{
|
||||||
// Start is called before the first frame update
|
/// <summary>
|
||||||
|
/// Melimit frame rate game ke 60 FPS
|
||||||
|
/// Limit dilakukan agar kalkulasi CPU yang digunakan sesuai dengan target FPS
|
||||||
|
/// </summary>
|
||||||
|
|
||||||
[SerializeField] private int frameRate = 60;
|
[SerializeField] private int frameRate = 60;
|
||||||
|
private int vSyncValue = 0;
|
||||||
void Start()
|
void Start()
|
||||||
{
|
{
|
||||||
// Set the target frame rate to 60
|
QualitySettings.vSyncCount = vSyncValue;
|
||||||
QualitySettings.vSyncCount = 0;
|
|
||||||
Application.targetFrameRate = frameRate;
|
Application.targetFrameRate = frameRate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -9,42 +9,18 @@ using UnityEngine;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class GridNode : PathFinding.Node<Vector2Int>
|
public class GridNode : PathFinding.Node<Vector2Int>
|
||||||
{
|
{
|
||||||
/// <summary>
|
public bool IsWalkable { get; set; }
|
||||||
/// Menentukan apakah node ini dapat dilalui oleh karakter.
|
|
||||||
/// True jika node dapat dilalui, false jika node adalah penghalang.
|
|
||||||
/// </summary>
|
|
||||||
public bool IsWalkable { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
internal GridMap gridMap;
|
||||||
/// Referensi ke GridMap yang mengelola seluruh grid.
|
|
||||||
/// Digunakan untuk mendapatkan tetangga dan operasi lain yang berhubungan dengan grid.
|
|
||||||
/// </summary>
|
|
||||||
public GridMap gridMap; // Change to internal or public
|
|
||||||
|
|
||||||
/// <summary>
|
public GridNode(Vector2Int value, GridMap gridMap) : base(value)
|
||||||
/// Constructor untuk membuat GridNode baru.
|
{
|
||||||
/// </summary>
|
IsWalkable = true;
|
||||||
/// <param name="value">Koordinat Vector2Int yang merepresentasikan posisi node di dalam grid</param>
|
this.gridMap = gridMap;
|
||||||
/// <param name="gridMap">Referensi ke GridMap yang mengelola grid ini</param>
|
}
|
||||||
public GridNode(Vector2Int value, GridMap gridMap)
|
|
||||||
: base(value)
|
|
||||||
{
|
|
||||||
IsWalkable = true; // Secara default node dapat dilalui
|
|
||||||
this.gridMap = gridMap; // Simpan referensi ke GridMap
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
public override List<PathFinding.Node<Vector2Int>> GetNeighbours()
|
||||||
/// 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.
|
return gridMap.GetNeighbours(this);
|
||||||
/// </summary>
|
}
|
||||||
/// <returns>Daftar node tetangga yang dapat dicapai dari node ini</returns>
|
|
||||||
public override
|
|
||||||
List<PathFinding.Node<Vector2Int>> GetNeighbours()
|
|
||||||
{
|
|
||||||
// Return an empty list for now.
|
|
||||||
// Later we will call gridMap's GetNeighbours
|
|
||||||
// function.
|
|
||||||
//return new List<PathFinding.Node<Vector2Int>>();
|
|
||||||
return gridMap.GetNeighbours(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -6,36 +6,19 @@ using UnityEngine;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class GridNodeView : MonoBehaviour
|
public class GridNodeView : MonoBehaviour
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Referensi ke SpriteRenderer untuk bagian dalam node.
|
|
||||||
/// </summary>
|
|
||||||
[SerializeField]
|
[SerializeField]
|
||||||
SpriteRenderer innerSprite;
|
SpriteRenderer innerSprite;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Referensi ke SpriteRenderer untuk bagian luar node.
|
|
||||||
/// </summary>
|
|
||||||
[SerializeField]
|
[SerializeField]
|
||||||
SpriteRenderer outerSprite;
|
SpriteRenderer outerSprite;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Properti yang menyimpan referensi ke objek GridNode yang terkait dengan view ini.
|
|
||||||
/// </summary>
|
|
||||||
public GridNode Node { get; set; }
|
public GridNode Node { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Mengatur warna sprite bagian dalam dari node.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="col">Warna yang akan diaplikasikan pada sprite bagian dalam.</param>
|
|
||||||
public void SetInnerColor(Color col)
|
public void SetInnerColor(Color col)
|
||||||
{
|
{
|
||||||
innerSprite.color = col;
|
innerSprite.color = col;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Mengatur warna sprite bagian luar dari node.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="col">Warna yang akan diaplikasikan pada sprite bagian luar.</param>
|
|
||||||
public void SetOuterColor(Color col)
|
public void SetOuterColor(Color col)
|
||||||
{
|
{
|
||||||
outerSprite.color = col;
|
outerSprite.color = col;
|
||||||
|
@ -5,19 +5,20 @@ using System.Collections.Generic;
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Metrik yang digunakan untuk mengukur performa pathfinding.
|
||||||
|
/// Dibuat dalam bentuk struct untuk efisiensi memori dan kemudahan penggunaan.
|
||||||
|
/// </summary>
|
||||||
public struct PathfindingMetrics
|
public struct PathfindingMetrics
|
||||||
{
|
{
|
||||||
// Untuk Pengukuran Kinerja
|
public float timeTaken; // miliseconds
|
||||||
public float timeTaken; // in milliseconds
|
|
||||||
public int pathLength;
|
public int pathLength;
|
||||||
public int nodesExplored; // number of nodes in path
|
public int nodesExplored;
|
||||||
public long memoryUsed; // memory used by pathfinding in bytes
|
public long memoryUsed; // bytes
|
||||||
|
|
||||||
// Untuk Visualisasi
|
public int maxOpenListSize;
|
||||||
public int maxOpenListSize; // maximum size of open list during pathfinding
|
public int maxClosedListSize;
|
||||||
public int maxClosedListSize; // maximum size of closed list during pathfinding
|
|
||||||
|
|
||||||
// Tambahan untuk cost metrics
|
|
||||||
public float totalGCost; // Total biaya G untuk jalur (jarak sebenarnya)
|
public float totalGCost; // Total biaya G untuk jalur (jarak sebenarnya)
|
||||||
public float totalHCost; // Total biaya H untuk jalur (heuristik)
|
public float totalHCost; // Total biaya H untuk jalur (heuristik)
|
||||||
public float totalFCost; // Total biaya F untuk jalur (G + H)
|
public float totalFCost; // Total biaya F untuk jalur (G + H)
|
||||||
@ -31,16 +32,10 @@ public class NPC : MonoBehaviour
|
|||||||
{
|
{
|
||||||
public float speed = 2.0f;
|
public float speed = 2.0f;
|
||||||
public Queue<Vector2> wayPoints = new Queue<Vector2>();
|
public Queue<Vector2> wayPoints = new Queue<Vector2>();
|
||||||
|
|
||||||
// Event that fires when pathfinding is complete with performance metrics
|
|
||||||
public event Action<PathfindingMetrics> OnPathfindingComplete;
|
|
||||||
|
|
||||||
// Last measured memory usage (for accessing from outside)
|
public event Action<PathfindingMetrics> OnPathfindingComplete;
|
||||||
public long LastMeasuredMemoryUsage { get; private set; } = 0;
|
public long LastMeasuredMemoryUsage { get; private set; } = 0;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Enumerasi yang merepresentasikan berbagai algoritma pathfinding yang tersedia.
|
|
||||||
/// </summary>
|
|
||||||
public enum PathFinderType
|
public enum PathFinderType
|
||||||
{
|
{
|
||||||
ASTAR,
|
ASTAR,
|
||||||
@ -57,36 +52,21 @@ public class NPC : MonoBehaviour
|
|||||||
|
|
||||||
public GridMap Map { get; set; }
|
public GridMap Map { get; set; }
|
||||||
|
|
||||||
// List to store all steps for visualization playback
|
|
||||||
private List<PathfindingVisualizationStep> visualizationSteps = new List<PathfindingVisualizationStep>();
|
private List<PathfindingVisualizationStep> visualizationSteps = new List<PathfindingVisualizationStep>();
|
||||||
private bool isVisualizingPath = false;
|
private bool isVisualizingPath = false;
|
||||||
private bool isMoving = false;
|
private bool isMoving = false;
|
||||||
|
|
||||||
// Public accessor for visualization state
|
|
||||||
public bool IsVisualizingPath => isVisualizingPath;
|
public bool IsVisualizingPath => isVisualizingPath;
|
||||||
|
|
||||||
// Public accessor for movement state
|
|
||||||
public bool IsMoving => isMoving;
|
public bool IsMoving => isMoving;
|
||||||
|
|
||||||
// Event that fires when visualization is complete
|
|
||||||
public event Action OnVisualizationComplete;
|
public event Action OnVisualizationComplete;
|
||||||
|
|
||||||
// Event that fires when movement is complete
|
|
||||||
public event Action OnMovementComplete;
|
public event Action OnMovementComplete;
|
||||||
|
|
||||||
// Properties to control visualization
|
public float visualizationSpeed = 0.0f;
|
||||||
[SerializeField]
|
public int visualizationBatch = 1;
|
||||||
|
|
||||||
// Visualization speed is time between visualization steps
|
public bool showVisualization = true;
|
||||||
public float visualizationSpeed = 0.0f; // Default 0; set higher for slower visualization
|
|
||||||
|
|
||||||
// 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
|
private struct PathfindingVisualizationStep
|
||||||
{
|
{
|
||||||
public enum StepType { CurrentNode, OpenList, ClosedList, FinalPath }
|
public enum StepType { CurrentNode, OpenList, ClosedList, FinalPath }
|
||||||
@ -132,27 +112,25 @@ public class NPC : MonoBehaviour
|
|||||||
{
|
{
|
||||||
while (wayPoints.Count > 0)
|
while (wayPoints.Count > 0)
|
||||||
{
|
{
|
||||||
// Set the moving flag when starting to move
|
|
||||||
if (!isMoving && wayPoints.Count > 0)
|
if (!isMoving && wayPoints.Count > 0)
|
||||||
{
|
{
|
||||||
isMoving = true;
|
isMoving = true;
|
||||||
UnityEngine.Debug.Log("NPC movement started");
|
UnityEngine.Debug.Log("NPC movement started");
|
||||||
}
|
}
|
||||||
|
|
||||||
yield return StartCoroutine(
|
yield return StartCoroutine(
|
||||||
Coroutine_MoveToPoint(
|
Coroutine_MoveToPoint(
|
||||||
wayPoints.Dequeue(),
|
wayPoints.Dequeue(),
|
||||||
speed));
|
speed));
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we were moving but now have no more waypoints, signal movement completion
|
|
||||||
if (isMoving && wayPoints.Count == 0)
|
if (isMoving && wayPoints.Count == 0)
|
||||||
{
|
{
|
||||||
isMoving = false;
|
isMoving = false;
|
||||||
UnityEngine.Debug.Log("NPC movement complete, invoking OnMovementComplete event");
|
UnityEngine.Debug.Log("NPC movement complete, invoking OnMovementComplete event");
|
||||||
OnMovementComplete?.Invoke();
|
OnMovementComplete?.Invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
yield return null;
|
yield return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -163,7 +141,6 @@ public class NPC : MonoBehaviour
|
|||||||
node.Value.x * Map.GridNodeWidth,
|
node.Value.x * Map.GridNodeWidth,
|
||||||
node.Value.y * Map.GridNodeHeight));
|
node.Value.y * Map.GridNodeHeight));
|
||||||
|
|
||||||
// We set a color to show the path.
|
|
||||||
GridNodeView gnv = Map.GetGridNodeView(node.Value.x, node.Value.y);
|
GridNodeView gnv = Map.GetGridNodeView(node.Value.x, node.Value.y);
|
||||||
gnv.SetInnerColor(Map.COLOR_PATH);
|
gnv.SetInnerColor(Map.COLOR_PATH);
|
||||||
}
|
}
|
||||||
@ -179,26 +156,20 @@ public class NPC : MonoBehaviour
|
|||||||
|
|
||||||
private void Start()
|
private void Start()
|
||||||
{
|
{
|
||||||
// Initialize pathfinder based on type
|
|
||||||
InitializePathFinder();
|
InitializePathFinder();
|
||||||
|
|
||||||
// Start the movement coroutine
|
|
||||||
StartCoroutine(Coroutine_MoveTo());
|
StartCoroutine(Coroutine_MoveTo());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializePathFinder()
|
private void InitializePathFinder()
|
||||||
{
|
{
|
||||||
// Hitung perkiraan jumlah node dalam grid
|
|
||||||
int estimatedNodeCount = 0;
|
int estimatedNodeCount = 0;
|
||||||
if (Map != null)
|
if (Map != null)
|
||||||
{
|
{
|
||||||
estimatedNodeCount = Map.NumX * Map.NumY;
|
estimatedNodeCount = Map.NumX * Map.NumY;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log informasi ukuran grid dan strategi optimisasi
|
|
||||||
bool isLargeGrid = estimatedNodeCount > 2500;
|
bool isLargeGrid = estimatedNodeCount > 2500;
|
||||||
|
|
||||||
// Create new pathfinder instance
|
|
||||||
switch (pathFinderType)
|
switch (pathFinderType)
|
||||||
{
|
{
|
||||||
case PathFinderType.ASTAR:
|
case PathFinderType.ASTAR:
|
||||||
@ -218,24 +189,20 @@ public class NPC : MonoBehaviour
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up callbacks
|
|
||||||
pathFinder.onSuccess = OnSuccessPathFinding;
|
pathFinder.onSuccess = OnSuccessPathFinding;
|
||||||
pathFinder.onFailure = OnFailurePathFinding;
|
pathFinder.onFailure = OnFailurePathFinding;
|
||||||
|
|
||||||
// Gunakan setting asli
|
|
||||||
pathFinder.HeuristicCost = GridMap.GetManhattanCost;
|
pathFinder.HeuristicCost = GridMap.GetManhattanCost;
|
||||||
pathFinder.NodeTraversalCost = GridMap.GetEuclideanCost;
|
pathFinder.NodeTraversalCost = GridMap.GetEuclideanCost;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void MoveTo(GridNode destination, bool silentMode = false)
|
public void MoveTo(GridNode destination, bool silentMode = false)
|
||||||
{
|
{
|
||||||
// inialisaasi pathfinder jika belum ada
|
|
||||||
if (pathFinder == null)
|
if (pathFinder == null)
|
||||||
{
|
{
|
||||||
InitializePathFinder();
|
InitializePathFinder();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (pathFinder.Status == PathFinderStatus.RUNNING)
|
if (pathFinder.Status == PathFinderStatus.RUNNING)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@ -252,7 +219,6 @@ public class NPC : MonoBehaviour
|
|||||||
|
|
||||||
SetStartNode(start);
|
SetStartNode(start);
|
||||||
|
|
||||||
// Reset grid colors
|
|
||||||
if (!silentMode)
|
if (!silentMode)
|
||||||
{
|
{
|
||||||
Map.ResetGridNodeColours();
|
Map.ResetGridNodeColours();
|
||||||
@ -261,7 +227,6 @@ public class NPC : MonoBehaviour
|
|||||||
visualizationSteps.Clear();
|
visualizationSteps.Clear();
|
||||||
isVisualizingPath = false;
|
isVisualizingPath = false;
|
||||||
|
|
||||||
// jika gagal menginisialisasi pathfinder, tidak perlu melanjutkan
|
|
||||||
if (!pathFinder.Initialise(start, destination))
|
if (!pathFinder.Initialise(start, destination))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@ -274,9 +239,7 @@ public class NPC : MonoBehaviour
|
|||||||
{
|
{
|
||||||
yield return StartCoroutine(MeasurePerformance(silentMode));
|
yield return StartCoroutine(MeasurePerformance(silentMode));
|
||||||
|
|
||||||
// Start visualization after calculation is complete regardless of success or failure
|
if (showVisualization && !silentMode &&
|
||||||
// This allows visualization of the explored area even when no path is found
|
|
||||||
if (showVisualization && !silentMode &&
|
|
||||||
(pathFinder.Status == PathFinderStatus.SUCCESS || pathFinder.Status == PathFinderStatus.FAILURE))
|
(pathFinder.Status == PathFinderStatus.SUCCESS || pathFinder.Status == PathFinderStatus.FAILURE))
|
||||||
{
|
{
|
||||||
yield return StartCoroutine(VisualizePathfinding());
|
yield return StartCoroutine(VisualizePathfinding());
|
||||||
@ -285,71 +248,54 @@ public class NPC : MonoBehaviour
|
|||||||
|
|
||||||
IEnumerator MeasurePerformance(bool silentMode = false)
|
IEnumerator MeasurePerformance(bool silentMode = false)
|
||||||
{
|
{
|
||||||
// Memory tracking for pathfinding structures - tetap untuk visualisasi
|
|
||||||
int maxOpenListSize = 0;
|
int maxOpenListSize = 0;
|
||||||
int currentOpenListSize = 0;
|
int currentOpenListSize = 0;
|
||||||
int maxClosedListSize = 0;
|
int maxClosedListSize = 0;
|
||||||
int currentClosedListSize = 0;
|
int currentClosedListSize = 0;
|
||||||
|
|
||||||
// Pre-allocate visualizationSteps with estimated capacity to avoid reallocations
|
|
||||||
visualizationSteps = new List<PathfindingVisualizationStep>(4);
|
visualizationSteps = new List<PathfindingVisualizationStep>(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);
|
long memoryBefore = System.GC.GetTotalMemory(false);
|
||||||
|
|
||||||
// Setup callbacks before running algorithm
|
|
||||||
SetupCallbacks(silentMode, ref maxOpenListSize, ref currentOpenListSize,
|
SetupCallbacks(silentMode, ref maxOpenListSize, ref currentOpenListSize,
|
||||||
ref maxClosedListSize, ref currentClosedListSize);
|
ref maxClosedListSize, ref currentClosedListSize);
|
||||||
|
|
||||||
// ===== STOPWATCH START: Pengukuran waktu algoritma =====
|
|
||||||
Stopwatch algorithmTimer = Stopwatch.StartNew();
|
Stopwatch algorithmTimer = Stopwatch.StartNew();
|
||||||
|
|
||||||
// Counter untuk jumlah step yang dilakukan algoritma
|
|
||||||
int stepCount = 0;
|
int stepCount = 0;
|
||||||
|
|
||||||
// Execute the pathfinding algorithm synchronously in a single frame without visualization
|
|
||||||
while (pathFinder.Status == PathFinderStatus.RUNNING)
|
while (pathFinder.Status == PathFinderStatus.RUNNING)
|
||||||
{
|
{
|
||||||
stepCount++;
|
stepCount++;
|
||||||
pathFinder.Step();
|
pathFinder.Step();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== STOPWATCH STOP: Akhir pengukuran waktu algoritma =====
|
|
||||||
algorithmTimer.Stop();
|
algorithmTimer.Stop();
|
||||||
|
|
||||||
// ===== MEMORY MEASUREMENT END: Ukur memory setelah algoritma =====
|
|
||||||
long memoryAfter = System.GC.GetTotalMemory(false);
|
long memoryAfter = System.GC.GetTotalMemory(false);
|
||||||
long memoryUsed = memoryAfter - memoryBefore;
|
long memoryUsed = memoryAfter - memoryBefore;
|
||||||
|
|
||||||
// Store the memory usage for external access
|
|
||||||
LastMeasuredMemoryUsage = memoryUsed > 0 ? memoryUsed : 1024;
|
LastMeasuredMemoryUsage = memoryUsed > 0 ? memoryUsed : 1024;
|
||||||
|
|
||||||
float milliseconds = (algorithmTimer.ElapsedTicks * 1000.0f) / Stopwatch.Frequency;
|
float milliseconds = (algorithmTimer.ElapsedTicks * 1000.0f) / Stopwatch.Frequency;
|
||||||
|
|
||||||
// Calculate path length once and reuse
|
|
||||||
int pathLength = 0;
|
int pathLength = 0;
|
||||||
int nodesExplored = 0;
|
int nodesExplored = 0;
|
||||||
float totalGCost = 0;
|
float totalGCost = 0;
|
||||||
float totalHCost = 0;
|
float totalHCost = 0;
|
||||||
float totalFCost = 0;
|
float totalFCost = 0;
|
||||||
|
|
||||||
// Add memory for path reconstruction (final path)
|
|
||||||
if (pathFinder.Status == PathFinderStatus.SUCCESS)
|
if (pathFinder.Status == PathFinderStatus.SUCCESS)
|
||||||
{
|
{
|
||||||
pathLength = CalculatePathLength();
|
pathLength = CalculatePathLength();
|
||||||
nodesExplored = pathFinder.ClosedListCount;
|
nodesExplored = pathFinder.ClosedListCount;
|
||||||
|
|
||||||
// Hitung total G, H, dan F cost
|
|
||||||
CalculatePathCosts(out totalGCost, out totalHCost, out totalFCost);
|
CalculatePathCosts(out totalGCost, out totalHCost, out totalFCost);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create and send metrics - waktu pengukuran algoritma yang tepat
|
|
||||||
PathfindingMetrics metrics = new PathfindingMetrics
|
PathfindingMetrics metrics = new PathfindingMetrics
|
||||||
{
|
{
|
||||||
timeTaken = milliseconds, // Waktu algoritma yang diukur dengan stopwatch
|
timeTaken = milliseconds,
|
||||||
pathLength = pathLength,
|
pathLength = pathLength,
|
||||||
nodesExplored = nodesExplored,
|
nodesExplored = nodesExplored,
|
||||||
memoryUsed = memoryUsed,
|
memoryUsed = memoryUsed,
|
||||||
@ -360,25 +306,20 @@ public class NPC : MonoBehaviour
|
|||||||
totalFCost = totalFCost,
|
totalFCost = totalFCost,
|
||||||
};
|
};
|
||||||
|
|
||||||
// *** IMPORTANT FIX: Always invoke the event, regardless of silent mode ***
|
|
||||||
// Report metrics before visualization
|
|
||||||
OnPathfindingComplete?.Invoke(metrics);
|
OnPathfindingComplete?.Invoke(metrics);
|
||||||
|
|
||||||
// Path visualization and handling
|
|
||||||
HandlePathFindingResult(silentMode, pathLength);
|
HandlePathFindingResult(silentMode, pathLength);
|
||||||
|
|
||||||
// Pastikan untuk mengembalikan nilai di akhir coroutine
|
|
||||||
yield return null;
|
yield return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void SetupCallbacks(
|
||||||
/// <summary>
|
bool silentMode,
|
||||||
/// Setup callbacks for tracking nodes in open/closed lists and visualization
|
ref int maxOpenListSize,
|
||||||
/// </summary>
|
ref int currentOpenListSize,
|
||||||
private void SetupCallbacks(bool silentMode, ref int maxOpenListSize, ref int currentOpenListSize,
|
ref int maxClosedListSize,
|
||||||
ref int maxClosedListSize, ref int currentClosedListSize)
|
ref int currentClosedListSize)
|
||||||
{
|
{
|
||||||
// Buat variabel lokal untuk menghindari masalah dengan ref parameter dalam lambda
|
|
||||||
int localCurrentOpenListSize = currentOpenListSize;
|
int localCurrentOpenListSize = currentOpenListSize;
|
||||||
int localMaxOpenListSize = maxOpenListSize;
|
int localMaxOpenListSize = maxOpenListSize;
|
||||||
int localCurrentClosedListSize = currentClosedListSize;
|
int localCurrentClosedListSize = currentClosedListSize;
|
||||||
@ -386,7 +327,6 @@ public class NPC : MonoBehaviour
|
|||||||
|
|
||||||
if (silentMode)
|
if (silentMode)
|
||||||
{
|
{
|
||||||
// In silent mode, just set minimal callbacks for metrics
|
|
||||||
pathFinder.onAddToOpenList = (node) =>
|
pathFinder.onAddToOpenList = (node) =>
|
||||||
{
|
{
|
||||||
localCurrentOpenListSize++;
|
localCurrentOpenListSize++;
|
||||||
@ -399,12 +339,11 @@ public class NPC : MonoBehaviour
|
|||||||
localCurrentClosedListSize++;
|
localCurrentClosedListSize++;
|
||||||
if (localCurrentClosedListSize > localMaxClosedListSize)
|
if (localCurrentClosedListSize > localMaxClosedListSize)
|
||||||
localMaxClosedListSize = localCurrentClosedListSize;
|
localMaxClosedListSize = localCurrentClosedListSize;
|
||||||
localCurrentOpenListSize--; // When a node is moved from open to closed list
|
localCurrentOpenListSize--;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// In regular mode, track and prepare for visualization
|
|
||||||
pathFinder.onAddToOpenList = (node) =>
|
pathFinder.onAddToOpenList = (node) =>
|
||||||
{
|
{
|
||||||
visualizationSteps.Add(new PathfindingVisualizationStep(
|
visualizationSteps.Add(new PathfindingVisualizationStep(
|
||||||
@ -426,7 +365,7 @@ public class NPC : MonoBehaviour
|
|||||||
if (localCurrentClosedListSize > localMaxClosedListSize)
|
if (localCurrentClosedListSize > localMaxClosedListSize)
|
||||||
localMaxClosedListSize = localCurrentClosedListSize;
|
localMaxClosedListSize = localCurrentClosedListSize;
|
||||||
|
|
||||||
localCurrentOpenListSize--; // When a node is moved from open to closed list
|
localCurrentOpenListSize--;
|
||||||
};
|
};
|
||||||
|
|
||||||
pathFinder.onChangeCurrentNode = (node) =>
|
pathFinder.onChangeCurrentNode = (node) =>
|
||||||
@ -439,38 +378,29 @@ public class NPC : MonoBehaviour
|
|||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setelah lambda selesai dijalankan, perbarui variabel ref
|
|
||||||
maxOpenListSize = localMaxOpenListSize;
|
maxOpenListSize = localMaxOpenListSize;
|
||||||
currentOpenListSize = localCurrentOpenListSize;
|
currentOpenListSize = localCurrentOpenListSize;
|
||||||
maxClosedListSize = localMaxClosedListSize;
|
maxClosedListSize = localMaxClosedListSize;
|
||||||
currentClosedListSize = localCurrentClosedListSize;
|
currentClosedListSize = localCurrentClosedListSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Handle path finding result (success or failure)
|
|
||||||
/// </summary>
|
|
||||||
private void HandlePathFindingResult(bool silentMode, int pathLength)
|
private void HandlePathFindingResult(bool silentMode, int pathLength)
|
||||||
{
|
{
|
||||||
if (pathFinder.Status == PathFinderStatus.SUCCESS)
|
if (pathFinder.Status == PathFinderStatus.SUCCESS)
|
||||||
{
|
{
|
||||||
OnSuccessPathFinding();
|
OnSuccessPathFinding();
|
||||||
|
|
||||||
// In non-silent mode, prepare visualization data for the path
|
|
||||||
if (!silentMode && showVisualization)
|
if (!silentMode && showVisualization)
|
||||||
{
|
{
|
||||||
// Add the path nodes for visualization in efficient batched way
|
|
||||||
PathFinder<Vector2Int>.PathFinderNode node = pathFinder.CurrentNode;
|
PathFinder<Vector2Int>.PathFinderNode node = pathFinder.CurrentNode;
|
||||||
List<Vector2Int> pathPositions = new List<Vector2Int>(pathLength); // Pre-allocate with known size
|
List<Vector2Int> pathPositions = new List<Vector2Int>(pathLength);
|
||||||
|
|
||||||
// Build path in reverse order
|
|
||||||
while (node != null)
|
while (node != null)
|
||||||
{
|
{
|
||||||
pathPositions.Add(node.Location.Value);
|
pathPositions.Add(node.Location.Value);
|
||||||
node = node.Parent;
|
node = node.Parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process path in correct order
|
|
||||||
for (int i = pathPositions.Count - 1; i >= 0; i--)
|
for (int i = pathPositions.Count - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
visualizationSteps.Add(new PathfindingVisualizationStep(
|
visualizationSteps.Add(new PathfindingVisualizationStep(
|
||||||
@ -482,40 +412,19 @@ public class NPC : MonoBehaviour
|
|||||||
else if (pathFinder.Status == PathFinderStatus.FAILURE)
|
else if (pathFinder.Status == PathFinderStatus.FAILURE)
|
||||||
{
|
{
|
||||||
OnFailurePathFinding();
|
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");
|
UnityEngine.Debug.Log($"Pathfinding failed - visualization will show {visualizationSteps.Count} exploration steps");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Memformat ukuran byte menjadi string yang lebih mudah dibaca
|
|
||||||
/// </summary>
|
|
||||||
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()
|
void OnSuccessPathFinding()
|
||||||
{
|
{
|
||||||
float totalGCost = 0;
|
float totalGCost = 0;
|
||||||
float totalHCost = 0;
|
float totalHCost = 0;
|
||||||
float totalFCost = 0;
|
float totalFCost = 0;
|
||||||
|
|
||||||
// Hitung biaya-biaya path menggunakan metode yang sudah ada
|
|
||||||
CalculatePathCosts(out totalGCost, out totalHCost, out totalFCost);
|
CalculatePathCosts(out totalGCost, out totalHCost, out totalFCost);
|
||||||
|
|
||||||
// Informasi dasar
|
|
||||||
int pathLength = CalculatePathLength();
|
int pathLength = CalculatePathLength();
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -525,12 +434,8 @@ public class NPC : MonoBehaviour
|
|||||||
UnityEngine.Debug.Log("Pathfinding failed");
|
UnityEngine.Debug.Log("Pathfinding failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Changes the pathfinding algorithm at runtime
|
|
||||||
/// </summary>
|
|
||||||
public void ChangeAlgorithm(PathFinderType newType)
|
public void ChangeAlgorithm(PathFinderType newType)
|
||||||
{
|
{
|
||||||
// Don't change if pathfinding is in progress
|
|
||||||
if (pathFinder != null && pathFinder.Status == PathFinderStatus.RUNNING)
|
if (pathFinder != null && pathFinder.Status == PathFinderStatus.RUNNING)
|
||||||
{
|
{
|
||||||
UnityEngine.Debug.Log("Cannot change algorithm while pathfinding is running");
|
UnityEngine.Debug.Log("Cannot change algorithm while pathfinding is running");
|
||||||
@ -539,14 +444,12 @@ public class NPC : MonoBehaviour
|
|||||||
|
|
||||||
pathFinderType = newType;
|
pathFinderType = newType;
|
||||||
|
|
||||||
// Hitung perkiraan jumlah node dalam grid
|
|
||||||
int estimatedNodeCount = 0;
|
int estimatedNodeCount = 0;
|
||||||
if (Map != null)
|
if (Map != null)
|
||||||
{
|
{
|
||||||
estimatedNodeCount = Map.NumX * Map.NumY;
|
estimatedNodeCount = Map.NumX * Map.NumY;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create new pathfinder instance
|
|
||||||
switch (pathFinderType)
|
switch (pathFinderType)
|
||||||
{
|
{
|
||||||
case PathFinderType.ASTAR:
|
case PathFinderType.ASTAR:
|
||||||
@ -566,11 +469,9 @@ public class NPC : MonoBehaviour
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up callbacks
|
|
||||||
pathFinder.onSuccess = OnSuccessPathFinding;
|
pathFinder.onSuccess = OnSuccessPathFinding;
|
||||||
pathFinder.onFailure = OnFailurePathFinding;
|
pathFinder.onFailure = OnFailurePathFinding;
|
||||||
|
|
||||||
// Gunakan setting asli
|
|
||||||
pathFinder.HeuristicCost = GridMap.GetManhattanCost;
|
pathFinder.HeuristicCost = GridMap.GetManhattanCost;
|
||||||
pathFinder.NodeTraversalCost = GridMap.GetEuclideanCost;
|
pathFinder.NodeTraversalCost = GridMap.GetEuclideanCost;
|
||||||
}
|
}
|
||||||
@ -594,17 +495,14 @@ public class NPC : MonoBehaviour
|
|||||||
|
|
||||||
UnityEngine.Debug.Log("Path visualization starting");
|
UnityEngine.Debug.Log("Path visualization starting");
|
||||||
isVisualizingPath = true;
|
isVisualizingPath = true;
|
||||||
|
|
||||||
// First, ensure grid is reset
|
|
||||||
Map.ResetGridNodeColours();
|
Map.ResetGridNodeColours();
|
||||||
|
|
||||||
// Visualize each step with a delay - use batch processing for efficiency
|
|
||||||
int stepCount = visualizationSteps.Count;
|
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;
|
bool pathfindingFailed = pathFinder.Status == PathFinderStatus.FAILURE;
|
||||||
|
|
||||||
if (pathfindingFailed)
|
if (pathfindingFailed)
|
||||||
{
|
{
|
||||||
UnityEngine.Debug.Log($"Visualizing failed pathfinding attempt with {stepCount} steps");
|
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);
|
int end = Mathf.Min(i + batchSize, stepCount);
|
||||||
|
|
||||||
// Process a batch of steps
|
|
||||||
for (int j = i; j < end; j++)
|
for (int j = i; j < end; j++)
|
||||||
{
|
{
|
||||||
var step = visualizationSteps[j];
|
var step = visualizationSteps[j];
|
||||||
@ -634,7 +531,7 @@ public class NPC : MonoBehaviour
|
|||||||
break;
|
break;
|
||||||
case PathfindingVisualizationStep.StepType.FinalPath:
|
case PathfindingVisualizationStep.StepType.FinalPath:
|
||||||
gnv.SetInnerColor(Map.COLOR_PATH);
|
gnv.SetInnerColor(Map.COLOR_PATH);
|
||||||
// Only add waypoints for successful pathfinding
|
|
||||||
if (!pathfindingFailed)
|
if (!pathfindingFailed)
|
||||||
{
|
{
|
||||||
GridNode pathNode = Map.GetGridNode(step.position.x, step.position.y);
|
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);
|
yield return new WaitForSeconds(visualizationSpeed);
|
||||||
}
|
}
|
||||||
|
|
||||||
isVisualizingPath = false;
|
isVisualizingPath = false;
|
||||||
UnityEngine.Debug.Log("Path visualization complete, invoking OnVisualizationComplete event");
|
UnityEngine.Debug.Log("Path visualization complete, invoking OnVisualizationComplete event");
|
||||||
// Notify any listeners that visualization is complete
|
|
||||||
OnVisualizationComplete?.Invoke();
|
OnVisualizationComplete?.Invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Menghitung biaya G, H, dan F untuk jalur
|
|
||||||
/// </summary>
|
|
||||||
private void CalculatePathCosts(out float totalGCost, out float totalHCost, out float totalFCost)
|
private void CalculatePathCosts(out float totalGCost, out float totalHCost, out float totalFCost)
|
||||||
{
|
{
|
||||||
// Inisialisasi nilai awal
|
|
||||||
totalGCost = 0;
|
totalGCost = 0;
|
||||||
totalHCost = 0;
|
totalHCost = 0;
|
||||||
totalFCost = 0;
|
totalFCost = 0;
|
||||||
|
|
||||||
// Jika tidak ada path yang ditemukan, return nilai 0
|
|
||||||
if (pathFinder.CurrentNode == null)
|
if (pathFinder.CurrentNode == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Untuk algoritma yang menggunakan heuristik
|
|
||||||
bool usesHeuristic = pathFinderType == PathFinderType.ASTAR ||
|
bool usesHeuristic = pathFinderType == PathFinderType.ASTAR ||
|
||||||
pathFinderType == PathFinderType.GREEDY;
|
pathFinderType == PathFinderType.GREEDY;
|
||||||
|
|
||||||
// Node final berisi total cost jalur
|
|
||||||
PathFinder<Vector2Int>.PathFinderNode finalNode = pathFinder.CurrentNode;
|
PathFinder<Vector2Int>.PathFinderNode finalNode = pathFinder.CurrentNode;
|
||||||
|
|
||||||
// G cost adalah biaya sebenarnya dari start ke goal, sudah terakumulasi di node akhir
|
|
||||||
totalGCost = finalNode.GCost;
|
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)
|
if (usesHeuristic)
|
||||||
{
|
{
|
||||||
// H cost dari node awal ke tujuan (untuk referensi)
|
|
||||||
totalHCost = finalNode.HCost;
|
totalHCost = finalNode.HCost;
|
||||||
|
|
||||||
// F cost adalah G + H di node akhir
|
|
||||||
totalFCost = finalNode.FCost;
|
totalFCost = finalNode.FCost;
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Algoritma tanpa heuristik (seperti Dijkstra)
|
|
||||||
totalFCost = totalGCost;
|
totalFCost = totalGCost;
|
||||||
}
|
}
|
||||||
|
|
||||||
//// Hitung rata-rata biaya per langkah untuk analisis
|
|
||||||
//int pathLength = CalculatePathLength();
|
|
||||||
//float avgCostPerStep = pathLength > 0 ? totalGCost / pathLength : 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
File diff suppressed because it is too large
Load Diff
@ -1,419 +0,0 @@
|
|||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.UI;
|
|
||||||
using TMPro;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Utility class to analyze pathfinding test results and display statistics
|
|
||||||
/// </summary>
|
|
||||||
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<TestResult> allResults = new List<TestResult>();
|
|
||||||
private Dictionary<string, Color> algorithmColors = new Dictionary<string, Color>()
|
|
||||||
{
|
|
||||||
{ "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<string> {
|
|
||||||
"All Algorithms",
|
|
||||||
"A*",
|
|
||||||
"Dijkstra",
|
|
||||||
"Greedy BFS",
|
|
||||||
"Backtracking",
|
|
||||||
"BFS"
|
|
||||||
});
|
|
||||||
algorithmFilterDropdown.onValueChanged.AddListener(OnFilterChanged);
|
|
||||||
|
|
||||||
// Set up statistic type dropdown
|
|
||||||
statisticTypeDropdown.ClearOptions();
|
|
||||||
statisticTypeDropdown.AddOptions(new List<string> {
|
|
||||||
"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<TestResult> 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<TestResult> 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<TestResult> 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<TestResult> 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<TestResult> 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<TestResult> 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<string> labels, List<float> 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<RectTransform>();
|
|
||||||
Image barImage = barObj.GetComponent<Image>();
|
|
||||||
|
|
||||||
// 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<TMP_Text>();
|
|
||||||
labelText.text = $"{values[i]:F1}{unit}";
|
|
||||||
labelText.fontSize = 12;
|
|
||||||
labelText.alignment = TextAlignmentOptions.Center;
|
|
||||||
labelText.color = Color.black;
|
|
||||||
|
|
||||||
RectTransform labelRect = labelObj.GetComponent<RectTransform>();
|
|
||||||
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<TMP_Text>();
|
|
||||||
nameText.text = GetAlgorithmName(labels[i]);
|
|
||||||
nameText.fontSize = 10;
|
|
||||||
nameText.alignment = TextAlignmentOptions.Center;
|
|
||||||
nameText.color = Color.black;
|
|
||||||
|
|
||||||
RectTransform nameRect = nameObj.GetComponent<RectTransform>();
|
|
||||||
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]}";
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 9fee3f5c29ad21b428f68d801f2377f1
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
@ -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<PathfindingTestResult> testResults = new List<PathfindingTestResult>();
|
|
||||||
|
|
||||||
// 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<PathfindingMetrics> 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<Vector2Int> 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";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: ed03b279fa9f3194f8caed356ea838dc
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
@ -6,6 +6,11 @@ using UnityEngine.UI;
|
|||||||
using TMPro;
|
using TMPro;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// PathfindingUIManager merupakan kelas yang mengelola antarmuka pengguna untuk sistem pathfinding.
|
||||||
|
/// pengguna dapat mengatur ukuran grid, memilih algoritma pathfinding, menjalankan pathfinding.
|
||||||
|
/// </summary>
|
||||||
|
|
||||||
public class PathfindingUIManager : MonoBehaviour
|
public class PathfindingUIManager : MonoBehaviour
|
||||||
{
|
{
|
||||||
[Header("References")]
|
[Header("References")]
|
||||||
@ -22,12 +27,11 @@ public class PathfindingUIManager : MonoBehaviour
|
|||||||
public TMP_Dropdown algorithmDropdown;
|
public TMP_Dropdown algorithmDropdown;
|
||||||
public Button runPathfindingButton;
|
public Button runPathfindingButton;
|
||||||
public Button resetButton;
|
public Button resetButton;
|
||||||
public Toggle allowDiagonalToggle; // Toggle untuk diagonal movement
|
public Toggle allowDiagonalToggle;
|
||||||
|
|
||||||
[Header("Visualization Controls")]
|
[Header("Visualization Controls")]
|
||||||
public Slider visualizationSpeedSlider;
|
public Slider visualizationSpeedSlider;
|
||||||
public Slider visualizationBatchSlider;
|
public Slider visualizationBatchSlider;
|
||||||
// public Toggle showVisualizationToggle;
|
|
||||||
public TMP_Text speedValueText;
|
public TMP_Text speedValueText;
|
||||||
public TMP_Text batchValueText;
|
public TMP_Text batchValueText;
|
||||||
|
|
||||||
@ -36,7 +40,7 @@ public class PathfindingUIManager : MonoBehaviour
|
|||||||
public TMP_Text pathLengthText;
|
public TMP_Text pathLengthText;
|
||||||
public TMP_Text nodesExploredText;
|
public TMP_Text nodesExploredText;
|
||||||
public TMP_Text memoryUsageText;
|
public TMP_Text memoryUsageText;
|
||||||
public TMP_Text cpuUsageText; // Text untuk menampilkan penggunaan CPU
|
public TMP_Text cpuUsageText;
|
||||||
|
|
||||||
[Header("Map Save/Load")]
|
[Header("Map Save/Load")]
|
||||||
public TMP_InputField mapNameInput;
|
public TMP_InputField mapNameInput;
|
||||||
@ -44,7 +48,7 @@ public class PathfindingUIManager : MonoBehaviour
|
|||||||
public Button loadButton;
|
public Button loadButton;
|
||||||
|
|
||||||
[Header("Application Controls")]
|
[Header("Application Controls")]
|
||||||
public Button exitButton; // Tombol untuk keluar aplikasi
|
public Button exitButton;
|
||||||
|
|
||||||
[Header("Optimization")]
|
[Header("Optimization")]
|
||||||
[SerializeField] private bool performWarmup = true;
|
[SerializeField] private bool performWarmup = true;
|
||||||
@ -54,29 +58,16 @@ public class PathfindingUIManager : MonoBehaviour
|
|||||||
public TMP_Dropdown mazeSizeDropdown;
|
public TMP_Dropdown mazeSizeDropdown;
|
||||||
public TMP_Dropdown mazeDensityDropdown;
|
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
|
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;
|
private bool isPathfindingRunning = false;
|
||||||
|
|
||||||
// Add a flag to track if visualization is running
|
|
||||||
private bool isVisualizationRunning = false;
|
private bool isVisualizationRunning = false;
|
||||||
|
|
||||||
// Add a flag to track if NPC is moving
|
|
||||||
private bool isNpcMoving = false;
|
private bool isNpcMoving = false;
|
||||||
|
|
||||||
private void Start()
|
private void Start()
|
||||||
{
|
{
|
||||||
// Initialize UI elements
|
|
||||||
InitializeUI();
|
InitializeUI();
|
||||||
|
|
||||||
// Add listeners
|
|
||||||
applyGridSizeButton.onClick.AddListener(OnApplyGridSize);
|
applyGridSizeButton.onClick.AddListener(OnApplyGridSize);
|
||||||
runPathfindingButton.onClick.AddListener(OnRunPathfinding);
|
runPathfindingButton.onClick.AddListener(OnRunPathfinding);
|
||||||
resetButton.onClick.AddListener(OnResetPathfinding);
|
resetButton.onClick.AddListener(OnResetPathfinding);
|
||||||
@ -84,56 +75,38 @@ public class PathfindingUIManager : MonoBehaviour
|
|||||||
saveButton.onClick.AddListener(OnSaveMap);
|
saveButton.onClick.AddListener(OnSaveMap);
|
||||||
loadButton.onClick.AddListener(OnLoadMap);
|
loadButton.onClick.AddListener(OnLoadMap);
|
||||||
|
|
||||||
// Add exit button listener if the button exists
|
|
||||||
if (exitButton != null)
|
if (exitButton != null)
|
||||||
exitButton.onClick.AddListener(OnExitApplication);
|
exitButton.onClick.AddListener(OnExitApplication);
|
||||||
|
|
||||||
// Add listener for maze generator
|
|
||||||
if (generateMazeButton != null)
|
if (generateMazeButton != null)
|
||||||
generateMazeButton.onClick.AddListener(OnGenerateMaze);
|
generateMazeButton.onClick.AddListener(OnGenerateMaze);
|
||||||
|
|
||||||
// Add visualization control listeners
|
|
||||||
if (visualizationSpeedSlider != null)
|
if (visualizationSpeedSlider != null)
|
||||||
visualizationSpeedSlider.onValueChanged.AddListener(OnVisualizationSpeedChanged);
|
visualizationSpeedSlider.onValueChanged.AddListener(OnVisualizationSpeedChanged);
|
||||||
|
|
||||||
if (visualizationBatchSlider != null)
|
if (visualizationBatchSlider != null)
|
||||||
visualizationBatchSlider.onValueChanged.AddListener(OnVisualizationBatchChanged);
|
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 += UpdatePerformanceMetrics;
|
||||||
npc.OnPathfindingComplete += OnPathfindingCompleted;
|
npc.OnPathfindingComplete += OnPathfindingCompleted;
|
||||||
|
|
||||||
// Subscribe to visualization completion event
|
|
||||||
npc.OnVisualizationComplete += OnVisualizationCompleted;
|
npc.OnVisualizationComplete += OnVisualizationCompleted;
|
||||||
|
|
||||||
// Subscribe to movement completion event
|
|
||||||
npc.OnMovementComplete += OnMovementCompleted;
|
npc.OnMovementComplete += OnMovementCompleted;
|
||||||
|
|
||||||
// Initialize performance metrics
|
|
||||||
ClearPerformanceMetrics();
|
ClearPerformanceMetrics();
|
||||||
|
|
||||||
// Initialize visualization controls
|
|
||||||
InitializeVisualizationControls();
|
InitializeVisualizationControls();
|
||||||
|
|
||||||
// Perform algorithm warmup
|
|
||||||
if (performWarmup)
|
if (performWarmup)
|
||||||
{
|
{
|
||||||
StartCoroutine(WarmupPathfindingSystem());
|
StartCoroutine(WarmupPathfindingSystem());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tampilkan lokasi penyimpanan
|
|
||||||
ShowSaveLocation();
|
ShowSaveLocation();
|
||||||
}
|
}
|
||||||
|
|
||||||
private IEnumerator WarmupPathfindingSystem()
|
private IEnumerator WarmupPathfindingSystem()
|
||||||
{
|
{
|
||||||
// Wait one frame to ensure everything is initialized
|
|
||||||
yield return null;
|
yield return null;
|
||||||
|
|
||||||
if (showWarmupMessage)
|
if (showWarmupMessage)
|
||||||
@ -141,20 +114,16 @@ public class PathfindingUIManager : MonoBehaviour
|
|||||||
//Debug.Log("Performing pathfinding warmup...");
|
//Debug.Log("Performing pathfinding warmup...");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get current NPC position
|
|
||||||
Vector3 npcPos = npc.transform.position;
|
Vector3 npcPos = npc.transform.position;
|
||||||
int startX = (int)(npcPos.x / gridMap.GridNodeWidth);
|
int startX = (int)(npcPos.x / gridMap.GridNodeWidth);
|
||||||
int startY = (int)(npcPos.y / gridMap.GridNodeHeight);
|
int startY = (int)(npcPos.y / gridMap.GridNodeHeight);
|
||||||
|
|
||||||
// Find destination node for warmup (try to use opposite corner)
|
|
||||||
int destX = gridMap.NumX - 1;
|
int destX = gridMap.NumX - 1;
|
||||||
int destY = gridMap.NumY - 1;
|
int destY = gridMap.NumY - 1;
|
||||||
|
|
||||||
// Ensure destination is walkable
|
|
||||||
GridNode destNode = gridMap.GetGridNode(destX, destY);
|
GridNode destNode = gridMap.GetGridNode(destX, destY);
|
||||||
if (destNode == null || !destNode.IsWalkable)
|
if (destNode == null || !destNode.IsWalkable)
|
||||||
{
|
{
|
||||||
// Find any walkable node for warmup
|
|
||||||
for (int x = 0; x < gridMap.NumX; x++)
|
for (int x = 0; x < gridMap.NumX; x++)
|
||||||
{
|
{
|
||||||
for (int y = 0; y < gridMap.NumY; y++)
|
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;
|
Vector3 originalDestPos = gridMap.Destination.position;
|
||||||
|
|
||||||
// Set temporary destination for warmup
|
|
||||||
gridMap.SetDestination(destX, destY);
|
gridMap.SetDestination(destX, destY);
|
||||||
|
|
||||||
// Run pathfinding quietly (without visualization)
|
|
||||||
GridNode startNode = gridMap.GetGridNode(startX, startY);
|
GridNode startNode = gridMap.GetGridNode(startX, startY);
|
||||||
|
|
||||||
if (startNode != null && destNode != null && destNode.IsWalkable)
|
if (startNode != null && destNode != null && destNode.IsWalkable)
|
||||||
{
|
{
|
||||||
// Temporarily disable visualization for warmup
|
|
||||||
float originalVisualizationSpeed = npc.visualizationSpeed;
|
float originalVisualizationSpeed = npc.visualizationSpeed;
|
||||||
npc.visualizationSpeed = 0f;
|
npc.visualizationSpeed = 0f;
|
||||||
bool originalShowVisualization = npc.showVisualization;
|
bool originalShowVisualization = npc.showVisualization;
|
||||||
npc.showVisualization = false;
|
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)))
|
foreach (NPC.PathFinderType algoType in System.Enum.GetValues(typeof(NPC.PathFinderType)))
|
||||||
{
|
{
|
||||||
// Save current algorithm
|
|
||||||
NPC.PathFinderType originalAlgorithm = npc.pathFinderType;
|
NPC.PathFinderType originalAlgorithm = npc.pathFinderType;
|
||||||
|
|
||||||
// Change to this algorithm
|
|
||||||
npc.ChangeAlgorithm(algoType);
|
npc.ChangeAlgorithm(algoType);
|
||||||
|
|
||||||
// Run silent pathfinding
|
|
||||||
npc.MoveTo(destNode, true);
|
npc.MoveTo(destNode, true);
|
||||||
|
|
||||||
// Wait a bit to ensure completion
|
|
||||||
yield return new WaitForSeconds(0.05f);
|
yield return new WaitForSeconds(0.05f);
|
||||||
|
|
||||||
// Reset back to original algorithm
|
|
||||||
npc.ChangeAlgorithm(originalAlgorithm);
|
npc.ChangeAlgorithm(originalAlgorithm);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restore visualization settings
|
|
||||||
npc.visualizationSpeed = originalVisualizationSpeed;
|
npc.visualizationSpeed = originalVisualizationSpeed;
|
||||||
npc.showVisualization = originalShowVisualization;
|
npc.showVisualization = originalShowVisualization;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restore original destination
|
|
||||||
gridMap.Destination.position = originalDestPos;
|
gridMap.Destination.position = originalDestPos;
|
||||||
|
|
||||||
// Clear metrics from warmup
|
|
||||||
ClearPerformanceMetrics();
|
ClearPerformanceMetrics();
|
||||||
|
|
||||||
if (showWarmupMessage)
|
if (showWarmupMessage)
|
||||||
{
|
{
|
||||||
//Debug.Log("Pathfinding warmup complete");
|
//Debug.Log("Pathfinding warmup complete");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Reset grid colors
|
|
||||||
gridMap.ResetGridNodeColours();
|
gridMap.ResetGridNodeColours();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnDestroy()
|
private void OnDestroy()
|
||||||
{
|
{
|
||||||
// Unsubscribe from events
|
|
||||||
if (npc != null)
|
if (npc != null)
|
||||||
{
|
{
|
||||||
npc.OnPathfindingComplete -= UpdatePerformanceMetrics;
|
npc.OnPathfindingComplete -= UpdatePerformanceMetrics;
|
||||||
@ -241,7 +190,6 @@ public class PathfindingUIManager : MonoBehaviour
|
|||||||
npc.OnMovementComplete -= OnMovementCompleted;
|
npc.OnMovementComplete -= OnMovementCompleted;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unsubscribe from UI events
|
|
||||||
if (allowDiagonalToggle != null)
|
if (allowDiagonalToggle != null)
|
||||||
{
|
{
|
||||||
allowDiagonalToggle.onValueChanged.RemoveListener(OnDiagonalMovementChanged);
|
allowDiagonalToggle.onValueChanged.RemoveListener(OnDiagonalMovementChanged);
|
||||||
@ -250,19 +198,15 @@ public class PathfindingUIManager : MonoBehaviour
|
|||||||
|
|
||||||
private void InitializeUI()
|
private void InitializeUI()
|
||||||
{
|
{
|
||||||
// Set initial values
|
|
||||||
gridSizeXInput.text = gridMap.NumX.ToString();
|
gridSizeXInput.text = gridMap.NumX.ToString();
|
||||||
gridSizeYInput.text = gridMap.NumY.ToString();
|
gridSizeYInput.text = gridMap.NumY.ToString();
|
||||||
|
|
||||||
// Set input fields to only accept integers
|
|
||||||
gridSizeXInput.contentType = TMP_InputField.ContentType.IntegerNumber;
|
gridSizeXInput.contentType = TMP_InputField.ContentType.IntegerNumber;
|
||||||
gridSizeYInput.contentType = TMP_InputField.ContentType.IntegerNumber;
|
gridSizeYInput.contentType = TMP_InputField.ContentType.IntegerNumber;
|
||||||
|
|
||||||
// Add input validation events
|
|
||||||
gridSizeXInput.onValidateInput += ValidateNumberInput;
|
gridSizeXInput.onValidateInput += ValidateNumberInput;
|
||||||
gridSizeYInput.onValidateInput += ValidateNumberInput;
|
gridSizeYInput.onValidateInput += ValidateNumberInput;
|
||||||
|
|
||||||
// Setup algorithm dropdown
|
|
||||||
algorithmDropdown.ClearOptions();
|
algorithmDropdown.ClearOptions();
|
||||||
algorithmDropdown.AddOptions(new System.Collections.Generic.List<string> {
|
algorithmDropdown.AddOptions(new System.Collections.Generic.List<string> {
|
||||||
"A*",
|
"A*",
|
||||||
@ -272,14 +216,12 @@ public class PathfindingUIManager : MonoBehaviour
|
|||||||
"BFS"
|
"BFS"
|
||||||
});
|
});
|
||||||
|
|
||||||
// Initialize diagonal movement toggle
|
|
||||||
if (allowDiagonalToggle != null)
|
if (allowDiagonalToggle != null)
|
||||||
{
|
{
|
||||||
allowDiagonalToggle.isOn = gridMap.AllowDiagonalMovement;
|
allowDiagonalToggle.isOn = gridMap.AllowDiagonalMovement;
|
||||||
allowDiagonalToggle.onValueChanged.AddListener(OnDiagonalMovementChanged);
|
allowDiagonalToggle.onValueChanged.AddListener(OnDiagonalMovementChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup maze size dropdown
|
|
||||||
if (mazeSizeDropdown != null)
|
if (mazeSizeDropdown != null)
|
||||||
{
|
{
|
||||||
mazeSizeDropdown.ClearOptions();
|
mazeSizeDropdown.ClearOptions();
|
||||||
@ -292,7 +234,6 @@ public class PathfindingUIManager : MonoBehaviour
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup maze density dropdown
|
|
||||||
if (mazeDensityDropdown != null)
|
if (mazeDensityDropdown != null)
|
||||||
{
|
{
|
||||||
mazeDensityDropdown.ClearOptions();
|
mazeDensityDropdown.ClearOptions();
|
||||||
@ -306,17 +247,15 @@ public class PathfindingUIManager : MonoBehaviour
|
|||||||
ClearPerformanceMetrics();
|
ClearPerformanceMetrics();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validation function to only allow numeric input
|
|
||||||
private char ValidateNumberInput(string text, int charIndex, char addedChar)
|
private char ValidateNumberInput(string text, int charIndex, char addedChar)
|
||||||
{
|
{
|
||||||
// Only allow digits
|
|
||||||
if (char.IsDigit(addedChar))
|
if (char.IsDigit(addedChar))
|
||||||
{
|
{
|
||||||
return addedChar;
|
return addedChar;
|
||||||
}
|
}
|
||||||
else
|
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";
|
nodesExploredText.text = $"{metrics.nodesExplored} nodes";
|
||||||
memoryUsageText.text = FormatBytes(metrics.memoryUsed);
|
memoryUsageText.text = FormatBytes(metrics.memoryUsed);
|
||||||
|
|
||||||
// Hitung dan tampilkan CPU usage
|
|
||||||
if (cpuUsageText != null)
|
if (cpuUsageText != null)
|
||||||
{
|
{
|
||||||
float cpuUsagePercentage = (metrics.timeTaken / TARGET_FRAME_TIME_MS) * 100f;
|
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) &&
|
if (int.TryParse(gridSizeXInput.text, out int newSizeX) &&
|
||||||
int.TryParse(gridSizeYInput.text, out int newSizeY))
|
int.TryParse(gridSizeYInput.text, out int newSizeY))
|
||||||
{
|
{
|
||||||
// Validate grid size limits
|
|
||||||
const int MAX_GRID_SIZE = 200;
|
const int MAX_GRID_SIZE = 200;
|
||||||
const int MIN_GRID_SIZE = 2;
|
const int MIN_GRID_SIZE = 2;
|
||||||
|
|
||||||
if (newSizeX > MAX_GRID_SIZE || newSizeY > MAX_GRID_SIZE)
|
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.");
|
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();
|
gridSizeXInput.text = gridMap.NumX.ToString();
|
||||||
gridSizeYInput.text = gridMap.NumY.ToString();
|
gridSizeYInput.text = gridMap.NumY.ToString();
|
||||||
|
|
||||||
// Don't proceed with resize
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for minimum size
|
|
||||||
if (newSizeX < MIN_GRID_SIZE || newSizeY < MIN_GRID_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.");
|
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();
|
gridSizeXInput.text = gridMap.NumX.ToString();
|
||||||
gridSizeYInput.text = gridMap.NumY.ToString();
|
gridSizeYInput.text = gridMap.NumY.ToString();
|
||||||
|
|
||||||
// Don't proceed with resize
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply the grid size (only if within limits)
|
|
||||||
if (gridMap.ResizeGrid(newSizeX, newSizeY))
|
if (gridMap.ResizeGrid(newSizeX, newSizeY))
|
||||||
{
|
{
|
||||||
ClearPerformanceMetrics();
|
ClearPerformanceMetrics();
|
||||||
@ -403,17 +332,14 @@ public class PathfindingUIManager : MonoBehaviour
|
|||||||
|
|
||||||
private void OnRunPathfinding()
|
private void OnRunPathfinding()
|
||||||
{
|
{
|
||||||
// Get current NPC position
|
|
||||||
Vector3 npcPos = npc.transform.position;
|
Vector3 npcPos = npc.transform.position;
|
||||||
int startX = (int)(npcPos.x / gridMap.GridNodeWidth);
|
int startX = (int)(npcPos.x / gridMap.GridNodeWidth);
|
||||||
int startY = (int)(npcPos.y / gridMap.GridNodeHeight);
|
int startY = (int)(npcPos.y / gridMap.GridNodeHeight);
|
||||||
|
|
||||||
// Get destination position
|
|
||||||
Vector3 destPos = gridMap.Destination.position;
|
Vector3 destPos = gridMap.Destination.position;
|
||||||
int destX = (int)(destPos.x / gridMap.GridNodeWidth);
|
int destX = (int)(destPos.x / gridMap.GridNodeWidth);
|
||||||
int destY = (int)(destPos.y / gridMap.GridNodeHeight);
|
int destY = (int)(destPos.y / gridMap.GridNodeHeight);
|
||||||
|
|
||||||
// Run pathfinding
|
|
||||||
GridNode startNode = gridMap.GetGridNode(startX, startY);
|
GridNode startNode = gridMap.GetGridNode(startX, startY);
|
||||||
GridNode endNode = gridMap.GetGridNode(destX, destY);
|
GridNode endNode = gridMap.GetGridNode(destX, destY);
|
||||||
|
|
||||||
@ -421,10 +347,9 @@ public class PathfindingUIManager : MonoBehaviour
|
|||||||
{
|
{
|
||||||
ClearPerformanceMetrics();
|
ClearPerformanceMetrics();
|
||||||
|
|
||||||
// Set flags that pathfinding, visualization, and movement will happen
|
|
||||||
isPathfindingRunning = true;
|
isPathfindingRunning = true;
|
||||||
isVisualizationRunning = true;
|
isVisualizationRunning = true;
|
||||||
isNpcMoving = true; // assume movement will happen
|
isNpcMoving = true;
|
||||||
SetUIInteractivity(false);
|
SetUIInteractivity(false);
|
||||||
|
|
||||||
npc.MoveTo(endNode);
|
npc.MoveTo(endNode);
|
||||||
@ -433,20 +358,15 @@ public class PathfindingUIManager : MonoBehaviour
|
|||||||
|
|
||||||
private void OnResetPathfinding()
|
private void OnResetPathfinding()
|
||||||
{
|
{
|
||||||
// Reset all flags to ensure UI will be enabled after reload
|
|
||||||
isPathfindingRunning = false;
|
isPathfindingRunning = false;
|
||||||
isVisualizationRunning = false;
|
isVisualizationRunning = false;
|
||||||
isNpcMoving = false;
|
isNpcMoving = false;
|
||||||
|
|
||||||
// Force enable UI - this ensures buttons will be enabled
|
|
||||||
// after reset regardless of editor/build status
|
|
||||||
SetUIInteractivity(true);
|
SetUIInteractivity(true);
|
||||||
|
|
||||||
// Reload the current scene
|
|
||||||
UnityEngine.SceneManagement.SceneManager.LoadScene(
|
UnityEngine.SceneManagement.SceneManager.LoadScene(
|
||||||
UnityEngine.SceneManagement.SceneManager.GetActiveScene().name);
|
UnityEngine.SceneManagement.SceneManager.GetActiveScene().name);
|
||||||
|
|
||||||
//Debug.Log("Reloading scene...");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnAlgorithmChanged(int index)
|
private void OnAlgorithmChanged(int index)
|
||||||
@ -460,7 +380,6 @@ public class PathfindingUIManager : MonoBehaviour
|
|||||||
ClearPerformanceMetrics();
|
ClearPerformanceMetrics();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper method to get the readable name of the algorithm
|
|
||||||
private string GetAlgorithmName(NPC.PathFinderType type)
|
private string GetAlgorithmName(NPC.PathFinderType type)
|
||||||
{
|
{
|
||||||
switch (type)
|
switch (type)
|
||||||
@ -488,7 +407,6 @@ public class PathfindingUIManager : MonoBehaviour
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Buat direktori jika belum ada
|
|
||||||
string saveDirectory = Path.Combine(Application.persistentDataPath, "GridSaves");
|
string saveDirectory = Path.Combine(Application.persistentDataPath, "GridSaves");
|
||||||
if (!Directory.Exists(saveDirectory))
|
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)");
|
Debug.Log($"Map saved to: {filePath} (includes grid state, NPC position, and destination position)");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Membuka folder penyimpanan di File Explorer
|
|
||||||
/// </summary>
|
|
||||||
public void OpenSaveFolder()
|
public void OpenSaveFolder()
|
||||||
{
|
{
|
||||||
string saveDirectory = Path.Combine(Application.persistentDataPath, "GridSaves");
|
string saveDirectory = Path.Combine(Application.persistentDataPath, "GridSaves");
|
||||||
|
|
||||||
// Buat direktori jika belum ada
|
|
||||||
if (!Directory.Exists(saveDirectory))
|
if (!Directory.Exists(saveDirectory))
|
||||||
{
|
{
|
||||||
Directory.CreateDirectory(saveDirectory);
|
Directory.CreateDirectory(saveDirectory);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Buka folder di file explorer
|
|
||||||
System.Diagnostics.Process.Start("explorer.exe", saveDirectory);
|
System.Diagnostics.Process.Start("explorer.exe", saveDirectory);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -530,22 +441,15 @@ public class PathfindingUIManager : MonoBehaviour
|
|||||||
|
|
||||||
if (!File.Exists(filePath))
|
if (!File.Exists(filePath))
|
||||||
{
|
{
|
||||||
//Debug.LogWarning($"Map file not found: {filePath}");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
gridMap.LoadGridState(filePath);
|
gridMap.LoadGridState(filePath);
|
||||||
ClearPerformanceMetrics();
|
ClearPerformanceMetrics();
|
||||||
|
|
||||||
//Debug.Log($"Map loaded from: {filePath}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Generates a random maze with the selected size and density
|
|
||||||
/// </summary>
|
|
||||||
private void OnGenerateMaze()
|
private void OnGenerateMaze()
|
||||||
{
|
{
|
||||||
// Get selected maze size
|
|
||||||
int sizeX = 20;
|
int sizeX = 20;
|
||||||
int sizeY = 20;
|
int sizeY = 20;
|
||||||
bool isLargeGrid = false;
|
bool isLargeGrid = false;
|
||||||
@ -569,10 +473,8 @@ public class PathfindingUIManager : MonoBehaviour
|
|||||||
sizeX = sizeY = 100;
|
sizeX = sizeY = 100;
|
||||||
isLargeGrid = true;
|
isLargeGrid = true;
|
||||||
break;
|
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)
|
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.");
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resize grid if needed
|
|
||||||
if (gridMap.NumX != sizeX || gridMap.NumY != sizeY)
|
if (gridMap.NumX != sizeX || gridMap.NumY != sizeY)
|
||||||
{
|
{
|
||||||
if (!gridMap.ResizeGrid(sizeX, sizeY))
|
if (!gridMap.ResizeGrid(sizeX, sizeY))
|
||||||
{
|
{
|
||||||
// Resize failed, abort maze generation
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update grid size inputs
|
|
||||||
gridSizeXInput.text = sizeX.ToString();
|
gridSizeXInput.text = sizeX.ToString();
|
||||||
gridSizeYInput.text = sizeY.ToString();
|
gridSizeYInput.text = sizeY.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get selected density
|
float density = 30f;
|
||||||
float density = 30f; // Default medium
|
|
||||||
|
|
||||||
switch (mazeDensityDropdown.value)
|
switch (mazeDensityDropdown.value)
|
||||||
{
|
{
|
||||||
@ -615,61 +513,40 @@ public class PathfindingUIManager : MonoBehaviour
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Untuk grid besar, nonaktifkan visualisasi sementara untuk performa lebih baik
|
|
||||||
bool originalShowVisualization = false;
|
bool originalShowVisualization = false;
|
||||||
float originalVisualizationSpeed = 0f;
|
float originalVisualizationSpeed = 0f;
|
||||||
|
|
||||||
if (isLargeGrid && npc != null)
|
if (isLargeGrid && npc != null)
|
||||||
{
|
{
|
||||||
// Simpan nilai asli
|
|
||||||
originalShowVisualization = npc.showVisualization;
|
originalShowVisualization = npc.showVisualization;
|
||||||
originalVisualizationSpeed = npc.visualizationSpeed;
|
originalVisualizationSpeed = npc.visualizationSpeed;
|
||||||
|
|
||||||
// Nonaktifkan visualisasi untuk grid besar
|
|
||||||
npc.showVisualization = false;
|
npc.showVisualization = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate the maze with selected density
|
|
||||||
gridMap.GenerateRandomMaze(density);
|
gridMap.GenerateRandomMaze(density);
|
||||||
|
|
||||||
// Kembalikan nilai visualisasi jika diubah
|
|
||||||
if (isLargeGrid && npc != null)
|
if (isLargeGrid && npc != null)
|
||||||
{
|
{
|
||||||
npc.showVisualization = originalShowVisualization;
|
npc.showVisualization = originalShowVisualization;
|
||||||
npc.visualizationSpeed = originalVisualizationSpeed;
|
npc.visualizationSpeed = originalVisualizationSpeed;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear performance metrics
|
|
||||||
ClearPerformanceMetrics();
|
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}%");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Menampilkan lokasi penyimpanan file di konsol
|
|
||||||
/// </summary>
|
|
||||||
private void ShowSaveLocation()
|
private void ShowSaveLocation()
|
||||||
{
|
{
|
||||||
string saveDirectory = Path.Combine(Application.persistentDataPath, "GridSaves");
|
string saveDirectory = Path.Combine(Application.persistentDataPath, "GridSaves");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Menutup aplikasi saat tombol exit ditekan
|
|
||||||
/// </summary>
|
|
||||||
public void OnExitApplication()
|
public void OnExitApplication()
|
||||||
{
|
{
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
// Jika di Unity Editor
|
|
||||||
UnityEditor.EditorApplication.isPlaying = false;
|
UnityEditor.EditorApplication.isPlaying = false;
|
||||||
#else
|
#else
|
||||||
// Jika di build
|
// Jika di build
|
||||||
Application.Quit();
|
Application.Quit();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Debug.Log("Application exit requested");
|
Debug.Log("Application exit requested");
|
||||||
@ -677,7 +554,6 @@ public class PathfindingUIManager : MonoBehaviour
|
|||||||
|
|
||||||
private void InitializeVisualizationControls()
|
private void InitializeVisualizationControls()
|
||||||
{
|
{
|
||||||
// Set initial slider values based on NPC settings
|
|
||||||
if (visualizationSpeedSlider != null)
|
if (visualizationSpeedSlider != null)
|
||||||
{
|
{
|
||||||
visualizationSpeedSlider.value = npc.visualizationSpeed;
|
visualizationSpeedSlider.value = npc.visualizationSpeed;
|
||||||
@ -699,7 +575,6 @@ public class PathfindingUIManager : MonoBehaviour
|
|||||||
|
|
||||||
private void OnVisualizationBatchChanged(float newValue)
|
private void OnVisualizationBatchChanged(float newValue)
|
||||||
{
|
{
|
||||||
// Pastikan nilai batch adalah integer
|
|
||||||
int batchValue = Mathf.RoundToInt(newValue);
|
int batchValue = Mathf.RoundToInt(newValue);
|
||||||
npc.visualizationBatch = batchValue;
|
npc.visualizationBatch = batchValue;
|
||||||
UpdateBatchValueText(batchValue);
|
UpdateBatchValueText(batchValue);
|
||||||
@ -725,31 +600,22 @@ public class PathfindingUIManager : MonoBehaviour
|
|||||||
batchValueText.text = value.ToString();
|
batchValueText.text = value.ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Menangani perubahan toggle diagonal movement
|
|
||||||
/// </summary>
|
|
||||||
private void OnDiagonalMovementChanged(bool isOn)
|
private void OnDiagonalMovementChanged(bool isOn)
|
||||||
{
|
{
|
||||||
gridMap.AllowDiagonalMovement = isOn;
|
gridMap.AllowDiagonalMovement = isOn;
|
||||||
// Reset performance metrics
|
|
||||||
ClearPerformanceMetrics();
|
ClearPerformanceMetrics();
|
||||||
}
|
}
|
||||||
|
|
||||||
// New method to handle pathfinding completion
|
|
||||||
private void OnPathfindingCompleted(PathfindingMetrics metrics)
|
private void OnPathfindingCompleted(PathfindingMetrics metrics)
|
||||||
{
|
{
|
||||||
// Pathfinding is completed, but visualization might still be running
|
|
||||||
isPathfindingRunning = false;
|
isPathfindingRunning = false;
|
||||||
|
|
||||||
// Check if pathfinding failed by looking at metrics or path length
|
|
||||||
bool pathfindingFailed = (metrics.pathLength == 0) ||
|
bool pathfindingFailed = (metrics.pathLength == 0) ||
|
||||||
(npc.pathFinder != null &&
|
(npc.pathFinder != null &&
|
||||||
npc.pathFinder.Status == PathFinding.PathFinderStatus.FAILURE);
|
npc.pathFinder.Status == PathFinding.PathFinderStatus.FAILURE);
|
||||||
|
|
||||||
if (pathfindingFailed)
|
if (pathfindingFailed)
|
||||||
{
|
{
|
||||||
// If pathfinding failed, there won't be any visualization or movement
|
|
||||||
Debug.Log("Pathfinding failed - re-enabling UI controls immediately");
|
Debug.Log("Pathfinding failed - re-enabling UI controls immediately");
|
||||||
isVisualizationRunning = false;
|
isVisualizationRunning = false;
|
||||||
isNpcMoving = false;
|
isNpcMoving = false;
|
||||||
@ -765,22 +631,15 @@ public class PathfindingUIManager : MonoBehaviour
|
|||||||
return;
|
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 (npc.showVisualization)
|
||||||
{
|
{
|
||||||
// If visualization is enabled in settings, assume it will start soon
|
|
||||||
// Keep UI disabled by keeping isVisualizationRunning true
|
|
||||||
isVisualizationRunning = true;
|
isVisualizationRunning = true;
|
||||||
// Do NOT enable UI here - wait for visualization to complete
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Only if visualization is completely disabled in settings, enable UI
|
|
||||||
isVisualizationRunning = false;
|
isVisualizationRunning = false;
|
||||||
|
|
||||||
// Only re-enable in editor mode
|
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
SetUIInteractivity(true);
|
SetUIInteractivity(true);
|
||||||
#else
|
#else
|
||||||
@ -790,25 +649,18 @@ public class PathfindingUIManager : MonoBehaviour
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// New method to handle visualization completion
|
|
||||||
private void OnVisualizationCompleted()
|
private void OnVisualizationCompleted()
|
||||||
{
|
{
|
||||||
// Visualization is completed, but NPC may start moving
|
|
||||||
isVisualizationRunning = false;
|
isVisualizationRunning = false;
|
||||||
|
|
||||||
// Check if NPC is moving or will move
|
|
||||||
if (npc.IsMoving || npc.wayPoints.Count > 0)
|
if (npc.IsMoving || npc.wayPoints.Count > 0)
|
||||||
{
|
{
|
||||||
// Movement is starting or in progress
|
|
||||||
isNpcMoving = true;
|
isNpcMoving = true;
|
||||||
// Leave UI disabled
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// No movement expected
|
|
||||||
isNpcMoving = false;
|
isNpcMoving = false;
|
||||||
|
|
||||||
// Only re-enable in editor mode
|
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
SetUIInteractivity(true);
|
SetUIInteractivity(true);
|
||||||
#else
|
#else
|
||||||
@ -818,13 +670,10 @@ public class PathfindingUIManager : MonoBehaviour
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// New method to handle movement completion
|
|
||||||
private void OnMovementCompleted()
|
private void OnMovementCompleted()
|
||||||
{
|
{
|
||||||
// Movement is completed, re-enable UI buttons only in editor
|
|
||||||
isNpcMoving = false;
|
isNpcMoving = false;
|
||||||
|
|
||||||
// Only re-enable in editor mode
|
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
SetUIInteractivity(true);
|
SetUIInteractivity(true);
|
||||||
#else
|
#else
|
||||||
@ -834,13 +683,10 @@ public class PathfindingUIManager : MonoBehaviour
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Method to enable/disable UI elements based on pathfinding, visualization, and movement state
|
|
||||||
private void SetUIInteractivity(bool enabled)
|
private void SetUIInteractivity(bool enabled)
|
||||||
{
|
{
|
||||||
// If any process is running, disable controls
|
|
||||||
bool shouldEnable = enabled && !isPathfindingRunning && !isVisualizationRunning && !isNpcMoving;
|
bool shouldEnable = enabled && !isPathfindingRunning && !isVisualizationRunning && !isNpcMoving;
|
||||||
|
|
||||||
// In builds (not editor), once disabled, buttons stay disabled until reset
|
|
||||||
#if !UNITY_EDITOR
|
#if !UNITY_EDITOR
|
||||||
if (shouldEnable && (isPathfindingRunning || isVisualizationRunning || isNpcMoving))
|
if (shouldEnable && (isPathfindingRunning || isVisualizationRunning || isNpcMoving))
|
||||||
{
|
{
|
||||||
@ -850,13 +696,10 @@ public class PathfindingUIManager : MonoBehaviour
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Add debug logging
|
|
||||||
Debug.Log($"SetUIInteractivity called with enabled={enabled}, pathfinding={isPathfindingRunning}, " +
|
Debug.Log($"SetUIInteractivity called with enabled={enabled}, pathfinding={isPathfindingRunning}, " +
|
||||||
$"visualization={isVisualizationRunning}, movement={isNpcMoving}, shouldEnable={shouldEnable}, " +
|
$"visualization={isVisualizationRunning}, movement={isNpcMoving}, shouldEnable={shouldEnable}, " +
|
||||||
$"inEditor={Application.isEditor}");
|
$"inEditor={Application.isEditor}");
|
||||||
|
|
||||||
// Keep reset and exit buttons always enabled
|
|
||||||
// Disable all other buttons when processes are running
|
|
||||||
|
|
||||||
if (applyGridSizeButton != null)
|
if (applyGridSizeButton != null)
|
||||||
applyGridSizeButton.interactable = shouldEnable;
|
applyGridSizeButton.interactable = shouldEnable;
|
||||||
@ -899,17 +742,12 @@ public class PathfindingUIManager : MonoBehaviour
|
|||||||
|
|
||||||
if (mapNameInput != null)
|
if (mapNameInput != null)
|
||||||
mapNameInput.interactable = shouldEnable;
|
mapNameInput.interactable = shouldEnable;
|
||||||
|
|
||||||
// Reset and exit buttons remain enabled
|
|
||||||
// resetButton and exitButton stay interactable
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Update()
|
private void Update()
|
||||||
{
|
{
|
||||||
// Continuously check for various states - this ensures buttons stay disabled
|
|
||||||
if (npc != null)
|
if (npc != null)
|
||||||
{
|
{
|
||||||
// Check visualization
|
|
||||||
if (npc.IsVisualizingPath && !isVisualizationRunning)
|
if (npc.IsVisualizingPath && !isVisualizationRunning)
|
||||||
{
|
{
|
||||||
Debug.Log("Detected active visualization - updating UI state");
|
Debug.Log("Detected active visualization - updating UI state");
|
||||||
@ -917,7 +755,6 @@ public class PathfindingUIManager : MonoBehaviour
|
|||||||
SetUIInteractivity(false);
|
SetUIInteractivity(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check movement
|
|
||||||
if (npc.IsMoving && !isNpcMoving)
|
if (npc.IsMoving && !isNpcMoving)
|
||||||
{
|
{
|
||||||
Debug.Log("Detected active NPC movement - updating UI state");
|
Debug.Log("Detected active NPC movement - updating UI state");
|
||||||
|
@ -9,21 +9,10 @@ using System.Collections.Generic;
|
|||||||
/// <typeparam name="TPriority">Tipe prioritas yang digunakan, harus implementasi IComparable</typeparam>
|
/// <typeparam name="TPriority">Tipe prioritas yang digunakan, harus implementasi IComparable</typeparam>
|
||||||
public class PriorityQueue<TElement, TPriority> where TPriority : IComparable<TPriority>
|
public class PriorityQueue<TElement, TPriority> where TPriority : IComparable<TPriority>
|
||||||
{
|
{
|
||||||
// Menyimpan pasangan elemen dan prioritasnya dalam struktur heap
|
|
||||||
private List<Tuple<TElement, TPriority>> elements = new List<Tuple<TElement, TPriority>>();
|
private List<Tuple<TElement, TPriority>> elements = new List<Tuple<TElement, TPriority>>();
|
||||||
// Menyimpan indeks setiap elemen dalam heap untuk akses cepat
|
|
||||||
private Dictionary<TElement, int> elementIndexMap = new Dictionary<TElement, int>();
|
private Dictionary<TElement, int> elementIndexMap = new Dictionary<TElement, int>();
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Mendapatkan jumlah elemen dalam antrean prioritas
|
|
||||||
/// </summary>
|
|
||||||
public int Count => elements.Count;
|
public int Count => elements.Count;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Menambahkan elemen baru ke dalam antrean prioritas dengan prioritas tertentu
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="element">Elemen yang akan ditambahkan</param>
|
|
||||||
/// <param name="priority">Prioritas dari elemen</param>
|
|
||||||
public void Enqueue(TElement element, TPriority priority)
|
public void Enqueue(TElement element, TPriority priority)
|
||||||
{
|
{
|
||||||
elements.Add(Tuple.Create(element, priority));
|
elements.Add(Tuple.Create(element, priority));
|
||||||
@ -31,12 +20,6 @@ public class PriorityQueue<TElement, TPriority> where TPriority : IComparable<TP
|
|||||||
HeapifyUp(elements.Count - 1);
|
HeapifyUp(elements.Count - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Mengambil dan menghapus elemen dengan prioritas tertinggi (nilai terkecil)
|
|
||||||
/// dari antrean prioritas
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Elemen dengan prioritas tertinggi</returns>
|
|
||||||
/// <exception cref="InvalidOperationException">Dilempar jika antrean kosong</exception>
|
|
||||||
public TElement Dequeue()
|
public TElement Dequeue()
|
||||||
{
|
{
|
||||||
if (elements.Count == 0)
|
if (elements.Count == 0)
|
||||||
@ -48,7 +31,6 @@ public class PriorityQueue<TElement, TPriority> where TPriority : IComparable<TP
|
|||||||
|
|
||||||
if (elements.Count > 0)
|
if (elements.Count > 0)
|
||||||
{
|
{
|
||||||
// Pindahkan elemen terakhir ke root, lalu atur ulang heap
|
|
||||||
elements[0] = last;
|
elements[0] = last;
|
||||||
elementIndexMap[last.Item1] = 0;
|
elementIndexMap[last.Item1] = 0;
|
||||||
HeapifyDown(0);
|
HeapifyDown(0);
|
||||||
@ -58,12 +40,6 @@ public class PriorityQueue<TElement, TPriority> where TPriority : IComparable<TP
|
|||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Memperbarui prioritas elemen yang sudah ada dalam antrean
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="element">Elemen yang akan diperbarui prioritasnya</param>
|
|
||||||
/// <param name="newPriority">Nilai prioritas baru</param>
|
|
||||||
/// <exception cref="InvalidOperationException">Dilempar jika elemen tidak ditemukan</exception>
|
|
||||||
public void UpdatePriority(TElement element, TPriority newPriority)
|
public void UpdatePriority(TElement element, TPriority newPriority)
|
||||||
{
|
{
|
||||||
if (!elementIndexMap.ContainsKey(element))
|
if (!elementIndexMap.ContainsKey(element))
|
||||||
@ -73,23 +49,16 @@ public class PriorityQueue<TElement, TPriority> where TPriority : IComparable<TP
|
|||||||
var oldPriority = elements[index].Item2;
|
var oldPriority = elements[index].Item2;
|
||||||
elements[index] = Tuple.Create(element, newPriority);
|
elements[index] = Tuple.Create(element, newPriority);
|
||||||
|
|
||||||
// Jika prioritas baru lebih tinggi (nilai lebih kecil), heapify up
|
|
||||||
if (newPriority.CompareTo(oldPriority) < 0)
|
if (newPriority.CompareTo(oldPriority) < 0)
|
||||||
{
|
{
|
||||||
HeapifyUp(index);
|
HeapifyUp(index);
|
||||||
}
|
}
|
||||||
// Jika prioritas baru lebih rendah (nilai lebih besar), heapify down
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
HeapifyDown(index);
|
HeapifyDown(index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Mempertahankan properti heap dengan memindahkan elemen ke atas jika
|
|
||||||
/// prioritasnya lebih tinggi dari parent
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="index">Indeks elemen yang akan dipindahkan ke atas</param>
|
|
||||||
private void HeapifyUp(int index)
|
private void HeapifyUp(int index)
|
||||||
{
|
{
|
||||||
var parentIndex = (index - 1) / 2;
|
var parentIndex = (index - 1) / 2;
|
||||||
@ -100,18 +69,12 @@ public class PriorityQueue<TElement, TPriority> where TPriority : IComparable<TP
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Mempertahankan properti heap dengan memindahkan elemen ke bawah jika
|
|
||||||
/// prioritasnya lebih rendah dari child
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="index">Indeks elemen yang akan dipindahkan ke bawah</param>
|
|
||||||
private void HeapifyDown(int index)
|
private void HeapifyDown(int index)
|
||||||
{
|
{
|
||||||
var leftChildIndex = 2 * index + 1;
|
var leftChildIndex = 2 * index + 1;
|
||||||
var rightChildIndex = 2 * index + 2;
|
var rightChildIndex = 2 * index + 2;
|
||||||
var smallest = index;
|
var smallest = index;
|
||||||
|
|
||||||
// Cari child dengan prioritas tertinggi (nilai terkecil)
|
|
||||||
if (leftChildIndex < elements.Count && elements[leftChildIndex].Item2.CompareTo(elements[smallest].Item2) < 0)
|
if (leftChildIndex < elements.Count && elements[leftChildIndex].Item2.CompareTo(elements[smallest].Item2) < 0)
|
||||||
{
|
{
|
||||||
smallest = leftChildIndex;
|
smallest = leftChildIndex;
|
||||||
@ -122,7 +85,6 @@ public class PriorityQueue<TElement, TPriority> where TPriority : IComparable<TP
|
|||||||
smallest = rightChildIndex;
|
smallest = rightChildIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Jika child memiliki prioritas lebih tinggi, tukar dan lanjutkan heapify down
|
|
||||||
if (smallest != index)
|
if (smallest != index)
|
||||||
{
|
{
|
||||||
Swap(index, smallest);
|
Swap(index, smallest);
|
||||||
@ -130,18 +92,12 @@ public class PriorityQueue<TElement, TPriority> where TPriority : IComparable<TP
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Menukar posisi dua elemen dalam heap dan memperbarui elementIndexMap
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="i">Indeks elemen pertama</param>
|
|
||||||
/// <param name="j">Indeks elemen kedua</param>
|
|
||||||
private void Swap(int i, int j)
|
private void Swap(int i, int j)
|
||||||
{
|
{
|
||||||
var temp = elements[i];
|
var temp = elements[i];
|
||||||
elements[i] = elements[j];
|
elements[i] = elements[j];
|
||||||
elements[j] = temp;
|
elements[j] = temp;
|
||||||
|
|
||||||
// Perbarui elementIndexMap untuk mencerminkan posisi baru
|
|
||||||
elementIndexMap[elements[i].Item1] = i;
|
elementIndexMap[elements[i].Item1] = i;
|
||||||
elementIndexMap[elements[j].Item1] = j;
|
elementIndexMap[elements[j].Item1] = j;
|
||||||
}
|
}
|
||||||
|
@ -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
|
|
|
@ -1,7 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: b5f5e061be1cbf946b769794cb0ba604
|
|
||||||
TextScriptImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
Reference in New Issue
Block a user