원클릭쓰리버그 2022. 1. 15. 21:58
728x90

다음은 테스트를 위해 

 

벽을 만들어 준다. 벽을 만들고 Material을 생성하여 Wall 오브젝트에 넣어준다.

 

그런 다음 Layer를 설정해주자. 

https://docs.unity3d.com/kr/530/Manual/Layers.html

 

유니티 - 매뉴얼: 레이어

Optimizing Shader Load Time 레이어 기반의 충돌 감지 레이어 Layers는 Cameras에 따라 씬의 일부만 렌더링 하거나, Lights에 따라 씬의 일부만 밝히는 등에 가장 자주 사용됩니다. 한편, 선택적으로 Collider를

docs.unity3d.com

여기서 볼 수 있 듯 Layer을 통해 Collider를 테스트해본다. 

 

using UnityEngine;

public class Node
{
    public bool IsWalkable;
    public Vector3 worldPosition;

    public int gridX, gridY;

    public Node(bool walkable, Vector3 pos, int x, int y)
    {
        this.IsWalkable = walkable;
        worldPosition = pos;
        gridX = x;
        gridY = y;
    }
}

Node.cs에 우선 각 노드 별 bool 값을 추가해준다.

다음은, Grid 설정.

using UnityEngine;

public class Grid : MonoBehaviour
{
    public LayerMask unwalkableMask;
    Vector2 gridSize;

    Node[,] grid;

    float nodeRadius;              //노드 반지름
    float nodeDiameter;            //노드 지름
    int gridXCount, gridYCount;   //Grid X축 Node 갯수, Grid Y축 Node 갯수
    private void Awake()
    {
        nodeRadius = 0.5f;
        nodeDiameter = nodeRadius * 2f;
        gridSize = new Vector2(30, 30);

        gridXCount = Mathf.RoundToInt(gridSize.x / nodeDiameter);
        gridYCount = Mathf.RoundToInt(gridSize.y / nodeDiameter);

        CreateGrid();

    }
   
    private void CreateGrid()
    {
        grid = new Node[gridXCount, gridYCount]; // [30,30]

        Vector3 worldBottomLeft = transform.position - new Vector3((gridSize.x * 0.5f), 0, (gridSize.y * 0.5f));

        for (int x = 0; x < gridXCount; x++)
        {
            for (int y = 0; y < gridYCount; y++)
            {
                Vector3 worldPosition = worldBottomLeft + new Vector3((x * nodeDiameter + nodeRadius), 0, (y * nodeDiameter + nodeRadius));

                bool walkable = !(Physics.CheckSphere(worldPosition, nodeRadius, unwalkableMask));

                grid[x, y] = new Node(walkable, worldPosition, x, y);
            }
        }

    }

    private void OnDrawGizmos()
    {
        Gizmos.DrawWireCube(transform.position, new Vector3(gridSize.x, 1f, gridSize.y));

        if (grid != null)
        {
            foreach (var node in grid)
            {
                Gizmos.color = node.IsWalkable ? Color.white : Color.red;

                Gizmos.DrawCube(node.worldPosition, Vector3.one * nodeDiameter);
            }
        }
    }
}

 

그리드 생성 시, Physics.CheckSphere(위치, 크기, layermask)를 확인한다. 현존하는 위치에 크기만큼 layermask가 있는지 원형으로 체크하는 함수를 bool 값으로 반환 

-> Gizmos.color = node.IsWalkable ? Color.white : Color.red; 

가상의 선을 그었을 때, 노드가 갈수 있는 곳이면 흰색, 갈 수 없는 곳이면 빨간색을 표시한다.

 

Test

 

잘 작동한다.

 

이제 플레이어를 만들어보자 

Player는 초록색으로 표시.

 

 

이제 Player의 위치값을 받아오자.

 

using UnityEngine;

public class Grid : MonoBehaviour
{
    GameObject player;
    
    public LayerMask unwalkableMask;
    Vector2 gridSize;

    Node[,] grid;

    float nodeRadius;              //노드 반지름
    float nodeDiameter;            //노드 지름
    int gridXCount, gridYCount;   //Grid X축 Node 갯수, Grid Y축 Node 갯수
    private void Awake()
    {
        player = GameObject.Find("Player");

        nodeRadius = 0.5f;
        nodeDiameter = nodeRadius * 2f;
        gridSize = new Vector2(30, 30);

        gridXCount = Mathf.RoundToInt(gridSize.x / nodeDiameter);
        gridYCount = Mathf.RoundToInt(gridSize.y / nodeDiameter);

        CreateGrid();

    }
   
    private void CreateGrid()
    {
        grid = new Node[gridXCount, gridYCount]; // [30,30]

        Vector3 worldBottomLeft = transform.position - new Vector3((gridSize.x * 0.5f), 0, (gridSize.y * 0.5f));

        for (int x = 0; x < gridXCount; x++)
        {
            for (int y = 0; y < gridYCount; y++)
            {
                Vector3 worldPosition = worldBottomLeft + new Vector3((x * nodeDiameter + nodeRadius), 0, (y * nodeDiameter + nodeRadius));

                bool walkable = !(Physics.CheckSphere(worldPosition, nodeRadius, unwalkableMask));

                grid[x, y] = new Node(walkable, worldPosition, x, y);
            }
        }

    }

    Node GetNodeFromWorldPosition(Vector3 WP)
    {
        float percen_X = (WP.x + gridSize.x * 0.5f) / gridSize.x;
        float percen_Y = (WP.z + gridSize.y * 0.5f) / gridSize.y;

        percen_X = Mathf.Clamp01(percen_X);
        percen_Y = Mathf.Clamp01(percen_Y);

        
        int x = Mathf.RoundToInt((gridXCount - 1) * percen_X);
        int y = Mathf.RoundToInt((gridYCount - 1) * percen_Y);

        return grid[x, y];
    }

    private void OnDrawGizmos()
    {
        Gizmos.DrawWireCube(transform.position, new Vector3(gridSize.x, 1f, gridSize.y));

        if (grid != null)
        {
            foreach (var node in grid)
            {
                if (node == GetNodeFromWorldPosition(player.transform.position))
                    Gizmos.color = Color.green;
                else
                    Gizmos.color = node.IsWalkable ? Color.white : Color.red;

                Gizmos.DrawCube(node.worldPosition, Vector3.one * nodeDiameter);
            }
        }
    }
}

 

먼저 시작할 때, player의 오브젝트를 설정해준다.

그리고 난 후 Gizomos를 실행할때, Player가 있는 Node를 GetNodeFromWorldPosition을 통해 받아온다.

 

플레이어의 world좌표를 받아, 위치 점을 소수점으로 반환해준다.

정해진 맵을 벗어나면 소수점이 1이상이 나오므로 범위는 Mathf.Clamp01를 통해 0~1 고정값을 받아온다. 이 값을 반올림 하여 grid의 갯수의 소수점 좌표위치로 설정해준 후 , Node 값을 반환해준다. 

 

잘 나온다. 사실 여기서 계속 값을 X, Y로 쓰다보니 실제 WorldPosition 값을 y값으로 받아와 혼란스러웠다. 3D를 오랜만에 하다보니 Z축을 생각해야한다는 점을 유의해야겠다.

 

만약, 맵을 Player가 벗어난 경우, 

정상적으로 1값을 반환하는 걸 볼 수 있다.