#include <SFML/Graphics.
hpp>
#include <SFML/Window.hpp>
#include <SFML/System.hpp>
#include <iostream>
#include <vector>
#include <cstdlib>
#include <ctime>
// ============================================================================
// Constants - Game Configuration
// ============================================================================
const int WINDOW_WIDTH = 800;
const int WINDOW_HEIGHT = 600;
const float GRAVITY = 1000.0f; // Downward acceleration (pixels/second^2)
const float JUMP_VELOCITY = -400.0f; // Initial upward velocity when the bird
jumps (pixels/second)
const float PIPE_SPEED = 200.0f; // Speed at which pipes move horizontally
(pixels/second)
const float PIPE_WIDTH = 70.0f; // Width of the pipes (pixels)
const float PIPE_GAP_SIZE = 200.0f; // Vertical gap between the top and
bottom pipes (pixels)
const float PIPE_SPAWN_INTERVAL = 1.5f; // Time interval between spawning new
pipe pairs (seconds)
// ============================================================================
// Structures - Data Representation
// ============================================================================
// Represents the bird
struct Bird {
sf::Sprite sprite; // Sprite for the bird
float velocity; // Vertical velocity of the bird
};
// Represents a pipe (top or bottom)
struct Pipe {
sf::Sprite sprite; // Sprite for the pipe
};
// ============================================================================
// Function Declarations
// ============================================================================
void updateBird(Bird& bird, float deltaTime);
std::pair<Pipe, Pipe> createPipePair(sf::Texture& pipeTexture);
void updatePipes(std::vector<std::pair<Pipe,Pipe>>& pipes, float deltaTime); //Pass
vector as pair of pipes
bool checkCollision(const Bird& bird, const std::vector<std::pair<Pipe,Pipe>>&
pipes); //Pass vector as pair of pipes
void resetGame(Bird& bird, std::vector<std::pair<Pipe,Pipe>>& pipes); //Pass vector
as pair of pipes
// ============================================================================
// Main Function - Entry Point
// ============================================================================
int main() {
// Seed the random number generator
std::srand(static_cast<unsigned int>(std::time(nullptr)));
// Create the game window
sf::RenderWindow window(sf::VideoMode(WINDOW_WIDTH, WINDOW_HEIGHT), "Flappy
Bird");
window.setFramerateLimit(60);
// ========================================================================
// Load Textures - Load game assets from files
// ========================================================================
sf::Texture birdTexture;
if (!birdTexture.loadFromFile("bird.png")) { // Replace with your bird image
file
std::cerr << "Error loading bird texture!" << std::endl;
return -1;
}
sf::Texture pipeTexture;
if (!pipeTexture.loadFromFile("pipe.png")) { // Replace with your pipe image
file
std::cerr << "Error loading pipe texture!" << std::endl;
return -1;
}
sf::Texture backgroundTexture;
if (!backgroundTexture.loadFromFile("background.png")) { // Replace with your
background image
std::cerr << "Error loading background texture!" << std::endl;
return -1;
}
sf::Sprite backgroundSprite(backgroundTexture);
// ========================================================================
// Initialize Game Objects
// ========================================================================
// Bird Initialization
Bird bird;
bird.sprite.setTexture(birdTexture);
bird.sprite.setPosition(100, WINDOW_HEIGHT / 2);
bird.velocity = 0.0f;
bird.sprite.setOrigin(birdTexture.getSize().x / 2.0f, birdTexture.getSize().y /
2.0f); // Set origin to center for rotation
// Pipes Initialization
std::vector<std::pair<Pipe,Pipe>> pipePairs; // Vector to store pairs of top
and bottom pipes
float pipeSpawnTimer = 0.0f;
// ========================================================================
// Game State Variables
// ========================================================================
bool isGameOver = false;
int score = 0;
// ========================================================================
// Load Font - Load a font for displaying text
// ========================================================================
sf::Font font;
if (!font.loadFromFile("arial.ttf")) {
std::cerr << "Error loading font!" << std::endl;
return -1;
}
// ========================================================================
// Initialize Text - Initialize the score and game over text
// ========================================================================
sf::Text scoreText;
scoreText.setFont(font);
scoreText.setString("Score: " + std::to_string(score));
scoreText.setCharacterSize(24);
scoreText.setFillColor(sf::Color::White);
scoreText.setPosition(10, 10);
sf::Text gameOverText;
gameOverText.setFont(font);
gameOverText.setString("Game Over! Press Space to Restart!");
gameOverText.setCharacterSize(30);
gameOverText.setFillColor(sf::Color::Red);
gameOverText.setPosition(WINDOW_WIDTH / 2 - gameOverText.getLocalBounds().width
/ 2,
WINDOW_HEIGHT / 2 - gameOverText.getLocalBounds().height
/ 2);
// ========================================================================
// Game Loop - Main game loop
// ========================================================================
sf::Clock clock; // Used to calculate elapsed time
while (window.isOpen()) {
float deltaTime = clock.restart().asSeconds(); // Time elapsed since last
frame
// ====================================================================
// Event Handling - Process user input and window events
// ====================================================================
sf::Event event;
while (window.pollEvent(event)) {
if (event.type == sf::Event::Closed) {
window.close();
}
if (event.type == sf::Event::KeyPressed && event.key.code ==
sf::Keyboard::Space) {
if (isGameOver) {
// Restart the game if it's game over
resetGame(bird, pipePairs);
isGameOver = false;
score = 0;
scoreText.setString("Score: " + std::to_string(score));
} else {
// Make the bird jump
bird.velocity = JUMP_VELOCITY;
}
}
}
// ====================================================================
// Update - Update game logic only if the game is not over
// ====================================================================
if (!isGameOver) {
// Update the bird's position
updateBird(bird, deltaTime);
// Spawn new pipes
pipeSpawnTimer += deltaTime;
if (pipeSpawnTimer >= PIPE_SPAWN_INTERVAL) {
std::pair<Pipe,Pipe> newPipePair = createPipePair(pipeTexture);
pipePairs.push_back(newPipePair);
pipeSpawnTimer = 0.0f;
}
// Update the pipes' positions
updatePipes(pipePairs, deltaTime);
// Check for collisions
if (checkCollision(bird, pipePairs)) {
isGameOver = true;
}
//Check if bird is out of bound
if (bird.sprite.getPosition().y < 0 || bird.sprite.getPosition().y >
WINDOW_HEIGHT)
{
isGameOver = true;
}
// Score increment (very basic)
for (auto& pipePair : pipePairs) { // Use auto& to get a reference to
the pair
Pipe& topPipe = pipePair.first; // Reference to the top pipe
if (topPipe.sprite.getPosition().x + PIPE_WIDTH <
bird.sprite.getPosition().x &&
topPipe.sprite.getPosition().x + PIPE_WIDTH + PIPE_SPEED *
deltaTime >= bird.sprite.getPosition().x) {
score++;
scoreText.setString("Score: " + std::to_string(score));
break;
}
}
}
// ====================================================================
// Render - Draw everything to the screen
// ====================================================================
window.clear(); // Clear the window
// Draw the background
window.draw(backgroundSprite);
// Draw the bird
window.draw(bird.sprite);
// Draw the pipes
for (auto& pipePair : pipePairs) {
window.draw(pipePair.first.sprite);
window.draw(pipePair.second.sprite);
}
// Draw the score
window.draw(scoreText);
// Draw the game over text if the game is over
if (isGameOver) {
window.draw(gameOverText);
}
window.display(); // Display the rendered frame
}
return 0;
}
// ============================================================================
// Function Implementations
// ============================================================================
// Updates the bird's position and rotation
void updateBird(Bird& bird, float deltaTime) {
// Apply gravity
bird.velocity += GRAVITY * deltaTime;
bird.sprite.move(0, bird.velocity * deltaTime);
// Rotate the bird based on the velocity
float rotationAngle = bird.velocity * 0.05; // Adjust multiplier to your
liking
rotationAngle = std::max(-45.0f, std::min(45.0f, rotationAngle)); // Limit
rotation
bird.sprite.setRotation(rotationAngle);
}
// Creates a pair of top and bottom pipes with a random gap position
std::pair<Pipe, Pipe> createPipePair(sf::Texture& pipeTexture) {
Pipe topPipe, bottomPipe;
float gapPosition = (float)(std::rand() % (int)(WINDOW_HEIGHT - PIPE_GAP_SIZE -
100)) + 50; // Ensure gap is within bounds
// Top Pipe
topPipe.sprite.setTexture(pipeTexture);
topPipe.sprite.setPosition(WINDOW_WIDTH, gapPosition -
pipeTexture.getSize().y);
topPipe.sprite.setRotation(180); // Rotate top pipe to face down
// Bottom Pipe
bottomPipe.sprite.setTexture(pipeTexture);
bottomPipe.sprite.setPosition(WINDOW_WIDTH, gapPosition + PIPE_GAP_SIZE);
return std::make_pair(topPipe, bottomPipe);
}
// Updates the position of the pipes, moving them to the left
void updatePipes(std::vector<std::pair<Pipe,Pipe>>& pipePairs, float deltaTime) {
for (size_t i = 0; i < pipePairs.size(); ++i) {
pipePairs[i].first.sprite.move(-PIPE_SPEED * deltaTime, 0); // Move top
pipe
pipePairs[i].second.sprite.move(-PIPE_SPEED * deltaTime, 0); // Move bottom
pipe
// Remove pipes that have gone off-screen
if (pipePairs[i].first.sprite.getPosition().x < -PIPE_WIDTH) {
pipePairs.erase(pipePairs.begin() + i); //Erase the pipe
break;
}
}
}
// Checks for collisions between the bird and the pipes
bool checkCollision(const Bird& bird, const std::vector<std::pair<Pipe,Pipe>>&
pipePairs) {
sf::FloatRect birdBounds = bird.sprite.getGlobalBounds();
for (const auto& pipePair : pipePairs) {
if (birdBounds.intersects(pipePair.first.sprite.getGlobalBounds()) ||
birdBounds.intersects(pipePair.second.sprite.getGlobalBounds())) {
return true;
}
}
return false;
}
// Resets the game state
void resetGame(Bird& bird, std::vector<std::pair<Pipe,Pipe>>& pipePairs) {
bird.sprite.setPosition(100, WINDOW_HEIGHT / 2);
bird.velocity = 0.0f;
pipePairs.clear(); // Remove all pipes
}