#장애물 랜덤 배치 기능, 플레이어 스폰 기능은 게임 매니저에서 다루기, 이번 글에서는 맵 제작 관련 정보를 작성함
1.정의
- 플레이어가 게임 플레이를 진행하는 공간이다.
- 맵에는 적 스폰 구역, 플레이어 스폰 구역, 이동 불가 장애물들이 존재
- 맵에서는 게임 시작 시 지정된 장애물 배치 구역에 랜덤하게 장애물을 배치
2.기획 의도
2.1.기획 배경
초기 기획으로 게임 시작마다 절차적 맵 생성으로 랜덤 맵을 생성하여 맵의 구조를 매번 다르게하여 플레이어의 지루함을 완화하고자 했으나 기술 구현하기 어려워서 빠른 포기했다. 그래서 통짜맵으로 구현하되 랜덤 맵 대신 지루함을 완화할 방법을 생각해봤다. 먼저 어떻게 작동되는지 생각해봤다.
적이 스폰되서 플레이어를 추적한다. 이동 경로 존재. 이동경로가 매번 바뀌면 전략적인 전투가 가능하지 않을까? > 내가 해본 게임에서 이런 경험이 있는지 머리 속에서 빅데이터를 돌려봄 > 약간 방어하는 느낌이니까 > 토탈워의 수성전 > 수성할 때 맵에 장애물을 설치하는 아틸라 토탈워가 떠올랐다.

아틸라 토탈워에서는 장애물 배치 포인트가 여러 군데 있지만 최소 한 개씩 장애물을 배치하여 적의 이동경로를 막고 전략적으로 방어할 수 있다. 이 기능을 활용하여 이 게임에서는 게임을 시작할 때마다 특정 위치에 랜덤하게 몇 개의 장애물을 배치하여 적들의 이동경로가 바뀐다면? 이렇게 되면 익숙한 맵이지만 배치된 장애물들의 개수에 따라 전투에 유리한 지역, 불리한 지역이 발생하고 이를 플레이어가 판단하고 장소를 선택하는 재미가 있지 않을까?
이는 원래 기획의도인 반복 플레이의 지루함을 맵 구조의 변화로 완화하기와 부합하는 같아서 통짜맵에 랜덤 장애물 배치를 구현하기로 했다.
2.2.확정된 맵 기획 의도
- 랜덤 장애물 배치를 통해 반복 플레이의 지루함 완화: 똑같은 구조의 단일 맵에 매번 플레이할 때마다 장애물의 배치를 랜덤하게 하여 적들이 공격 이동 경로를 바꿔 플레이어의 방어 패턴에 변화를 줌
3.구성 요소
- 장애물 랜덤 배치 포인트: 게임 시작 시 장애물이 배치될 수 있는 구역
- 플레이어 스폰 포인트: 게임 시작 시 플레이어가 스폰하는 구역
- 적 스폰 포인트: 게임 시작 시 적들이 생성되는 구역
- 맵 배경 및 이동 장애물(장식물, 건물, 언덕, 성벽, 성문, 교회): 맵을 꾸미고 플레이어와 적들이 이동 불가능한 오브젝트들
- 타일: 맵의 주요 이동 경로를 표시해주고 맵의 길을 표현하는 바닥 타일들
- 샛길: 장애물이 배치되어 적들의 이동 경로가 길어지면 플레이어를 추적하는 시간이 길어지기 때문에 이동 경로가 길어지는 구간에 샛길을 추가했다. 적들의 빠른 추적을 위함이지만 플레이어의 경우 전투 시 일정 거리 유지가 필요하기 때문에 샛길을 활용해서 적들과의 거리를 벌릴 수 있다.
4.맵 소개
4.1.맵 초안
첫 번째 맵 초안

맵 초안 작성할 때 막막해서 중세 지도 참고하다가 구매한 맵 제작용 에셋의 이미지를 참고하여 대충 그려봤다. 원래는 성벽이 없고 나무 울타리로된 중세 시대 농촌 마을을 생각했는데, 밀밭 에셋하고 농촌마을 건물이 부족해서 포기하고
가지고 있는 에셋의 건물들이 빨간 기와가 있어서 돈 좀 버는 상업도시 느낌으로 컨셉을 잡았다. 중세시대에 중요한 건물인 교회, 광장을 중심으로 그려봤다. 초기에는 적 스폰 위치를 4군데로 하려고 했으나 그렇게 되면 너무 맵이 사각형이라 동일한 전투 패턴을 보여줄 것 같아서 비대칭적인 구조로 비틀어봤다. 교회를 바깥에 배치하니 맵이 작은 것 같아서
교회를 중앙으로 하고 광장을 없애봤다.
두 번째 맵 초안

