2D 포트폴리오 Fire Emblem 

 

파이어엠블렘은 전략 RPG게임으로, 1990년대부터 지금까지도 시리즈가 발매 되고 있는 Intelligent Systems 사의

 

대표작중 하나입니다. 게임을 간단히 소개하면 타일맵 기반, 多대多 전투가 스토리에 따라

 

다양한 맵에서 다양한 캐릭터로 이루어지는 방식입니다. 전투에서는 무기 상성, 직업, 장애물등 전략 요소가 있습니다.

 

전투는 방금 언급한 부분이 전부이므로, 주요 메인 스토리 외에 플레이어 보유 캐릭터들간의 스토리에

 

공이 많이 들어가있는터라, 한번 죽은 캐릭터는 다신 사용 못하는 영구사망 시스템을 위한 UI가 따로 있기도 합니다.

 

 

이번 한달 포트폴리오에서는 파이어엠블렘에서 사용된

 

범위표시기법, 길찾기, 선택 및 번복, 턴제, 부드러운 UI 등을 직접 구현해보는것을 목적으로 하였습니다


 

- 게임 전체적인 구조

 

구조를 간단히 설명하고 넘어가자면, 게임상 모든 오브젝트들은 게임오브젝트 클래스를 상속받게 만들어두어

 

Singleton  패턴으로 만들어 둔 오브젝트 매니져를 통해 필요시 가져올 수 있게 만들어두었습니다.

 

오브젝트 매니져는 STL중 키값으로 찾을 목적인 Map을 이용하여 만들었었습니다.

 

매번 자주 쓰이는 타일은 Flyweight 패턴을 참고하여 생성되어있는 객체를 필요시 참조로 가져오게 하였습니다.

 

턴제 게임이다보니 State 패턴에서 빌려올 수 있는 부분을 많이 참고하였고 플레이어와 상대방은

 

거의 체스 플레이어들처럼 비슷한 부분이 많기에 해당 공통사항은 상속을 통해 간결화 하였습니다.


 

-타이틀 화면 이전 로딩 구현

한번의 업데이트로 여러개를 불러오는것이 부담스러운 파일들에 대해 한 프레임당 하나씩 로드하게 해두는

 

로딩씬을 만들었습니다. 로딩해야 할 양에 비례한 수치로 맨 옆 캐릭터 영상이 출력됩니다.

 

캐릭터가 하늘 위에 도달시 로딩은 종료되고 타이틀 화면이 나오게 됩니다.


 

-타이틀 구성

유저의 입력이 있을때까지 타이틀 전용 BGM이 나오며, 빨간색 선택창의 알파값이 서서히 밝아졌다가 

 

어두워졌다가를 반복하게끔 만들어두었습니다. 맵툴씬으로 갈것인지 인게임으로 갈것인지 여기서 정할 수 있습니다.

 

사운드매니져와 씬매니져에서 만들어둔 Fade in, Fade out을 이용해서 부드러운 씬 전환을 구현해두었습니다.

 


 

- 맵툴 구현. 실제 파일로 저장

매번 지형을 새로 수동으로 만들기엔 무리가 있습니다. 맵툴을 구현해두었는데,

 

지형 및 장애물을 깔 수 있습니다. 적 또한 미리 배치해둘 수 있고, 저장버튼을 누르게 되면

 

적 정보와 타일 정보는 각각 다른 파일로 저장됩니다. C++로 작성한 코드이기에 Fstream이나

 

혹은 json 라이브러리를 사용할 수도 있었으나, Windows API를 해보고있는만큼 여기선

 

CreateFile, writeFile등 API서 주어진 기능을 이용해서 파일 저장 및 불러오기를 구현하였습니다


 

-캐릭터 행동 범위 표시, Flood Fill 알고리즘 활용

인게임 핵심 기능중 하나인 캐릭터 행동 범위 표시 기능입니다. 캐릭터마다 이동범위, 공격범위가 다릅니다.

 

커서를 올리기만 해도 아군이든 적군이든 행동 범위가 표시되는데, 연산량을 줄이기 위해

 

업데이트마다 계산이 아닌 한번 계산이 이루어진 후엔 연산을 다시 안하게 처리하였습니다.

 

 

구현하기 위해 Flood Fill 알고리즘이 사용되었는데, 처음엔 재귀함수를 이용한 DFS 방식으로 만들었었습니다만 

 

