01시작하기
이 문서는 컨트롤러 입력부터 화면 출력까지 HARAPEKO SHIROHEBI(はらぺこしろへび)의 구현을 설명합니다. 제작자는 DAISUKE OBA입니다. 흰 뱀을 조종하여 석류를 먹고 몸을 늘리면서 지뢰와 자신의 몸을 피하는 게임입니다. 제공하는 프로그램, 직접 제작한 리소스, 이 문서는 MIT 라이선스로 배포합니다.
화면 크기는 160 × 144픽셀입니다. 하나의 ROM에서 초기 Game Boy(DMG)와 Game Boy Color(CGB)를 모두 지원하며, 실행 중에 컬러 표시 여부를 선택합니다. 카트리지 구성은 MBC5, 64 KiB ROM, 배터리로 내용이 유지되는 8 KiB RAM입니다. 플레이할 수 있는 배포본은 게임의 itch.io 페이지에서 받을 수 있습니다.
권장 읽기 순서: 먼저 흐름도를 보고, 머리 이동과 몸통 추종 부분을 함께 읽어 보세요. 라이브러리 표에는 각 기법을 사용하는 소스가 나와 있습니다. 식별자에 쓰인 Apple은 화면의 석류를, Block은 지뢰를 뜻합니다.
02빌드와 파일 구성
Rust 1.85 이상으로 Windows, Linux, macOS ARM 및 macOS Intel용 컴파일러와 모든 보조 도구를 빌드할 수 있습니다. 네이티브 실행 파일에는 .NET이 필요하지 않으며, 리소스 처리 도구에도 Python이나 Pillow가 필요하지 않습니다.
PowerShell 샘플 빌드는 최상위 Windows 실행 파일을 사용합니다. Linux/macOS에서는 같은 C 입력과 옵션을 네이티브 컴파일러에 전달하거나 PowerShell 7을 사용하세요. CHR 그래픽과 C 소스는 별도 입력이며 font.chr은 원래 샘플 글꼴을 유지합니다.
powershell -NoProfile -ExecutionPolicy Bypass -File .\apps\harapeko_shirohebi\build.ps1
빌드 스크립트는 분할된 C 소스 17개를 정해진 순서로 이어 붙입니다. 음악, 내보낸 리소스, ROM 뱅크 선택용 래퍼 파일은 별도의 컴파일 입력입니다. 저장소의 lib/를 링크하므로 비공개 그래픽 프로젝트나 리소스 내보내기 도구는 필요하지 않습니다. 분할 소스들은 합쳐진 번역 단위 안에서 선언을 공유합니다. 따라서 일반 C 프로젝트에 각각 추가하여 따로 컴파일하면 안 됩니다.
| 옵션 / 출력 파일 | 용도 |
|---|---|
-KitaqgbRoot | kitaqgb.exe와 lib/가 있는 디렉터리를 지정합니다. 저장소의 기본 구성을 사용한다면 생략할 수 있습니다. |
-OutputDirectory | 출력 디렉터리를 지정합니다. 기본값은 게임 디렉터리의 out/입니다. |
-KeepBuildFiles | 빌드 성공 후에도 합쳐진 소스, 임시 오디오 조정 파일, 컴파일러 진단 정보를 보관합니다. |
shirohebi.gb | 이번 컴파일에서 생성한 ROM입니다. 빌드가 실패했다면 이전에 남아 있던 ROM을 이번 결과로 사용하지 마세요. |
shirohebi.map | 이번 빌드의 심볼 주소표입니다. 디버깅할 ROM과 같은 빌드에서 생성된 파일을 사용해야 합니다. |
build_manifest.json | 컴파일러와 ROM의 해시, 출력 크기, 인터럽트 벡터 주소를 기록합니다. |
빌드 후 처리 단계가 필요한 이유
어떤 전환식 ROM 뱅크를 선택한 상태에서도 VBlank 음악 인터럽트 코드를 호출할 수 있어야 합니다. 스크립트는 인터럽트 관련 심볼 네 개가 고정 뱅크 0에 있는지 확인한 뒤 $0040에 JP 명령을 설치하고 카트리지의 체크섬 두 개를 다시 계산합니다. 인터럽트 벡터가 예상과 다르면 빌드를 중단합니다. 임시 오디오 소스 사본에서는 일반 음악 서비스를 비활성화하여 메인 루프와 인터럽트가 음악을 중복 진행하지 않도록 합니다. 또 다른 임시 사본은 오디오 보조 함수를 배치하고 도달 가능한 절대 점프를 사용합니다. 저장소의 라이브러리 원본은 수정하지 않습니다.
전체 입력 순서와 컴파일 옵션은 build.ps1에서 확인할 수 있습니다. 주요 옵션은 --profile=release --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5_ram_battery --romsize=64k --ramsize=8k입니다.
03프로그램 흐름과 상태
| 상태 전환 | 조건과 결과 |
|---|---|
| TITLE → READY → PLAY | START를 누르면 게임을 시작합니다. START 안내 연출이 끝난 뒤 이동을 시작합니다. |
| PLAY ⇄ PAUSE | START로 일시정지하거나 재개합니다. 일시정지 중에는 물리 계산과 몸통 추종 갱신을 멈춥니다. |
| PAUSE → RETURN_CONFIRM | SELECT를 누르면 타이틀 복귀 확인창을 열며, 기본 선택은 NO입니다. 취소하면 같은 게임의 일시정지 상태로 돌아가고, YES를 선택하면 TITLE로 돌아갑니다. |
| PLAY → MISS | 지뢰 또는 자신의 몸과 충돌하면 해당 실패 연출을 시작합니다. |
| MISS → NAME_ENTRY / GAMEOVER | 순위에 들어갈 점수라면 이름 입력으로 이동하고, 그렇지 않으면 곧바로 재시도 여부를 묻습니다. 이름을 확정하면 저장한 뒤 재시도 화면으로 이동합니다. |
| GAMEOVER → READY / TITLE | YES를 선택하면 새 게임을 시작하고, NO를 선택하면 타이틀로 돌아갑니다. |
| TITLE ⇄ ERASE | SELECT+START로 점수 삭제 확인창을 엽니다. A는 확인, B 또는 START는 취소입니다. 기본 선택은 NO입니다. |
버튼 해제 대기 처리는 모든 버튼을 놓을 때까지 입력을 무시하며, 버튼을 놓았다고 확인한 루프에서도 동작을 실행하지 않습니다. 화면을 열 때 누른 버튼이 다음 동작까지 바로 확정하지 않도록 하기 위한 것입니다. 데모 모드는 자동 방향 입력으로 같은 이동·충돌 코드를 실행합니다. 타이틀의 무입력 시간은 메인 루프 900회로, 데모 길이는 VBlank 1,800회로 셉니다. 어느 쪽도 정확한 실제 경과 시간을 보장하는 값으로 설명해서는 안 됩니다.
04머리 이동: 고정소수점과 방향 전환
머리만 KQBody2D를 사용합니다. 머리가 속한 KQWorld2D에는 활성 바디가 하나뿐이며, 중력과 접촉 해석 반복 횟수는 모두 0입니다. 이동 후 충돌 판정은 게임이 직접 수행합니다. Q4 좌표는 정수 16단위가 1픽셀을 나타냅니다. 예를 들어 160은 10픽셀입니다. 따라서 부동소수점 연산 없이도 1픽셀보다 작은 이동량을 유지할 수 있습니다.
base = 8 + 2 * min(score / 5, 10) // Q4 component limit
first UP update: limit = base + (base >> 2)
held UP: limit += max(base >> 2, 1), capped at 2 * base
released UP: limit -= max(base >> 2, 1), floored at base
pixel_center = (q4_position + 8) >> 4
기본 속도 성분의 상한은 갱신당 0.5~1.75픽셀입니다. 방향은 시계 방향으로 16단계이며 오른쪽이 0, 아래쪽이 4, 왼쪽이 8, 위쪽이 12입니다. LEFT/RIGHT는 목표 방향을 한 단계씩 바꿉니다. 계속 누르면 처음에는 8회 갱신의 카운트다운을 사용하고, 이후에는 4회 갱신의 카운트다운으로 방향 전환을 반복합니다. 좌우를 동시에 누르면 방향을 바꾸지 않습니다.
목표 속도 성분은 상한 전체, 4분의 3, 4분의 1 또는 0으로 구성합니다. 실제 속도의 각 성분은 갱신당 최대 3 Q4 단위만큼 목표에 가까워집니다. 이는 계산량이 적은 정수 근사이며, 삼각함수로 정규화한 벡터는 아닙니다. 그래서 화면에 보이는 방향이 먼저 바뀌고 실제 이동 방향이 점차 따라올 수 있습니다.
kq2d_step 호출 후 머리 중심이 경계를 넘으면 반대편으로 옮깁니다. X가 4보다 작으면 156으로, 156보다 크면 4로 설정합니다. Y도 같은 방식으로 4와 140을 사용합니다. 그런 다음 픽셀 좌표로 한 번 반올림하여 그리기와 충돌 판정에 함께 사용합니다. 머리는 이 경계 이동 규칙을 쓰지만, 몸통은 좌표를 160/144로 나눈 나머지에 해당하는 방식으로 순환시킵니다.
관련 소스: shirohebi_snake.c / shirohebi_flow.c.
05몸통이 머리를 따라가는 방법
ChainBody는 SND_TraceX, SND_TraceY, SND_TraceDir를 직접 참조합니다. 이 배열은 머리가 지나간 경로가 아니라 각 마디의 현재 위치와 방향을 저장합니다. 충돌 판정과 화면 그리기는 같은 배열을 읽습니다. 뱀은 머리 하나와 몸통 세 마디로 시작하며, 머리를 포함해 최대 31마디까지 늘어납니다.
화면이 좁으면 그림을 가로로 스크롤하세요.
- 머리의 픽셀 좌표와 방향을 인덱스 0에 기록하고,
pull을 그 방향으로 설정합니다. - 마디
i의 기존 방향을 보관합니다. 이미 갱신된 앞 마디의 위치에서 방향에 따른 작은 오프셋을 빼 목표 위치를 구합니다. - 경계 순환을 고려한 X/Y 최단 변위를 구합니다. 아래 표에 따라 각 축에서 0, 1 또는 2픽셀 움직입니다.
- 현재 마디의 방향을
pull로 설정하고 새 위치를 영역 안으로 순환시킨 다음, 보관한 기존 방향을 마디i + 1에 전달합니다.
| 부호 있는 변위 d | 각 축의 보정량 |
|---|---|
| d > 4 | +2 |
| 0 < d ≤ 4 | +1 |
| d = 0 | 0 |
| −4 ≤ d < 0 | −1 |
| d < −4 | −2 |
방향 0~15의 X 오프셋은 [4,4,3,1,0,-1,-3,-4,-4,-4,-3,-1,0,1,3,4]입니다. Y는 같은 표에서 인덱스를 12단계 옮겨 사용합니다. 좌표는 160 × 144 영역 안에서 순환합니다. 길이가 W인 축에서 변위가 W/2보다 크면 W를 빼고, −W/2보다 작으면 W를 더합니다. 정확히 영역 절반만큼 떨어진 경우에는 원래 변위의 부호를 유지합니다.
화면 가장자리에서의 계산 예
앞 마디가 (2, 50)에 있고 오른쪽을 향한다면, 다음 마디의 목표는 네 픽셀 뒤인 X = (2 − 4) mod 160 = 158입니다. 다음 마디가 X=156이면 변위는 +2이므로 이번에는 +1만 움직여 X=157이 됩니다. X=0에 있다면 최단 변위는 −2이므로 X=159로 이동합니다. 이 방식으로 화면 전체를 가로지르지 않고 가장자리를 넘어 앞 마디를 따라갑니다.
길이를 늘릴 때는 꼬리의 위치와 방향을 복사한 뒤 활성 마디 수를 늘립니다. 새 마디는 이후 추종 갱신에서 점차 떨어져 나옵니다. 기존 꼬리는 20회 갱신 동안 강조 표시합니다. 라이브러리 래퍼는 추종 함수와 보조 함수를 뱅크 2에 함께 배치하여 마디마다 뱅크를 전환하는 비용을 없앱니다.
/* Excerpt: the game owns these arrays for the entire run. */
chain_body_init(&SND_ChainBody, SND_TraceX, SND_TraceY, SND_TraceDir,
SND_MAX_SEGMENTS, SND_SCREEN_W, SND_SCREEN_H);
chain_body_reset(&SND_ChainBody, SND_Length,
SND_HeadX, SND_HeadY, SND_Facing);
/* Once per PLAY update, after integrating and rounding the head. */
SND_ChainBody.count = SND_Length;
chain_body_step(&SND_ChainBody, SND_HeadX, SND_HeadY, SND_Facing);
/* On fruit collection, within capacity. */
chain_body_grow(&SND_ChainBody);
SND_Length = SND_ChainBody.count;
이 발췌 코드는 배열의 수명과 호출 순서를 보여 줍니다. 전체 게임에서는 길이 증가의 성공 여부도 확인하고 강조 효과를 설정합니다. 관련 소스: shirohebi_snake.c / shirohebi_chain.c / chain.c.
06과일, 충돌, 실패 연출
PLAY 상태의 갱신 순서는 가속 → 방향 전환 → 머리 물리 계산 → 머리 경계 이동 및 반올림 → 몸통 추종 → 지뢰 충돌 → 자기 몸 충돌 → 과일 → 강조 표시 종료 처리입니다. 같은 갱신에서 치명적인 충돌과 과일 획득이 겹치면 충돌을 우선 처리합니다.
| 검사 대상 | 실제 규칙 |
|---|---|
| 지뢰 | 각 마디 중심이 경계를 포함하는 사각형 안에 있는지 검사합니다. X=46…58 또는 102…114, Y=62…74입니다. |
| 자기 몸 | 머리와 인덱스 2 이후의 마디를 비교합니다. 경계 순환을 고려한 최단 거리에서 |dx| < 3 및 |dy| < 3이어야 합니다. 머리 바로 뒤 마디는 제외합니다. |
| 과일 | 머리와 과일 중심 사이에서 경계 순환을 적용하지 않은 일반 거리가 |dx| < 7 및 |dy| < 7이면 획득합니다. 점수를 더하고 길이 증가를 시도한 뒤 다음 위치를 고릅니다. |
이 판정은 두 축의 거리를 각각 비교하는 방식이며 원형 거리 검사가 아닙니다. 고정된 과일 후보 위치 16개를 정해진 순서로 검사합니다. 지뢰와의 최소 타일 거리는 점수 0, 10, 20, 30에서 각각 4, 3, 2, 1로 줄어듭니다. 지뢰와 같은 타일인 후보는 항상 제외합니다. 한 번의 탐색에서 최대 16개 후보를 시도합니다. 점수는 8비트 값으로 저장하므로 무한히 증가할 수 있는 카운터가 아닙니다.
자기 몸에 부딪히면 그 자세를 고정한 채 서서히 사라지게 합니다. 지뢰에 부딪히면 고정소수점 속도로 마디를 흩뿌리고, 중력을 늦게 적용하며 폭발 시점을 엇갈리게 한 뒤 구덩이를 표시합니다. 실패 타이머가 끝나면 순위에 들어갈 점수는 이름 입력으로, 나머지는 재시도 안내로 이동합니다. CGB는 컬러 효과를, DMG는 명암 팔레트를 사용합니다.
07화면 그리기: 타일과 스프라이트 순서
초기화에서는 안전한 화면 갱신 시점을 기다린 뒤 LCD를 끄고, VRAM을 지우며, 타일·글자·팔레트 데이터를 불러온 다음 화면을 켭니다. CGB 경로는 배경 속성과 컬러 팔레트를 설정하고, DMG 경로는 명암 레지스터를 사용합니다. 내보낸 배경 데이터는 한 줄에 20타일이지만 하드웨어 타일맵은 한 줄에 32타일이므로 인덱스를 혼용하면 안 됩니다.
루프마다 RAM에 섀도 OAM 버퍼를 구성합니다. 스프라이트 좌표에는 하드웨어 오프셋 X+8, Y+16을 더해 기록합니다. system_wait_vblank() 이후 OAM 데이터를 전송합니다. 뱀은 슬롯 0…30을 사용하고, 문자는 남은 슬롯을 사용합니다. 사용하지 않는 항목은 지웁니다.
처음 여섯 마디의 OAM 우선순위는 고정합니다. 그 뒤의 마디들은 그릴 때마다 위상을 4씩 증가시키며 순서를 순환시킵니다. 이는 한 스캔라인에 최대 열 개의 스프라이트만 표시할 수 있는 제약의 영향을 뒤쪽 마디들에 나누는 방법입니다. 하드웨어 제한을 없애거나 몸통 마디를 삭제하는 것은 아닙니다. 최대 길이를 바꿀 때는 배열 용량과 OAM 슬롯 40개 전체의 배치를 함께 확인해야 합니다.
START와 PAUSE 오버레이는 표시하려는 상태, 실제 표시 상태, 다시 그릴 필요가 있는지를 따로 기록합니다. 문자를 지울 때는 현재 과일 상태를 먼저 확인하여 각 칸을 복원하고, 필요한 경우 배경 맵을 사용합니다. 영역 전체를 하나의 빈 타일로 채우면 문자 아래의 과일도 지워집니다. 글자 마스크는 1비트 행 여덟 개에서 2비트 타일 데이터 16바이트로 변환하며, 글자 픽셀이 배경 무늬보다 우선합니다.
shirohebi_render.c / shirohebi_display.c / shirohebi_pause.c
08음악과 VBlank의 역할
audio_vblank.c가 고정 뱅크의 음악 스트림을 읽습니다. 일반 레코드의 순서는 대기 시간, CH1, CH2, CH3, CH4이며, 각각 스위프가 있는 펄스, 펄스, 파형, 노이즈에 해당합니다. 따라서 채널 순서는 실제 하드웨어와 같습니다. 음악 제어에는 AudioVBlank_PlayMusic, AudioVBlank_Stop, AudioVBlank_SetEnabled를 사용합니다.
일반 audio.c 드라이버는 게임 효과음을 담당합니다. 이 드라이버의 음악 서비스는 빌드 중 임시 매크로 정의로 제외합니다. 게임 효과음은 __bankof로 데이터의 뱅크를 구하고 우선순위에 따라 재생합니다. 지뢰 효과에는 CH4 노이즈도 직접 사용합니다. 일시정지는 음악과 효과음을 함께 조정합니다. 메인 루프에서 호출하는 SND_AudioServiceWork는 의도적으로 비워 두었습니다. 마디를 더 많이 처리한다고 음악 시계까지 더 빨리 진행되어서는 안 되기 때문입니다.
AudioVBlank_FrameHook은 게임의 고정 뱅크에 있는 SND_DemoVBlankHook을 호출합니다. 이 훅은 8비트 데모 카운터를 증가시키고, P1 선택 값을 보존하면서 시스템 버튼 그룹을 읽습니다. 해당 기능이 활성화되어 있으면 타이틀의 SELECT+START와 일시정지 중 SELECT 입력을 저장하므로, 메인 루프가 바빠도 짧은 입력을 놓치지 않습니다. 훅 안에서는 그리기나 전환식 뱅크 함수 호출을 하지 않습니다.
/* Excerpt from initialization, after shared state has been set up. */
SND_AudioInit();
AudioVBlank_FrameHook = SND_DemoVBlankHook;
AudioVBlank_EnableIrq();
09점수 저장과 이름 입력
슬롯 0에는 응용 프로그램 데이터 42바이트를 저장합니다. 식별 값 0x53, 버전 2, 각각 1바이트인 점수 열 개, 이름 문자 서른 개로 구성됩니다. 저장 라이브러리는 바깥 형식의 길이와 체크섬을 검사합니다. 읽기 코드는 5바이트와 15바이트 점수 형식도 인식하지만, 쓸 때는 42바이트 형식을 사용합니다.
순위 조건을 만족하는 0이 아닌 점수는 내림차순으로 삽입합니다. 엄격한 초과 비교를 사용하므로 같은 점수는 기존 기록 뒤에 들어갑니다. 이름은 해당 점수와 함께 이동합니다. 새 기록의 이름은 RAM에서 AAA로 시작하며, 플레이어가 세 글자 이름을 확정할 때 SRAM에 씁니다. 아직 확정하지 않은 결과를 포기하면 저장하지 않고 기존 순위를 다시 불러옵니다.
UP/DOWN으로 A–Z 또는 마침표를 고릅니다. RIGHT/SELECT는 다음 글자로, LEFT는 이전 글자로 순환 이동합니다. B는 커서가 첫 글자에 있지 않을 때만 뒤로 이동합니다. A는 다음 글자로 이동하되 마지막 글자에서는 확정하고, START는 어느 위치에서든 확정합니다. 버튼 해제 대기 처리는 이 확인 입력이 재시도 선택까지 실행하지 못하게 합니다.
10KITAQGB 라이브러리 사용법
저장소의 라이브러리 소스와 헤더를 짝으로 사용하세요. 게임 전용 래퍼와 설정도 예제의 일부이며, 특히 뱅크 배치와 두 오디오 처리 경로의 조정을 살펴봐야 합니다.
| 라이브러리 | 진입점과 용도 | 사용 예제 소스 |
|---|---|---|
system.c | system_init, system_wait_vblank시스템 상태를 초기화하고 화면 전송 시점을 동기화합니다. 오디오 IRQ 훅은 별도로 설치합니다. | shirohebi_main.c |
chain.c | chain_body_init, chain_body_reset, chain_body_step, chain_body_grow계속 유지되는 배열을 연결하고, 초기 자세를 구성하며, 머리를 따라가고 꼬리를 추가합니다. 원형 이력 버퍼를 사용하는 Chain API는 사용하지 않습니다. | shirohebi_snake.c |
physics2d.c | kq2d_world_init, kq2d_stepQ4 머리 바디 하나의 운동을 적분합니다. 중력과 접촉 해석 반복 횟수는 0으로 설정합니다. | shirohebi_snake.c |
sprite.c | sprite_init; kq_sprite_oam 및 관련 상태섀도 OAM을 초기화하고 사용합니다. 게임이 항목을 직접 채운 뒤 전송하므로 슬롯과 좌표 규칙을 지켜야 합니다. | shirohebi_render.c |
audio_vblank.c | AudioVBlank_Init, PlayMusic, Stop, SetEnabled, EnableIrq, FrameHook상주 음악 스트림을 재생하고 인터럽트에서 게임 콜백을 호출합니다. IRQ 코드와 데이터는 고정 ROM에 둡니다. 첫 항목 뒤의 약칭에도 모두 AudioVBlank_ 접두사가 붙습니다. | shirohebi_audio.c |
audio.c | Audio_Init, Audio_Update, Audio_PlaySFXBanked, Audio_StopSfx, Audio_SetPaused효과음 재생, 우선순위, 일시정지를 VBlank 음악 드라이버와 조정합니다. | shirohebi_audio.c |
save.c | save_exists, save_read, save_write슬롯 0에서 버전 정보가 있는 순위 데이터를 읽고 씁니다. save_read는 요청한 정확한 길이와 체크섬을 검사한 뒤 성공 또는 실패를 반환하며, 게임도 자체 식별 값을 확인합니다. | shirohebi_save.c |
cgb_palette.c | cgb_bg_palette, cgb_obj_paletteCGB 색상을 설정합니다. DMG 명암 팔레트 레지스터는 별도로 명시적으로 설정합니다. | shirohebi_display.c |
map.c | map_load 및 내보낸 map_t 기술 구조체리소스 파일의 맵 보조 함수를 지원합니다. 게임 중 배경 복원은 게임 코드가 칸별로 직접 수행합니다. | shirohebi_assets.c |
fixed.c | 고정소수점 지원지원 코드로 링크됩니다. 머리 이동은 physics2d와 정수 Q4 연산으로 구현합니다. | shirohebi_common.c |
audio_hwregs_gb.c | 공유 오디오 레지스터 정의두 오디오 처리 경로에 필요한 Game Boy 하드웨어 레지스터 정의를 제공합니다. | shirohebi_audio.c |
라이브러리와 함께 사용하는 컴파일러 내장 함수
__readpadex(previous)는 하위 바이트에 누르고 있는 버튼을, 상위 바이트에 새로 누른 버튼을 반환합니다. __cgb_is_cgb()는 컬러 처리 경로를 선택하는 데 사용합니다. __settile, __settileattr 같은 타일·속성 내장 함수는 배경 칸을 갱신하며, __bankof는 효과음 데이터의 ROM 뱅크 번호를 제공합니다. 이들은 컴파일러 기능이므로 별도의 C 구현을 게임에 복사할 필요가 없습니다.
11소스 파일 안내와 수정 시 확인 사항
| 파일 | 역할 |
|---|---|
shirohebi_common.c | 타입, 상수, 공유 상태, 전방 선언. 심볼을 추적할 때 먼저 읽어 보세요. |
shirohebi_main.c | 시작 순서와 입력 → 갱신 → 그리기 → VBlank의 메인 루프. |
shirohebi_flow.c | 상태별 처리 분기: 타이틀 옵션, READY, PLAY, 일시정지, 실패, 이름 입력, 재시도. |
shirohebi_input.c | 버튼 해제 대기, 타이틀 단축 조작, 타이틀 복귀 확인. |
shirohebi_snake.c | 머리 물리 계산, 가속, 상대적 방향 전환, 경계 순환, 몸통 추종 초기화·갱신·증가. |
shirohebi_chain.c | 공유 chain 구현 전체를 포함하는 뱅크 2 래퍼. |
shirohebi_collision.c | 지뢰·자기 몸 충돌, 충돌 유형 판별, 마디가 흩어지는 애니메이션. |
shirohebi_apples.c | 석류 후보 위치 선택, 획득, 점수 증가, 몸 길이 증가. 내부 식별자는 Apple입니다. |
shirohebi_demo.c | 데모 조향, 긴 뱀의 자세, VBlank 카운터를 이용한 경과 틱 집계. |
shirohebi_render.c | 섀도 OAM 구성, 머리·몸통·꼬리 타일, 우선순위 순환, 실패 효과. |
shirohebi_display.c | LCD/VRAM 초기화, 팔레트와 글자 업로드, 타이틀과 순위 화면 배치. |
shirohebi_background.c | 게임 배경 칸, 지뢰, 덮어쓴 칸 복원. |
shirohebi_pause.c | START/PAUSE 문자 오버레이의 요청·표시·재그리기 상태. |
shirohebi_text.c | 문자에서 타일로의 변환, 문자열, 숫자 점수, 메뉴 커서. |
shirohebi_save.c | 순위 검증과 삽입, 세 글자 이름, SRAM 저장. |
shirohebi_audio.c | 음악·효과음 초기화, 시작과 정지, 옵션, 일시정지, 채널 조정. |
music.c | 시작 짧은 곡, 메인 곡, 게임 종료 곡을 위한 상주 고정 길이 VBlank 음악 레코드. |
sfx.c | 게임 오디오 함수에서 선택하는 효과음 바이트 스트림. |
text_tiles.c | 글자 마스크와 Game Boy 2비트 타일 행 데이터로의 변환. |
shirohebi_assets.c | 내보낸 타일, 타일맵, 속성, 충돌·이벤트 표, 맵 기술 구조체와 보조 함수. |
shirohebi_input_shared.h | 인터럽트와 공유하는 WRAM 입력 플래그 및 고정 뱅크 콜백 선언. |
shirohebi_lib_bank0.c | 고정 뱅크의 VBlank 입력·시계 콜백, 공유 WRAM 플래그, 뒤따르는 시스템·오디오 컴파일 입력의 뱅크 0 선택. |
shirohebi_lib_bank1.c | 뒤따르는 지원 라이브러리 컴파일 입력의 뱅크 1 선택. |
게임을 수정할 때 확인할 사항
- 속도와 방향 전환: Q4 상수를 바꾼 뒤 저속 회전과 UP 가속을 확인하세요. 머리가 바라보는 방향 단계를 정확한 속도 방향으로 간주하면 안 됩니다.
- 길이: 배열 용량, 체인 용량, OAM 예약 슬롯을 함께 조정하세요. 꼬리의 지뢰·자기 몸 충돌과 스캔라인별 스프라이트 제한도 확인하세요.
- 그래픽: 타일 번호, 맵의 행 간격, CGB 속성을 유지하세요. 오버레이를 지울 때는 그 아래 칸의 현재 내용을 복원하세요.
- 오디오: 인터럽트 코드는 고정 뱅크에 상주시켜야 하며 음악 시계를 진행하는 주체는 정확히 하나여야 합니다. 정상 재생뿐 아니라 일시정지와 재개도 테스트하세요.
- 저장: 데이터 구성을 바꾸면 버전도 바꾸세요. 인덱스로 접근하기 전에 길이를 확인하고, 초기 상태와 잘못된 SRAM 데이터를 테스트하세요.
다시 빌드한 뒤에는 그 빌드에서 생성한 주소표를 사용하세요. DMG와 CGB 모드 모두에서 타이틀, START 연출, 이동, 회전, 과일, 자기 몸·지뢰 충돌, 일시정지·재개, 복귀 확인, 순위를 확인하세요. 컴파일 성공만으로 이러한 동작이나 실기에서의 정상 작동이 입증되지는 않습니다.