Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
7.3 KiB
Plan de migración Orni Attack → SDL3 GPU + física vectorial
Documento maestro de la rama rewrite/physics-gpu. Resumen del progreso y
qué queda. Si pierdes el contexto de la sesión, léeme primero.
Visión
Modernizar la base técnica del juego antes de tunear jugabilidad:
- Limpiar legacy (polares, mezcla catalán/inglés, header guards).
- Adoptar formato 16:9 (1280×720).
- Importar subsistemas robustos de AEEA (audio, input deferido).
- Implementar física vectorial 2D con rigid bodies + colisiones elásticas.
- Migrar renderizado a SDL3 GPU sin fallback (memoria: project-no-sdl-fallback).
- Postprocesado y color.
El tuning de feeling se hace al final, post-GPU.
Estado actual
Rama de trabajo: rewrite/physics-gpu (pusheada a Gitea).
Tag de seguridad: beta-3.0 (snapshot de main antes de empezar).
| Fase | Estado | Commits |
|---|---|---|
| 0 — Limpieza de código muerto (primitives, polígonos, Bresenham, polares) | ✅ | 6cf990b |
1a — Punt → Vec2 con operadores modernos |
✅ | cd38101 |
| 1b — Renames de entidades + métodos virtuales a camelBack | ✅ | ae5cc1c |
| 1c — Renames de escenas | ✅ | 5871d29 |
| 1d — Renames effects/stage_system/locales | ✅ | 7ee359b |
| 1e — Pragma once + locales restantes + comentarios castellano | ✅ | bf83f16 |
(fix) — clave YAML quadrat → cuadrado post-sweep |
✅ | 56533ca |
| 2 — Cambio a 1280×720 (16:9) | ✅ | a4f6a55 |
| 3 — Import del subsistema de Audio desde AEEA | ✅ | ed98ef6 |
| 4 — Input parcial AEEA | ⏭️ saltado (decisión usuario) | — |
5 — Infraestructura física (RigidBody, PhysicsWorld) |
✅ | 0fd9360 |
6a+b — body_ en Entity + world_ en GameScene |
✅ | 0574077 |
| 6c — Migrar Ship | ✅ | 2fe22ff |
| 6d — Migrar Enemy | ✅ | 27242f5 |
| 6e — Migrar Bullet | ✅ | 9993b2d |
| 7a — Infra GPU (shaders + wrappers, runtime dormido) | ✅ | ba6fd00 |
| 7b+c — Swap atómico a SDL3 GPU + line_renderer al pipeline | ✅ | fa7da4c |
| 7d — Validación visual del usuario en hardware real | ⏳ pendiente | — |
| 8 — Postprocesado, color, paleta por tipo | 🔲 | — |
| 9 — Refactor de GameScene (2.877 LOC → módulos) | 🔲 | — |
| 10 — Tuning final de masa/restitución/damping | 🔲 | — |
| 8 — Postprocesado, color, paleta por tipo | 🔲 | — |
| 9 — Refactor de GameScene (2.877 LOC → módulos) | 🔲 | — |
| 10 — Tuning final de masa/restitución/damping | 🔲 | — |
Lo que queda inmediato (Fase 7d — validación del usuario)
El proyecto ya no contiene SDL_Renderer ni una sola línea.
Todo el rendering pasa por SDL3 GPU.
Estado tras fa7da4c:
- Backend: Vulkan en Linux/Windows, Metal pendiente en macOS (shaders MSL todavía no se generan en build, solo SPIR-V).
SDLManagerposee unRendering::Renderer(alias deGpuFrameRenderer).clear()→beginFrame(),present()→endFrame(). Letterbox víasetViewportmid-frame. VSync víaSDL_SetGPUSwapchainParameters.linea(renderer, x1,y1,x2,y2, brightness, thickness): la implementación empuja la línea como quad extrudido (pushLine) al batch del frame. El grosor es configurable por línea o global (default 1.5 px).- Todo el resto del juego (entities, effects, scenes, title, vector_text,
starfield, shape_renderer) pasa
Rendering::Renderer*opaco. Sololine_renderer.cpptoca métodos del backend.
Smoke test xvfb: ✅ compila, arranca con Backend GPU: vulkan, carga shapes
y termina limpio. La pantalla del xvfb sale negra porque el host no tiene
aceleración 3D (VMware), pero eso no es un bug del juego — es xvfb que no
muestra el swapchain Vulkan.
Acción pendiente del usuario: probar el binario en hardware real con Vulkan nativo para validar que las líneas se dibujan correctamente. Si hay problemas visuales, los ajustes que más probablemente harán falta son:
- Convención de orientación de los triángulos (FRONTFACE_COUNTER_CLOCKWISE está en el pipeline; si se ven invertidos, cambiar a CLOCKWISE).
- Si las líneas se ven demasiado finas o gruesas:
setLineThickness(N)global o pasarthicknesspor llamada. - Si hay flicker o screen tearing: revisar
Options::rendering.vsync.
Tras la validación visual, la migración técnica de SDL3 GPU está cerrada y podemos pasar a Fase 8 (postprocesado / paleta de colores por tipo de entidad) o Fase 9 (refactor de GameScene).
Memoria del proyecto
Las preferencias y decisiones persistentes están en:
~/.claude/projects/-home-sergio-gitea-orni-attack/memory/MEMORY.md y los
archivos enlazados desde ahí. Léelos al empezar sesión nueva.
Resumen:
- No fallback a SDL_Renderer en Fase 7 — solo GPU o falla.
- Naming:
.clang-tidyexigeCamelCasetipos,camelBackmétodos,lower_case_miembros privados,UPPER_CASEconstantes. - Costuras del título tras 16:9 son tuning artístico, no bug, post-Fase 10.
- Push automático tras cada commit en
rewrite/physics-gpu. - Smoke test xvfb (
timeout 5 xvfb-run -a ./build/orni 2>&1 | head -40) tras builds importantes para validar arranque.
Convenciones técnicas
- Lenguaje código: inglés. Lenguaje comentarios: castellano. Strings de UI: valenciano (preservados).
- Coordenadas: cartesianas (
Vec2). Polares prohibidas (legacy). - Resolución lógica: 1280×720. Constantes en
core/defaults.hpp. - Audio: import de AEEA. API:
Audio::get()->playSound/playMusiccon crossfade y efectos. - Física:
Physics::RigidBodyencore/physics/rigid_body.hpp,Physics::PhysicsWorldencore/physics/physics_world.hpp/.cpp. Entity tienebody_como member. Flujo tri-fase por frame enGameScene::update:physics_world_.update(dt)— integrar y resolverentity.postUpdate(dt)— sincronizar mirror (center_, angle_)- Lógica del juego (input, AI, etc.) — aplica fuerzas/cambia velocity
Cómo reanudar tras compactación
- Lee este archivo.
- Lee
MEMORY.md(memorias persistentes). git log --oneline -20para ver últimos commits.git statuspara ver si hay trabajo en curso sin commitear.- Si la fase actual estaba a medias: continúa donde se quedó (este archivo indica en qué fase estamos).
- Tras cada commit, push automático:
git push origin rewrite/physics-gpu.
Configuración física actual (Fase 6e)
| Entidad | mass | radius (world) | restitution | linear_damping | angular_damping |
|---|---|---|---|---|---|
| Ship | 10.0 | 12 | 0.6 | 1.5 | 0.0 |
| Enemy Pentagon | 5.0 | 20 | 1.0 | 0.0 | 0.0 |
| Enemy Quadrat | 8.0 | 20 | 1.0 | 0.0 | 0.0 |
| Enemy Molinillo | 4.0 | 20 | 1.0 | 0.0 | 0.0 |
| Bullet | 0.5 | 0 (cinemática) | 0 | 0 | 0 |
| Wall (PLAYAREA bounds) | ∞ (estático) | — | — | — | — |
Nota: Bullet usa radius=0 en el body físico para que no participe en las
colisiones del world (ni body-body ni bounds). El radio de gameplay
(Defaults::Entities::BULLET_RADIUS = 3) lo expone vía getCollisionRadius()
y lo consumen check_collision y la detección de salida del PLAYAREA en
Bullet::update.
Las paredes son implícitas: physics_world_.setBounds(PLAYAREA) en
GameScene::init. No son RigidBody separados, sino un AABB que rebota.