Winapi 최종 프로젝트 회고록

2025. 5. 7. 11:31원티드_언리얼RPG 2기/WinApi

 

1. 프로젝트 개요

  • 프로젝트명:  타계
  • 깃허브 링크: https://github.com/PotenUpRunBetterRun/RunBetterRun
  • 개발 기간: 4/13 ~ 4/25
  • 주요 기능/목표:
    • 제한된 시야와 공간 안에서 퍼즐과 탐험을 통해 공포를 유도하는 2.5D 탈출 게임을 구현했습니다. 타일맵 에디터를 만들어 맵 제작을 하고 레이캐스팅을 활용해 2D를 3D처럼 보이게 했습니다.
  • 사용 기술: C++, WinAPI, TileMapEditor, RayCasting, SDL_mixer, mfAPI 
  • 협업 툴: Git , Figma , Notion
  • 팀 구성: 고필규, 이보형, 서희영, 백승환 
  • 담당파트:
    • 타일맵 에디터 - 인게임 레벨 디자인 도구
    • 바이너리 파일 시그니처 기반 데이터 직렬화 - 파일 무결성 검증 및 효율적인 리소스 관리 시스템

2. 개발 과정에서 겪은 문제들

  • 처음에 예상했던 문제 vs 실제 겪은 문제 

프로젝트 시작 전에는 레이캐스팅 이해에 어려움이 있을 것이라 예상했지만, 실제로는 맵 에디터 구현에 문제가 생겼습니다. 에디터를 만들 때 배치할 오브젝트들의 확장성을 고려하지 않고 설계했습니다. 이후 여러 아이템과 장애물이 추가되면서 플레이어와 상호작용하는 아이템/장애물을 구분해야 하는 상황이 발생했습니다. 기존 코드를 여러 번 수정해야 했고, 이로 인해 불필요한 중복 코드가 많아졌습니다.

  • 기술적인 어려움 

 수식적인 부분을 이해하는 것이 어려웠습니다. 특히 DDA 알고리즘을 이해하는 데 어려움이 있었습니다. 격자를 타일맵이라 생각하면 한 칸의 길이가 1인 정사각형 타일입니다. 충돌 지점의 축을 x축과 y축으로 구분해 계산합니다. sideDist는 플레이어 위치에서 쏜 광선이 처음 충돌한 타일의 x와 y좌표를 나타내고, deltaDist는 충돌 지점에서 다음 충돌 지점까지의 거리를 나타냅니다.

 

deltaDist를 구하고 그 값으로 sideDist를 구한 후, 벽의 충돌 지점 좌표를 알아내고 이를 기반으로 벽의 높이, 천장/바닥 구분, 텍스처의 음영을 구분합니다. 이런 과정들은 복잡한 수식들로 유도되는데, 이것들을 이해하는 것이 처음에는 어려웠습니다.

 

또한 맵 에디터에서 상대 좌표를 구하는 것이 어려웠습니다. 맵 에디터는 메인 배치 영역, 샘플 타일 영역, 샘플 오브젝트 영역 세 가지로 구분됩니다. 샘플들의 이미지를 보여주고 마우스 좌표와 각 영역 간의 상대 좌표를 계산하는 것이 복잡했습니다. 맵에 확대/축소, 스크롤, 드래그 기능을 넣으면서 변수가 많아지고 좌표 계산이 더 복잡해졌습니다.

  • 감정적인 흔들림

 단축키 기능 추가에만 몰두하다가 마감 이틀 전에 에디터가 새로 추가된 아이템들을 저장하지 못한다는 사실을 깨달았습니다. 맵 에디터 문제로 인해 기존 레벨 디자인과 테스트 계획이 지연되었습니다. 어떻게든 완성시켜서 아침에 맵 제작을 끝내고 점심 전에 부랴부랴 테스트까지 마쳤습니다. QA 시간이 부족했고, 아이템을 벽 안에 배치하는 실수로 게임 클리어가 불가능한 상태로 릴리즈하게 되었습니다. 시연에는 문제가 없었지만, 계획대로 진행하지 못해 팀원들에게 미안했고 맵 에디터를 제대로 활용하지 못한 아쉬움이 컸습니다.


3. 문제 해결 과정

 

1. 프레임드랍을 유발하는 에디터 성능 최적화

  • 레이캐스팅 기반 타일맵 게임에서 에디터로 배치한 오브젝트와 타일들이 인게임에서 어떻게 보일지 실시간으로 확인할 필요가 있었습니다.
  • 게임에서는 프레임 안정성을 위해 레이캐스팅 작업을 5개의 쓰레드로 분산 처리했지만, 에디터에서 동일한 방식을 사용하면 프레임드랍이 발생했습니다.
  • 기존 에디터는 MVC 패턴(Model-View-Controller)으로 설계되어 렌더링, UI, 에디터 기능이 분리되어 있었습니다.
  • 성능 개선을 위해 객체 호출과 참조를 최소화하는 방향으로 설계를 변경했다. 일부 클래스 간 결합도가 높아지더라도 관련 클래스들을 통합하는 최적화를 진행했습니다.
  • 게임 실행 시 에디터에 필요한 인스턴스를 미리 생성해두고, 에디터 씬으로 전환될 때 바로 사용할 수 있게 하여 전환 지연을 최소화 했습니다.

