2d 자동 맵생성

맵 자동 생성(5)

게임만드는학생 2025. 5. 11. 19:14

이번 글에서는 통로를 완성시켜보겠다. 

 

내가 몇번 살펴본 결과, 통로는 

이렇게 2가지이다. 

 

ㄷ자, ㄹ자는 등장하지 않는다. 따라서 일자와 위 사진 같은 경로만 만들면 된다.

그럼 어떻게할까? 간단하게 ㄱ자 2개를 만들기로 했다. 

 

문과 문 사이에 지정된 범위 안에서 중간위치를 랜덤생성한 후, 시작점 - 중간위치, 중간위치 - 끝점 을 만들어주면 된다. 

 

랜덤이 문앞에서 일어나면 부자연스러워지기 때문에 25%~75% 지점를 범위로 지정했다. 

만약 각 x나 y의 값이 동일하다면 일자도 자연스레 만들어지기 때문에 이 방법을 택한다. 

 

void GenerateCorridors(GameObject parent,Vector2 v, Vector2 v2, bool mode/* true : x -> y , false : y -> x */)
{
    Debug.Log("mode : " + mode);
    Vector2Int pos = new Vector2Int((int)v.x, (int)v.y);
    Vector2Int pos2 = new Vector2Int((int)v2.x, (int)v2.y);
    Debug.Log(pos.ToString());
    Debug.Log(pos2.ToString());
    if (mode)
    {
        // for문 수치 파악해보기
        for (int x = pos.x; x != (pos2.x + ((pos2.x > pos.x) ? 1 : -1)); x += (pos2.x > pos.x) ? 1 : -1)
        {
            GameObject go = Instantiate(corridorTile, new Vector2(x, pos.y), Quaternion.identity);
            go.transform.SetParent(parent.transform, true);
        }

        for (int y = pos.y; y != (pos2.y + ((pos2.y > pos.y) ? 1 : -1)); y += (pos2.y > pos.y) ? 1 : -1)
        {
            GameObject go = Instantiate(corridorTile, new Vector2(pos2.x, y), Quaternion.identity);
            go.transform.SetParent(parent.transform, true);
        }
    }
    else
    {
        for (int y = pos.y; y != (pos2.y + ((pos2.y > pos.y) ? 1 : -1)); y += (pos2.y > pos.y) ? 1 : -1)
        {
            GameObject go = Instantiate(corridorTile, new Vector2(pos.x, y), Quaternion.identity);
            go.transform.SetParent(parent.transform, true);
        }
        for (int x = pos.x; x != (pos2.x + ((pos2.x > pos.x) ? 1 : -1)); x += (pos2.x > pos.x) ? 1 : -1)
        {
            GameObject go = Instantiate(corridorTile, new Vector2(x, pos2.y), Quaternion.identity);
            go.transform.SetParent(parent.transform, true);
        }
    }
    
}

이게 함수이다. 

 

IEnumerator GenerateCorridor()
{
	// kruscal 알고리즘 실행..

	foreach (Edge e in mst)
    {
   		Vector2 randPos = GetRandomPos(door.transform.position, door2.transform.position);
        bool mode;
        Vector2 inputVec = door.transform.position;
        Vector2 inputVec2 = door2.transform.position;
        if (snappedDir == Vector2.up || snappedDir == Vector2.down)
        {
            mode = false;
            if(snappedDir == Vector2.up)
            {
                inputVec.y++; inputVec2.y--;
            }
            else
            {
                inputVec2.y++; inputVec.y--;
            }

        }
        else
        {
            mode = true;
            if(snappedDir == Vector2.right)
            {
                inputVec2.x--;
            }
            else
            {
                inputVec.x--;
            }
        }
        CreateCorridors(corridors, inputVec, randPos, mode);
        CreateCorridors(corridors, randPos, inputVec2, !mode);
    }
	
}

여기서 각 edge 마다 통로를 생성해준다. GetRandomPos를 통해서 중간지점 랜덤값을 받아온다. 

그 값을  CreateCorridors 함수에 넘겨줘서 ㄱ자 통로를 생성하는 것이다. 

if-else 문은 내가 한 프로젝트 특성상 다듬는 작업에 불과하다. 봐야할 것은 mode 뿐이다.

