--- 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//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//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 ``` 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./.MainActivity ``` ## Firma del APK Generar keystore una vez: ```bash keytool -genkey -v -keystore release.keystore -keyalg RSA -keysize 2048 \ -validity 10000 -alias ``` Guardar en `~/keystores/` (gitignored) y referenciarlo en `gradle.properties`: ``` RELEASE_STORE_FILE=/home/lucas/keystores/.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 ` para ver `printf` y crashes. SDL3 redirige stdout/stderr a logcat por default. Funcion: `bash/functions/infra/android_logcat_filter.sh ` que tail -f con filtros utiles. ## e2e_checks ```yaml e2e_checks: - id: build_apk cmd: "bash bash/functions/pipelines/build_android_cpp_pipelines.sh " timeout_s: 600 - id: apk_size cmd: "test $(stat -c%s cpp/apps//android/app/build/outputs/apk/release/app-release.apk) -lt 20971520" - id: apk_install_emulator cmd: "bash bash/functions/infra/android_emulator_smoke.sh " 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).