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