173 lines
5.2 KiB
C#
173 lines
5.2 KiB
C#
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
public class AStar_Manhattan : MonoBehaviour
|
|
{
|
|
public MengaturGrid grid;
|
|
public Transform pintuDepan;
|
|
public Transform pintuDapur;
|
|
public Transform pintuGarasi;
|
|
|
|
|
|
public List<Node> FindPath(Vector3 startPos, Vector3 targetPos)
|
|
{
|
|
grid.visitedNodeCount = 0;
|
|
|
|
Node startNode = grid.NodeFromWorldPoint(startPos);
|
|
Node targetNode = grid.NodeFromWorldPoint(targetPos);
|
|
|
|
List<Node> openSet = new List<Node>();
|
|
HashSet<Node> closedSet = new HashSet<Node>();
|
|
|
|
openSet.Add(startNode);
|
|
|
|
while (openSet.Count > 0)
|
|
{
|
|
Node currentNode = openSet[0];
|
|
for (int i = 1; i < openSet.Count; i++)
|
|
{
|
|
if (openSet[i].fCost < currentNode.fCost ||
|
|
(openSet[i].fCost == currentNode.fCost && openSet[i].hCost < currentNode.hCost))
|
|
{
|
|
currentNode = openSet[i];
|
|
}
|
|
}
|
|
|
|
openSet.Remove(currentNode);
|
|
closedSet.Add(currentNode);
|
|
grid.visitedNodeCount++;
|
|
|
|
if (currentNode == targetNode)
|
|
return RetracePath(startNode, targetNode);
|
|
|
|
foreach (Node neighbour in grid.GetNeighbours(currentNode))
|
|
{
|
|
if (!neighbour.walkable || closedSet.Contains(neighbour)) continue;
|
|
|
|
int newCost = currentNode.gCost + GetDistance(currentNode, neighbour);
|
|
|
|
if (newCost < neighbour.gCost || !openSet.Contains(neighbour))
|
|
{
|
|
neighbour.gCost = newCost;
|
|
neighbour.hCost = GetManhattanDistance(neighbour, targetNode);
|
|
neighbour.parent = currentNode;
|
|
|
|
if (!openSet.Contains(neighbour))
|
|
openSet.Add(neighbour);
|
|
}
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public PathResult FindCombinedPath(Vector3 start, Vector3 pintu, Vector3 zonaAman, string label = "")
|
|
{
|
|
var stopwatch = new System.Diagnostics.Stopwatch();
|
|
stopwatch.Start();
|
|
|
|
grid.visitedNodeCount = 0;
|
|
|
|
List<Node> path1 = FindPath(start, pintu);
|
|
Node lastNodePath1 = grid.NodeFromWorldPoint(pintu);
|
|
|
|
List<Node> path2 = FindPath(pintu, zonaAman);
|
|
Node lastNodePath2 = grid.NodeFromWorldPoint(zonaAman);
|
|
|
|
if (path1 == null || path2 == null) return null;
|
|
|
|
path1.AddRange(path2);
|
|
|
|
int panjangJalur = path1.Count;
|
|
|
|
float langkahUnit = 1f;
|
|
float kecepatanPlayer = 3f;
|
|
|
|
// Penalty
|
|
float penaltyCost = GetExitPenalty(pintu);
|
|
|
|
// Hitung total cost
|
|
int totalCost = lastNodePath1.gCost + lastNodePath2.gCost + Mathf.RoundToInt(penaltyCost * 10f);
|
|
|
|
float waktuTempuh = totalCost / (kecepatanPlayer * 10f);
|
|
|
|
stopwatch.Stop();
|
|
float waktuKomputasi = stopwatch.ElapsedMilliseconds / 1000f;
|
|
int jumlahNodeDikunjungi = grid.visitedNodeCount;
|
|
|
|
Debug.Log(
|
|
$"[Evaluasi A* - {label}]\n" +
|
|
$"- Waktu Komputasi: {waktuKomputasi:F4} detik\n" +
|
|
$"- Jumlah Node Dikunjungi: {jumlahNodeDikunjungi}\n" +
|
|
$"- Panjang Jalur: {panjangJalur} langkah\n" +
|
|
$"- Total Cost: {totalCost}\n" +
|
|
$"- Estimasi Waktu Tempuh: {waktuTempuh:F2} detik\n" +
|
|
$"- Pintu Keluar: {label}"
|
|
);
|
|
|
|
return new PathResult
|
|
{
|
|
path = path1,
|
|
totalCost = totalCost,
|
|
keterangan = label,
|
|
panjangJalur = panjangJalur,
|
|
estimasiWaktu = waktuTempuh
|
|
};
|
|
}
|
|
|
|
public List<PathResult> FindMultiplePathsMelaluiPintu(Vector3 startPos, Vector3 zonaAman, List<Vector3> pintuList, int maxJalur, List<string> namaPintu)
|
|
{
|
|
List<PathResult> semuaJalur = new List<PathResult>();
|
|
|
|
for (int i = 0; i < pintuList.Count; i++)
|
|
{
|
|
Vector3 pintu = pintuList[i];
|
|
string label = (namaPintu != null && i < namaPintu.Count) ? namaPintu[i] : $"Pintu ({pintu.x:F0},{pintu.z:F0})";
|
|
|
|
PathResult jalur = FindCombinedPath(startPos, pintu, zonaAman, label);
|
|
if (jalur != null)
|
|
semuaJalur.Add(jalur);
|
|
}
|
|
|
|
semuaJalur.Sort((a, b) => a.totalCost.CompareTo(b.totalCost));
|
|
return semuaJalur.GetRange(0, Mathf.Min(maxJalur, semuaJalur.Count));
|
|
}
|
|
|
|
List<Node> RetracePath(Node startNode, Node endNode)
|
|
{
|
|
List<Node> path = new List<Node>();
|
|
Node currentNode = endNode;
|
|
|
|
while (currentNode != startNode)
|
|
{
|
|
path.Add(currentNode);
|
|
currentNode = currentNode.parent;
|
|
}
|
|
|
|
path.Reverse();
|
|
return path;
|
|
}
|
|
|
|
int GetDistance(Node a, Node b)
|
|
{
|
|
return 10 * (Mathf.Abs(a.gridX - b.gridX) + Mathf.Abs(a.gridY - b.gridY));
|
|
}
|
|
|
|
int GetManhattanDistance(Node a, Node b)
|
|
{
|
|
return 10 * (Mathf.Abs(a.gridX - b.gridX) + Mathf.Abs(a.gridY - b.gridY));
|
|
}
|
|
|
|
float GetExitPenalty(Vector3 pintu)
|
|
{
|
|
if (pintuDepan != null && Vector3.Distance(pintu, pintuDepan.position) < 1f)
|
|
return 2f; // Pintu depan
|
|
else if (pintuDapur != null && Vector3.Distance(pintu, pintuDapur.position) < 1f)
|
|
return 1f; // Pintu dapur
|
|
else if (pintuGarasi != null && Vector3.Distance(pintu, pintuGarasi.position) < 1f)
|
|
return 1f; // Pintu garasi
|
|
|
|
return 0f; // Tidak ada penalty
|
|
}
|
|
}
|