Skip to content

Scene ManagerΒ #34

@LyeZinho

Description

@LyeZinho

πŸ—ΊοΈ Scene Manager

Fase: 4 β€” O CΓ©rebro
Namespace: Caffeine::Scene
Arquivos: src/scene/SceneManager.hpp, src/scene/SceneSerializer.hpp
Status: πŸ“… Planejado
RF: RF4.5


VisΓ£o Geral

O Scene Manager organiza entidades em hierarquia parent/child para transformaΓ§Γ΅es locais, e permite salvar/carregar o estado completo da cena em formato binΓ‘rio .caf.

Componentes chave:

  • Parent β€” link hierΓ‘rquico entre entidades
  • WorldTransform β€” matrix de transformaΓ§Γ£o global calculada
  • SceneSerializer β€” serializaΓ§Γ£o para .caf binΓ‘rio
  • SceneManager β€” stack de cenas e transiΓ§Γ΅es

API Planejada

namespace Caffeine::Scene {

// ============================================================================
// @brief  Componente de hierarquia parent/child.
// ============================================================================
struct Parent {
    ECS::Entity parent = ECS::Entity::INVALID;
    bool        dirty  = true;  // world transform precisa recalcular
};

// ============================================================================
// @brief  World transform β€” matrix resultante da hierarquia.
//
//  Calculado pelo TransformSystem: acumula transforms parent β†’ child.
// ============================================================================
struct WorldTransform {
    Mat4 matrix = Mat4::identity();
};

// ============================================================================
// @brief  Serializa e deserializa uma cena (ECS::World) para .caf.
//
//  Formato .caf de cena:
//  - Header (32 bytes)
//  - Entity count + Archetype table
//  - Component data packed per archetype (binary)
//  - String table (entity names)
//  - Asset reference table (paths das texturas, Γ‘udios, etc.)
// ============================================================================
class SceneSerializer {
public:
    explicit SceneSerializer(ECS::World& world);

    // BinΓ‘rio .caf β€” produΓ§Γ£o
    bool serialize(const char* path) const;
    bool deserialize(const char* path);

    // JSON β€” dev/debug (legΓ­vel por humanos)
    bool serializeJson(const char* path) const;
    bool deserializeJson(const char* path);

    // Exporta estado atual para bytes (sem arquivo)
    bool serializeToMemory(std::vector<u8>& out) const;
    bool deserializeFromMemory(const std::vector<u8>& data);

private:
    ECS::World& m_world;
};

// ============================================================================
// @brief  Gerenciador de cenas.
//
//  Suporta:
//  - Single scene (switch)
//  - Scene stack (push/pop para menus sobre gameplay)
//  - TransiΓ§Γ΅es animadas (fade, slide)
//  - Async loading em background
// ============================================================================
class SceneManager {
public:
    enum class TransitionType : u8 {
        None,    // InstantΓ’neo
        Fade,    // Fade out β†’ Fade in
        Slide,   // Slide lateral
        Custom   // Callback customizado
    };

    struct TransitionConfig {
        TransitionType type     = TransitionType::Fade;
        f32            duration = 0.5f;
        Color          fadeColor = Color::BLACK;
    };

    // ── Carregamento ───────────────────────────────────────────
    Assets::LoadHandle loadScene(const char* path, bool async = false);

    // ── NavegaΓ§Γ£o ──────────────────────────────────────────────
    void switchScene(const char* path,
                     TransitionConfig trans = {});

    // Stack para menus sobre gameplay:
    void pushScene(const char* path);
    void popScene();

    // ── Consultas ──────────────────────────────────────────────
    ECS::World* activeWorld() const { return m_activeWorld; }
    bool        isTransitioning() const { return m_transitioning; }

    // ── Update (processa transiΓ§Γ΅es) ───────────────────────────
    void update(f64 dt);

private:
    ECS::World*                     m_activeWorld    = nullptr;
    std::vector<std::unique_ptr<ECS::World>> m_sceneStack;
    bool                            m_transitioning  = false;
    TransitionConfig                m_currentTrans;
    f32                             m_transProgress  = 0.0f;
};

}  // namespace Caffeine::Scene

Formato de SerializaΓ§Γ£o (.caf Scene)

[CafHeader]         (32 bytes β€” magic, version, type=Scene)
[SceneHeader]       (20 bytes β€” entityCount, archetypeCount, offsets)
[Archetype Table]   [N Γ— ArchetypeDesc]
[Entity Data]       [componentes packed por archetype]
[String Table]      [nomes de entidades, null-terminated]
[Asset Ref Table]   [paths de assets referenciados]
[CRC32]             (4 bytes β€” verificaΓ§Γ£o de integridade)

Exemplo de salvamento:

Salvar 1K entidades β†’ ~100KB arquivo .caf
Carregar β†’ round-trip < 200ms (critΓ©rio de aceitaΓ§Γ£o)

Sistema de Hierarquia (TransformSystem)

// TransformSystem (ECS::ISystem, priority = 50 β€” antes de tudo):
world.query(parentQuery, [](WorldTransform& wt, Position2D& pos,
                            const Parent& parent) {
    if (parent.dirty) {
        Mat4 localTransform = Mat4::translate(pos.x, pos.y);
        if (parent.parent.isValid()) {
            auto* parentWT = world.get<WorldTransform>(parent.parent);
            wt.matrix = parentWT->matrix * localTransform;
        } else {
            wt.matrix = localTransform;
        }
    }
});

Exemplos de Uso

// ── Salvar cena ───────────────────────────────────────────────
Caffeine::Scene::SceneSerializer serializer(world);
if (!serializer.serialize("saves/level1.caf")) {
    CF_ERROR("Scene", "Failed to save level1");
}

// ── Carregar cena ─────────────────────────────────────────────
if (!serializer.deserialize("assets/scenes/level1.caf")) {
    CF_FATAL("Scene", "Could not load level1");
}

// ── TransiΓ§Γ£o de cena ─────────────────────────────────────────
Caffeine::Scene::SceneManager sceneManager;

// Vai para prΓ³ximo nΓ­vel com fade
sceneManager.switchScene("assets/scenes/level2.caf",
    { .type = SceneManager::TransitionType::Fade,
      .duration = 0.5f });

// Abre menu pause (preserva gameplay por baixo)
sceneManager.pushScene("assets/scenes/pause_menu.caf");
// ...volta ao jogo
sceneManager.popScene();

// ── Hierarquia ────────────────────────────────────────────────
Entity sword = world.create("Sword");
world.add<Position2D>(sword, {15, -5});  // local ao player
world.add<Parent>(sword, { .parent = playerEntity });
// WorldTransform do sword = WorldTransform do player * Mat4::translate(15, -5)

DecisΓ΅es de Design

DecisΓ£o Justificativa
Scene stack Menus sobre gameplay sem destruir o mundo
Formato binΓ‘rio .caf Zero-parsing β€” load limitado pela velocidade do SSD
JSON opcional Legibilidade em debug sem comprometer performance
dirty flag no Parent Evita recalcular WorldTransform quando nΓ£o mudou
Async loading TransiΓ§Γ΅es de nΓ­vel sem freeze

CritΓ©rio de AceitaΓ§Γ£o

  • Serialize + deserialize 1K entidades round-trip < 200ms
  • Scene stack push/pop sem memory leaks
  • TransiΓ§Γ£o Fade funcional (sem artefatos visuais)
  • Hierarquia: WorldTransform calculado corretamente para 3+ nΓ­veis de depth

DependΓͺncias


ReferΓͺncias

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions