MSFR – DirectX11-Based Game Framework

github link

1) One-line Summary

An end-to-end validated game framework built with C++17 + SDL2 + DirectX 11, focusing on state transitions, input abstraction, and a stable rendering pipeline.


2) Problem Statement / Goals

My previous graphics practice projects usually stopped at “drawing something on screen,” and did not sufficiently validate the structural challenges required for real game development—such as state management, resource lifecycle rules, and adapting to input changes.

For this project, I aimed to go beyond a simple rendering demo and build a maintainable game framework architecture that stays scalable as features grow.

Success Criteria

  • A consistent state flow: Splash → MainMenu → GamePlay
  • Minimal gameplay code changes when input mapping changes
  • Stable rendering even with repeated state transitions and window resizing

3) My Role (Contribution)

This is a solo project, and I fully implemented the following areas:

  • Application entry point and DX11 app layer (main.cpp, DX11App)
  • Engine core “service hub” architecture (Engine singleton + manager set)
  • State-driven game flow (GameStateManager)
  • Input layer separation (Input + ActionSystem)
  • Object/component structure and player state machine (GameObject, Sprite, Player)
  • Resource cleanup rules during state transitions, validated at runtime

4) Implementation Details (Including Technical Rationale)

A. SDL2 + DirectX11 Integrated Rendering Loop

  • What I implemented
    Built D3D11 initialization on top of SDL2 window/event handling: Device/SwapChain/RTV/DSV setup and a frame loop (Clear → Draw → Present).
  • Why this approach
    I wanted SDL2 for fast and reliable window/input handling, while using DirectX 11 to learn and control an explicit graphics pipeline.
  • Alternatives and why I didn’t choose them
    Using SDL2’s higher-level rendering abstraction would reduce initial complexity, but it would also limit pipeline learning and fine-grained control, so I excluded it.
  • Result
    Rendering responsibilities are cleanly separated from gameplay logic, reducing architectural friction when expanding states and objects.

B. State Transitions via GameStateManager

  • What I implemented
    A state machine with a clear lifecycle: START → LOAD → UPDATE → UNLOAD → SHUTDOWN → EXIT.
  • Why this approach
    Separating distinct screens (Splash/MainMenu/GamePlay) reduces coupling and limits the impact scope of future feature additions.
  • Alternatives and why I didn’t choose them
    A single loop with conditional branching is fast to build, but branching complexity explodes as features grow, so I excluded it.
  • Result
    Each state owns its responsibilities for loading/updating/unloading, making debugging and maintenance significantly easier.

C. Separating Input from ActionSystem

  • What I implemented
    Added a layer that converts raw input (keyboard/mouse) into semantic Actions, so gameplay code consumes meaning-based input rather than key codes.
  • Why this approach
    To minimize gameplay code changes when key mappings or input devices change.
  • Alternatives and why I didn’t choose them
    Handling key codes directly inside gameplay is convenient short-term, but becomes expensive for remapping, platform changes, and long-term maintenance.
  • Result
    Input-related changes remain localized to the Input/Action layers, reducing refactoring risk.

D. Player State Machine + Sprite Animation

  • What I implemented
    Implemented a Player with Idle/Move states and connected direction-based animation switching.
  • Why this approach
    If movement/idle/direction logic grows inside a single update function, complexity increases rapidly. A state-based design keeps behavior extensible.
  • Alternatives and why I didn’t choose them
    A single update function with accumulated animation branches works early, but becomes difficult to maintain when adding new states (Dash/Attack/Hurt).
  • Result
    Built a foundation where new behaviors can be added primarily by introducing new states instead of rewriting existing logic.

5) Troubleshooting

Case 1) Potential Resource Residue During Repeated State Transitions

  1. Symptom: Logs suggested possible memory/resource residue after switching states multiple times
  2. Hypothesis: Unload() timing and texture manager cleanup timing were mixed
  3. Verification: Repeated transition tests + tracing lifecycle logs
  4. Fix: Separated state unload timing from global resource cleanup, and enforced an explicit order
  5. Prevention: Managed a checklist for Load/Update/Unload whenever a new state is added

Case 2) Coupling Issue Between SDL Window and DX11 Initialization

  1. Symptom: If initialization order changed, SwapChain creation became unstable
  2. Hypothesis: SDL window handle (HWND) acquisition timing was unclear
  3. Verification: Validated handle acquisition timing via SDL_SysWMinfo
  4. Fix: Enforced a strict order: window creation → handle acquisition → DXGI/D3D resource initialization
  5. Prevention: Added step-by-step failure logs and fail-fast returns per initialization stage

6) Quality / Validation

  • Repeatedly verified correct state flow: Splash → MainMenu → GamePlay
  • Validated Splash skip behavior via Enter/Space/Mouse input
  • Verified menu hit-testing for Play/HowToPlay/Quit branching
  • Confirmed correct render-target recreation and stable output after window resizing
  • Validated smooth direction-based animation switching during player movement/idle at runtime

At this stage, I prioritized architectural stability and lifecycle validation over advanced profiling tools (frame time collectors / GPU profilers).


7) Results and Improvement Plan

Current Outcome

  • Stable integration of an SDL2 event loop with a DirectX 11 2D rendering pipeline
  • Established state-driven game flow and input abstraction architecture
  • Completed player state machine and sprite animation integration

Limitations

  • HowToPlay/Quit features are not yet complete as a full user experience
  • Camera/audio/UI frameworks are not implemented yet
  • Data-driven loading (e.g., JSON-based) is planned for the next phase

Next Steps (Priority Order)

  1. Build a UI system (button components / text rendering)
  2. Improve collision handling with layers and masks
  3. Add a camera system (clear separation of world vs. screen coordinates)
  4. Implement an audio system (BGM/SFX)
  5. Expand into data-driven asset/state loading

8) Reflection

The biggest takeaway from this project is that long-term cost is determined less by “working code” and more by scalable structure.

In particular, input abstraction and a well-defined state lifecycle significantly reduced the scope of changes required for new features and made debugging points clearer—improving overall development stability.

For my next project, I plan to adopt these rules as baseline conventions from the start:

  • Document and standardize resource load/unload timing rules
  • Automate state transition test scenarios
  • Introduce performance metrics tools (frame time / memory tracking) earlier in development