# Capability group: `gamedev-engine` — runtime de juego C++ multiplataforma (PC + WebAssembly) Cluster de primitivas C++ que forman el núcleo de un runtime de juego 2D portable a escritorio (Windows/Linux/macOS) y navegador (WebAssembly via emscripten). Stack: **SDL3** (ventana + input + GL context) + **sokol_gfx** (render) + **miniaudio** (audio). Nacido del Issue 0072b. A diferencia del grupo hermano `gamedev-2d` (generación de *assets* 2D con ComfyUI y puente a Godot), este grupo es el **motor que ejecuta el juego**: el bucle de simulación, la cámara, el input, el render por lotes y el audio. No genera arte; lo consume en tiempo de ejecución. Tag: `gamedev-engine`. Filtro: `mcp__registry__fn_search query="" tag="gamedev-engine"`. ## Funciones del grupo | ID | Firma corta | Qué hace | Pureza | |---|---|---|---| | `game_loop_cpp_gamedev` | `loop_run(SDL_Window*, const LoopCfg&) -> void` | Game loop fixed-timestep estilo Glenn Fiedler ("Fix Your Timestep"): desacopla `on_fixed_update` (dt fijo) de `on_render` (factor de interpolación), acumulador con cap anti spiral-of-death. Branch automático desktop (while loop) vs `__EMSCRIPTEN__` (`emscripten_set_main_loop`). | impure | | `input_unified_cpp_gamedev` | `input_begin_frame(InputState&); input_process_event(InputState&, const SDL_Event*)` | Snapshot unificado de input por frame para SDL3: mapea teclado (WASD+flechas), ratón, gamepad y touch a botones lógicos (left/right/up/down/action_a..y/start/back) y ejes analógicos, con flags `*_pressed` de rising edge limpio. | impure | | `camera_2d_cpp_gamedev` | `world_to_screen / screen_to_world(Camera2D, Vec2) -> Vec2; visible_world_rect(Camera2D) -> Rect; view_proj_matrix(Camera2D, float[16])` | Cámara ortográfica 2D pura (pos centro, zoom, rotación, viewport): conversiones world↔screen, AABB visible y matriz view-projection 4×4 column-major lista para cualquier renderer. Fast-path sin trig si `rotation==0`. | pure | | `sokol_setup_cpp_gfx` | `make_environment() -> sg_environment; make_swapchain(int w, int h) -> sg_swapchain` | Builders puros para inicializar sokol_gfx sobre un GL context creado por SDL3 (no por sokol_app): `sg_environment` con defaults RGBA8 + depth/stencil y `sg_swapchain` del default framebuffer del contexto activo. | pure | | `sprite_batch_cpp_gfx` | `sprite_batch_create(int cap=4096) -> SpriteBatch; sprite_batch_begin/draw/end` | Batched textured quad renderer sobre sokol_gfx: begin/draw/end con auto-flush por cambio de atlas o capacidad llena. Vertex layout pos+uv+color, alpha blending estándar, GLSL 330 / GLES 300. Base de plataformeros, top-down y UI sprites. | impure | | `audio_engine_cpp_gamedev` | `engine_init() -> Engine; engine_shutdown(Engine&); engine_set_volume(Engine&, float)` | Lifecycle del engine de audio basado en miniaudio (single-header, public domain): inicializa device default, master volume, libera recursos. Cross-platform (WASAPI/ALSA/CoreAudio y WebAudio bajo emscripten). Única TU que define `MINIAUDIO_IMPLEMENTATION`. | impure | | `audio_play_cpp_gamedev` | `sound_load(Engine&, const char*) -> Sound; sound_play/stop/set_volume/destroy(Sound&); play_sound_oneshot(Engine&, const char*, float)` | Reproducción de audio sobre `fn::audio::Engine`: carga con streaming desde disco (wav/mp3/flac/ogg), play/stop/volumen por sonido, y helper fire-and-forget para one-shots sin handle. | impure | | `build_wasm_cpp_app_bash_infra` | `build_wasm_cpp_app(app_name, [--no-budget-check]) -> void` | Compila una app C++ del registry (`cpp/apps/`) a WebAssembly via emscripten. Produce `build/wasm//.{html,js,wasm,wasm.gz}`. Falla si el gzip supera 2 MB (budget). | impure | ## Ejemplo canónico (esqueleto de un juego 2D) Las primitivas se componen así dentro del `main()` de una app C++ del registry. Cada identificador (`fn::game_loop`, `fn::input`, `fn::camera2d`, `fn::gfx`, `fn::audio`) proviene de la función homónima del registry; la app solo aporta la lógica de juego. ```cpp // 1. Ventana + GL context con SDL3, sokol_gfx encima (sokol_setup) sg_setup(...); // usa make_environment() del registry auto swap = fn::gfx::make_swapchain(W, H); auto batch = fn::gfx::sprite_batch_create(4096); auto audio = fn::audio::engine_init(); fn::audio::play_sound_oneshot(audio, "assets/music.ogg", 0.6f); // 2. Estado de cámara e input fn::game::Camera2D cam{ .pos = {0,0}, .zoom = 1.0f, .viewport = {W, H} }; fn::input::InputState in{}; // 3. Game loop fixed-timestep: simulación e interpolación desacopladas fn::game::LoopCfg cfg{ .on_event = [&](const SDL_Event* e){ fn::input::input_process_event(in, e); }, .on_fixed_update = [&](float dt){ /* mover entidades usando in.left_pressed... */ }, .on_render = [&](float alpha){ fn::gfx::sprite_batch_begin(batch); // draw sprites con cam.view_proj_matrix(...) como transform fn::gfx::sprite_batch_end(batch); }, }; fn::game::loop_run(window, cfg); // 4. Distribuir a navegador: build_wasm_cpp_app "" -> build/wasm// ``` ## Fronteras (qué NO cubre) - **Generación de assets** (sprites, tiles, VFX): es el grupo hermano `gamedev-2d` (ComfyUI → post-proceso → Godot). Este grupo solo los *renderiza* en runtime. - **Física / colisiones / ECS**: no incluidos. El `on_fixed_update` recibe el dt fijo; la simulación la pone la app. - **TileSet / mapas / escenas**: no hay sistema de niveles; `sprite_batch` dibuja quads sueltos. - **App end-to-end consumidora**: a fecha de hoy estas primitivas son el núcleo del runtime (Issue 0072b) pero **no hay todavía una app C++ que las componga end-to-end** (varias están marcadas `pendiente-usar`). El ejemplo de arriba es el esqueleto previsto, no un binario validado. Cuando exista la primera app, su `app.md` declarará estas IDs en `uses_functions` y servirá de validación e2e. ## Prerequisitos / notas - **SDL3** para ventana + input + GL context; **sokol_gfx** (vendored en `cpp/vendor/`) para render; **miniaudio** (single-header) para audio. - Target nativo: GLSL 330. Target WebAssembly (emscripten): GLES 300 — `sprite_batch` emite ambos shaders. - `build_wasm_cpp_app` exige el SDK de emscripten en el entorno y respeta un budget de 2 MB gzip por defecto (`--no-budget-check` para saltarlo).