교회의 위치를 중앙으로 변경하고 대략적으로 구역의 컨셉도 잡았다. 대신 맵이 작다보니까 맵 넓이를 효율적으로 사용하고 싶었다. 일단 맵 크기를 안키우는 이유는 맵이 너무 크면 플레이어가 이동만 해서 재미가 없다. 넓은 맵에서 플레이어와 적과 만나기 위해서 적이 스폰되는 양이 많아져야한다. 맵 크기에 채울 오브젝트들이 많아져서 최적화가 어렵다.
따라서 인게임에서 돌아다녀봤을 때 이정도 크기가 적당하고 판단했다.(유니티 plane 스케일 8, 1, 8) 그래서 중앙을 언덕으로 바꿨다. 교회가 언덕에 있으면 상징적인 위치여서 파악하기도 좋을 것 같고 계속되는 평지와 다른 이미지를 줄 수 있어서 시각적 재미?를 줬다. 이 후 남는 구역 하나와 구체적인 장애물 배치를 정하기 위해서 맵을 다시한번 정리해서 그렸다.
확정된 맵 초안

거의 확정된 초안이다. 깔끔하게 구역을 나눴고, 장애물 배치 포인트, 적 스폰 포인트, 플레이어 스폰 포인트 등 자세한 정보를 추가했다. 이전에서 미정이었던 구역은 공원 구역으로 변경했다. 유니티에서 맵을 구현하는데 큰 도움이 됐지만 실제 맵 크기와 맞지 않아서 빈곤층 구역과 공원 구역, 부자집 구역은 소소하게 변경했다.
4.2.맵 전경

현재 시점에서 샛길이 잘 안보여서 쿼터뷰 시점으로 변경했는데 조이스틱 움직임 방향과 캐릭터 이동 방향이 달라서 샛길이 잘보이게 맵의 방향을 변경했다. 부자집 지역을 확장했고, 공원은 묘지로 변경, 빈곤층 구역은 대각선 대신 넓은 공터 느낌으로 변경했다.
4.3.맵 구역

4.구현 과정
음... 이번 구현은 주로 맵에 오브젝트들을 배치하는게 전부라서 딱히 적을게 없다. 그냥 프리팹 배치했다. 끝...
이러면 아쉬우니 맵에 오브젝트를 배치할 때 몇가지 정보를 작성한다.
4.1.프리팹 위치 정렬하기
맵 제작 시 프리팹을 활용하면 아주 좋다. 하지만 새로운 프리팹을 만들 때 빈 게임오브젝트(부모 오브젝트)에 프리팹 재료 오브젝트(자식 오브젝트)를 넣고 만들다 보면 가끔 부모 오브젝트와 자식 오브젝트 사이의 거리가 매우 먼 경우가 발생한다. 이 때 자식 오브젝트들을 선택하고 위치를 0, 0, 0 으로 초기화하면 기존의 형태는 사라지고 자식오브젝트들이 0, 0, 0에 몰려버린다.


이때 정렬 팁은 자식오브젝트 하나를 선택해서 빈 게임 오브젝트를 생성하고 이 빈 게임 오브젝트를 빼내서 자식오브젝트들을 이곳에 담는다. 그리고 이 게임 오브젝트의 위치를 0, 0, 0으로 초기화하면 자식 오브젝트들의 형태를 유지한채로 부모 오브젝트와 자식오브젝트의 위치를 정렬할 수 있다.



4.2.Nav Mesh 생성하기
유니티 상단 창의 Window > AI > Navigation을 누르면 인스펙터창 옆에 Navigation 창이 생긴다.
이곳에서 네비 메쉬를 생성할 수 있다.
네비 메쉬를 생성할 오브젝트(주로 Plane)을 선택하고 인스펙터창의 Static에서 Navigation Static을 선택한다.