2. 효율적인 파일 저장 방식

  • 게임 개발에서 데이터 관리를 위해 CSV, JSON, 바이너리 파일 중 적합한 방식을 검토했습니다.
  • CSV는 가독성이 좋고 편집이 쉬우며, JSON은 구조화된 데이터를 표현하기 좋지만, 두 방식 모두 파싱 시간이 길어지는 단점이 있습니다.
  • 프로젝트 규모가 작았기 때문에 단순 바이너리 저장 방식으로도 충분했지만, 학습 목적으로 더 확장성 있는 파일 시스템을 구현하기로 결정했습니다. 나중에 더 큰 규모의 프로젝트를 진행할 때 유용한 경험이 될 것이라 판단했습니다.
  • 바이너리 형태의 가독성 문제를 해결하기 위해 파일 시그니처 개념을 적용했습니다.
  • 파일 헤더에 M(맵 데이터), I(아이템), O(장애물), P(플레이어) 등의 매직넘버를 부여해 파일 유형을 구분했습니다.
  • 비록 게임이 단순했지만, 실제 게임 엔진처럼 버전 정보도 포함시켜 데이터 포맷 변경 시 버전 관리가 가능하도록 설계했습니다.
  • 이런 과정에서 게임 개발 업계의 표준적인 접근 방식을 배울 수 있었고, 실무적인 경험을 쌓을 수 있었습니다.

참고 자료:

1. 파일 시그니처 모음 : http://forensic-proof.com/archives/300 

 

파일 시그니처 모음 (Common File Signatures) | FORENSIC-PROOF

 

forensic-proof.com

2. MVC 패턴 : https://ko.wikipedia.org/wiki/%EB%AA%A8%EB%8D%B8-%EB%B7%B0-%EC%BB%A8%ED%8A%B8%EB%A1%A4%EB%9F%AC

 

모델-뷰-컨트롤러 - 위키백과, 우리 모두의 백과사전

위키백과, 우리 모두의 백과사전. 모델, 뷰, 컨트롤러의 관계를 묘사하는 간단한 다이어그램. 웹 애플리케이션에서 일반적인 MVC 구성요소 다이어그램 모델-뷰-컨트롤러(model–view–controller, MVC)

ko.wikipedia.org

3.레이캐스팅 : https://github.com/365kim/raycasting_tutorial

 

GitHub - 365kim/raycasting_tutorial: (한글) 레이캐스팅 튜토리얼 번역

(한글) 레이캐스팅 튜토리얼 번역. Contribute to 365kim/raycasting_tutorial development by creating an account on GitHub.

github.com

 

 

완벽한 해결은 아니더라도 '돌파구'를 찾은 부분

 초기 에디터 개발 시 확장성을 고려하지 않았고, 그 결과 기능 구분이 필요한 아이템과 장애물을 현재 클래스 구조에서는 처리할 수 없는 상황에 직면했습니다. 예를 들어, 게임 시작 시 플레이어가 스폰되는 엘레베이터와 게임 탈출 시 사용하는 엘레베이터는 다른 기능을 수행하므로 구분해서 배치해야 했습니다. 현재 구조를 수정하기에는 시간이 부족했기에, 아이템, 장애물을 개발한 팀원과 상의하여 해결책을 찾았습니다. 임시 방편이지만 장애물, 아이템, 몬스터 등 에디터에서 배치하는 각 구조체들에 고유 ID를 추가하고 0~12까지의 ID로 각 오브젝트들을 구분해 배치, 저장, 로드 기능을 수행하게 했습니다.

 


4. 가장 인상 깊었던 순간

  • 가장 몰입했던 순간
    • 레이캐스팅을 공부할 때 팀원들과 서로 이해할 때까지 물어보며 함께 학습했습니다. 다들 열정이 넘쳤고 3일 동안 레이캐스팅 학습에 몰입했습니다.
  • ‘내가 만든 게 살아있다’고 느꼈던 순간
    • 각자 작업한 것을 합치고 처음 플레이했을 때 타일맵으로 만든 미로가 정말 3D처럼 보인다는 것이 신기했습니다. 레벨 디자인을 하면서 미로 속 막다른 공간이나 열리지 않는 문처럼 플레이어를 속이는 기믹들을 넣었습니다. 다른 팀들에게 테스트 플레이를 부탁했을 때, 플레이하면서 기믹들이 더 아슬아슬한 경험을 만들어 재미있었다고 피드백을 받았습니다. 설계 의도에 맞는 결과를 얻어 매우 뿌듯했습니다.
  • 예상치 못한 성과나 반응
    • 열정 넘치는 팀원들과 함께 게임을 만들어 배포한 것이 처음이라 정말 신기하고 재미있는 경험이었습니다. 예상 외로 많은 사람들이 재미있다고 해주고, 게임을 플레이하거나 유튜브에 영상을 올려준 분들도 있어서 감사했습니다.

