feat saving include npc position

This commit is contained in:
Bobby Rafael
2025-05-10 21:57:10 +07:00
parent 278e141ab9
commit d4699d6b36
5 changed files with 188 additions and 14 deletions

8
Assets/Docs.meta Normal file
View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 3c5678ad04a64df4db73256f9e23d75c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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}

View File

@ -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);
}
}

View File

@ -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>

View File

@ -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>