서론

이 문서에서는 SFMB 개발에 사용된 기술을 나열하고 설명한다.

개발 툴

  • Visual Studio 2015
  • Visual Studio 2017
  • Visual Studio 2019
  • Visual Studio 2022
  • Visual Studio Code

프로그래밍 언어

  • C++ 17
  • Javascript (NodeJS)

게임 엔진

기초 라이브러리

NaLib

  • 게임이 아닌 곳에도 활용될 것으로 예상되는 기초 코드들을 묶은 라이브러리로 직접 만들어 사용하였다.
  • 전 직장에서의 경험을 통해 크로스 플랫폼을 염두에 두고 개발하였다.
  • "Na"는 NeoArc를 줄여 클래스의 Prefix로 사용한 것이다. 나트륨을 의미하는 것이 아님
  • 사실상 NaLib이 SFMB의 여러 핵심 코드를 가지고 있으며, SFMB는 이것을 활용하는 형태로 구현되어 있다.

ExtLib

  • 오픈 소스 라이브러리를 가져다가 묶은 것이다.
  • Box2D
    • 앵그리버드 등에 사용된 유명한 2D 물리엔진
    • 게임의 물리엔진 교체에 사용해보려고 가져왔지만, 사용되지 못했다.
  • catch2
    • 단위 테스트 프레임워크
  • cppcodec
  • cxxopts
  • dirent
    • 파일시스템 관련 라이브러리
    • 게임 초기 구현시 사용되었으나, 모던 C++를 사용하게 됨에 따라 필요없게 되었다.
  • fmt
    • 문자열 포맷 라이므러리
  • JsonLib
  • libpng
  • MemoryModule
  • miniz
  • miniz-cpp
  • msgpack
  • PicoSHA2
  • zlib
  • discordbuddy
    • BatteryShark가 구현한 discordbuddy를 포팅하여 직접 구현한 것
  • discord-rpc
  • discord-sdk
    • Discord Rich Presense 연동을 위해 사용
  • rapidjson
    • discord 라이브러리에서 사용되기 때문에 포함되었다.
  • curl
    • 네트워킹 라이브러리
  • DirectX 9.1
  • OpenGL
  • SDL
  • FMOD
  • BASE

랜더 엔진

  • 최초에는 DirectX 9.1을 기준으로 구현했다가, OpenGL을 지원해보기 위해 추상화하게 되었다.
  • 다음의 기능들을 각각의 랜더러에 맞도록 구현하였다.
    • 레터 박스 (그려질 내용의 비율을 유지하고 그 외의 부분은 검은 바탕 출력)
    • 비트맵 로딩 (bmp, png)
    • 텍스쳐의 일부분을 원하는 위치에 그리기
      • 회전하기
    • 커스텀 텍스쳐 생성
    • 픽셀 셰이더를 통한 효과
    • Windows 폰트로 텍스트 출력

픽셀 셰이더

  • 주로 색상 변환에 사용되고 있다.
  • 아직 초보 수준의 구현

스프라이트 처리

  • 필요한 테마에 적합한 텍스쳐를 로딩하고, .sprite 파일을 통해 스프라이트 시트의 분할 영역 데이터를 제어한다.
  • HD 텍스쳐
    • 텍스쳐 파일명 + @2x 에 해당하는 파일이 존재하면 함께 로딩하여 HD 텍스쳐로 사용한다.
    • 2배 크기의 텍스쳐를 로딩하여 50% 로 축소하여 랜더링하는 것이다.
  • 매 프레임마다 그려야 할 스프라이트 정보를 수집하고 Z-order별로 정렬하여 순서대로 랜더링

입력 처리

DirectInput

  • 조이스틱 입력 처리
    • 조이스틱 입력을 분석하여 키보드 입력으로 치환하고 있다.
  • 키보드 입력 처리

마우스

  • 마우스 입력은 단순히 마우스 메세지 (WM_MOUSE…)를 사용한다.
  • UI 엔진에서 마우스 클릭을 버튼 클릭으로 변환하여 처리한다.

사운드 처리

FMod

  • mp3, ogg, wav 파일 재생에 사용된다.
  • 다중 채널 동시 재생, 개별 볼륨 설정을 지원한다.
  • Loop point를 지원한다.

BASE

  • 마리오 메이커와 다른 방식으로 음악 블록을 구현하기 위해 사용되었다.
  • 사운드 폰트를 로딩하고 미디 음원을 재생할 수 있다.

장면

  • State Machine으로 구현
  • 주로, 장면에서 장면으로 전환을 처리한다.
    • 특정 시간이 흐르면 자동으로
    • UI를 통해
  • 일부 장면 전환은 플레이어 객체가 요청하여 처리하기도 한다.

네트워킹

  • 게임 내 대부분의 네트워킹은 비동기로 처리하기 위해 트랜젝션 쓰레드에서 처리된다.
  • 트랜젝션 쓰레드에서 네트워킹 작업이 끝나면 GameState 내에 구현된 콜백을 호출하는 방식

게임 오브젝트

  • State Machine으로 구현
  • 모든 게임 오브젝트가 공통된 상태 목록을 사용한다.
    • Idle
    • Walk
    • Jump
    • Die
  • 초기에는 Raw pointer를 직접 사용하는 형태로 구현했었으나, 알 수 없는 크래시의 추적에 한계를 느끼고

스테이지

  • 게임 내 가장 많은 코드를 가진 클래스 중 하나
  • 타일 맵 관리
  • 맵 내 살아있는 객체 관리

