오늘은 스파르타 코딩 클럽 unity 게임 개발 과정 47일차(던전 생성기)

2024. 1. 9. 08:46스파르타코딩클럽 게임개발

반응형

던전을 만드는 DungeonGenerator을 만들었다.

이 부분을 다루는 내용은 워낙 길어 추후에 정리해서 다시 올리도록 하는 편이 좋을것 같다.

하지만 여기서 사용한 알고리즘이 몇개 있는데 그 부분을 소개해 보도록 하겠다.

 

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Random = UnityEngine.Random;

public static class ProveduralGenerationAlgorithms
{
    public static List<Vector2Int> PositionPoints = new List<Vector2Int>();
    public static HashSet<Vector2Int> SimpleRandomWalk(Vector2Int startPosition, int walkLength)
    {
        HashSet<Vector2Int> path = new HashSet<Vector2Int>();

        path.Add(startPosition);
        var previousPosition = startPosition;

        for (int i = 0; i < walkLength; i++)
        {
            var newPosition = previousPosition + Direction2D.GetRandomCardinalDirection();
            path.Add(newPosition);
            previousPosition = newPosition;
        }
        return path;
    }
    public static List<Vector2Int> RandomWalkCorridor(Vector2Int startPosition, int corridorLength)
    {
        List<Vector2Int> corridor = new List<Vector2Int>();
        var direction = Direction2D.GetRandomCardinalDirection();
        var currentPosition = startPosition;
        corridor.Add(currentPosition);

        for (int i = 0; i < corridorLength; i++)
        {
            currentPosition += direction;
            corridor.Add(currentPosition);
        }
        return corridor;
    }

    public static List<BoundsInt> BinarySpacePartitioning(BoundsInt spaceToSprit, int minWidth, int minHeigth)
    {
        Queue<BoundsInt> roomsQueue = new Queue<BoundsInt>();
        List<BoundsInt> roomsList = new List<BoundsInt>();
        roomsQueue.Enqueue(spaceToSprit);
        while (roomsQueue.Count > 0)
        {
            var room = roomsQueue.Dequeue();
            if (room.size.y >= minHeigth && room.size.x >= minWidth)
            {
                if (Random.value < 0.5f)
                {
                    if (room.size.y >= minHeigth * 2)
                    {
                        SplitHorizontally(minHeigth, roomsQueue, room);
                    }
                    else if (room.size.x >= minWidth * 2)
                    {
                        SpiltVertically(minWidth, roomsQueue, room);
                    }
                    else if (room.size.x >= minWidth && room.size.y >= minHeigth)
                    {
                        roomsList.Add(room);
                    }
                }
                else
                {
                    if (room.size.x >= minWidth * 2)
                    {
                        SpiltVertically(minWidth, roomsQueue, room);
                    }
                    else if (room.size.y >= minHeigth * 2)
                    {
                        SplitHorizontally(minHeigth, roomsQueue, room);
                    }
                    else if (room.size.x >= minWidth && room.size.y >= minHeigth)
                    {
                        roomsList.Add(room);
                    }
                }
            }
        }
        return roomsList;
    }

    private static void SpiltVertically(int minWidth, Queue<BoundsInt> roomsQueue, BoundsInt room)
    {
        var xSplit = Random.Range(1, room.size.x);
        BoundsInt room1 = new BoundsInt(room.min, new Vector3Int(xSplit, room.size.y, room.size.z));
        BoundsInt room2 = new BoundsInt(new Vector3Int(room.min.x + xSplit, room.min.y, room.min.z),
            new Vector3Int(room.size.x - xSplit, room.size.y, room.size.z));
        roomsQueue.Enqueue(room1);
        roomsQueue.Enqueue(room2);
    }

    private static void SplitHorizontally(int minHeigth, Queue<BoundsInt> roomsQueue, BoundsInt room)
    {
        var ySplit = Random.Range(1, room.size.y);
        BoundsInt room1 = new BoundsInt(room.min, new Vector3Int(room.size.x, ySplit, room.size.z));
        BoundsInt room2 = new BoundsInt(new Vector3Int(room.min.x, room.min.y + ySplit, room.min.z),
            new Vector3Int(room.size.x, room.size.y - ySplit, room.size.z));
        roomsQueue.Enqueue(room1);
        roomsQueue.Enqueue(room2);
    }
}

public static class Direction2D
{
    public static List<Vector2Int> cardinalDirectionsList = new List<Vector2Int>
    {
        new Vector2Int(0,1),  // UP
        new Vector2Int(1,0),  // RIGHT
        new Vector2Int(0,-1), // DOWN
        new Vector2Int(-1,0), // LEFT
    };
    public static List<Vector2Int> diagonalDirectionsList = new List<Vector2Int>
    {
        new Vector2Int(1,1),  // UP-RIGHT
        new Vector2Int(1,-1),  // RIGHT -DOWN
        new Vector2Int(-1,-1), // DOWN - LEFT
        new Vector2Int(-1,1), // LEFT - UP
    };

    public static List<Vector2Int> eightDirectionsList = new List<Vector2Int>
    {
        new Vector2Int(0,1), //UP
        new Vector2Int(1,1), //UP-RIGHT
        new Vector2Int(1,0), //RIGHT
        new Vector2Int(1,-1), //RIGHT-DOWN
        new Vector2Int(0, -1), // DOWN
        new Vector2Int(-1, -1), // DOWN-LEFT
        new Vector2Int(-1, 0), //LEFT
        new Vector2Int(-1, 1) //LEFT-UP
    };

    public static Vector2Int GetRandomCardinalDirection()
    {
        return cardinalDirectionsList[Random.Range(0, cardinalDirectionsList.Count)];
    }
}

1. 간단한 랜덤 워크 (Simple Random Walk)

목적: 시작 위치에서 랜덤한 경로를 생성한다.
작동 방식: 시작 위치에서 시작하여 지정된 길이만큼 반복하면서 매 단계마다 랜덤한 기본 방향(상, 하, 좌, 우)으로 이동한다.
활용: 미로 생성, 지형 생성 등에 사용될 수 있다.

2. 랜덤 워크 복도 (Random Walk Corridor)

목적: 시작 위치에서 일정한 방향으로 이동하면서 복도를 생성한다.
작동 방식: 먼저 랜덤한 방향을 결정하고, 그 방향으로 지정된 길이만큼 이동하면서 복도를 생성한다.
활용: 던전 또는 복도 형태의 지형 생성에 적합한다.

3. 이진 공간 분할 (Binary Space Partitioning)

목적: 주어진 공간을 분할하여 여러 개의 방이나 영역을 생성한다.
작동 방식:
주어진 공간을 수직 또는 수평으로 랜덤하게 분할한다.
분할된 각 공간이 최소 너비와 높이 조건을 만족하면 추가로 분할하거나 최종 방 목록에 추가한다.
이 과정을 모든 공간에 대해 반복한다.
활용: 복잡한 레이아웃의 던전이나 방 구조 생성에 사용된다.

4. 방향 관련 유틸리티 (Direction2D)

목적: 2D 게임에서 사용할 수 있는 다양한 방향을 제공한다.
제공 방향:
cardinalDirectionsList: 기본 방향 (상, 하, 좌, 우)
diagonalDirectionsList: 대각선 방향
eightDirectionsList: 기본 및 대각선 방향을 모두 포함하는 8방향
활용: 랜덤 워크 및 기타 알고리즘에서 방향 결정에 사용된다.

반응형