#include <SFML/Graphics.hpp>
#include <SFML/Window.hpp>
#include <SFML/System.hpp>
#include <random>
#include <cmath>
const sf::Time FRAME_TIME = sf::seconds(1/60.0f);
const unsigned PROGRESS_RATE = 400; // X speed
const float GRAVITY = 1000;
const float JUMP_VELOCITY = 600;
const unsigned WINDOW_WIDTH = 800;
const unsigned WINDOW_HEIGHT = 600;
const unsigned BLOCK_WIDTH = 50;
const unsigned BLOCK_INTERVAL = 200;
const unsigned BLOCK_GAP_HEIGHT = 250;
const unsigned BIRD_WIDTH = 60;
const unsigned BIRD_HEIGHT = 50;
// Make sure the end of the block is not seen
const unsigned BLOCK_HEIGHT = WINDOW_HEIGHT;
// Fit enought blocks to fill the screen
const unsigned BLOCKS_ON_SCREEN = (WINDOW_WIDTH / (BLOCK_WIDTH + BLOCK_INTERVAL)) + 1;
struct Game {
sf::RenderWindow* window;
sf::RectangleShape block;
sf::RectangleShape bird;
bool started = false;
bool running = false;
bool alive = true;
float gapSequencePosition;
float gapAltitudes[BLOCKS_ON_SCREEN];
float birdVelocity = 0;
float gravity = 0;
float totalProgress = 0;
void logic(double dt);
void run();
void processEvents();
void draw();
};
void Game::processEvents() {
sf::Event event;
while(window->pollEvent(event)) {
switch(event.type) {
case sf::Event::Closed: {
window->close();
} break;
case sf::Event::KeyPressed: {
if(event.key.code == sf::Keyboard::Space) {
if(!started) {
started = true;
running = true;
gravity = GRAVITY;
}
if(running) birdVelocity = -JUMP_VELOCITY;
}
} break;
default: {
} break;
}
}
}
void Game::logic(double dt) {
bird.move(0, birdVelocity * dt);
// Orient bird along velocity, convert to degrees from radians
bird.setRotation(atan2(birdVelocity, PROGRESS_RATE) * 180 / M_PI);
birdVelocity += gravity * dt;
float progress = PROGRESS_RATE * dt;
totalProgress += progress;
// Collision check with blocks
sf::FloatRect bounds = bird.getGlobalBounds();
sf::FloatRect collisionBlock;
collisionBlock.width = BLOCK_WIDTH;
collisionBlock.height = BLOCK_HEIGHT;
for(unsigned i = 0; i < BLOCKS_ON_SCREEN; ++i) {;
collisionBlock.left = (gapSequencePosition + (i * (BLOCK_WIDTH + BLOCK_INTERVAL))),
collisionBlock.top = gapAltitudes[i];
if(bounds.intersects(collisionBlock)) running = false;
collisionBlock.top -= ((float)BLOCK_GAP_HEIGHT + BLOCK_HEIGHT);
if(bounds.intersects(collisionBlock)) running = false;
}
// Collision with borders
if(bounds.top < 0) running = false;
if(bounds.top + bounds.height > WINDOW_HEIGHT) running = false;
// If the front block gap went off screen, move blocks forward, overwrite each
// block gap with the next one, and generate a new one on the last position
// which isn't visible yet
if(running) gapSequencePosition -= progress;
if(gapSequencePosition < -1 * (float)BLOCK_WIDTH) {
gapSequencePosition += (float)BLOCK_WIDTH + BLOCK_INTERVAL;
for(unsigned i = 0; i < BLOCKS_ON_SCREEN - 1; ++i) {
gapAltitudes[i] = gapAltitudes[i + 1];
}
gapAltitudes[BLOCKS_ON_SCREEN - 1] =
(rand() % (WINDOW_HEIGHT - BLOCK_GAP_HEIGHT)) + BLOCK_GAP_HEIGHT;
}
}
void Game::draw() {
window->clear(sf::Color(0x52, 0xb3, 0xa1));
bool started = false;
for(unsigned i = 0; i < BLOCKS_ON_SCREEN; ++i) {
block.setPosition(
(gapSequencePosition + (i * (BLOCK_WIDTH + BLOCK_INTERVAL))),
gapAltitudes[i]
);
window->draw(block);
block.move(0, -1 * ((float)BLOCK_GAP_HEIGHT + BLOCK_HEIGHT));
window->draw(block);
}
window->draw(bird);
window->display();
}
void Game::run() {
// NOTE: Consider time(NULL) instead for different course each time
srand(1);
window = new sf::RenderWindow(
sf::VideoMode(800, 600),
"Flappy Burd",
sf::Style::Titlebar
);
window->setPosition({50, 50});
gapSequencePosition = WINDOW_WIDTH;
// Generate first gaps
for(unsigned i = 0; i < BLOCKS_ON_SCREEN; ++i) {
gapAltitudes[i] =
(rand() % (WINDOW_HEIGHT - BLOCK_GAP_HEIGHT)) + BLOCK_GAP_HEIGHT;
}
block.setSize({BLOCK_WIDTH, BLOCK_HEIGHT});
block.setFillColor(sf::Color::Black);
bird.setSize({BIRD_WIDTH, BIRD_HEIGHT});
bird.setFillColor(sf::Color::Blue);
bird.setPosition(WINDOW_WIDTH / 4, WINDOW_HEIGHT / 2);
bird.setOrigin(BIRD_WIDTH / 2, BIRD_HEIGHT / 2);
sf::Clock clock;
sf::Time previousTime, currentTime;;
sf::Time accTime = sf::Time::Zero;
while(window->isOpen()) {
currentTime = clock.getElapsedTime();
accTime += currentTime - previousTime;
previousTime = currentTime;
while(accTime > FRAME_TIME) {
processEvents();
accTime -= FRAME_TIME;
logic(FRAME_TIME.asSeconds());
}
draw();
}
}
int main() {
Game game;
game.run();
return EXIT_SUCCESS;
}