추후 해당 기능에 최단거리 길찾기 기능을 추가하기 위해 STL중 Queue를 이용한 BFS로 알고리즘을 새로 짰었습니다.

 

Flood Fill 알고리즘에 대한 정리를 블로그에 따로 해두었습니다.


 

-모든 적의 행동 범위 일괄 표시

위에서 사용된 범위 표시 기능의 확장입니다. 모든 적 캐릭터의 이동+공격 범위를 보라색으로 일괄 표시하는 기능인데,

 

위 파랑+빨강 표시하는 기능과 상당수 코드부가 겹치기에, 캐릭터별 FloodFill 연산을 마친 후

 

[행동범위+공격범위]를 표시할지, [보라색 모든 적 위협범위] 를 표시할지를 함수포인터를 이용하여 구현하였습니다.

 

적 캐릭터가 많다보니, 중복된 타일이 컨테이너에 담길 수 있는데, 그걸 방지하기 위해 STL중 Set을 사용하였습니다

 

연산량을 줄이기 위해 토글키를 이용해서 끄고 키는 방식을 채택하였습니다.


 

- 캐릭터 드래깅을 통한 모의 이동 및 최단거리 화살표 표시

커서로 선택된 캐릭터를 잡아다가 드래깅을 할 시에, 예측 이동경로가 뜨게 만들었습니다. 

 

업데이트가 돌 때, 커서로 클릭된 캐릭터가 있는지부터 판단 후, 해당 캐릭터 제어권을 최우선시 합니다.

 

디자인 패턴중 State패턴과 비슷하게, 모든 캐릭은 먼저 상태를 판단하는 코드를 거치고, 상태에 따른 행동을 합니다

 

캐릭터 드래깅이 일어나면, 기존 캐릭터 랜더 이외에 예측 이동 지점에 반투명한 캐릭터가 하나 더 그려집니다.

 

드래깅 시작 위치부터 현재 위치까지 계산된 최단경로를 담아둘 목적으로 STL중 vector을 사용하였습니다

 

플레이어가 드래깅 한 위치까지의 최단경로가 화살표로 표시되며, 파란 타일 내에서만 드래깅 가능토록 해두었습니다


- 이동 확정 및 번복 기능. 플레이어 출발 위치 및 최단경로 기억

드래깅을 통해 목적지까지 당긴 후 확인 키를 누르면 Fade in 효과로 주변이 어두워지며 선택 UI가 뜹니다.

 

최종 확인 버튼이 나오는데, 이동확정을 누르면 화살표대로 캐릭터가 한타일씩 움직이며, 목적지까지 이동합니다.

 

반대로 이동확정 대신 취소버튼을 눌렀다면, 캐릭터가 드래깅 시작된 원점으로 돌아가고, 선택 해제상태가 됩니다.


 

- 모의 전투 구현. 전투 예측 창을 보고 공격 확정 및 번복 가능. 무기 상성 및 명중률 표시

드래깅을 해서 적 위에 커서를 올려놓고 확인버튼을 누를시 바로 공격하지 않고 먼저 전투 예측화면을 띄웁니다.

 

화면은 예측일 뿐, 크리티컬율과 명중과 같은 난수를 고려하지 않았기에 예측대로 될수도, 아닐수도 있습니다.

 

해당 예측 화면을 보고 있는 상태서 확인버튼을 한번 더 누르면 캐릭터가 이동한 후 전투를 진행합니다.

 

취소버튼을 눌렀다면 드래깅 취소와 마찬가지로 원점으로 돌아가게 되며, 픽 상태도 해지됩니다.

 

 

전투에 대해 설명을 더 하자면, [칼,도끼,창] 세 무기간의 가위바위보 상성이 있고, 무기 우위를 점한 상대에게

 

20퍼센트 추가 데미지를 줍니다. 상대방과 속도가 5 이상 차이나면 두번 공격하게 구현해두었으며

 

크리티컬 입은 상대에게는 2배의 데미지를 입히게끔 해두었습니다. 

 

데미지 0을 입혔을때, miss(빗맞춤), 크리티컬 모두 각기 다른 사운드를 재생시킵니다.


 

 - 적의 AI, Astar 알고리즘 활용

적의 턴일땐 적이 담인 vector를 돌며 아직 행동을 취하지 않은 캐릭터 하나씩 선택 상태로 바꾸어

 

