본문 바로가기

Unity/Unity 스파르타

9주차 - Procedureal Dungoen Generator + 미니맵

728x90

1.  Procedureal Dungoen Generator 

어떤 방식으로 프로시저 알고리즘을 짤것인가

  • 던전의 생성 위치를 리스트에 저장하고, 리스트에 던전 생성 위치의 존재 여부를 판단해 던전을 생성 시키는 방법
    • 단순한 구조로 빠른 시일내에 구현하기위해 선택
  1. 던전의 정보를 담은 던전룸 클래스 리스트
  2. 던전을 생성할 위치정보를 담은 리스트
  3. 던전의 생성위치를 사용해 던전을 그릴 타일맵 로직
  4. 던전간 이동을 위한 문 로직

2. 미니맵

  1. 미니맵을 보여줄 카메라 만들기
  2. 미니맵을 찍고 있는 카메라에만 보일 스프라이트 생성
  3. 던전과 던전사이를 이어줄 라인 생성하기
  4. 플레이어 위치를 시각적으로 보여주기, 탐색하지 않은 방 물음표 표시.

1.  Procedureal Dungoen Generator 

1.  사전 준비

  1. 던전 매니저 만들어 던전 정보 관리하기

  2. 던전정보 클래스 만들기

  • 스크립트 오브젝트 만들어 기본적인 정보를 생성해놓기
    • 높이,넓이, 생성할 문 오브젝트 등
  • 스크립트오브젝트를 파라미터로 받아 초기화 동시에 스크립트 오브젝트를 설정해주고 넓이와 높이를 가져오자.
  • 던전의 생성위치와 던전넘버를 초기화 하는 함수 만들기(BoundsInt, 문의 생성 위치까지 동시에 설정해주자)
    • BoundsInt를 사용해 타일맵을 그릴것이기 때문에 던전 생성위치를 기준으로 (0,0)위치를 계산해 설정해주자.
    • 던전의 생성위치를 사용해 미리 문을 생성할 위치를 계산해 놓자
더보기

정수형 벡터를 사용해 경계영역을 표현할 수 있으며 정수형 좌표를 사용하여 경계영역을 정의합니다.

BoundsInt 를 생성할때는 경계영역의 시작위치와, 경계영역의 크기를 매개변수로 받아 생성합니다.

ex)BoundsInt bounds = new BoundsInt(경계영역 시작위치, 경계영역 크기);

더보기
 public void SetRoomData(Vector2Int createPoint,int roomNumber)
    {
        center = createPoint;
        this.roomNumber = roomNumber;
        bounds = new BoundsInt(new Vector3Int(center.x - (width / 2), center.y - (height / 2), 0), new Vector3Int(width, height, 0));
        minCamLimit = new Vector2(center.x - width / 2, center.y - height / 2) + new Vector2(DunGoenManager.Instance.cameraWidth, DunGoenManager.Instance.cameraHeight);
        maxCamLimit = new Vector2(center.x + width / 2, center.y + height / 2) - new Vector2(DunGoenManager.Instance.cameraWidth, DunGoenManager.Instance.cameraHeight);

        leftDoorPoint = new Vector2Int(center.x - (width / 2), center.y);
        rightDoorPoint = new Vector2Int(center.x + (width / 2), center.y);
        topDoorPoint = new Vector2Int(center.x, center.y + (height / 2));
        bottomDoorPoint = new Vector2Int(center.x, center.y - (height / 2));
    }

 

2. DungoenGenerator

 1. Procedureal Dungoen Generator()

 던전 생성 위치 리스트  생성하기

  • 포문으로 생성할 던전 수만큼 돌아주면서 내부 와일문으로 랜덤하게 4방향중 한 방향을 선택해 던전 정보 클래스의 넓이와 높이를 사용해 던전의 생성위치를 구해 리스트에 담고, 리스트에 위치정보가 존재하는지 확인하기, 존재하지 않을때까지 반복
  • 와일문이 끝나면 생성위치저장, 생성할 던전의 정보 저장
  • 마무리 생성위치 저장 리스트 반환
