2 minute read

Grid 기능

이번 주는 저번 주에 계획했던 대로 기존 게임 시스템을 개선하고 추가 기능을 구현하는 주간이다. 나의 담당 업무는 Grid를 만들어 기존에 사용하던 NavMesh Agent 대신 이동 기능을 담당하게 하는 것이다. 크게 보면 다음과 같은 목표가 있다:

  1. Grid를 만들고 맵에 적용
  2. Grid를 기준으로 이동 기능 수정
  3. 단체 이동 시 Grid를 기준으로 자동 정렬되게 하기
  4. Grid에서 이동 시 이동하면서 공격하는 기능 적용
  5. Grid에서 이동 불가 지역을 피해서 유닛들이 이동하도록 길찾기 시스템 적용 (시간이 남으면 할 계획이다)

오늘은 Grid를 제작하고 적용한 다음, 이동 불가지역을 설정하고 이동 기능까지 구현했다.

public Grid(int width, int height, float cellSize, bool isPrinted = false, Vector2? origin = null)
{
    this.width = width;
    this.height = height;
    this.cellSize = cellSize;
    this.origin = origin ?? Vector2.zero;

    gridArray = new int[width, height];

    BlockOutline();

    if (isPrinted)
    {
        PrintGrid();
    }
}

그리드는 클래스 생성자로 만들 수 있는데, 기본적으로 배열과 큰 차이는 없지만 개발자가 사용하기 편하게 몇 가지 기능을 추가했다. 우선 BlockOutline은 외부 테두리를 이동 불가지역으로 설정해 gridArray의 값들을 1로 바꿔준다. PrintGrid 함수는 이동 불가지역을 빨간색으로, 평범한 지역은 하얀색으로 출력한다.

// 그리드를 화면상에 출력하는 함수
private void PrintGrid()
{
    for (int x = 0; x < width; x++)
    {
        for (int y = 0; y < height; y++)
        {
            if (y + 1 < height)
            {
                PrintLine(GetWorldPosition(x, y), GetWorldPosition(x, y + 1));
            }
            if (x + 1 < width)
            {
                PrintLine(GetWorldPosition(x, y), GetWorldPosition(x + 1, y));
            }
        }
    }

    // 상단 테두리
    for (int x = 0; x < width - 1; x++)
    {
        PrintLine(GetWorldPosition(x, height - 1), GetWorldPosition(x + 1, height - 1));
    }

    // 우측 테두리
    for (int y = 0; y < height - 1; y++)
    {
        PrintLine(GetWorldPosition(width - 1, y), GetWorldPosition(width - 1, y + 1));
    }
}

// 이동 가능한지 판단하여 줄 색깔 출력
private void PrintLine(Vector2 startPos, Vector2 endPos)
{
    Color color = Color.white;
    Vector2Int startGridPos = WorldToGridPosition(startPos);
    Vector2Int endGridPos = WorldToGridPosition(endPos);

    if (IsValidGridPosition(startGridPos) && IsValidGridPosition(endGridPos))
    {
        if (gridArray[startGridPos.x, startGridPos.y] == 1 && gridArray[endGridPos.x, endGridPos.y] == 1)
        {
            color = Color.red;
        }
    }
    Debug.DrawLine(startPos, endPos, color, 100f);
}

// 월드 포지션을 그리드 포지션으로 변환
private Vector2Int WorldToGridPosition(Vector2 worldPosition)
{
    int x = Mathf.FloorToInt((worldPosition - origin).x / cellSize);
    int y = Mathf.FloorToInt((worldPosition - origin).y / cellSize);
    return new Vector2Int(x, y);
}

PrintGrid는 전체 범위를 순회하며 줄을 출력하고, PrintLine은 각 지점이 이동 가능한지 판단하여 줄을 출력한다. 마지막으로 마우스 좌표를 받으면 Grid의 월드 좌표 중 가까운 곳에 할당한다.

public Vector2 MouseToGrid(Vector2 mousePosition)
{
    Vector2 worldPosition = Camera.main.ScreenToWorldPoint(mousePosition);
    
    int x = Mathf.FloorToInt((worldPosition - origin).x / cellSize);
    int y = Mathf.FloorToInt((worldPosition - origin).y / cellSize);

    x = Mathf.Clamp(x, 0, width - 1);
    y = Mathf.Clamp(y, 0, height - 1);

    return GetWorldPosition(x, y);
}

스크린샷 2024-07-08 194324

아직 NavMesh를 완전히 대체하지는 못했기 때문에 Grid와 NavMesh가 함께 그려져 있다. 이제 만들어진 기능을 이용해서 이동 기능을 완전히 대체할 계획이다.