Minimal Game Boy Hello World using GBDK-2020 and PyBoy
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
Matteo Benedetto c741944c68 feat: add victory screen and 1-rat panic spawn mechanic 3 days ago
src feat: add victory screen and 1-rat panic spawn mechanic 3 days ago
tests fix: redesign game over screen with properly centered creepy ascii rat 3 days ago
.gitignore feat: Add rat AI, 4-channel tracker, interactive cursor, and test suite updates 3 days ago
AGENTS.md feat: Add rat AI, 4-channel tracker, interactive cursor, and test suite updates 3 days ago
Makefile feat: add victory screen and 1-rat panic spawn mechanic 3 days ago
README.md docs: add note about DAS (Delayed Auto Shift) implementation 3 days ago
build_autotiles.py feat: implement 16-variant autotiling inspired by the AI mockup 3 days ago
build_bush_tiles.py feat: procedural bushy vegetation autotiles 3 days ago
build_pause.py fix: solid white background for pause sprites, stop music and play sound on pause 3 days ago
build_pro_tiles.py feat: pro pixel art autotiles matching mockup organic style 3 days ago
convert_mockup.py feat: convert AI mockup to exact GB ROM reproduction 3 days ago
extract_and_compare.py chore: add intermediate tile visualization scripts 3 days ago
extract_to_tiles_c.py feat: rip true tiles from AI mockup 3 days ago
fix_mockup.py feat: clean mockup artifacts and fix tile reduction 3 days ago
make_bomb_sprites.py feat: add bomb mechanic with explosion to kill rats 3 days ago
make_mockup_tiles.py feat: create separate mockup ROM to test organic hedges and textured floor 3 days ago
make_rat.py feat: design 40x32 pixel art rat sprite for Game Over screen and invert palette 3 days ago
prepare_bg.py fix: protect text tiles from being deduplicated in background generator 3 days ago
prepare_title.py feat: toggle music with SELECT and make newborn rats face opposite parent dir 3 days ago
prepare_victory.py feat: add victory screen and 1-rat panic spawn mechanic 3 days ago
render_tiles.py chore: add intermediate tile visualization scripts 3 days ago
screenshot.png feat: Add randomized hedge tiles for visual variety 3 days ago
test_audio.c test: update test_audio to use main game music engine 3 days ago
test_mockup.c feat: convert AI mockup to exact GB ROM reproduction 3 days ago

README.md

MICE!

Un progetto per Game Boy, scritto in linguaggio C usando GBDK-2020. Nata come semplice generazione procedurale di un labirinto perfetto (Recursive Backtracker), l'applicazione si è evoluta integrando intelligenza artificiale, interattività e un motore audio custom.

Screenshot del Gioco

Funzionalità Aggiunte

  • Intelligenza Artificiale (Rat): Un topolino animato (costruito tramite Meta-Sprite 16x8 per ottimizzare la memoria) esplora autonomamente e fluidamente i percorsi del labirinto.
  • Combattimento (Bomberman-style): Un cursore lampeggiante controllabile tramite D-Pad permette di sganciare bombe (tasto A) con conto alla rovescia ed esplosioni a croce letali per i topi.
  • Toggle Audio: Premi SELECT in qualsiasi momento per silenziare la colonna sonora e ascoltare in solitaria gli effetti sonori.
  • Tracker Musicale a 4 Canali: Una sontuosa colonna sonora chiptune generata da un sequencer nativo scritto in C, senza l'uso di engine esterni. Sfrutta l'onda quadra, la Wave RAM customizzata per il basso, e il Noise per le percussioni, ruotando su una traccia in quattro parti da ~35 secondi.

Struttura del Progetto

