Rewrite technical README: features, modes, architecture, build/publish guides (web, Android APK, Gitea release)
This commit is contained in:
@@ -2,61 +2,164 @@
|
||||
|
||||
# JuggleBoard Game
|
||||
|
||||
Un gioco arcade **reaction & chaining** per dispositivi mobili (orizzontale), realizzato in **Godot 4.7** (GDScript).
|
||||
Gioco arcade **reaction & chaining** per dispositivi mobili (orizzontale), realizzato in **Godot 4.7** (GDScript), ispirato al prodotto riabilitativo **"Juggle Board" by Craig Quat / Play Juggling**.
|
||||
|
||||
**Prodotto per la [Cooperativa Sociale UPENDI](https://www.upendi.it/)** (Gravina in Puglia) — Pedagogia del Circo e Circo Sociale.
|
||||
**Prodotto per la [Cooperativa Sociale UPENDI](https://www.upendi.it/)** (Gravina in Puglia) — Pedagogia del Circo e Circo Sociale. Target: bambini, anche neurodivergenti (autismo, ADHD, iperreattività sensoriale): suoni morbidi (sine, niente onde quadre/sega), palette tenue, modalità Zen e errori gentili.
|
||||
|
||||
> Il logo UPENDI è stato generato con l'API Gemini.
|
||||
|
||||
Un gioco arcade **reaction & chaining** per dispositivi mobili (orizzontale), realizzato in **Godot 4.7** (GDScript), ispirato al prodotto riabilitativo "Juggle Board".
|
||||
---
|
||||
|
||||
## Gameplay
|
||||
- **5 corsie orizzontali**, ognuna con una **pallina colorata** a riposo sul lato destro.
|
||||
- Un **bersaglio in alto** indica quale pallina toccare.
|
||||
- Toccando la pallina giusta: scivola a sinistra e poi torna a destra, con un **suono di conferma**.
|
||||
- Un nuovo bersaglio appare **solo se c'è almeno una pallina a riposo**.
|
||||
- Devi concatenare i lanci **prima che scada il timeout**: più concateni, più punti (combo).
|
||||
- **Difficoltà crescente**: il tempo parte molto disteso (~4,5s) e **si riduce** con il tempo trascorso e il punteggio.
|
||||
- **Tap sbagliato** → suono di errore e reset combo (nessun game over).
|
||||
- **Combo con decadimento**: il combo cresce con i tap rapidi ma **si riduce pian piano** in proporzione al tempo che aspetti prima di cliccare la pallina bersaglio (rate configurabile `COMBO_DECAY`).
|
||||
- **Timeout scaduto** → suono dedicato, reset combo, nuovo bersaglio (nessun game over).
|
||||
- I suoni (lancio, errore, timeout) sono generati proceduralmente in 8-bit.
|
||||
|
||||
- **5 corsie orizzontali in legno** (plancia a tutta larghezza), ognuna con una **pallina colorata** glossy (corallo, blu, verde, giallo, viola) a riposo sull'alloggiamento destro.
|
||||
- Un **bersaglio in alto** (HUD a cartiglio) indica quale pallina toccare; la pallina rotola a sinistra e torna (con **rotazione realistica** attorno all'asse perpendicolare al moto).
|
||||
- Devi concatenare i lanci prima che scada il timeout. **Difficoltà crescente legata SOLO al punteggio** (`score_ramp = 8000` → velocità massima).
|
||||
- Punteggio per lancio = **combo × valore punto** (`points_per_combo`, default 1; combo max 5).
|
||||
- **Suoni generati proceduralmente** in 8-bit (sine morbide); BGM circense AI (Lyria) in OGG.
|
||||
|
||||
### Modalità di gioco (dialog all'avvio)
|
||||
| Modalità | Descrizione |
|
||||
|----------|-------------|
|
||||
| **Target (Classica)** | Raggiungi il punteggio `target_score` (default 1000) → VITTORIA |
|
||||
| **Target (Zen)** | Come Target ma **senza countdown né decay** combo |
|
||||
| **Infinita (Survival)** | **A vite**: 4 cuori; errore o timeout = −1 cuore; 0 cuori → **GAME OVER** (ignora il target) |
|
||||
|
||||
Le modalità sono **mutuamente esclusive**; lo Zen è disponibile **solo** per la modalità Target.
|
||||
|
||||
### Protezione adulti (lock)
|
||||
Il menù **Opzioni** è protetto: va sbloccato premendo la sequenza **giallo → verde → blu → rosso** tra le 5 biglie (pallini che mostrano i colori premuti; reset dopo feedback rosso 2s se la sequenza di 4 è errata).
|
||||
|
||||
---
|
||||
|
||||
## Requisiti
|
||||
- Godot 4.x (sviluppato e testato con 4.7.1, renderer GL Compatibility).
|
||||
|
||||
- **Godot 4.7.x** (testato con 4.7.1), renderer **GL Compatibility**.
|
||||
- Per l'APK Android: JDK 17, Android SDK (vedi sotto), template Android.
|
||||
|
||||
## Eseguire
|
||||
```bash
|
||||
# dal percorso del progetto
|
||||
godot --path .
|
||||
```
|
||||
Oppure apri il progetto con l'editor Godot (`godot -e .`) e premi **Play** (F5).
|
||||
|
||||
## Controlli
|
||||
- **Desktop**: click del mouse sulla pallina.
|
||||
- **Mobile**: tocco sullo schermo.
|
||||
```bash
|
||||
godot --path . # gioco
|
||||
godot -e . # editor
|
||||
godot --headless --path . --quit-after 120 # smoke test (errori)
|
||||
```
|
||||
|
||||
## Struttura
|
||||
|
||||
```
|
||||
project.godot # configurazione (viewport 1280x720, landscape, GL Compatibility)
|
||||
icon.svg
|
||||
scenes/
|
||||
main.tscn # scena principale (radice Node2D + script)
|
||||
project.godot # viewport 1280x720 landscape, GL Compatibility, ETC2/ASTC
|
||||
export_presets.cfg # preset Web e Android
|
||||
assets/
|
||||
wood_board.png # texture legno betulla (generata con Antigravity)
|
||||
wood_board_rounded.png # variante con angoli arrotondati (board)
|
||||
circus_arena_bg.png # sfondo arena circo (generato)
|
||||
fonts/NotoSerifDisplay-CondensedExtraBold.ttf # font display circense
|
||||
audio/circus.ogg # BGM circense (generata con API Lyria, loop in import)
|
||||
branding/ # logo UPENDI (boot splash, avatar, README)
|
||||
docs/ # mockup visuali
|
||||
scenes/main.tscn
|
||||
scripts/
|
||||
main.gd # logica di gioco, HUD, punteggio, difficoltà
|
||||
ball.gd # pallina: Area2D, scivolata sinistra↔destra, input
|
||||
target_dot.gd # indicatore del colore bersaglio
|
||||
settings.gd # AUTOLOAD Settings: parametri configurabili, user://settings.cfg
|
||||
main.gd # logica gioco, HUD, difficoltà, vittoria/game over
|
||||
board.gd # plancia legno: corsie a larghezza variabile, alloggiamenti, circo
|
||||
ball.gd # biglia: glossy, venature casuali per colore, rotazione (asse Y)
|
||||
target_dot.gd # biglia bersaglio nell'HUD
|
||||
hearts_display.gd # cuori (survival)
|
||||
lock_menu.gd # lock sequenza colori (protegge Opzioni)
|
||||
options_menu.gd # menù impostazioni (persistente, riavvio)
|
||||
mode_select.gd # dialog scelta modalità all'avvio
|
||||
victory_overlay.gd # overlay VITTORIA / GAME OVER (show_end)
|
||||
hud_cartouche.gd # HUD a cartiglio circense
|
||||
```
|
||||
|
||||
## Personalizzare la difficoltà
|
||||
In `scripts/main.gd` (sezione "Tuning difficoltà"):
|
||||
- `START_TIME` → tempo iniziale tra un ordine e l'altro (s). Più alto = partenza più distesa.
|
||||
- `MIN_TIME` → tempo minimo (velocità massima) raggiunto a difficoltà piena.
|
||||
- `TIME_RAMP` → secondi di gioco per raggiungere la difficoltà piena.
|
||||
- `SCORE_RAMP` → punteggio per raggiungere la difficoltà piena.
|
||||
## Impostazioni (`settings.gd` → `user://settings.cfg`)
|
||||
|
||||
La difficoltà cresce col massimo tra il fattore-tempo e il fattore-punteggio.
|
||||
Parametri modificabili dal menù Opzioni: tempi (start/min), `score_ramp`, `points_per_combo`, `target_score`, `max_combo`, decay combo (threshold/rate), `difficulty_decay_rate`, `gentle_errors`, `game_mode` (0 Classica, 1 Zen, 2 Survival).
|
||||
|
||||
## Esportazione mobile
|
||||
Installa dal Project Manager il template **Android** (o iOS su macOS), poi
|
||||
*Project → Export* con preset `Android` / `Web`.
|
||||
> **CRITICO**: `settings.gd` ha `const VERSION`. Quando si cambiano i **default**, incrementare `VERSION`: i file `settings.cfg` salvati con versione più vecchia vengono **resettati ai nuovi default** (altrimenti i vecchi valori salvati sovrascrivono i nuovi default).
|
||||
|
||||
---
|
||||
|
||||
## Build Web
|
||||
|
||||
```bash
|
||||
godot --headless --path . --export-release "Web" build/web/index.html
|
||||
```
|
||||
|
||||
## Pubblicare il Web (enne2.net)
|
||||
|
||||
```bash
|
||||
cd build/web
|
||||
scp -r . debian@enne2.net:/home/debian/public_html/autgame/
|
||||
```
|
||||
→ URL: **https://enne2.net/pub/autgame/** (Nginx: `location /pub/` alias `/home/debian/public_html/`, autoindex on).
|
||||
Nota: troppe connessioni SSH rapide attivano fail2ban → raggruppa i comandi.
|
||||
|
||||
## Build APK Android
|
||||
|
||||
Setup una tantum (macchina di sviluppo, senza sudo):
|
||||
|
||||
```bash
|
||||
# 1) JDK portatile (Temurin 17)
|
||||
# ~/.local/opt/jdk-17.0.20+8 (scarica da api.adoptium.net)
|
||||
# 2) Android SDK in ~/Android/Sdk:
|
||||
# cmdline-tools/latest/bin/sdkmanager (scaricare commandlinetools)
|
||||
# sdkmanager "platform-tools" "platforms;android-35" "platforms;android-36" \
|
||||
# "build-tools;35.0.0" "build-tools;36.0.0" (con JAVA_HOME esportato)
|
||||
# 3) Impostazioni editor Godot (~/.config/godot/editor_settings-4.7.tres):
|
||||
# export/android/android_sdk_path = "~/Android/Sdk"
|
||||
# export/android/java_sdk_path = "~/.local/opt/jdk-17.0.20+8"
|
||||
# 4) Debug keystore (alias androiddebugkey, pass android):
|
||||
# ~/.local/share/godot/keystores/debug.keystore
|
||||
# (usato anche come release keystore nel preset export_presets.cfg)
|
||||
# 5) project.godot deve avere rendering/textures/vram_compression/import_etc2_astc=true
|
||||
```
|
||||
|
||||
Build + firma:
|
||||
|
||||
```bash
|
||||
export JAVA_HOME=~/.local/opt/jdk-17.0.20+8
|
||||
export ANDROID_HOME=~/Android/Sdk
|
||||
export PATH="$JAVA_HOME/bin:$ANDROID_HOME/build-tools/36.0.0:$PATH"
|
||||
godot --headless --path . --export-release "Android" build/android/autgame.apk
|
||||
# output: APK firmato v2/v3 (apksigner) in build/android/autgame.apk
|
||||
```
|
||||
|
||||
Il preset usa l'**export classico** (`gradle_build/use_gradle_build=false`), che richiede i template Android in `~/.local/share/godot/export_templates/4.7.1.stable/` (da Godot_v4.7.1-stable_export_templates.tpz: `android_release.apk`, `android_debug.apk`, `android_source.zip`).
|
||||
|
||||
## Pubblicare la release su git.enne2.net (API)
|
||||
|
||||
```bash
|
||||
TOKEN=$(python3 -c "import yaml;d=yaml.safe_load(open('$HOME/.config/tea/config.yml'));print([l['token'] for l in d['logins'] if l['name']=='enne2'][0])")
|
||||
# nuova release (o riusa l'id esistente, es. 15 per v0.1):
|
||||
curl -s -X POST https://git.enne2.net/api/v1/repos/enne2/autgame/releases \
|
||||
-H "Authorization: token $TOKEN" -H "Content-Type: application/json" \
|
||||
-d '{"tag_name":"v0.2","name":"0.2","body":"..."}' # -> id RELEASE_ID
|
||||
# sostituire l'asset APK:
|
||||
AID=$(curl -s https://git.enne2.net/api/v1/repos/enne2/autgame/releases/$RELEASE_ID \
|
||||
-H "Authorization: token $TOKEN" | python3 -c "import sys,json;print(json.load(sys.stdin)['assets'][0]['id'])")
|
||||
curl -s -X DELETE .../releases/$RELEASE_ID/assets/$AID -H "Authorization: token $TOKEN"
|
||||
curl -s -X POST .../releases/$RELEASE_ID/assets?name=autgame.apk -H "Authorization: token $TOKEN" \
|
||||
-F "attachment=@build/android/autgame.apk"
|
||||
```
|
||||
|
||||
Note: il CLI `tea repos create` ha un bug ("GetOrgByName") con owner utente → creare repo via `POST /api/v1/user/repos`. Avatar repo: `POST /api/v1/repos/{owner}/{repo}/avatar` con body JSON `{"image": "<base64 puro>"}`.
|
||||
|
||||
## Workflow sviluppo (pi/Codex)
|
||||
|
||||
- La **logica** va implementata e verificata direttamente in Godot (test headless + screenshot).
|
||||
- Per il **polish visivo** (tema legno/ottone/circo), mandare gli **screenshot** dello stato attuale a Codex CLI con prompt chiaro:
|
||||
```bash
|
||||
codex exec -C /home/enne2/Dev/autgame --dangerously-bypass-approvals-and-sandbox \
|
||||
-i shot1.png -i shot2.png < prompt.txt
|
||||
```
|
||||
- **Chiudere sempre il processo godot dopo gli screenshot** (altrimenti interferisce).
|
||||
|
||||
## Asset generati con AI
|
||||
|
||||
- Texture legno e sfondo circo: modello immagini Antigravity.
|
||||
- BGM circense: **API Gemini Lyria** (`lyria-3-pro-preview`), convertita in **OGG Vorbis** (Godot non importa .opus) con loop attivo nell'import.
|
||||
- Logo UPENDI (boot splash, avatar repo, README): Gemini image / immagine da upendi.it.
|
||||
|
||||
---
|
||||
|
||||
© Cooperativa Sociale UPENDI — Gravina in Puglia
|
||||
|
||||
Reference in New Issue
Block a user