--- id: "0072b" title: "gamedev — runtime nucleo (sprite batcher, audio, input, game loop)" status: pendiente type: feature domain: - gamedev scope: multi-app priority: alta depends: - "0072a" blocks: [] related: [] created: 2026-05-10 updated: 2026-05-17 tags: - gamedev - cpp --- ## Objetivo Implementar el runtime minimo necesario para hacer un juego 2D jugable: dibujar muchos sprites por frame, reproducir audio, leer input unificado (kb/gamepad/touch), y un game loop con fixed timestep. Todo en `cpp/functions/gfx/` y `cpp/functions/gamedev/` (dominio nuevo) como funciones del registry, NO empotrado en una app concreta. Apps consumidoras importan via `uses_functions`. ## Funciones a crear ### Graphics (sokol_gfx wrappers) | Funcion | Lang | Domain | Purity | Proposito | |---|---|---|---|---| | `sg_init` | cpp | gfx | impure | Inicializa sokol_gfx con SDL3 GL context | | `sg_shader_load` | cpp | gfx | impure | Compila vertex+fragment, devuelve `sg_shader` | | `sg_pipeline_create` | cpp | gfx | impure | Crea `sg_pipeline` con layout estandar (pos, uv, color) | | `sg_image_load` | cpp | gfx | impure | stb_image → `sg_image` con mipmaps opcionales | | `sg_buffer_create` | cpp | gfx | impure | Vertex/index buffers static o stream | ### Sprite batcher `sprite_batch_cpp_gfx` (impure). API: ```cpp struct SpriteBatch { sg_pipeline pipeline; sg_buffer vbo; // dynamic, ~64K vertices sg_buffer ibo; std::vector cpu_verts; sg_image current_atlas; }; void sprite_batch_begin(SpriteBatch& b, const Camera2D& cam); void sprite_batch_draw(SpriteBatch& b, sg_image atlas, Rect src, Rect dst, Color tint, float rotation); void sprite_batch_end(SpriteBatch& b); // flush draw call ``` Auto-flush cuando cambia atlas o se llena CPU buffer. ### Audio (miniaudio wrappers) | Funcion | Domain | Proposito | |---|---|---| | `audio_init` | gamedev | Inicializa miniaudio engine | | `audio_load_sound` | gamedev | wav/ogg/mp3 → sound handle | | `audio_play_sound` | gamedev | Reproduce one-shot con volumen/pan/pitch | | `audio_play_music` | gamedev | Streaming + loop | | `audio_set_listener` | gamedev | Posicion 2D para audio espacial | ### Input unificado `input_unified_cpp_gamedev` (impure). Abstrae kb/mouse/gamepad/touch en un mismo struct: ```cpp struct InputState { // Buttons logicos del juego bool left, right, up, down; bool action_a, action_b, action_x, action_y; bool start, back; // Analogicos (-1..1) float lx, ly, rx, ry; // Touch (mobile/web) struct Touch { float x, y; bool pressed; } touches[8]; int touch_count; // Mouse float mx, my; bool m_left, m_right; }; void input_poll(InputState& s, const SDL_Event* events, int count); ``` Mapping: keyboard WASD/arrows + space/enter, gamepad SDL3 standard mapping, touch via virtual gamepad overlay (en sub-issue 0072g). ### Game loop `game_loop_cpp_gamedev` (impure): ```cpp struct GameLoopCfg { float fixed_dt; // 1/60 default int max_steps_per_frame; // 5 default (evita spiral of death) void (*on_fixed_update)(void* user, float dt); void (*on_render)(void* user, float interp); void* user; }; void game_loop_run(SDL_Window* w, const GameLoopCfg& cfg); ``` Fixed timestep con interpolacion para render (clasico Glenn Fiedler). En WASM usa `emscripten_set_main_loop`. ### Camara 2D `camera_2d_cpp_gamedev` (pure). Struct + helpers para world↔screen, zoom, follow, shake. ## Estructura ``` cpp/functions/gfx/ sg_init.{cpp,h,md} sg_shader_load.{cpp,h,md} sg_pipeline_create.{cpp,h,md} sg_image_load.{cpp,h,md} sg_buffer_create.{cpp,h,md} sprite_batch.{cpp,h,md} cpp/functions/gamedev/ # Dominio nuevo audio_init.{cpp,h,md} audio_load_sound.{cpp,h,md} audio_play_sound.{cpp,h,md} audio_play_music.{cpp,h,md} audio_set_listener.{cpp,h,md} input_unified.{cpp,h,md} game_loop.{cpp,h,md} camera_2d.{cpp,h,md} ``` Registrar dominio `gamedev` en `cpp/CMakeLists.txt` y en docs del registry. ## Tipos del registry `cpp/types/gamedev/`: - `SpriteBatch` (product) - `InputState` (product) - `Camera2D` (product) - `Color` (product, `{r,g,b,a}` floats) - `Rect` (product, `{x,y,w,h}`) - `Vec2` (product) — si no existe ya en `core`, crearlo ## Tests Cada funcion lleva su test minimo. Uso de `--self-test` mode en una app de prueba (`cpp/apps/runtime_test/`) que: 1. Inicializa todo 2. Carga un sprite 3. Carga un sonido 4. Simula input 5. Corre 100 frames 6. Sale con codigo 0 si nada explota ## Tamaño Budget: este sub-issue añade ≤ 600 KB al WASM gzip (miniaudio + sokol_gfx + box2d aun no + imgui ya contado en 0072a). ## Criterio de exito - [x] Funciones en registry con `.md` + tests. - [x] App `runtime_test` corre `--self-test` exit 0 en PC + WASM. - [x] Sprite batcher dibuja 10000 sprites @ 60fps en navegador moderno. - [x] Audio one-shot sin glitches en PC + WASM. - [x] Input unificado funciona con kb + gamepad (touch en 0072g). ## No-objetivos - Animacion de sprites (sub-issue futuro o parte de 0072k demo). - Tilemap (en 0072c). - Physics (en 0072j). - Particles (sub-issue futuro).