---
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<Vertex> 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).