CreateCorridors함수는 mode변수로 true일때는 x-> y 순으로 ㄱ 같은 통로를 만들고, false 일때는 y->x 순으로 ㄴ 같은 모양을 만든다.

 

Vector2 GetRandomPos(Vector3 pos, Vector3 pos2)
{
    float maxY = Mathf.Max(pos.y, pos2.y);
    float minY = Mathf.Min(pos.y, pos2.y);

    float maxX = Mathf.Max(pos.x, pos2.x);
    float minX = Mathf.Min(pos.x, pos2.x);

    int offsetY = Mathf.RoundToInt((maxY - minY) / 4);
    int offsetX = Mathf.RoundToInt((maxX - minX) / 4);

    
    float randX = Random.Range(minX + offsetX, maxX - offsetX);
    float randY = Random.Range(minY + offsetY, maxY - offsetY);
    randX = Mathf.Round(randX);
    randY = Mathf.Round(randY);

    Debug.Log("RandPoint : "+randX +", "+randY);
    return new Vector2(randX, randY);
}

x, y 모두 25% ~ 75% 사이 랜덤값을 생성한다. 

 

if (mode)
{
    // for문 수치 파악해보기
    for (int x = pos.x; x != (pos2.x + ((pos2.x > pos.x) ? 1 : -1)); x += (pos2.x > pos.x) ? 1 : -1)
    {
        GameObject go = Instantiate(corridorTile, new Vector2(x, pos.y), Quaternion.identity);
        go.transform.SetParent(parent.transform, true);
    }

    for (int y = pos.y; y != (pos2.y + ((pos2.y > pos.y) ? 1 : -1)); y += (pos2.y > pos.y) ? 1 : -1)
    {
        GameObject go = Instantiate(corridorTile, new Vector2(pos2.x, y), Quaternion.identity);
        go.transform.SetParent(parent.transform, true);
    }
}
else
{
    for (int y = pos.y; y != (pos2.y + ((pos2.y > pos.y) ? 1 : -1)); y += (pos2.y > pos.y) ? 1 : -1)
    {
        GameObject go = Instantiate(corridorTile, new Vector2(pos.x, y), Quaternion.identity);
        go.transform.SetParent(parent.transform, true);
    }
    for (int x = pos.x; x != (pos2.x + ((pos2.x > pos.x) ? 1 : -1)); x += (pos2.x > pos.x) ? 1 : -1)
    {
        GameObject go = Instantiate(corridorTile, new Vector2(x, pos2.y), Quaternion.identity);
        go.transform.SetParent(parent.transform, true);
    }
}

CreateCorridors 의 로직을 살펴보겠다. mode에 따라서 for문 순서만 바뀐다. 

위치에 따라서 생성해주는 코드인데, for문에 일일이 비교해서 pos.x 가 큰지 pos2.x 가 큰지 다 변수에 저장하고 비교하는 것은 코드가 더러워질것 같아서 ai에 물어보니 이런 우아한 코드를 알려줬다.

 

for (int x = pos.x; x != (pos2.x + ((pos2.x > pos.x) ? 1 : -1)); x += (pos2.x > pos.x) ? 1 : -1)

pos.x 로 시작한다. x가 pos2.x 보다 한칸 더 나아간 상태인지를 체크한다. (pos2.x 일때까지 실해하기 위함이다.)

x의 증가값을 삼항연산자로 정한다. 

만약 pos2.x가 더 오른쪽에 있다면 1이 아니라면 -1이 자동으로 연산된다. 

 

이로써 맵자동생성은 거의거의 완료되었다고 볼 수 있다. 

남은 것은 방을 생성하는 코드 쪽에 추가해서 방안의 장애물, 구조물 등을 어떻게 추가할지만 생각하면 된다. 

 

보면 방끼리 연결이 아주 잘 되어있다. 

 

구조물 생성, 장애물 생성까지 다뤄서 맵 자동 생성을 마무리해보겠다.

'2d 자동 맵생성' 카테고리의 다른 글

맵 자동 생성(7)  (0) 2025.05.13
맵 자동 생성(6)  (0) 2025.05.12
맵 자동 생성(4)  (0) 2025.05.11
맵 자동 생성(3)  (0) 2025.05.10
맵 자동 생성(2)  (1) 2025.05.09