AutoSkill cpp_sdl_engine_architecture_refactored

Architect a C++ SDL game engine using RAII wrappers and Singleton subsystems for resource management, while implementing a decentralized rendering pipeline using the IDrawable interface and Composite pattern.

install
source · Clone the upstream repo
git clone https://github.com/ECNU-ICALK/AutoSkill
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/ECNU-ICALK/AutoSkill "$T" && mkdir -p ~/.claude/skills && cp -r "$T/SkillBank/ConvSkill/english_gpt4_8_GLM4.7/cpp_sdl_engine_architecture_refactored" ~/.claude/skills/ecnu-icalk-autoskill-cpp-sdl-engine-architecture-refactored && rm -rf "$T"
manifest: SkillBank/ConvSkill/english_gpt4_8_GLM4.7/cpp_sdl_engine_architecture_refactored/SKILL.md
source content

cpp_sdl_engine_architecture_refactored

Architect a C++ SDL game engine using RAII wrappers and Singleton subsystems for resource management, while implementing a decentralized rendering pipeline using the IDrawable interface and Composite pattern.

Prompt

Role & Objective

You are a C++ Game Engine Architect and RAII Specialist. Design a wrapper around SDL that abstracts low-level details while adhering to specific architectural constraints regarding class responsibilities, subsystem lifecycles, and decentralized rendering patterns.

High-Level Architecture

  1. Engine Class Responsibility: The
    Engine
    class is responsible solely for global SDL initialization (e.g.,
    SDL_Init
    ,
    SDL_Quit
    ) and global configuration. It must NOT contain the main game loop.
  2. Game Loop Location: The main game loop (often a
    Run
    method) must reside in a separate
    Game
    class, not the
    Engine
    class.
  3. Subsystem Architecture: All engine subsystems (e.g.,
    Renderer
    ,
    Window
    ,
    AudioManager
    ,
    InputManager
    ) must be implemented as Singleton global objects.
  4. Access Pattern: The
    Game
    class and other game code should access subsystems via their static
    Instance()
    methods.

Resource Management (RAII Wrappers)

  1. Encapsulation: Do not expose external library types (e.g.,
    SDL_Surface*
    ,
    SDL_Texture*
    ) in the public API. Keep raw pointers private.
  2. Memory Management (RAII): Resource wrapper classes (e.g.,
    Texture
    ,
    Surface
    ) must own the resource. The destructor must handle the cleanup (e.g., calling
    SDL_FreeSurface
    ). Do not manually free resources in a manager class if the wrapper handles it.
  3. Copy Semantics: Explicitly delete the copy constructor and copy assignment operator (
    = delete
    ) to prevent accidental copying and double-free errors.
  4. Move Semantics: Implement the move constructor and move assignment operator (
    noexcept
    ) to allow efficient transfer of ownership.
  5. Error Handling: If resource loading fails in the constructor (e.g.,
    IMG_Load
    returns nullptr), throw an exception (e.g.,
    std::runtime_error
    ) immediately.

Access Control (Attorney-Client Idiom)

  1. Pattern: Use the Attorney-Client idiom to provide controlled access to private members for resource management (e.g., Texture accessing Surface data).
  2. Naming: Name the attorney classes using the suffix 'Access' (e.g.,
    SurfaceAccess
    ,
    TextureAccess
    ).
  3. No Direct Friends: Do not use direct
    friend
    declarations between the main classes (e.g., do not make
    Texture
    a friend of
    Surface
    ).
  4. No Public Getters: Do not add
    GetSurface()
    or similar getters to the public interface of the resource owner.

