Unity/Study

유니티 로그라이크 방 생성

홍삼맛 2023. 12. 15. 20:50

기능 구현

GameScene.cs

public class GameScene : MonoBehaviour
{
    #region Field

    [SerializeField] private int roomAmount;

    private Room _roomPrefab;
    private Room[,] _roomArray = new Room[20, 20];

    #endregion

    #region Init

    private void Start()
    {
        Main.Resource.Initialize();

        // #1. 방 생성
        _roomPrefab = Main.Resource.GetObject("Room").GetComponent<Room>();
        CreateLevel();

        // #2. 플레이어 생성
        Main.Game.SpawnPlayer(this.transform);
    }

    #endregion

    #region Room Method

    /// <summary>
    /// 던전 생성
    /// </summary>
    private void CreateLevel()
    {
        // 방 생성을 위한 좌표 리스트
        List<Vector2Int> alternativeRoomList = new();
        List<Vector2Int> hasBeenRemoveRoomList = new();

        // 시작 방 생성
        int outsetX = _roomArray.GetLength(0) / 2;
        int outsetY = _roomArray.GetLength(1) / 2;
        Room startRoom = _roomArray[outsetX, outsetY] = CreateRoom(new Vector2Int(outsetX, outsetY));

        // 방 생성 델리게이트
        Action<int, int> action = (newX, newY) =>
        {
            Vector2Int coordinate = new(newX, newY);

            if (_roomArray[newX, newY] == null)
            {
                if (alternativeRoomList.Contains(coordinate))
                {
                    alternativeRoomList.Remove(coordinate);
                    hasBeenRemoveRoomList.Add(coordinate);
                }
                else if (!hasBeenRemoveRoomList.Contains(coordinate))
                {
                    alternativeRoomList.Add(coordinate);
                }
            }
        };

        // 지정된 수 만큼 방 생성
        for (int i = 1; i < roomAmount; i++)
        {
            int x = startRoom.coordinate.x;
            int y = startRoom.coordinate.y;

            action(x + 1, y);
            action(x - 1, y);
            action(x, y + 1);
            action(x, y - 1);

            Vector2Int newRoomCoordinate = alternativeRoomList[UnityEngine.Random.Range(0, alternativeRoomList.Count)];
            startRoom = _roomArray[newRoomCoordinate.x, newRoomCoordinate.y] = CreateRoom(newRoomCoordinate);
            alternativeRoomList.Remove(newRoomCoordinate);
        }
    }

    /// <summary>
    /// 해당 좌표에 방 생성
    /// </summary>
    /// <param name="coordinate"></param>
    /// <returns></returns>
    private Room CreateRoom(Vector2Int coordinate)
    {
        Room newRoom = Instantiate(_roomPrefab, transform);
        newRoom.coordinate = coordinate;

        int x = coordinate.x - _roomArray.GetLength(0) / 2;
        int y = coordinate.y - _roomArray.GetLength(1) / 2;
        newRoom.transform.position = new Vector2(y * Globals.RoomWidth, x * Globals.RoomHeight);

        return newRoom;
    }

    #endregion
}

CreateLevel Method

private void CreateLevel()
{
    // 방 생성을 위한 좌표 리스트
    List<Vector2Int> alternativeRoomList = new();
    List<Vector2Int> hasBeenRemoveRoomList = new();

    // 시작 방 생성
    int outsetX = _roomArray.GetLength(0) / 2;
    int outsetY = _roomArray.GetLength(1) / 2;
    Room startRoom = _roomArray[outsetX, outsetY] = CreateRoom(new Vector2Int(outsetX, outsetY));

    // 방 생성 델리게이트
    Action<int, int> action = (newX, newY) =>
    {
        Vector2Int coordinate = new(newX, newY);
        
        if (_roomArray[newX, newY] == null)
        {
            if (alternativeRoomList.Contains(coordinate))
            {
                alternativeRoomList.Remove(coordinate);
                hasBeenRemoveRoomList.Add(coordinate);
            }
            else if (!hasBeenRemoveRoomList.Contains(coordinate))
            {
                alternativeRoomList.Add(coordinate);
            }
        }
    };
    
    // 지정된 수 만큼 방 생성
    for (int i = 1; i < roomAmount; i++)
    {
        int x = startRoom.coordinate.x;
        int y = startRoom.coordinate.y;
        
        action(x + 1, y);
        action(x - 1, y);
        action(x, y + 1);
        action(x, y - 1);
        
        Vector2Int newRoomCoordinate = alternativeRoomList[UnityEngine.Random.Range(0, alternativeRoomList.Count)];
        startRoom = _roomArray[newRoomCoordinate.x, newRoomCoordinate.y] = CreateRoom(newRoomCoordinate);
        alternativeRoomList.Remove(newRoomCoordinate);
    }
}
  • _roomArray는 2차원 배열로, 방이 생성된 위치를 기록한다
  • alternativeRoomList는 새로운 방을 생성할 수 있는 후보 좌표를 저장한다
  • hasBeenRemoveRoomList는 이미 방이 생성되었지만 나중에 제거될 방의 좌표를 저장한다
  • 시작 지점으로 _roomArray의 중심으로부터 start 방을 생성한다
  • action 델리게이트 : _roomArray에서 해당 좌표에 방이 없으면 후보 리스트에 추가하거나 삭제한다
  • 방 생성 for 문 : 현재 방에서 상하좌우 방향으로 각각 좌표를 체크하고 후보 리스트에 추가하거나 삭제한다
  • 후보 리스트에서 무작위로 좌표를 선택하여 새로운 방을 생성하고, 해당 좌표를 후보 리스트에서 삭제한다

CreateRoom Method

private Room CreateRoom(Vector2Int coordinate)
{
    Room newRoom = Instantiate(_roomPrefab, transform);
    newRoom.coordinate = coordinate;
    
    int x = coordinate.x - _roomArray.GetLength(0) / 2;
    int y = coordinate.y - _roomArray.GetLength(1) / 2;
    newRoom.transform.position = new Vector2(y * Globals.RoomWidth, x * Globals.RoomHeight);
    
    return newRoom;
}
  • 좌표에 따라 새로운 방을 생성, 좌표를 기반으로 방의 위치를 설정하고, 생성된 방을 반환한다

구현 결과

방 10개 랜덤 생성 1
방 10개 랜덤 생성 2
방 50개 랜덤 생성