From d4699d6b364f87a9d7f8802dedf1c0ae51c468d9 Mon Sep 17 00:00:00 2001
From: Bobby Rafael <134128710+BobbyRafael31@users.noreply.github.com>
Date: Sat, 10 May 2025 21:57:10 +0700
Subject: [PATCH] feat saving include npc position
---
Assets/Docs.meta | 8 ++
Assets/Scenes/Pathfinding.unity | 8 +-
Assets/Scripts/GridMap.cs | 172 +++++++++++++++++++++++--
Assets/Scripts/NPC.cs | 12 +-
Assets/Scripts/PathfindingUIManager.cs | 2 +-
5 files changed, 188 insertions(+), 14 deletions(-)
create mode 100644 Assets/Docs.meta
diff --git a/Assets/Docs.meta b/Assets/Docs.meta
new file mode 100644
index 0000000..c13278a
--- /dev/null
+++ b/Assets/Docs.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 3c5678ad04a64df4db73256f9e23d75c
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scenes/Pathfinding.unity b/Assets/Scenes/Pathfinding.unity
index f3b08f9..a73e332 100644
--- a/Assets/Scenes/Pathfinding.unity
+++ b/Assets/Scenes/Pathfinding.unity
@@ -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}
diff --git a/Assets/Scripts/GridMap.cs b/Assets/Scripts/GridMap.cs
index be39f7c..ad1c6f2 100644
--- a/Assets/Scripts/GridMap.cs
+++ b/Assets/Scripts/GridMap.cs
@@ -489,6 +489,22 @@ public class GridMap : MonoBehaviour
}
}
+ ///
+ /// Simple class to store grid positions for serialization
+ ///
+ [System.Serializable]
+ public class SerializablePosition
+ {
+ public float x;
+ public float y;
+
+ public SerializablePosition(float x, float y)
+ {
+ this.x = x;
+ this.y = y;
+ }
+ }
+
///
/// Kelas untuk menyimpan status grid untuk keperluan save/load
///
@@ -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
}
///
@@ -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(json);
+
+ // Configure deserializer settings to handle missing properties (compatibility)
+ JsonSerializerSettings settings = new JsonSerializerSettings
+ {
+ MissingMemberHandling = MissingMemberHandling.Ignore
+ };
+
+ GridState gridState = JsonConvert.DeserializeObject(json, settings);
// Periksa apakah ukuran grid dalam file sesuai dengan grid saat ini
if (gridState.walkableStates.GetLength(0) != numX ||
gridState.walkableStates.GetLength(1) != numY)
{
- //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}");
+ }
+ }
+
+ ///
+ /// Find a walkable node and set the NPC position to it
+ ///
+ private void FindWalkableNodeAndSetNPC()
+ {
+ // Find first walkable node
+ for (int i = 0; i < numX; i++)
+ {
+ for (int j = 0; j < numY; j++)
+ {
+ GridNode node = GetGridNode(i, j);
+ if (node != null && node.IsWalkable)
+ {
+ npc.SetStartNode(node);
+ return;
+ }
+ }
}
}
@@ -1007,4 +1121,46 @@ public class GridMap : MonoBehaviour
}
}
}
+
+ ///
+ /// Find a walkable node and set the destination position to it
+ ///
+ private void FindWalkableNodeAndSetDestination()
+ {
+ // Try to place destination in the far corner from the NPC
+ int npcX = (int)(npc.transform.position.x / GridNodeWidth);
+ int npcY = (int)(npc.transform.position.y / GridNodeHeight);
+
+ // Try opposite corner first
+ int destX = numX - 1;
+ int destY = numY - 1;
+
+ GridNode destinationNode = GetGridNode(destX, destY);
+ if (destinationNode != null && destinationNode.IsWalkable)
+ {
+ SetDestination(destX, destY);
+ return;
+ }
+
+ // If the opposite corner isn't walkable, find any walkable node that's different from NPC position
+ for (int i = numX - 1; i >= 0; i--)
+ {
+ for (int j = numY - 1; j >= 0; j--)
+ {
+ // Skip the node where NPC is
+ if (i == npcX && j == npcY)
+ continue;
+
+ GridNode node = GetGridNode(i, j);
+ if (node != null && node.IsWalkable)
+ {
+ SetDestination(i, j);
+ return;
+ }
+ }
+ }
+
+ // If no suitable node found, use the NPC node as a fallback
+ SetDestination(npcX, npcY);
+ }
}
\ No newline at end of file
diff --git a/Assets/Scripts/NPC.cs b/Assets/Scripts/NPC.cs
index 0265ee1..e607e9b 100644
--- a/Assets/Scripts/NPC.cs
+++ b/Assets/Scripts/NPC.cs
@@ -254,6 +254,9 @@ public class NPC : MonoBehaviour
// Pre-allocate visualizationSteps with estimated capacity to avoid reallocations
visualizationSteps = new List(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;
}
-
+
///
/// Setup callbacks for tracking nodes in open/closed lists and visualization
///
diff --git a/Assets/Scripts/PathfindingUIManager.cs b/Assets/Scripts/PathfindingUIManager.cs
index 4cc3c3a..a98f602 100644
--- a/Assets/Scripts/PathfindingUIManager.cs
+++ b/Assets/Scripts/PathfindingUIManager.cs
@@ -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)");
}
///