본문 바로가기
개발/기본) 알고리즘

알고리즘) Procedural Dungeon Generation Algorithm(절차적 던전 생성)

by 테샤르 2021. 12. 30.

Procedural Dungeon Generation Algorithm(절차적 던전 생성)

 

이리저리 포럼을 찾다가 절차적 던전 생성 알고리즘이라는 소스코드와 자료를 보게 되어서 포스팅하게 되었다.

간단하게 절차적 던전 생성은 임의의 크기인 생성될 오브젝트를 생성하고 난 이후에 겹치지 않도록 공간을 비집고 해당 공간을 연결해주는 방식으로 재조립하는 알고리즘이었다.

반응형

테스트해본 소스코드로 영상은 다음과 같다.

< 생성 테스트 >

 

던전을 생성하는 여러가지 방법이 있을 텐데 이런 방식이 있다는 것에 대해서 알게 되어서 재미있다고 생각했다.

Unity 코드로는 룰에 의거한 임의의 DungeonRoom을 생성하고

그 이후에 FixedUpdate를 통해서 겹치지 않는 방으로 해당 방을 이동시키는 방식을 사용한다.

이후에 해당 방들을 연결한다.

반응형

< DungeonGenerator >

 public IEnumerator GenerateFloor(FloorRules floorRules)
        {
            //Create room placer for this floor and generate rooms with correctly shaped colliders
            InitialRoomPlacerAgent roomPlacer = new InitialRoomPlacerAgent(ref _random, floorRules);
            _dungeon.Layout.AddRooms(roomPlacer.GenerateRooms());

            //Rooms have rigidbodies and are being simulated
            Debug.Log("Simulating rooms...");

            //Wait for simulation to end
            yield return new WaitForFixedUpdate();
            yield return WaitForBodySimulationEnd();

            //Convert to trigger, end simulation of bodies
            _dungeon.Layout.ApplyToEachRoom((DungeonRoom r) => r.Collider.isTrigger = true);
            //Then snap all rooms to the grid (overlap should not happen)
            _dungeon.Layout.ApplyToEachRoom((DungeonRoom r) => GridUtility.SnapToCell(_dungeon.WorldGrid, r));

            yield return new WaitForFixedUpdate();
            yield return WaitForBodySimulationEnd();

            //NOTE: Possibly check if rooms overlap?
            Debug.Log("Physical simulation of dungeon rooms complete...");

            //Search and set neighbours of the rooms
            NeighbourSearchAgent neighbourSearcher = new NeighbourSearchAgent(_dungeon.Layout);
            neighbourSearcher.SetNeighboursAllRooms();

            TilePlacer tilePlacer = new TilePlacer(_dungeon.GroundTilemap, _dungeon.ObstacleTilemap);
            _dungeon.Layout.ApplyToEachRoom(r => tilePlacer.TileStructure(r));

            //Generate graph of connected rooms
            GraphGenAgent graphGen = new GraphGenAgent(_dungeon.Layout, floorRules);
            graphGen.Execute();
        }

 

< DungoenRoom >

[ExecuteInEditMode]
    public class DungeonRoom : MonoBehaviour
    {
        [SerializeField] int _id;
        static int currentID = -1;

        //For simulation
        [SerializeField] Collider2D _collider;
        [SerializeField] Rigidbody2D _rb;
        Vector3 _lastFixedFramePosition;
        [SerializeField] Vector3 _velocity;

        [Header("Neighbourhood")]
        [SerializeField] List<DungeonRoom> _neighbours;

        [Header("Rules")]
        [SerializeField] public RoomRules _rules;

        public int ID { set => _id = value; get => _id; }
        public Collider2D Collider { get => _collider; }
        public Rigidbody2D Rb { get => _rb; }
        public float Speed { get => Vector3.Magnitude(_velocity); }
        public List<DungeonRoom> Neighbours { get => _neighbours; }

        public void Awake()
        {
            _collider = GetComponent<Collider2D>();
            _rb = GetComponent<Rigidbody2D>();

            _lastFixedFramePosition = transform.position;
        }

        private void FixedUpdate()
        {
            SetVelocity();
        }

        /// <summary>
        /// Sets velocity based on former position
        /// </summary>
        public void SetVelocity()
        {
            _velocity = transform.position - _lastFixedFramePosition;
            _lastFixedFramePosition = transform.position;
        }

        public void SetNeighbourhood(List<DungeonRoom> neighbours)
        {
            _neighbours = neighbours;
        }

        public void SetName()
        {
            gameObject.name = "Room " + _id.ToString();
        }

        public static int GetNextID()
        {
            //I know I can just do ++currentID, but readable code is better!
            currentID++;
            return currentID;
        }

        public static void ResetNextID()
        {
            currentID = -1;
        }

        private void OnDrawGizmos()
        {
            Gizmos.DrawCube(transform.position, new Vector3(1, 1, 0));
            Gizmos.DrawWireCube(_collider.bounds.center, _collider.bounds.size);

            foreach (DungeonRoom room in _neighbours)
            {
                Gizmos.DrawLine(transform.position, room.transform.position);
            }
        }
    }

Unity Source Code : [링크]

 

Dungeon Game

WIP Dungeon crawler with procedural generation

unitylist.com

 

절차적 던전 생성 알고리즘 설명 : [링크]

 

Procedural Dungeon Generation Algorithm

This post explains a technique for generating randomized dungeons that was first described by the developer of Tiny Keep. I'll go over it in a little more detai

www.gamedeveloper.com

추가 정보 : [링크]

 

Procedural Dungeon Generation Algorithm Explained

So today I'm going to be a little different and talk about one technical aspect of my game [TinyKeep](http://tinykeep.com), that is random...

www.reddit.com

 

 

★★★☆☆

 

반응형

댓글