
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 (
Enginesingleton + 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 withIdle/Movestates 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
- Symptom: Logs suggested possible memory/resource residue after switching states multiple times
- Hypothesis:
Unload()timing and texture manager cleanup timing were mixed - Verification: Repeated transition tests + tracing lifecycle logs
- Fix: Separated state unload timing from global resource cleanup, and enforced an explicit order
- Prevention: Managed a checklist for
Load/Update/Unloadwhenever a new state is added
Case 2) Coupling Issue Between SDL Window and DX11 Initialization
- Symptom: If initialization order changed, SwapChain creation became unstable
- Hypothesis: SDL window handle (HWND) acquisition timing was unclear
- Verification: Validated handle acquisition timing via
SDL_SysWMinfo - Fix: Enforced a strict order: window creation → handle acquisition → DXGI/D3D resource initialization
- 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)
- Build a UI system (button components / text rendering)
- Improve collision handling with layers and masks
- Add a camera system (clear separation of world vs. screen coordinates)
- Implement an audio system (BGM/SFX)
- 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