맵 에디터 만들기

2025. 4. 29. 09:56원티드_언리얼RPG 2기/WinApi

   https://kofeeel.itch.io/transmundus

 2주동안 winapi에서 타일맵기반의 게임을 만드는 프로젝트를 진행했다. 레이캐스팅을 활용한 타일맵 기반 공포게임을 만들었다.  팀에서 데이터 관리와 맵을 담당했고 맵 에디터를 제작했다. 

 

레퍼런스한 레이캐스팅 게임 에디터

 

 

처음 에디터를 만들기로 생각했을때 3가지를 고려하고 설계했다.

타일, 몬스터, 아이템을 선택해서 배치할 수 있어야 함.

에디터를 인게임에서 전환 가능한 신으로 구현할지 외부에서 따로 구현할지 정하기
에디터에서 맵 저장/로드 기능 있어야함
레벨 데이터(적과 아이템의 수량, 위치 정보, 플레이어 위치 정보, 맵크기, 타일 정보) 저장

 

 

초기 맵 에디터-  좌표계산을 잘못해서 샘플타일영역이 뒤죽박죽 되어있다.

 

쓰다보니 편리한 기능을 추가하고 싶었다.

저장/로드/맵 초기화/배치/삭제/esc로 인게임복귀 기능 추가함

샘플타일영역과 맵을 분리해야함
마우스커서위치 항상 가운데로 클릭하게해야함

 

에디터를 만들다보니 편의성이 중요하다고 생각해서 맵 에디터에 단축키 기능을 이것저것 추가했다. 그러다보니 코드가 1000줄이 넘어갔다. 그래서 에디터를 몇개의 컴포넌트로 구성하는 클래스로 바꾸려고 했다.

맵 에디터 구조 변경사항

데이터 매니저/ 맵 매니저/ 맵 에디터

DataManager: 파일 I/O
MapManager: 게임 내 맵 관리
MapEditor: 에디터 UI와 편집 기능

에디터 → DataManager → 파일
파일 → DataManager → 게임

DataManager: 맵 데이터 관리 ,싱글톤
로드/세이브 기능
맵 데이터 구조 관리
플레이어 위치, 몬스터, 아이템 데이터 관리

 

단일책임원칙을 지키려고했다. 맵매니저는 인게임 맵정보들을 관리하고 데이터매니저는 모든 데이터들의 저장/로드 기능을 수행하도록 했다. 또 여러 클래스에서 호출하기때문에 싱글톤으로 만들어서 메모리를 관리했다. 


또한 모델-뷰-컨트롤러 패턴을 활용해서 에디터를 만들었다. 에디터의 상대좌표를 계산하고 내부로직을 수행하는 모델, 화면을 보여주기 위해 렌더만 하는 뷰, 에디터기능을 수행하기위해 숏컷기능을 비롯해 사용자로부터 입력을 받는 컨트롤러로 나눴다. 

EditorUI 클래스: MODEL

EditorRender 클래스 : VIEW

MapEditor: CONTROLLER

 

맵 에디터 클래스 -> 맵 에디터, 에디터 UI, 에디터 렌더 , 데이터 매니저 클래스로 분리

UI : 단축키, 타일 배치, 삭제, 좌표 변환 같은 메서드는
렌더: 샘플타일, 메인타일, 맵에 배치할 아이콘들을 렌더함
데이터매니저: 저장/로드 파일입출력관련 기능을 수행할때 불러옴
맵 에디터: 위의 세 클래스가 컴포넌트로 구성되어 전체적인 에디터 기능을 수행

에디터에서 맵을 저장할때 PrepareDataForSave() 메서드도 실행되도록 추가
맵 가장자리는 벽으로 강제 설정, 인게임 데이터를 데이터 매니저를 저장할 준비하도록 설정
숏컷기능 구현 (1번 : 타일배치, 2번: 시작위치 배치, 3번: 아이템 배치, 4번: 몬스터 배치, 5번: 장애물 배치
s: 세이브 , L: 로드 , C: 맵 클리어 , ESC: 인게임으로 복귀)

중간중간 UI를 바꿨다. 배치할 타입에 따라 색이 구분되게했고, 가장자리는 빨간색테두리로 표시했다.  

 

 

샘플타일, 샘플 스트라이트, 메인 배치 영역으로 나눈모습 / 상단에 에디터의 현재상태를 알수있는 UI를 만들었다.

 

UI수정: 단축키 상세정보, 현재 좌표, 선택한 타일, 선택한 샘플 스프라이트 색으로 구분
배치된 스프라이트 삭제시 현재모드의 타입이 아니면 삭제 불가능하도록 수정
중앙배치, 드래그 on/off 기능 추가

추가된 아이템과 장애물 저장/로드 기능 추가
저장/로드를 구조체의 id기반으로 수정,
id: 0 ~ 8 아이템 (key, phone, display.. ), 100 몬스터,
1000 ~ 1002(pile, elevator, final_elevator)

 

데이터 관리쪽에서도 변한 부분이 있었다. 초반에 확장을 고려하지 않고 2개의 아이템 타입, 1개의 장애물 타입만 배치할 수 있도록 만들었다. 그런데 아이템과 장애물 종류가 11개로 늘어났다. 팀원과 어떻게 해결할지 고민하다가 각 데이터 구조체들에 고유 ID를 넣어서 데이터를 저장하거나 관리할때 ID를 쓰도록 했다.

typedef struct tagObstacle
{
	DWORD			id;
	POINT			pos;
	BOOL			block;
	Direction		dir;
	float			distance;
	Texture*		texture;
	AnimationInfo	aniInfo;
} Obstacle;

typedef struct tagSprite
{
	DWORD			id;
	SpriteType		type;
	FPOINT			pos;
	float			distance;
	Texture*		texture;
	AnimationInfo	aniInfo;
} Sprite;

typedef struct tagItemSaveData
{
	DWORD id;
	FPOINT pos;             
	AnimationInfo aniInfo;  
}ItemData;

typedef struct tagMonsterSaveData
{
	DWORD id;
	FPOINT pos;            
	AnimationInfo aniInfo;  
}MonsterData;

typedef struct tagObstacleSaveData
{
	DWORD id; 
	POINT pos;           
	Direction dir;        
	AnimationInfo aniInfo; 
}ObstacleData;