Files
fn_registry/dev/issues/0072g-gamedev-android-build.md

253 lines
7.3 KiB
Markdown

---
id: "0072g"
title: "gamedev — Android build (NDK + touch input + virtual gamepad + WalletConnect)"
status: pendiente
type: feature
domain:
- gamedev
scope: multi-app
priority: media
depends:
- "0072b"
- "0072c"
blocks: []
related: []
created: 2026-05-10
updated: 2026-05-17
tags:
- gamedev
- android
- mobile
---
## Objetivo
Compilar el runtime gamedev a APK Android, ejecutarlo en device fisico, gestionar touch input + virtual gamepad overlay, integrar wallet crypto via WalletConnect deep links, y mantener APK base ≤ 20 MB.
## Toolchain
Requisitos en host:
- Android SDK (cmdline-tools)
- Android NDK r27+ (LTS)
- JDK 17
- Gradle 8+
- `adb` para deploy a device
Funcion bash: `bash/functions/infra/setup_android_ndk.sh` que descarga e instala SDK+NDK en `~/android-sdk/`. Idempotente.
## Estructura del proyecto Android
SDL3 ya provee plantilla Android. Adaptar:
```
cpp/apps/<app>/android/
build.gradle # Top-level
settings.gradle
app/
build.gradle # App module (NDK config, ABI filters)
AndroidManifest.xml
src/main/
java/com/.../MainActivity.java # Hereda SDL3 main activity
jni/ # NDK wrapper
Android.mk / CMakeLists.txt
res/
drawable/icon.png
values/strings.xml
```
## CMake cross-compile
```cmake
# Ya en cpp/apps/<app>/CMakeLists.txt, sin cambios.
# Build invocado desde Android Studio o gradle:
# ./gradlew assembleRelease
```
ABIs target:
- `arm64-v8a` — obligatorio (todos los devices modernos)
- `armeabi-v7a` — opcional (devices viejos)
- `x86_64` — solo para emulador
Cada ABI ≈ 5-7 MB del APK base. Distribuir como **App Bundle (AAB)** en Play Store, que entrega solo la ABI del device.
## Touch input
`cpp/functions/gamedev/input_unified` (de 0072b) ya tiene array `touches[8]`. Aqui lo poblamos.
SDL3 provee:
```c
SDL_FINGERDOWN, SDL_FINGERUP, SDL_FINGERMOTION
```
`input_unified` mapea a `InputState.touches`. Cada touch tiene `id`, `x`, `y` (normalizado 0..1), `pressure`.
## Virtual gamepad
Funcion nueva: `virtual_gamepad_cpp_gamedev` (impure):
```cpp
struct VGamepadCfg {
bool show_dpad; // Lado izquierdo
bool show_buttons; // Lado derecho (A, B, X, Y)
float dpad_radius; // En pixels
float button_radius;
Color color_active;
Color color_idle;
};
void vgamepad_render(const VGamepadCfg& cfg, InputState& out_input,
const InputState& touch_input);
```
Logica:
- Detecta touches dentro del area del dpad → setea `out_input.left/right/up/down`.
- Detecta touches en area de botones → setea `out_input.action_a/b/x/y`.
- Render: dibuja con `sprite_batch` los iconos del dpad y botones.
Estilo: semi-transparente, configurable color/tamaño. Sprites por defecto en `cpp/assets/vgamepad/` (dpad.png, btn_a.png, ...).
Auto-hide opcional cuando hay gamepad fisico conectado (Bluetooth):
```cpp
if (SDL_GetGamepads(NULL) > 0) cfg.show_dpad = cfg.show_buttons = false;
```
## Safe area / notch
Funcion: `safe_area_cpp_gamedev` (pure):
```cpp
struct SafeArea { int top, bottom, left, right; }; // En pixels
SafeArea safe_area_get(SDL_Window* w);
```
SDL3: `SDL_GetWindowSafeArea(SDL_Window*, SDL_Rect*)`. Render UI dentro de ese rect.
## Permisos
`AndroidManifest.xml`:
```xml
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.VIBRATE" />
<!-- WalletConnect deep link: -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="<app_name>" />
</intent-filter>
```
NO pedir permisos de:
- Storage (escribir solo en `getFilesDir()`, no necesita permiso).
- Camera/Mic — solo si el juego los usa.
## Crypto wallet (WalletConnect)
En mobile no hay `window.ethereum`. Usuario abre el juego, pulsa "Connect Wallet" → SDK genera URI WalletConnect → abre app de wallet (MetaMask Mobile, Trust, Rainbow) via deep link → wallet firma → callback con resultado.
Plan: empotrar **WalletConnect Sign SDK** version C (¿existe?). Si no:
1. Implementar protocolo WalletConnect v2 minimo en C++ (WebSocket + JSON-RPC + AES-256-GCM). ~1000 LoC.
2. Funcion: `walletconnect_session_cpp_crypto`, `walletconnect_request_cpp_crypto`.
Alternativa pragmatica: webview embebida con bridge JS (mismo patron que 0072e). Android tiene `WebView`, iOS tiene `WKWebView`. Pesa mas pero reusa codigo.
Decision pendiente — sub-issue dedicado al protocolo si se complica.
## Pipeline de build
`bash/functions/pipelines/build_android_cpp_pipelines.sh`:
```bash
#!/usr/bin/env bash
set -euo pipefail
APP="$1"
cd "cpp/apps/$APP/android"
./gradlew assembleRelease
APK="app/build/outputs/apk/release/app-release.apk"
echo "APK: $(stat -c%s "$APK") bytes"
```
Para deploy a device:
```bash
adb install -r app/build/outputs/apk/release/app-release.apk
adb shell am start -n com.fn.<app>/.MainActivity
```
## Firma del APK
Generar keystore una vez:
```bash
keytool -genkey -v -keystore release.keystore -keyalg RSA -keysize 2048 \
-validity 10000 -alias <app_name>
```
Guardar en `~/keystores/` (gitignored) y referenciarlo en `gradle.properties`:
```
RELEASE_STORE_FILE=/home/lucas/keystores/<app>.keystore
RELEASE_STORE_PASSWORD=...
RELEASE_KEY_ALIAS=...
RELEASE_KEY_PASSWORD=...
```
## Tamaño
| Componente | Tamaño aprox |
|---|---|
| arm64-v8a `.so` (runtime) | 4-6 MB |
| SDL3 lib | 1-2 MB |
| Java glue | 50 KB |
| Resources (icons) | 50 KB |
| Assets bundle | depende del juego |
APK base objetivo: **≤ 20 MB** (sin assets pesados).
## Logging
`adb logcat | grep <app>` para ver `printf` y crashes. SDL3 redirige stdout/stderr a logcat por default.
Funcion: `bash/functions/infra/android_logcat_filter.sh <app_name>` que tail -f con filtros utiles.
## e2e_checks
```yaml
e2e_checks:
- id: build_apk
cmd: "bash bash/functions/pipelines/build_android_cpp_pipelines.sh <app>"
timeout_s: 600
- id: apk_size
cmd: "test $(stat -c%s cpp/apps/<app>/android/app/build/outputs/apk/release/app-release.apk) -lt 20971520"
- id: apk_install_emulator
cmd: "bash bash/functions/infra/android_emulator_smoke.sh <app>"
severity: warning # Emulador puede no estar disponible
timeout_s: 300
```
## Tests en device
Smoke test manual al principio. Automatizar despues con `adb shell input tap` + `adb shell screencap` + comparacion de imagenes (si vale la pena).
## Criterio de exito
- [x] `engine_demo` corre en Android device (Pixel/Samsung de prueba).
- [x] Touch input + virtual gamepad funcionan.
- [x] Audio sin glitches.
- [x] Safe area respetada (notch invisible no tapa UI).
- [x] APK release ≤ 20 MB.
- [x] WalletConnect: firma de mensaje funciona con MetaMask Mobile (basico, profundizar en sub-issue dedicado si hace falta).
- [x] Pipeline reproducible desde Linux WSL + Windows.
## Riesgos
1. **WalletConnect en C++ puro** — protocolo no trivial. Plan B: webview.
2. **NDK API levels** — minSdk 24 (Android 7). Devices < 7 fuera.
3. **App Bundle requerido en Play Store**`bundleRelease` en lugar de `assembleRelease` para production.
4. **Apple-style audio latency** — Android variable. miniaudio AAudio backend ayuda en API 26+.
## No-objetivos
- Push notifications.
- Google Play Billing — out of scope, prioridad crypto payments.
- AdMob / ads.
- iOS (en 0072h).