Al fine di mantenere un codice ordinato, pulito ed espandibile, i sorgenti sono stati suddivisi:

  • src/ : Contiene tutto il codice sorgente del gioco.
    • main.c: Entry point, loop principale, inizializzazione hardware e rendering (scrolling).
    • maze.c / maze.h: Core algoritmico per la generazione del labirinto.
    • rat.c / rat.h: Logica dell'automa e gestione degli sprite hardware per l'animazione del topo.
    • cursor.c / cursor.h: Logica interattiva del selettore del giocatore.
    • music.c / music.h: Mini-tracker musicale, sequencer e definizione dei registri audio.
    • tiles.c / tiles.h: Dichiarazione e definizione della grafica dei tile utilizzati.
  • obj/ : Cartella autogenerata durante il processo di compilazione per immagazzinare i file intermedi (.o e metadati).
  • tests/ : Script e utilities di testing.
    • test_pyboy.py: Esegue l'emulatore PyBoy in modalità headless, salvando la prova del funzionamento in un'immagine temporanea.
  • Makefile : Sistema di build preconfigurato per GBDK-2020.
  • maze.gb : La ROM finale giocabile (generata dopo il build).
  • test_audio.gb / test_audio.c: Micro-rom dedicata alla diagnostica dell'hardware sonoro.

Come Compilare

Assicurati di aver installato la toolchain GBDK-2020 nel path corretto (solitamente configurato su ~/.local/gbdk in base a questo repository). Per compilare la ROM, basterà lanciare:

make

Come Testare

Se vuoi validare la compilazione senza aprire GUI o se sei su un server remoto, puoi lanciare lo script headless:

python3 tests/test_pyboy.py

Questo genererà un file PNG in locale (/tmp/maze_gb.png) per farti visualizzare l'output atteso della ROM. Puoi anche testare le ROM su emulatori diretti da terminale come pyboy maze.gb.

Architettura e Ottimizzazioni Hardware (Retro-Engineering)

Poiché il processore custom del Game Boy (SM83, simile allo Z80) lavora a soli 4.19 MHz e non è provvisto di hardware dedicato per le moltiplicazioni o le divisioni (FPU o ALU avanzata), il codice sorgente fa un uso intensivo di "trucchi" dell'epoca per garantire i 60 FPS costanti, anche con 15 sprite complessi (Meta-Sprite) a schermo che eseguono pathfinding indipendente:

  1. Allocazione Memoria in Potenze di 2 (MAZE_PITCH = 32) In C, per leggere un elemento da un array bidimensionale come maze[y][x], il compilatore esegue un'operazione matematica: y * LARGHEZZA_RIGA + x. Nelle prime versioni, una larghezza di 20 richiedeva una lenta routine di moltiplicazione software. Per ovviare al problema, la riga logica in RAM è stata allargata a 32 (una potenza di due). In questo modo il compilatore SDCC risolve la moltiplicazione in un singolo, velocissimo bit-shift a sinistra (y << 5), azzerando del tutto il carico del processore.

  2. Divisioni sostituite da Maschere Bitwise (Bitmasks) La funzione rand() % num usa l'operatore Modulo (%), che su un'architettura a 8-bit invoca un disastroso ciclo di sottrazioni ripetute per trovare il resto. Poiché la scelta della direzione richiede valori da 0 a 3, l'operatore modulo è stato rimosso in favore di un & 3 (Bitwise AND). È istantaneo e produce un numero da 0 a 3 in un singolo colpo di clock.

  3. Collisioni "Lazy" (Early-Exit Evaluation) Piuttosto che testare le sovrapposizioni millimetriche (pixel_x / pixel_y) su O(N²) iterazioni (105 combinazioni) per frame, la logica confronta in short-circuit soltanto le coordinate grossolane in griglia (rat_x != rat_y). Se i topi non si trovano nemmeno sulla stessa mattonella, l'algoritmo ignora istantaneamente tutto il resto. Questa singola riga taglia l'80% delle istruzioni necessarie per i check di collisione.

  4. DAS (Delayed Auto Shift) per l'Input Per rendere fluida la navigazione del cursore sulla griglia, è stata implementata una logica a stati temporizzati ereditata dai classici come Tetris. Invece di far scattare il cursore a ogni micro-pressione o farlo "scivolare" in modo incontrollabile, il codice legge l'input continuo (joypad()) e utilizza un timer a due fasi:

    • Initial Delay: alla prima pressione lo spostamento è istantaneo, dopodiché la logica va in "pausa" per 12 frame (~0.2 secondi). Questo previene i doppi scatti accidentali.
    • Auto-Repeat Delay: se il giocatore continua a tenere premuto oltre la soglia iniziale, la pausa tra un movimento e l'altro si accorcia a 6 frame, permettendo uno scorrimento automatico e fulmineo lungo l'intero labirinto.

Queste tecniche mostrano la filosofia del vero retro-programming, dove ogni ciclo di CPU conta.