About:
A custom-built game engine replicating the classic Flappy Bird, this project exemplifies my expertise in C++ programming, SFML library, and modular software design. It showcases my ability to develop clean, efficient, and scalable codebases tailored for performance.
Technologies and Tools Used:
Programming Language:
C++
Graphics Library:
SFML
Design Patterns:
-
State Pattern for game state management.
-
Singleton Pattern for asset management.
Project Overview
I built this game engine as a personal challenge to create a modular and scalable system that mirrors professional-grade game development processes. The engine features dynamic asset management, smooth state transitions, and efficient input handling, all wrapped in a structured architecture that is easy to expand.
Challenges and Learnings:
This project enhanced my skills in several key areas:
-
Scalable Architecture Design: Building modular systems for ease of future expansion.
-
Game Optimization Techniques: Improving resource management and rendering efficiency.
-
Problem-Solving Under Constraints: Overcoming challenges such as handling real-time input while maintaining consistent frame rates.
-
Code Clarity and Documentation: Writing clean, maintainable code with detailed documentation for future scalability.
-
Game Loop & Timing: Refined understanding of the game loop, ensuring smooth updates and rendering every frame.
This project helped me grasp the importance of clean code structure, design patterns, and efficient resource management in game development.
Key Features:
-
Modular Architecture:
-
Every functionality is encapsulated into individual modules, such as game states, asset management, and input handling. This design ensures:
-
Ease of Scalability: Adding new features or game modes is straightforward.
-
Improved Debugging: Isolated modules make troubleshooting efficient.
-
-
​
-
Seamless State Transitions:
-
The State Machine component ensures smooth transitions between game states such as:
-
Main Menu
-
Gameplay
-
Game Over Screen
-
-
This structure enhances user experience and reduces bugs during state changes.
-
​
-
Dynamic Asset Management:
-
An Asset Manager dynamically loads and manages assets (e.g., textures, fonts, and audio), optimizing performance by reducing memory usage and loading times by 25%.
-
​
-
Real-Time Input Handling:
-
The Input Manager captures user inputs with precision, ensuring responsive gameplay mechanics, which are critical in fast-paced games like Flappy Bird.
-
​
-
Optimized Game Loop:
-
The engine’s game loop is designed to:
-
Capture input efficiently.
-
Update the game logic seamlessly.
-
Render graphics at consistent frame rates.
-
-
Code Breakdown
Game Loop:
-
The game loop is a fundamental part of the game engine that controls the flow of the game by continuously cycling through key operations such as input processing, game updates, and rendering. This loop ensures that the game runs at a consistent frame rate, giving the player a smooth and responsive experience.
-
Key Components:
-
Input Processing: Captures and processes player input.
-
Game Update: Handles game logic, including physics, movement, and AI.
-
Rendering: Draws the updated game state to the screen.
-
Example: In Game.cpp, the gameLoop() function defines the main loop, cycling through these operations:​
-
void Game::gameLoop()
{
while (window.isOpen()) // Main loop
{
ProcessInput(); // Capture player input
Update(dt); // Update game state based on input and logic
Draw(); // Render the updated game state to the screen
}
}​
​
-
ProcessInput() handles user input (like keyboard or mouse).
-
Update(dt) updates the game state, such as moving the bird or generating obstacles.
-
Draw() renders the current game state onto the window.​
​
This structure ensures that all operations for a single frame are completed before moving on to the next frame, maintaining a consistent gameplay experience.
​​
​​
Game States
-
The game uses a state-based architecture, where each part of the game (e.g., the main menu, active game, game over screen) is managed as a separate state. This approach simplifies transitioning between different parts of the game, while keeping the code organized and manageable.
-
Key Components:
-
State Interface: A base class with virtual methods like Update(), HandleInput(), and Draw().
-
State Classes: Derived classes representing specific game states like MainMenuState, GameState, and GameOverState.
-
Example: In the game, each state (such as the gameplay state or the game-over state) is a separate class that inherits from a State interface. Here's how the GameState class might be implemented:
-
void GameState::Update(float dt)
{
// Game logic for the active game state
bird.update(dt); // Update the bird's position and behavior
obstacleManager.update(dt); // Move obstacles
score.update(); // Update score​
// Other game-specific updates
}​
​
-
The Update() method in GameState is responsible for updating game logic while the game is in progress.
-
The bird’s position is updated based on physics and user input (flap or gravity).
-
Obstacles are moved across the screen, and the score is continuously updated.​
​
State transitions are handled smoothly by calling specific methods in the corresponding state classes. For instance, when the player presses a key to start the game, the game transitions from the MainMenuState to the GameState.
​
State Transitions:​
​​
-
When the game reaches the "game over" condition, a transition to the GameOverState is triggered. This is managed by a StateManager that keeps track of the current state and ensures smooth transitions:
if (bird.isOutOfBounds() || bird.collidesWithObstacle())
{
stateManager.changeState(new GameOverState());
}
​
In this way, game states are handled cleanly and separated logically, improving maintainability and making the game structure scalable for future additions.
​
HandleInput()​
​​
-
Example: In GameState, the HandleInput() function is used to capture player actions, like pressing the spacebar to make the bird flap. For instance:
void GameState::HandleInput()
{
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Space)) // Main loop
{
bird.flap(); // Make the bird flap if the spacebar is pressed
}
}
​
This function checks for specific keypresses (e.g., spacebar for flapping the bird) and responds accordingly.
-
-
Efficient handling of meshes and textures to ensure that the procedural generation runs smoothly:​
-
Conclusion:
The Flappy Bird Game Engine project demonstrates a structured approach to game development using C++ and SFML. Key highlights include:
-
Modular Architecture for easier scalability and maintenance, with separate classes handling different game states like the main menu, gameplay, and game over.
-
Game Loop Management to ensure smooth and consistent gameplay by processing input, updating game logic, and rendering each frame.
-
State-Based Design for managing transitions between different game modes, enhancing game flow and user experience.
-
Input Handling and Game Logic that provide responsive controls and dynamic gameplay, like updating the bird's movement and obstacles.
This project showcases effective game loop management, state-based design, and user input handling, providing a solid foundation for creating 2D platformer games in C++ using SFML.