서론

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

개발 툴

  • 오랜 기간 개발한 만큼 꾸준히 최신버전의 개발툴로 교체해왔다.
    • Visual Studio 2015
    • Visual Studio 2017
    • Visual Studio 2019
    • Visual Studio 2022
  • Visual Studio Code
    • vscode의 경우, 서버나 bot 개발시에만 사용하고 있다.

프로그래밍 언어

  • C++ 17
  • Javascript (NodeJS)

게임 엔진

기초 라이브러리

NaLib

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

ExtLib

  • 오픈 소스 라이브러리를 가져다가 묶은 것이다.
  • Box2D
    • 앵그리버드 등에 사용된 유명한 2D 물리엔진
    • 게임의 물리엔진 교체에 사용해보려고 가져왔지만, 사용되지 못했다.
  • catch2
    • 단위 테스트 프레임워크
  • cppcodec
  • cxxopts
  • dirent
    • 파일시스템 관련 라이브러리
    • 게임 초기 구현시 사용되었으나, 모던 C++를 사용하게 됨에 따라 필요없게 되었다.
  • fmt
    • 문자열 포맷 라이므러리
  • JsonLib
  • libpng
  • magic_enum
  • 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
    • Swim
    • SpinJump
    • GroundPound
    • WallSlide
    • PropellerJump
    • Die
    • 위와 같은 상태가 150가지 이상 정의되어 있다.
  • 초기에는 Raw pointer를 직접 사용하는 형태로 구현했었으나, 알 수 없는 크래시의 추적에 한계를 느끼고 이후 구조를 개선하였다.

스테이지

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

카메라

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

물리 엔진

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

UI 엔진

  • 직접 구현한 것으로 꼭 필요한 최소한의 컴포넌트와 최소한의 기능만 갖고 있다.
  • 기본 컴포넌트
    • Panel
    • Container
    • Text
    • AreaText
    • Button
    • ImageButton
    • CheckBox
    • Edit
    • Spin
    • Rectangle (Image)
    • AnimationRectangle
    • Tooltip
    • Popup
    • List
    • VirtualList
    • ScrollForm
  • 게임 전용 컴포넌트
    • AbilityFlagImage (능력 표시)
    • CountryFlagTextButton (나라 깃발 버튼)
    • UserAvatarButton / UserNameButton (유저 아바타/이름 버튼)
    • SingleStageButton / WorldStageButton (스테이지 버튼)
    • CustomGameButton
    • CommentDollEdit
  • UI 화면을 손쉽게 배치하기 위한 별도의 편집 도구도 만들어 두었다.

게임

어플리케이션

Windows

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

CustomURL Scheme

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

MAC OS

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

장면 (Scene)

  • 게임은 다수의 장면으로 구성되며, 성격별로 묶으면 다음과 같다.

시작 / 시스템

  • Logo
  • Title
  • RegisterUser
  • Profile
  • Credits
  • AppUpdate
  • GameThemeUpdate
  • SystemAlert
  • ErrorMessage

월드 / 스테이지 선택

  • WorldStage
  • WorldStageDirectPlay
  • LocalStagePack
  • EnterStage
  • StageIntro / SkyIntro

플레이 중 이동 / 이벤트

  • Enter/Exit Door, Pipe, Warp, Sky
  • PageScroll
  • Play
  • NpcTalk / Speech
  • MessageBlock
  • ShapeChange
  • LoseHP
  • Die

클리어 / 종료

  • Goal
  • HitGoalBox / HitGoalOrb / HitGoalPost
  • FlagDown
  • CutBridge
  • RescuePrincess
  • SingleStageClear / SingleStageGameOver
  • GameOver
  • TimeUp

그 외 모드 / 기능

  • ChallengeMode / ChallengeSummary
  • MiniGame
  • Gallery / Showcase
  • ReportStage
  • ShowUserList
  • ManageDevice
  • Pause
  • AvatarPaint
    • 자신만의 아바타를 그릴 수 있는 그림판 기능이 직접 구현되어 있다. (캔버스 144 x 144)
    • Pencil, Eraser, Bucket 도구
    • 색상 팔레트
    • Undo / Redo (최대 10단계)
    • 그리드 표시 토글
    • 여러 슬롯 + 슬롯 간 복사
    • 애니메이션 지원
    • 프리셋 제공
    • 내보내기

맵 에디터

툴바

  • 최초에는 툴바를 가진 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프레임만 랜더링한다.

각종 다이얼로그

  • 맵 에디터에는 테마와 각종 리소스를 관리하기 위한 다수의 전용 창이 있다.

테마 / 배경 관련

  • 테마 관리 창 (게임테마 복제, 테마 설정 관리)
  • 테마 정의 창 (하늘색, 패럴랙스 배경, 배경 오브젝트 등 / [[sfmb_tutorial_theme_settings]] 참고)
  • 테마 선택 창
  • 커스텀 테마 선택 창

픽셀아트 관리 창

  • 파일에서 불러오기
  • 즐겨찾기
  • 애니메이션 프레임 선택
  • 그림판과 같은 그리기 기능이 직접 구현되어 있다.
    • Pencil, Eraser, MagicEraser, Bucket, ColorPicker 도구
    • 색상 팔레트 / 색 스펙트럼
    • Undo / Redo
    • 그리드 표시 토글
    • 캔버스 크기 변경

픽셀아트 생성 창

  • 새 픽셀아트의 캔버스 크기를 선택해 생성한다.

커스텀 적 / 블록 관리 창

  • 커스텀 적 관리 / 갤러리 / 즐겨찾기
  • 캐릭터 정의 편집
  • 커스텀 블록 관리

코스튬 / 캐릭터 / 능력 선택 창

  • 코스튬 선택
  • 캐릭터 선택
  • 무기 위치 선택
  • 목소리 종류 선택

AbilityFlag 선택 창

  • 객체가 가질 능력 플래그(AbilityFlag) 선택
  • 능력 프리셋 관리

음악 / 사운드 창

  • 배경 음악 선택
  • 효과음 선택
  • 음악 블록 도우미 / 음정 조절

그 외

  • 스테이지 미리보기
  • 하위 스테이지 설정
  • 오브젝트 목록
  • 태그 선택
  • 타일셋 불러오기
  • 실시간 텍스트 편집
  • 제작자 코멘트

게임 서버

  • 최초 도입시에는 서버리스(serverless)로 구현되어 서버 앱이 존재하지 않았었다.
  • 해킹으로 서버 데이터베이스를 모두 날려먹은 후에 재발방지를 위해 서버 앱을 개발하게 되었다.
  • Node.js로 구현
    • Express : 게임과의 통신, 스테이지 업로드 처리
    • Firebase : 유저, 스테이지, 순위표, 차단 목록 등 저장
    • 구글 스프레드시트, 디스코드 연동
    • 운영 환경은 alpha / beta로 분리
  • 클라우드 구축 과정은 [[make_nodejs_server_on_oracle_cloud]] 참고

디스코드 봇

  • 베타테스트 커뮤니티(디스코드 서버) 운영을 위한 별도의 봇. Node.js로 구현
    • discord.js : 슬래시 명령어, 모달 입력 처리
    • canvas, gifencoder : 프로필 카드, GIF 이미지 생성
    • 명령어를 권한 등급(일반 / 모더레이터 / 관리자 등)별로 분리 처리