학교에서 같은 반 프로그래머들 6명이서 모여서 제작한 던그리드 모작 입니다.
해당 작품으로 교내 프로젝트 경진대회 에서 최우수상을 받았습니다.
작업한 기간도 2달 남짓한 기간으로 짧았고 다들 처음 해보는 팀 프로젝트여서 부족한 부분도 많이 있었지만 그래도 좋은 성적을 받아서 뿌듯했던 팀 프로젝트 였습니다.
해당 프로젝트에서 제가 작업한 내용은 Unity 타일맵을 이용한 시작 마을맵과 던전 맵 제작, 맵에 있는 모든 상호작용 (NPC와 대화, 빠른이동, Tab을 이용해 던전 지도 보기)제작, 던전 생성 알고리즘을 이용한 던전 생성, 미니맵 제작 등의 작업을 했습니다.
그중 직접 제작한 던전 생성 방식과 던전 생성 알고리즘에 대해 기술해 보겠습니다.
일단 던전의 각 방은 Unity 2D Tilemap 기능을 이용하여 제작해 프리팹으로 만들어 두었고
각 방은 3개의 크기(Large,Medium,Small)와 6개의 타입(Start, Restaurant, Shop, End, Boss, Normal)으로 나누어져 있습니다.
방의 타입에서
Start와 End는 던전의 한 층의 시작과 끝방을 의미하고
Shop, Restaurant, Boss 방은 각층에 하나씩만 존재 하는 특수 방들입니다.
나머지 Normal은 평범하게 몬스터들이 스폰되고 각 방을 클리어하면 확률에 따라 상자가 스폰됩니다.
맵 스폰은 2차원 배열의 공간에서 재귀함수를 이용한 탐색 알고리즘(깊이 우선 탐색)을 이용해서 스폰됩니다.
우선 각 방들은 자신의 위치정보와 자신의 타입과, 상하좌우에 위치한 이동 가능한 방의 정보를 정의하고 있습니다.
public class StageData
{
public enum MapType { NOMAL, RESTAURANT, SHOP, MAPTYPEMAX };
public MapType type;
public StageData RightMap = null;
public StageData LeftMap = null;
public StageData UpMap = null;
public StageData DownMap = null;
public int Num;
public int indexX;
public int indexY;
public void InitSttting(int num, int x, int y, MapType type)
{
this.Num = num;
this.indexX = x;
this.indexY = y;
this.type = type;
}
public void InitSttting(int num, int x, int y)
{
this.Num = num;
this.indexX = x;
this.indexY = y;
this.type = MapType.NOMAL;
}
}
그러고는 2차원 배열의 공간에서 시작점으로부터 최대 개수 의 맵이 만들어 질때까지 상,하,좌,우 방향중에서 랜덤으로 갈 방향을 정해서 해당 방향으로 이동하는데 그렇게 탐색한 개수가 최대 방의 개수가 되면 더 이상 탐색을 하지 않고 강제적으로 종료합니다.
public Vector2Int MapSpawn(int x, int y, StageData Parent)
{
//맵이 최대 개수까지 만들어 지면 종료한다.
if (current.NowCount >= option.MaxNum)
{
return new Vector2Int(x, y);
}
int RandNum;
int yval = option.MaxListSize;
//들어오면 해당 위치에 방정보를 하나 만들어 준다.
if (current.Maplist[x + (y * yval)] == null)
{
current.Maplist[x + (y * yval)] = new StageData();
current.Maplist[x + (y * yval)].InitSttting(current.NowCount, x, y);
current.NowCount++;
//각 방들은 서로 연결되어서 이동할 수 있어야 하지만 인접해 있다고 항상 연결되어 있어야 하는것은 아니다
//따라서 탐색을 할때 자신이 현재 탐색한 위치에서 이전에 있었던 위치를 넘겨줌으로써
//탐색을 수행한 경로가 방을 이어주는 통로가 될 수 있도록 해준다.
if(Parent!=null)
{
if (Parent.indexX == x)
{
if(Parent.indexY>y)//아래쪽과 연결
{
current.Maplist[x + (y * yval)].DownMap = Parent;
Parent.UpMap = current.Maplist[x + (y * yval)];
}
else//위쪽과 연결
{
current.Maplist[x + (y * yval)].UpMap = Parent;
Parent.DownMap = current.Maplist[x + (y * yval)];
}
}
else if (Parent.indexY == y)
{
if(Parent.indexX>x)//오른쪽과 연결
{
current.Maplist[x + (y * yval)].RightMap = Parent;
Parent.LeftMap = current.Maplist[x + (y * yval)];
}
else//왼쪽과 연결
{
current.Maplist[x + (y * yval)].LeftMap = Parent;
Parent.RightMap = current.Maplist[x + (y * yval)];
}
}
}
}
else
{
return new Vector2Int(x, y);
}
//천번째 방은 항상 오른쪽으로간다.
if (current.NowCount == 1)
{
MapSpawn(x + 1, y, current.Maplist[(x) + (y * yval)]);
return new Vector2Int(x + 1, y);
}
//왼쪽
RandNum = UnityEngine.Random.Range(0, 100);
if (RandNum <= 70)
{
if (x - 1 >= 1 && current.Maplist[(x - 1) + (y * yval)] == null)
{
MapSpawn(x - 1, y, current.Maplist[(x) + (y * yval)]);
}
}
//오른쪽
RandNum = UnityEngine.Random.Range(0, 100);
if (RandNum <= 70)
{
if (x + 1 <= option.MaxListSize - 1 && current.Maplist[(x + 1) + (y * yval)] == null)
{
MapSpawn(x + 1, y, current.Maplist[(x) + (y * yval)]);
}
}
//위쪽
RandNum = UnityEngine.Random.Range(0, 100);
if (RandNum <= 70)
{
if (y - 1 >= 0 && current.Maplist[x + ((y - 1) * yval)] == null)
{
MapSpawn(x, y - 1, current.Maplist[(x) + (y * yval)]);
}
}
//아래쪽
RandNum = UnityEngine.Random.Range(0, 100);
if (RandNum <= 70)
{
if (y + 1 <= option.MaxListSize - 1 && current.Maplist[x + ((y + 1) * yval)] == null)
{
MapSpawn(x, y + 1, current.Maplist[(x) + (y * yval)]);
}
}
//더 이상 갈곳이 없으면 리턴
return new Vector2Int(x, y);
}
이렇게 방이 만들어질 자리들을 모두 정했으면
처음으로 생성된 방은 Start 맨 마지막으로 생성된 방은 End 방으로 정해주고
연결된 길이 한개인지, 두개인지,세개,네개 인지에 따라서 방의 크기(Small, Medium, Large)를 정하고
정해진 방의 크기와 현재 층의 층 수 등에 따라 특수 방들(Restaurant, Shop, Boss)을 지정해줌으로써 던전 생성이 종료됩니다.
해당 프로젝트를 진행 하면서 물론 프로그래밍 실력 적으로도 한 단계 레벨 업 했다는것을 느낄 수 있었지만
그 이상으로 여러 프로그래머들과 협업을 해봄으로써 프로그래머들 끼리의 협업 방식에 대해 배우고 느끼게 되었습니다.
자신이 짜는 코드(시스템)이 어느곳에 들어가도 독립적으로 잘 실행될 수 있어야 하는것은 물론이고
다양한 수정사항, 예외사항 들에도 유연하게 대응할 수 있어야 하고 다른사람이 봐도 알아보기 쉬워야 하고
다른 시스템 과의 연결이 필요할 때는 최대한 다른사람이 사용하기 쉽게 제작을 해야 겠구나 라고 깨달았습니다.
또한 커다란 시스템(서로 서로 연결되어 있는 시스템)을 제작할 때는 공통의 포멧(클래스 구조, 데이터를 주고받을 방식, 데이터를 처리할 방식)을 정할 필요가 있겠구나 라고 생각했습니다.
풀 소스 본인 작업 내용
https://github.com/quattroro/DungreedCopy_Jo.git
풀 소스 깃 주소
https://github.com/quattroro/DungreedCopy.git
'포트폴리오' 카테고리의 다른 글
WinAPI를 이용해 제작한 2D슈팅게임 모작 (0) | 2023.03.19 |
---|---|
Unity 엔진을 이용해 제작한 디아블로 모작(A* 길찾기 이용) (0) | 2023.03.08 |
팀 프로젝트로 제작한 3D 소울 라이크 게임 (0) | 2023.02.20 |