본문 바로가기
개발/C#

유니티 C# 길찾기 코드 간단 구현 Path finding

by SPNK 2023. 8. 13.
반응형
  • 코드 작성
using UnityEngine;
using System.Collections.Generic;

public class Pathfinding : MonoBehaviour
{
    public Transform seeker;   // 시작 위치
    public Transform target;   // 목표 위치
    public LayerMask obstacleMask;
    public float nodeRadius = 0.5f;

    private Node startNode;
    private Node targetNode;

    private void Start()
    {
        // 시작 및 목표 경로를 위치를 기반으로 찾기
        startNode = NodeFromWorldPoint(seeker.position);
        targetNode = NodeFromWorldPoint(target.position);

        // 경로를 찾아 저장
        List<Node> path = FindPath(startNode, targetNode);
    }

    // 경로를 찾기
    List<Node> FindPath(Node startNode, Node targetNode)
    {
        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);

            if (currentNode == targetNode)
            {
                return RetracePath(startNode, targetNode);
            }

            foreach (Node neighbor in GetNeighbors(currentNode))
            {
                if (!neighbor.walkable || closedSet.Contains(neighbor))
                {
                    continue;
                }

                int newMovementCostToNeighbor = currentNode.gCost + GetDistance(currentNode, neighbor);
                if (newMovementCostToNeighbor < neighbor.gCost || !openSet.Contains(neighbor))
                {
                    neighbor.gCost = newMovementCostToNeighbor;
                    neighbor.hCost = GetDistance(neighbor, targetNode);
                    neighbor.parent = currentNode;

                    if (!openSet.Contains(neighbor))
                    {
                        openSet.Add(neighbor);
                    }
                }
            }
        }

        return null;
    }

    // 경로 추적
    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;
    }

    // 주변 좌표 얻기
    List<Node> GetNeighbors(Node node)
    {
        List<Node> neighbors = new List<Node>();

        for (int x = -1; x <= 1; x++)
        {
            for (int y = -1; y <= 1; y++)
            {
                if (x == 0 && y == 0)
                {
                    continue;
                }

                int checkX = node.gridX + x;
                int checkY = node.gridY + y;

                if (checkX >= 0 && checkX < GridManager.instance.gridSizeX && checkY >= 0 && checkY < GridManager.instance.gridSizeY)
                {
                    neighbors.Add(GridManager.instance.grid[checkX, checkY]);
                }
            }
        }

        return neighbors;
    }

    // 두 지점 사이의 거리
    int GetDistance(Node nodeA, Node nodeB)
    {
        int dstX = Mathf.Abs(nodeA.gridX - nodeB.gridX);
        int dstY = Mathf.Abs(nodeA.gridY - nodeB.gridY);

        return dstX + dstY;
    }

    // 월드 좌표에서 지점 좌표 얻기
    Node NodeFromWorldPoint(Vector3 worldPosition)
    {
        float percentX = (worldPosition.x + GridManager.instance.gridWorldSize.x / 2) / GridManager.instance.gridWorldSize.x;
        float percentY = (worldPosition.z + GridManager.instance.gridWorldSize.y / 2) / GridManager.instance.gridWorldSize.y;
        percentX = Mathf.Clamp01(percentX);
        percentY = Mathf.Clamp01(percentY);

        int x = Mathf.FloorToInt((GridManager.instance.gridSizeX - 1) * percentX);
        int y = Mathf.FloorToInt((GridManager.instance.gridSizeY - 1) * percentY);

        return GridManager.instance.grid[x, y];
    }
}
반응형

댓글