fad4006f60
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
253 lines
7.3 KiB
Markdown
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).
|