행동 우선권을 줍니다. 적은 본인 Flood Fill 범위 내에 상대방이 있는지 먼저 체크 후, 있다면 이동 후 공격을,

 

없다면 가장 가까운 상대방을 맨허튼 추정값으로 판단해 거기까지 A star 알고리즘을 이용해 최단거리를 구합니다.

 

그 최단거리중 본인이 이동 가능한 거리만큼 이동만 하고 행동을 마치게 됩니다.

 

A star 상세 구현 내역은 블로그에 따로 적어두었습니다


 

- 적군 마우스 호버시 클릭방지 및 정보만 표시. 카메라 설명

본인 캐릭이 아닌 적 캐릭 위에 마우스를 두었을 시엔 범위 표시가 옅게 되고, 픽은 불가능한 상태가 됩니다.

 

카메라는 매번 커서를 따라다니긴 하지만, 부드럽게 따라가는걸 구현하기 위해 현재 좌표값 및 이동하고자 하는

 

최종 목적지 좌표 두가지를 가지고 있습니다. 현 카메라의 위치와 목적지까지의 거리가 멀면 카메라의 속도가

 

빠르게, 목적지와 가깝다면 갈수록 느려지게 만들어서 마치 Unity의 Lerp와 비슷하게 작동하도록 하였습니다

 

성능 향상을 위해 카메라에 잡힌 오브젝트들만 랜더되게 해두었으며, 매 업데이트마다 모든 타일을 전부

 

검사하지 않기 위해 카메라에 잡힌 타일들만 따로 클리핑을 해둔 후 컨테이너에 담아둡니다.


 - 영구 사망 구현, 죽은 캐릭터는 일정 주기로 모아서 삭제

파이어 엠블렘은 캐릭터가 사망시 다시는 나오지 않는 영구사망 시스템이 적용되어있어,

 

플레이어 소유의 캐릭터 사망은 중요한 요소입니다. 적군과 달리, 아군 캐릭터는 사망 직전에

 

사망 전용 UI를 Fade in을 통해 불러들인 후 멘트를 천천히 뱉어냅니다.

 

유저의 키입력이 들어오면 비로소 캐릭터는 서서히 Fade out 하며 결국 사망상태로 변합니다. 

 

 

적군이나 아군이나 죽었을때 그자리에서 바로 객체가 지워지는것이 아닌, 

 

다음 업데이트를 돌때 사망 처리된 캐릭터를 찾아 일괄적으로 지우는 방식으로 구현해두었습니다.


 

 - 행동력이 다 된걸 판단시 자동 턴 전환. 

현재 누구의 턴인지를 시스템은 알고있기에, 턴매니져는 현재 턴 플레이어 군대의 모든 캐릭터가

 

행동이 끝났는지 지속적으로 체크합니다. 만약 모든 캐릭터가 행동을 마친것이 확인되었다면

 

턴 매니져는 어느 누구도 제어가 불가능한 UI에게 턴을 넘겨주어 턴 전환 이펙트를 발생시킵니다.

 

그 후에 자동으로 다음 턴의 사람에게 턴이 돌아가게 됩니다. 


 

 - 코드 안정성을 위한 assert, 캡슐화 사용

코드의 일부긴 하나, 안전검사를 위해 필요한 부분이다 판단되었던 많은 부분에 assert를 걸어두었습니다.

 

특정 속성을 바로 건드리는 위험한 행동을 방지하기 위해 GetIndex, SetIndex와 같은 겟터셋터를 이용하여

 

정보은닉, 캡슐화를 달성하였고, 값복사에 의한 성능 저하를 막기위해

 

일부 자료에 대해선 const 참조자를 사용하였습니다

 


 

 - Github를 통한 프로젝트 관리

협업이 아닌 개인 프로젝트였고, 딱히 새로운 버전 시도가 없었으므로 브랜치를 굳이 나누진 않았습니다. 

 

진행 기간동안 필요할 때마다 깃허브를 통해 변동사항을 추적 가능하게끔 이용하였습니다.


위 언급된 기능들을 단 하나씩만 담아 빠르게 영상으로 담아보았습니다

 

https://youtu.be/PbHeuzTbI44

 

'결과물 > C,C++ Direct 2D' 카테고리의 다른 글

한달 포트폴리오 계획서  (0) 2020.06.23

+ Recent posts