개발/기본) 알고리즘

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

테샤르 2021. 12. 30. 21: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

 

 

★★★☆☆

 

반응형