더보기
    public List<Vector2Int> RandomCreateRoomPosition(Vector2Int startPoint,int maxRoomCount)
    {
        List<Vector2Int> path = new List<Vector2Int>();

        Vector2Int curPoint = startPoint;
        // -- Init
        path.Add(curPoint);
        RoomData curRoomData = new RoomData(roomDataSOs[Random.Range(0,roomDataSOs.Count)]);
        curRoomData.SetRoomData(curPoint,0);
        curRoomData.clear = true;
        DunGoenManager.Instance.dungoenRoomDataList.Add(curRoomData);
        // -- Init

        for (int i = 1; i < maxRoomCount; i++)
        {
            curRoomData = new RoomData(roomDataSOs[Random.Range(0, roomDataSOs.Count)]);
            curPoint += GetCreatePoint(curRoomData);

            while (path.Contains(curPoint))
            {
                curPoint += GetCreatePoint(curRoomData);
            }
            path.Add(curPoint);
            curRoomData.SetRoomData(curPoint,i);
            
            DunGoenManager.Instance.dungoenRoomDataList.Add(curRoomData);
        }
        return path;
    }
      
    Vector2Int GetCreatePoint(RoomData roomData)
    {
        Vector2Int ranDir = dir[Random.Range(0, dir.Count)];
        ranDir *= new Vector2Int(roomData.width+offset,roomData.height+offset);

        return ranDir;
    }

 

3. 던전의 생성위치를 사용해 던전을 그릴 타일맵 로직

타일드로우 클래스 만들기

  • 타일맵을 바닥,벽, 레이아웃이 가장높은 타일맵 세개로 나누자.
     [SerializeField] private Tilemap floorTilemap;
     [SerializeField] private Tilemap wallTilemap;
     [SerializeField] private Tilemap frontTilemap;
  • 어떤 타일을 사용해 타일맵을 그릴지 TileBase로 타일을 설정

바닥깔기

  • 먼저 타일을 깔 위치를 모두 구하자
    • 생성할 방의 정보들을 가져와 각 방의 생성위치를 사용해 BoundsInt를 설정해 놨기때문에 해당 방들의 BoundsInt 값만 리스트로 모아서 타일을 깔 위치를 모두 구해주자
    • 벽도 비슷한 방식으로 깔아주자
더보기
    void DrawFloorTiles()
    {
        List<Vector2Int> drawPoint = GetDrawFloorPoint();
        foreach (Vector2Int point in drawPoint)
        {
            DrawTile(floorTilemap, point, floorTileList[Random.Range(0, floorTileList.Count)]);
        }
    }

    List<Vector2Int> GetDrawFloorPoint()
    {
        List<BoundsInt> bounds = DunGoenManager.Instance.dungoenRoomDataList.Select(c => c.bounds).ToList();
        List<Vector2Int> drawFloorList = new List<Vector2Int>();

        foreach (BoundsInt bound in bounds)
        {
            for (int x = 0; x < bound.size.x; x++)
            { 
                for (int y = 0; y < bound.size.y; y++)
                {
                    Vector2Int newPot = (Vector2Int)bound.min + new Vector2Int(x, y);
                    drawFloorList.Add(newPot);
                }
            }
        }

        return drawFloorList;
    }
    
    
    void DrawTile(Tilemap tilemap,Vector2Int drawPoint,TileBase tile)
    {
        Vector3Int position = tilemap.WorldToCell((Vector3Int)drawPoint);
        tilemap.SetTile(position, tile);
    }

 

4. 던전간 이동을 위한 문 로직

  • 문생성 위치는 랜덤하게 생성할 것이지만, 이동할 던전의 문과 현재 던전의 문이 서로를 바라봐야합니다. 
  • 생성할 던전의 위치정보를 사용해 상하좌우에 생성된 맵이 있는지 확인하고,
    있다면 해당 방향으로 문생성,  반대 방향의 던전정보에 현재 생성하는 문과 바라보게 위치를 지정해 줍니다.
  • 플레이어가 해당 문에 접근했을때 반대편 방으로 이동해야 함으로, 플레이어를 이동시킬 위치를 세팅 해놔야합니다.
  • 문이 만들어짐과 동시에 문이 가지고 있어야할 데이터를 세팅해줍니다. 
    • 만들어질 문 타일 오브젝트
    • 현재 방의 정보 와 이동할 방의 정보(던전 데이터에서 만들어진 던전 방의 정보를 가지고 있음으로, 해당 방의 룸넘버만 받기)
    • 현재 문의 위치, 이동할 문의 위치,
    • 후일 타일맵으로 만든 문을 등장시키고 퇴장시킬때 사용할 위치정보를 담고 있어야 합니다.
  • 해당 문은 던전매니저에 생성해 모아놓은 던전방 리스트를 사용해 만들 것이며, 던전방에 상하좌우를 확인해 생성된 던전방이 있는지 확인하고 있다면 현재 방과, 다음 방의 정보, int 값(포문으로 4개의 방향을 확인할 것이기 때문에)을 매개변수로 전달 받아 문을 생성하는 함수에 전달하고, 방향에 따라 생성할 문을 스위치를 통해 문을 만들고, 문이 가지고 있어야할 데이터를 세팅해줍니다.