카메라

  • 스테이지의 어느 부분을 보여줄지 처리하는 클래스
  • 땅의 흔들림이나 강제 스크롤도 카메라를 통해 처리하고 있다.

물리 엔진

  • 내가 직접 구현한 것으로, 단순 무식하게 구현되었다.
  • 여러번 위기에 도달했을때마다 성능 개선을 위해 구조를 크게 바꾸었다.
  • 대략 다음과 같은 역할을 매 프레임마다 수행한다.
    • 스테이지 내 살아있는 모든 객체를 수집
    • 각 객체를 현재 속도에 기반하여 이동될 위치로 이동시킴
    • 각 객체들이 타일과 충돌하는지 판단
      • 충돌한다면 각 타일 타입에 따라 flag 세팅
        • 일반 타일
        • 점프 블록
        • 마림마 블록
        • 용암
        • 스타 블록/데스 블록
    • 각 객체들이 경사로와 충돌하는지 판단하여 위치/속도 보정
    • 각 객체들을 서로 비교하여 충돌하는지 확인 (N^2)
      • 기본적으로 AABB 로 검사하나, 원형 히트박스 또는 다중 히트박스를 가진 객체도 지원
      • 서로 충돌한 객체들의 목록을 생성하여 저장
    • 충돌한 객체들의 상태에 따라 피드백 처리
      • 충돌 상태만 기억하고 있거나 (기본)
      • 밀리거나 (둘중 하나가 겹치는 것을 허용하지 않는 물체일때)
      • 탑승하거나 (발판 등)
    • 각 객체를 원래 위치로 복원

UI 엔진

  • 직접 구현한 것으로 꼭 필요한 최소한의 컴포넌트와 최소한의 기능만 갖고 있다.
  • 컴포넌트
    • Panel
    • Text
    • Button
    • CheckBox
    • Edit
    • Spin
    • Rectangle (Image)
    • VirtualList

게임

어플리케이션

Windows

  • Win32 응용프로그램으로 구현하였다.
  • 윈도우를 생성하고 윈도우 핸들을 DirectX, OpenGL에 넘겨 랜더링되도록 했다.
  • SDL의 경우는 윈도우를 생성하는 것부터 SDL이 처리하게 되어 있어서, 다른 방식으로 처리했다.

CustomURL Scheme

  • "sfmb://" 프로토콜을 통한 여러 연동 기능 구현에 사용한다.
  • 게임 실행시 CustomURL scheme을 등록한다. (레지스트리에 직접 등록)
  • Force Update 및 Direct Play에 사용한다.

MAC OS

  • (오래되어 잘 기억나지 않지만) GLView를 가진 응용프로그램으로 개발하였다.
  • GLView는 OpenGL을 지원하는 일종의 윈도우만 미리 만들어진 형태라고 보면 된다.

장면 (Scene)

로고

타이틀

맵 에디터

툴바

  • 최초에는 툴바를 가진 MDI 형태의 프로그램으로 작성했었다.
  • 툴바 버튼이 점점 많아짐에 따라 아이콘 구분이 어렵고, 정돈되지 않은 느낌이 강해 툴바를 리본바로 대체하였다.

리본바 동적 구성

  • MFC의 디자인 툴을 통해 리본바를 만들지 않고, 코드레벨에서 동적으로 메뉴를 구성한다.
    • 아이콘으로 사용할 비트맵도 DesignPalette 이미지에서 동적으로 생성
  • 메뉴 구성은 서버에서 보내준다.
    • 즉, 오프라인 크랙을 통해 플레이할 수 없도록 설계했다. (7.0 부터)

Design Palette 동적 전환

  • 각 테마마다 보여줘야 하는 아이콘이 달라지기 때문에, 활성화된 스테이지가 변경될 때마다 리본바 전체의 아이콘을 새로 로딩한다.
  • 모든 버튼 컨트롤을 수집한 후, 새로 만든 비트맵으로 교체하는 방식
  • 이 기능을 구현하기 위해 기본 리본 버튼을 사용하지 않고 직접 만든 클래스를 사용한다.

속성창

  • MFC의 PropertyGrid를 사용한다.
  • NaPropertyObjectBase 클래스를 상속하여 구현한 모든 객체의 속성을 쉽게 보여줄 수 있다.
  • 일부 속성값 타입에 대한 PropertyItem 클래스를 필요에 따라 직접 구현하였다.
    • Bool : true/false를 선택하는 대신 체크박스 표시
    • Enum(int) : 실제로는 int 값이지만, 매직 넘버로 보이지 않도록 의미에 해당하는 텍스트를 보여줌
    • Color(int) : 클릭하면 커스텀된 컬러 선택창이 표시된다.
    • Extended : value란을 버튼 형태로 표시하여, 클릭하면 별도의 편집창을 보여주는 방식

상태바

문서창

  • MDI Document 내부를 게임 엔진이 직접 그리는 형태
  • 높은 성능을 내기 위해 WM_PAINT 메세지가 발생할 때, 1프레임만 랜더링한다.

각종 다이얼로그

테마 관리 창

테마 정의 창

픽셀아트 관리 창

커스텀 적 템플릿 관리 창

코스튬 선택 창

AbilityFlag 선택 창

게임 서버

  • 최초에 온라인 모드가 도입되었을 때는 서버리스(serverless)로 구현되어 서버 앱이 존재하지 않았었다.
  • 해킹으로 서버 데이터베이스를 모두 날려먹은 후에 재발방지를 위해 서버 앱을 개발하게 되었다.