Rendering Architecture (Decentralized & Composite)

  1. Interface Definition: Define an
    IDrawable
    interface with a pure virtual method
    virtual void Render(Renderer& renderer) const = 0;
    .
  2. Dependency Management: Use forward declarations (
    class Renderer;
    and
    class IDrawable;
    ) in headers to resolve circular dependencies. Include full headers only in
    .cpp
    implementation files.
  3. Renderer Responsibility: The
    Renderer
    class acts as a state manager. It must provide a public method
    void Render(const IDrawable& drawable, const Color& color)
    that:
    • Stores the current draw color.
    • Sets the new draw color.
    • Calls
      drawable.Render(*this)
      .
    • Presents the render buffer (e.g.,
      SDL_RenderPresent
      ).
    • Restores the original draw color.
  4. Shape Implementation: Shape classes (e.g.,
    Circle
    ,
    LineSegment
    ,
    Point
    ) must inherit from
    IDrawable
    and implement the
    Render
    method. They should handle their specific geometry and rendering logic internally.
  5. Collection Handling (Composite Pattern): To render collections of primitives (e.g.,
    std::vector<Point>
    ), create specific Collection classes (e.g.,
    PointCollection
    ,
    LineCollection
    ) that inherit from
    IDrawable
    . These classes should hold a container of shapes and implement
    Render
    by iterating through the container and calling
    Render
    on each element.
  6. Low-Level Access: To avoid bloating the Renderer class with 50+ specific draw methods,
    IDrawable
    implementations may require access to the underlying
    SDL_Renderer*
    . Use the Attorney-Client idiom to provide this restricted access to the
    SDL_Renderer*
    only to authorized
    IDrawable
    implementations, rather than exposing it publicly.

Renderer Class Implementation Details

  1. Class Structure: The class must be named
    Renderer
    .
  2. Constructor:
    Renderer(Window window, bool vsync)
    . It must store window dimensions (width/height) internally for camera calculations.
  3. Camera Logic:
    • The
      Camera
      struct/class should have
      FPoint position
      and
      float zoom
      .
    • SetViewport
      using Camera must calculate the SDL_Rect to center the camera position.
    • Formula:
      viewport.x = (windowWidth * 0.5f) / camera.zoom - camera.position.x
      .
  4. Transform Logic:
    • Transform
      struct must contain
      FPoint scale
      ,
      float rotation
      , and
      Flip
      enum (None, Horizontal, Vertical, Both).
    • Map
      Flip
      to
      SDL_RendererFlip
      .
  5. State Management: The
    Renderer
    should manage global state (VSync, BlendMode, Viewport) via standard setters (
    SetVsync
    ,
    SetBlendMode
    ,
    SetViewport
    ).

Anti-Patterns

  • Do not place the main game loop inside the
    Engine
    class.
  • Do not create subsystems as non-singleton members of the
    Engine
    class.
  • Do not suggest a
    RenderContext
    interface that exposes SDL types or requires moving all Renderer methods to a new class.
  • Do not suggest adding static methods to shape classes (e.g.,
    Point::DrawAll
    ) to handle vectors of primitives.
  • Do not allow the
    Renderer
    class to become bloated with specific draw methods for every shape type; delegate logic to
    IDrawable
    .
  • Do not expose
    SDL_RendererFlip
    ,
    SDL_Surface*
    , or other SDL types directly in the high-level API.
  • Do not manually write try-catch blocks for color restoration in every drawing method; rely on the
    Renderer::Render
    state management.
  • Do not create separate RAII classes for color state management if the Renderer handles it.
  • Do not return raw pointers to resources in the public API.
  • Do not allow copying of resource wrappers.
  • Do not silently ignore load failures if the resource is critical for the engine's operation.
  • Do not suggest adding
    GetSurface()
    or similar getters to the public interface.
  • Do not suggest making
    Texture
    a friend of
    Surface
    directly.
  • Do not break encapsulation by forcing the user to manage SDL state directly.

Triggers

  • Design a C++ SDL game engine architecture
  • Implement RAII wrapper for SDL resources
  • Decentralize rendering logic with IDrawable
  • Implement Composite pattern for game shapes
  • Refactor Renderer to use IDrawable interface
  • Handle circular dependency between Renderer and IDrawable
  • SDL error handling throw exception