더보기
    public void SetData(RoomData room, int nextRoomnum, int curRoomNumber,GameObject tileDoor,Vector2 orgPot)
    {
        
        nextRoomNumber = room.roomNumber;
        tile_door = tileDoor;

        orginColor = tile_door.transform.GetChild(0).GetComponent<Tilemap>().color;
        Color newColor = orginColor;
        newColor.a = 0;
        targetColor = newColor;

        this.curRoomNumber = curRoomNumber;
        originPosition = orgPot;
        appearanvePot = originPosition + Vector2.up * 100;
        switch (nextRoomnum) //RTLB
        {
            case 0:
                outPoint = room.leftDoorPoint + new Vector2Int(1,0)*2;
                break;
            case 1:
                outPoint = room.bottomDoorPoint + new Vector2Int(0, 1)*2;
                break;
            case 2:
                outPoint = room.rightDoorPoint + new Vector2Int(-1, 0);
                break;
            case 3:
                outPoint = room.topDoorPoint + new Vector2Int(0, -1)*2;
                break;

        }
        
    }

 

 

2. 미니맵

1. 미니맵을 보여줄 카메라 만들기

2. 미니맵을 찍고 있는 카메라에만 보일 스프라이트 생성

3. 방과 방 사이를 이어줄 라인 생성하기

  • 가로 줄과 세로줄 스프라이트 만들기
  • 방과 방사이의 중간지점을 계산
    • 방과 방사이의 거리를 구할때 가로 줄을 생성할지 세로 줄을 생성할지 판단해야합니다
      • int 값과 vector2Int 값을 가진 구조체를 만들어, 해결
더보기
    void CreateMinimapLine()
    {
        foreach (RoomData roomData in DunGoenManager.Instance.dungoenRoomDataList)
        {
            for (int i = 0; i < dir.Count; i++)
            {
                int dirPot = 0;
                Vector2Int checkNextRoomPoint = dir[i];
                checkNextRoomPoint *= new Vector2Int(roomData.width + 10, roomData.height + 10);
                checkNextRoomPoint = roomData.center + checkNextRoomPoint;
                if (path.Contains(checkNextRoomPoint))
                {
                    RoomData nextRoomData = FindRoomdata(checkNextRoomPoint);

                    Vector2Int point = (nextRoomData.center- roomData.center) /2;

                    if (point.x == 0) dirPot = 1;
                    else if(point.y == 0) dirPot = 2;                   
                    point = roomData.center + point;
                    minimapLineList.Add(new LinePath(dirPot, point));

                }
            }
        }
        foreach(LinePath l in minimapLineList)
        {

            switch (l.dir)
            {
                case 1:
                    GameObject newLine = Instantiate(rowLine,(Vector2)l.point, Quaternion.identity);
                    newLine.transform.SetParent(DunGoenManager.Instance.container.transform);
                    break;
                case 2:
                    GameObject newLine1 = Instantiate(colLine, (Vector2)l.point, Quaternion.identity);
                    newLine1.transform.SetParent(DunGoenManager.Instance.container.transform);
                    break;
            }
        }
    }
    
    public struct LinePath
   {
    public int dir;
    public Vector2Int point;
    
        public LinePath(int _dir, Vector2Int _point)
        {
       	 dir = _dir;
       	 point = _point;
        }
    }

4. 플레이어 위치를 시각적으로 보여주기, 탐색하지 않은 방 물음표 표시.

 

728x90