네비 메쉬를 선택할 오브젝트를 모두 Navigation Static으로 설정했다면 Navigation창 > Bake > AI Agent의 반경과 높이, 네비 메쉬를 생성할 수 있는 각도(Max Slope), Step Height(네비 메쉬를 생성할 수 있는 오브젝트 높이)를 설정하고 아래의 Bake 버튼을 누른다.

그러면 씬창에서 파란색 네비 메쉬를 확인할 수 있다.

4.3.Nav Mesh Obstacle
장애물을 배치했을 때 적 AI가 이동할 수 없게 장애물 오브젝트에 Nav Mesh Obstacle 컴포넌트를 추가한다.
Nav Mesh Obstacle의 size를 지정하고 Carve를 선택하면 설정 완료

나는 전체 오브젝트에 Nav Mesh Obstacle을 설정하느라 일일이 사이즈를 정했는데 자식 오브젝트들(모델링 오브젝트)에서 Nav Mesh Obstacle 컴포넌트를 추가하면 자동으로 오브젝트 크기에 맞게 추가해준다.
#추가 팁 Collider 컴포넌트도 오브젝트 크기에 맞게 자동으로 추가해준다.

이 후 네비메쉬를 확인해보면 Nav Mesh Obstacle로 설정한 만큼 네비 메쉬가 제거되어있어 적 AI가 이동할 수 가 없다.

4.4.네비게이션 기본 정보 참고 영상
https://www.youtube.com/watch?v=RmDRjoXUaTI
이 영상을 보고 적AI의 Nav Mesh Agent의 priority를 99로 수정해서 적이 다가와도 플레이어가 안밀리게 했음
4.5.여담
아 맞다. 깜빡하고 안 적은게 있다.
언덕 위치에서 파이어볼 범위 지정UI가 바닥에 고정됨(y값 0.1f으로 고정), 그래서 파이어볼 스킬 사용 시 플레이어 위치에서 생성되게 변경함
언덕에서 파이어볼 사용 시 언덕 높이에서 파이어볼이 도착하여 공중에 떠있는 문제 발생함. 이 부분은 내가 구현할 때 언덕은 생각안하고 만들어서 그렇다. 언덕 추가한 것도 맵 제작하다가 필요해서 즉흥적으로 정한 것...
그래서 베지어 메소드가 t = 1일 때 최종 위치에 도착하니까 t <= 1일 때 까지만 베지어 메소드를 실행하고 1이 넘어가면 베지어 메소드를 실행 정지 시켰다. 그러면 리지드바디의 중력에 의해 자동으로 바닥으로 떨어진다.
그래서 문제를 해결하긴 했는데... 좀 이상하게 구현됐다 ㅋㅋㅋ. 갑자기 확떨어짐. 뭐 이거대로 멋이 있긴 한데...
탄도 방정식으로 구현해야 했나 싶기도 하다. 뭐 갑자기 내려 꽂히는 게 멋져서 일단 문제 해결 완료했다.

역시 게임 개발을 하다보면 수정 상황도 발생하고 그에 맞게 여러 기능들을 개선 및 조정해야한다. 처음부터 모든 것을 상정할 수 없다. 옛날에 게임 스쿨에서도 팀 프로젝트로 게임 개발을 할 때도 마찬가지였다. 일단 최대한 핵심 위주의 기능을 기획서로 작성하고 구현하면서 개선, 보완하도록 하자. 그러니 기획서 작성할 때 너무 완벽하게 작성하려고 힘 빼지말자. 논리적이되 핵심 위주로 꼭 필요한 정보 위주로 간결하게 작성하는 연습을 하자(주의! 이 생각은 틀릴 수 있다. 왜냐? 현업은 다를 수 있음)
'게임 개발 > 유니티 게임 개발 일지' 카테고리의 다른 글
| #17 PlayerSpawner, ItemSpawner, ObstacleSpawner 구현 (0) | 2020.08.11 |
|---|---|
| #16 UIManager, GameManager, EnemySpawner 구현 (0) | 2020.08.09 |
| #14 순간이동 스킬 구현(마나 소비, 마나 자동 회복, 스킬재사용대기시간) (0) | 2020.07.24 |
| #13 라이트닝 스트라이크 스킬 구현하기 (0) | 2020.07.21 |
| #12 파이어볼 스킬 구현하기 (0) | 2020.07.14 |
댓글