본문 바로가기
🎮 게임 제작

[ Unity ] Random Walker 알고리즘으로 랜덤 맵 생성하기

by Mr.Baobab 2025. 2. 23.
반응형

🤔 랜덤 워커 알고리즘이란??

 

워커(걷는 친구)가 맵을 랜덤으로 상하좌우로 돌아다니면서 맵을 채우는 알고리즘

출처 https://www.youtube.com/watch?v=6B7yOnqpK_Y [ Game Dev Garnet 채널 ]
출처 https://www.youtube.com/watch?v=6B7yOnqpK_Y [ Game Dev Garnet 채널 ]

 

이렇게 워커가 맵을 자유롭게 돌아다니면서 맵을 넓히는 알고리즘 이다. 이때 이전 위치에 visit처리를 해줘야지 코드가 무한 반복 되는 것을 막을 수 있다. ( ex. 위->아래->위->아래->위->아래->위->아래 ... )

 

🎈결과

 

 

😎 느낀점

 

랜덤하게 움직여서 다양한 맵을 생성할 수 있다는 장점이 있지만, 맵의 크기가 너무 불규칙하다는 큰 단점 때문에 사용하지는 않을 것 같다.

💻 코드

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RandomWalker : MonoBehaviour
{
    // 맵 크기 및 노드 간 거리 설정
    public int mapSize = 16;
    public int nodeDistance = 10;
    public int walkerCnt = 1;

    // 노드와 랜덤 워커의 프리팹
    public GameObject Prefab_node;
    public GameObject Prefab_walker;

    // 현재 움직이고 있는 워커 오브젝트
    GameObject currentWorker;

    // 맵 데이터와 방문 여부를 저장하는 2차원 배열
    int[][] maps;
    bool[][] visit;

    private void Start()
    {
        // 맵 데이터 초기화
        Setup();
        
        // 코루틴을 이용해 랜덤 워커 생성 시작
        StartCoroutine(StartGenerate(walkerCnt));
    }

    // 맵 데이터 및 방문 여부 초기화
    void Setup()
    {
        maps = new int[mapSize][];
        visit = new bool[mapSize][];

        for (int i = 0; i < mapSize; i++)
        {
            maps[i] = new int[mapSize];
            visit[i] = new bool[mapSize];
        }
    }

    // 지정된 개수만큼 랜덤 워커를 생성하는 코루틴
    IEnumerator StartGenerate(int WalkerCnt)
    {
        // 워커가 맵 중앙에서 시작하도록 설정
        int startX = mapSize / 2;
        int startY = mapSize / 2;

        for (int i = 0; i < WalkerCnt; i++)
        {
            // 워커(이동 객체) 생성 및 현재 워커로 저장
            GameObject worker = Instantiate(Prefab_walker);
            currentWorker = worker;

            // 랜덤 워커 프로세스를 시작하고, 완료될 때까지 대기
            yield return StartCoroutine(RandomWalkerProcess(startY, startX));

            // 방문 데이터 초기화 (새로운 워커가 시작할 수 있도록)
            ResetVisit();
        }
    }

    // 방문 배열을 리셋하는 함수
    void ResetVisit()
    {
        for (int i = 0; i < mapSize; ++i)
        {
            for (int j = 0; j < mapSize; ++j)
            {
                visit[i][j] = false;
            }
        }
    }

    // 랜덤 워커의 이동을 처리하는 코루틴
    IEnumerator RandomWalkerProcess(int y, int x)
    {
        // 현재 위치 방문 처리
        visit[y][x] = true;

        // 이동 간격(0.25초) 대기
        yield return new WaitForSeconds(0.25f);

        // 현재 워커의 위치 갱신
        currentWorker.transform.position = new Vector3(x * nodeDistance, 1, y * nodeDistance);

        // 아직 노드가 생성되지 않은 경우, 새로운 노드 생성
        if (maps[y][x] != 1)
        {
            GameObject newObj = Instantiate(Prefab_node);
            newObj.transform.position = new Vector3(x * nodeDistance, 0, y * nodeDistance);
            newObj.transform.SetParent(this.transform);
            newObj.name = $"[{y},{x}] node";

            maps[y][x] = 1; // 해당 위치를 노드로 표시
        }

        // 랜덤 방향 선택 (0: 왼쪽, 1: 오른쪽, 2: 위쪽, 3: 아래쪽)
        int randomNum = Random.Range(0, 4);

        if (randomNum == 0 && x - 1 >= 0 && !visit[y][x - 1])
        {
            yield return StartCoroutine(RandomWalkerProcess(y, x - 1)); // 왼쪽 이동
        }

        if (randomNum == 1 && x + 1 < mapSize && !visit[y][x + 1])
        {
            yield return StartCoroutine(RandomWalkerProcess(y, x + 1)); // 오른쪽 이동
        }

        if (randomNum == 3 && y - 1 >= 0 && !visit[y - 1][x])
        {
            yield return StartCoroutine(RandomWalkerProcess(y - 1, x)); // 위쪽 이동
        }

        if (randomNum == 2 && y + 1 < mapSize && !visit[y + 1][x])
        {
            yield return StartCoroutine(RandomWalkerProcess(y + 1, x)); // 아래쪽 이동
        }
    }
}
반응형