5. 얻은 것 & 배운 점

  • 기술적인 성장: 
    • 단일 책임 원칙과 MVC 패턴을 적용했습니다.
    • 게임 개발에서 데이터 저장 방식이 어떻게 이루어지는지 학습했고, 필요에 따라 선택해 적용할 수 있게 되었습니다.
    • 특히 파일 시그니처와 바이너리 데이터 직렬화 같은 고급 기술을 학습 목적으로 구현해보며 실제 게임 엔진의 작동 원리를 깊이 이해할 수 있었습니다.
    • 게임 엔진에서 사용하는 레벨 디자인 툴과 유사한 도구를 직접 만들어보았습니다.
  • 개발에 대한 태도나 문제 접근법의 변화
    • 설계나 코드를 작성할 때 팀원들에게 명확한 의도 전달이 중요함을 절실히 느꼈습니다.
    • 원하는 게임 방향이 있다면 적극적으로 어필하고 자료 조사나 데모를 만들어 다른 팀원들을 설득하거나 절충안을 찾아야 함을 배웠습니다.
    • 다른 파트의 팀원들에게 필요한 기능이 있으면 명확하게 요구해야 한다는 점을 깨달았습니다.
  • 협업, 피드백, 기록 등 비개발적인 면에서의 성찰
    • 깃허브의 백로그 기능과 노션, 피그마 등을 최대한 활용해 협업했는데, 이를 통해 프로젝트 일정 관리와 애자일 개발 방식을 제대로 배웠습니다.
    • 모든 사람은 각자 생각과 역량이 다르므로, 역할 분담을 잘할수록 프로젝트의 품질과 개발 속도가 향상될 수 있다는 점을 체감했습니다.

6. 아쉬운 점 & 다음 도전

  • 못 다한 기능/연출
    • 여러 스테이지를 만들고 싶었지만 에디터 툴 제작이 지연되어 실현하지 못한 점이 아쉬웠습니다.
    • 게임 시작 시 플레이어의 몰입을 위한 4컷 만화를 보여주고 싶었으나 텍스트 애니메이션으로 대체한 것이 아쉬웠습니다.
  • 더 잘 다듬었으면 좋았을 부분
    • 강사님이 제공한 프로젝트 평가 기준에 맞춰 발표를 진행했다면 더 좋았을 것 같습니다.
    • 감도 조절, 볼륨 조절 등의 사용자 편의 기능을 추가했으면 좋았을 것 같습니다.
    • 구현한 고급 파일 시스템의 이점을 더 활용할 수 있는 기능들(맵 자동 백업, 여러 버전의 맵 저장 등)을 구현했다면 더 가치가 있었을 것입니다.
  • 다음 프로젝트에서 시도해보고 싶은 것
    • 언리얼 엔진으로 전환하여 실제 3D를 구현하고 싶습니다. 루멘, 레이트레이싱 등을 활용한 라이팅 기능을 사용해보고 싶습니다.
    • 액션, 멀티플레이어, 턴제, 미니게임 요소 등이 포함된 게임을 만들어보고 싶습니다.
    • 이번에 학습한 파일 시스템과 데이터 구조화 지식을 바탕으로 효율적인 게임 세이브 시스템을 구현해보고 싶습니다.

7. 마무리 한 마디

나에게 이 프로젝트는 어떤 의미였는가?
지금 돌아보면 어떤 감정이 가장 먼저 떠오르는가?


 이 프로젝트는 게임 개발의 재미를 알려준 소중한 경험이었습니다. 최고의 팀원들과 함께해서 즐거웠고, '어떻게 하면 게임이 더 재미있어질까?', '플레이어가 우리 게임에 몰입하려면 어떻게 해야 할까?' 같은 질문들을 깊이 고민한 애정이 담긴 게임입니다. 학습을 위해 때로는 필요 이상의 기술을 적용해보는 도전도 해보았고, 이 과정에서 실제 게임 개발에 대한 이해를 크게 높일 수 있었습니다. 처음으로 인디게임 스토어에 출시도 해보고, 부족한 게임이지만 생각보다 많은 사람들이 플레이해준 것이 매우 뜻깊은 경험이었습니다.