Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6.9 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 Vulkan | ✅ | — |
9a — Extraer Systems::Collision |
✅ | 896a899 |
9b — Extraer Systems::ContinueScreen |
✅ | 816bc02 |
9c — Extraer Systems::InitHud |
✅ | a4942fc |
9d — Descomponer GameScene::update (339→18 LOC) |
✅ | 808abb2 |
| 8 — Postprocesado + paleta de colores por entidad | 🔲 siguiente | — |
| 10 — Tuning final de masa/restitución/damping | 🔲 | — |
| Mac/Metal — shaders MSL en build | 🔲 | — |
| 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 8 — postprocesado + paleta de colores)
Fase 7 y Fase 9 cerradas. Validación visual SDL3 GPU OK (Backend GPU: vulkan).
GameScene.cpp 1429 → 1015 LOC; update() 339 → 18 LOC, complexity baja a
< 10 por sub-paso. Tres sistemas extraídos a source/game/systems/:
Systems::Collision(~210 LOC propios)Systems::ContinueScreen(~160 LOC propios)Systems::InitHud(~155 LOC propios)
Fase 8 — siguientes pasos:
- Color por entidad (paleta semántica): naves blancas, balas verdes,
pentagons azules, quadrats rojos, molinillos magenta, debris hereda
color del padre. Hoy todo se pinta con el color global del oscilador
(
g_current_line_color). Hay que pasar el color alpushLinedesde las entidades (cada entity expone ungetColor()). - Postprocesado básico:
- Bloom/glow: render-to-texture al swapchain real con fragment shader que aplique kernel separable de blur sobre los píxeles iluminados. Requiere un pipeline extra y un par de texturas off-screen.
- Opcional: scanlines o leve aberración cromática para feel CRT.
Fase 10 (tras 8): tuning de mass/restitution/damping con feedback visual de los colores.
Mac/Metal: cuando vayamos a release de macOS, añadir compilación
de los shaders también a MSL (con spirv-cross o glslangValidator).
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.