mirror of
https://github.com/BobbyRafael31/Unity-MazeRunner-Pathfinding-Visualizer.git
synced 2025-08-10 08:22:21 +00:00
feat saving include npc position
This commit is contained in:
8
Assets/Docs.meta
Normal file
8
Assets/Docs.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3c5678ad04a64df4db73256f9e23d75c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -336,7 +336,7 @@ RectTransform:
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 0, y: 0}
|
||||
m_AnchoredPosition: {x: 52.600037, y: 49.700073}
|
||||
m_AnchoredPosition: {x: 52.600098, y: 49.700073}
|
||||
m_SizeDelta: {x: 81.71, y: 19.77002}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!114 &23184662
|
||||
@ -1684,7 +1684,7 @@ RectTransform:
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 1, y: 0}
|
||||
m_AnchorMax: {x: 1, y: 1}
|
||||
m_AnchoredPosition: {x: -13.392517, y: 0}
|
||||
m_AnchoredPosition: {x: -13.392578, y: 0}
|
||||
m_SizeDelta: {x: 26.785, y: 0}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!114 &249188175
|
||||
@ -7311,8 +7311,8 @@ MonoBehaviour:
|
||||
m_Script: {fileID: 11500000, guid: 42bfb8854d690bf4fa7ccf911313d0ae, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
numX: 20
|
||||
numY: 20
|
||||
numX: 10
|
||||
numY: 10
|
||||
gridNodeViewPrefab: {fileID: 5382759684804752574, guid: bfa28317eb98f2846969e3ffce05f685, type: 3}
|
||||
allowDiagonalMovement: 0
|
||||
COLOR_WALKABLE: {r: 1, g: 1, b: 1, a: 1}
|
||||
|
@ -489,6 +489,22 @@ public class GridMap : MonoBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Simple class to store grid positions for serialization
|
||||
/// </summary>
|
||||
[System.Serializable]
|
||||
public class SerializablePosition
|
||||
{
|
||||
public float x;
|
||||
public float y;
|
||||
|
||||
public SerializablePosition(float x, float y)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Kelas untuk menyimpan status grid untuk keperluan save/load
|
||||
/// </summary>
|
||||
@ -496,6 +512,8 @@ public class GridMap : MonoBehaviour
|
||||
public class GridState
|
||||
{
|
||||
public bool[,] walkableStates;
|
||||
public SerializablePosition npcPosition; // Position of NPC
|
||||
public SerializablePosition destinationPosition; // Position of destination
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -525,15 +543,33 @@ public class GridMap : MonoBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
// Save NPC position using our serializable class
|
||||
gridState.npcPosition = new SerializablePosition(
|
||||
npc.transform.position.x / GridNodeWidth,
|
||||
npc.transform.position.y / GridNodeHeight
|
||||
);
|
||||
|
||||
// Save destination position using our serializable class
|
||||
gridState.destinationPosition = new SerializablePosition(
|
||||
destination.position.x / GridNodeWidth,
|
||||
destination.position.y / GridNodeHeight
|
||||
);
|
||||
|
||||
// Configure serializer settings to ignore Unity-specific circular references
|
||||
JsonSerializerSettings settings = new JsonSerializerSettings
|
||||
{
|
||||
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
|
||||
};
|
||||
|
||||
// Mengkonversi ke JSON dan menyimpan ke file
|
||||
string json = JsonConvert.SerializeObject(gridState);
|
||||
string json = JsonConvert.SerializeObject(gridState, settings);
|
||||
File.WriteAllText(filePath, json);
|
||||
|
||||
//Debug.Log($"Grid state saved to {filePath}");
|
||||
Debug.Log($"Grid state saved to {filePath}");
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
//Debug.LogError($"Error saving grid state: {e.Message}");
|
||||
Debug.LogError($"Error saving grid state: {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
@ -547,19 +583,26 @@ public class GridMap : MonoBehaviour
|
||||
{
|
||||
if (!File.Exists(filePath))
|
||||
{
|
||||
//Debug.LogError($"Save file not found: {filePath}");
|
||||
Debug.LogError($"Save file not found: {filePath}");
|
||||
return;
|
||||
}
|
||||
|
||||
// Membaca dan mengkonversi data dari file JSON
|
||||
string json = File.ReadAllText(filePath);
|
||||
GridState gridState = JsonConvert.DeserializeObject<GridState>(json);
|
||||
|
||||
// Configure deserializer settings to handle missing properties (compatibility)
|
||||
JsonSerializerSettings settings = new JsonSerializerSettings
|
||||
{
|
||||
MissingMemberHandling = MissingMemberHandling.Ignore
|
||||
};
|
||||
|
||||
GridState gridState = JsonConvert.DeserializeObject<GridState>(json, settings);
|
||||
|
||||
// Periksa apakah ukuran grid dalam file sesuai dengan grid saat ini
|
||||
if (gridState.walkableStates.GetLength(0) != numX ||
|
||||
gridState.walkableStates.GetLength(1) != numY)
|
||||
{
|
||||
//Debug.LogWarning($"Grid size mismatch. File: {gridState.walkableStates.GetLength(0)}x{gridState.walkableStates.GetLength(1)}, Current: {numX}x{numY}. Resizing grid...");
|
||||
Debug.LogWarning($"Grid size mismatch. File: {gridState.walkableStates.GetLength(0)}x{gridState.walkableStates.GetLength(1)}, Current: {numX}x{numY}. Resizing grid...");
|
||||
ResizeGrid(gridState.walkableStates.GetLength(0), gridState.walkableStates.GetLength(1));
|
||||
}
|
||||
|
||||
@ -573,11 +616,82 @@ public class GridMap : MonoBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
//Debug.Log($"Grid state loaded from {filePath}");
|
||||
// Check if this is an older save file version (backward compatibility)
|
||||
bool hasPositionData = gridState.npcPosition != null && gridState.destinationPosition != null;
|
||||
|
||||
// Load NPC position if saved
|
||||
if (npc != null)
|
||||
{
|
||||
if (hasPositionData)
|
||||
{
|
||||
// Find the closest valid grid node position
|
||||
int npcX = Mathf.Clamp(Mathf.RoundToInt(gridState.npcPosition.x), 0, numX - 1);
|
||||
int npcY = Mathf.Clamp(Mathf.RoundToInt(gridState.npcPosition.y), 0, numY - 1);
|
||||
|
||||
// Make sure the position is walkable
|
||||
GridNode npcNode = GetGridNode(npcX, npcY);
|
||||
if (npcNode != null && npcNode.IsWalkable)
|
||||
{
|
||||
// Set NPC position
|
||||
npc.SetStartNode(npcNode);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Find a walkable node if the saved position isn't walkable
|
||||
FindWalkableNodeAndSetNPC();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// For older save files, find a walkable position
|
||||
FindWalkableNodeAndSetNPC();
|
||||
}
|
||||
}
|
||||
|
||||
// Load destination position if saved
|
||||
if (destination != null)
|
||||
{
|
||||
if (hasPositionData)
|
||||
{
|
||||
// Find the closest valid grid node position
|
||||
int destX = Mathf.Clamp(Mathf.RoundToInt(gridState.destinationPosition.x), 0, numX - 1);
|
||||
int destY = Mathf.Clamp(Mathf.RoundToInt(gridState.destinationPosition.y), 0, numY - 1);
|
||||
|
||||
// Set destination position
|
||||
SetDestination(destX, destY);
|
||||
}
|
||||
else
|
||||
{
|
||||
// For older save files, set destination to the opposite corner or a walkable node
|
||||
FindWalkableNodeAndSetDestination();
|
||||
}
|
||||
}
|
||||
|
||||
Debug.Log($"Grid state loaded from {filePath}");
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
//Debug.LogError($"Error loading grid state: {e.Message}");
|
||||
Debug.LogError($"Error loading grid state: {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find a walkable node and set the NPC position to it
|
||||
/// </summary>
|
||||
private void FindWalkableNodeAndSetNPC()
|
||||
{
|
||||
// Find first walkable node
|
||||
for (int i = 0; i < numX; i++)
|
||||
{
|
||||
for (int j = 0; j < numY; j++)
|
||||
{
|
||||
GridNode node = GetGridNode(i, j);
|
||||
if (node != null && node.IsWalkable)
|
||||
{
|
||||
npc.SetStartNode(node);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1007,4 +1121,46 @@ public class GridMap : MonoBehaviour
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find a walkable node and set the destination position to it
|
||||
/// </summary>
|
||||
private void FindWalkableNodeAndSetDestination()
|
||||
{
|
||||
// Try to place destination in the far corner from the NPC
|
||||
int npcX = (int)(npc.transform.position.x / GridNodeWidth);
|
||||
int npcY = (int)(npc.transform.position.y / GridNodeHeight);
|
||||
|
||||
// Try opposite corner first
|
||||
int destX = numX - 1;
|
||||
int destY = numY - 1;
|
||||
|
||||
GridNode destinationNode = GetGridNode(destX, destY);
|
||||
if (destinationNode != null && destinationNode.IsWalkable)
|
||||
{
|
||||
SetDestination(destX, destY);
|
||||
return;
|
||||
}
|
||||
|
||||
// If the opposite corner isn't walkable, find any walkable node that's different from NPC position
|
||||
for (int i = numX - 1; i >= 0; i--)
|
||||
{
|
||||
for (int j = numY - 1; j >= 0; j--)
|
||||
{
|
||||
// Skip the node where NPC is
|
||||
if (i == npcX && j == npcY)
|
||||
continue;
|
||||
|
||||
GridNode node = GetGridNode(i, j);
|
||||
if (node != null && node.IsWalkable)
|
||||
{
|
||||
SetDestination(i, j);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If no suitable node found, use the NPC node as a fallback
|
||||
SetDestination(npcX, npcY);
|
||||
}
|
||||
}
|
@ -254,6 +254,9 @@ public class NPC : MonoBehaviour
|
||||
// Pre-allocate visualizationSteps with estimated capacity to avoid reallocations
|
||||
visualizationSteps = new List<PathfindingVisualizationStep>(4);
|
||||
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers(); // Tunggu semua finalizers selesai
|
||||
|
||||
// ===== MEMORY MEASUREMENT START: Ukur memory sebelum algoritma =====
|
||||
long memoryBefore = System.GC.GetTotalMemory(false);
|
||||
|
||||
@ -281,6 +284,13 @@ public class NPC : MonoBehaviour
|
||||
long memoryAfter = System.GC.GetTotalMemory(false);
|
||||
long memoryUsed = memoryAfter - memoryBefore;
|
||||
|
||||
// float miliseconds = algorithmTimer.ElapsedMilliseconds;
|
||||
|
||||
//UnityEngine.Debug.Log("$algorithmTimer.ElapsedTicks: " + algorithmTimer.ElapsedTicks);
|
||||
//UnityEngine.Debug.Log("$Stopwatch.Frequency: " + Stopwatch.Frequency);
|
||||
//float seconds = (float)algorithmTimer.ElapsedTicks / Stopwatch.Frequency;
|
||||
//UnityEngine.Debug.Log("$seconds: " + seconds);
|
||||
|
||||
float milliseconds = (algorithmTimer.ElapsedTicks * 1000.0f) / Stopwatch.Frequency;
|
||||
|
||||
// Calculate path length once and reuse
|
||||
@ -327,7 +337,7 @@ public class NPC : MonoBehaviour
|
||||
yield return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Setup callbacks for tracking nodes in open/closed lists and visualization
|
||||
/// </summary>
|
||||
|
@ -374,7 +374,7 @@ public class PathfindingUIManager : MonoBehaviour
|
||||
string filePath = Path.Combine(saveDirectory, $"{mapNameInput.text}.json");
|
||||
gridMap.SaveGridState(filePath);
|
||||
|
||||
Debug.Log($"Map saved to: {filePath}");
|
||||
Debug.Log($"Map saved to: {filePath} (includes grid state, NPC position, and destination position)");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
Reference in New Issue
Block a user