Search

How to Make Flappy Bird with C++

Step by step code and also video tutorial


How to Make Flappy Bird with C++


Flappy Bird is a mobile game developed by Vietnamese video game artist and programmer Dong Nguyen (Vietnamese: Nguyễn Hà Đông), under his game development company .Gears.

The game is a side-scroller where the player controls a bird, attempting to fly between columns of green pipes without hitting them. Nguyen created the game over the period of several days, using a bird protagonist that he had designed for a cancelled game in 2012.

In this article we will see how to recreate with C++ and SFML, at the end of the article there is also a video that demonstrates in video all the steps done here.


01. DRAW BACKGROUND, FLAPPY AND PIPES ON SCREEN

Declare and initialize core members and member functions.

Create the files:

Makefile
flappy.cpp
flappy.hpp
main.cpp
./resources/ # img/font 

Click to expand

flappy.hpp
#pragma once
#include <SFML/Graphics.hpp>
#include <memory>

class FlappyBird {
  std::shared_ptr<sf::RenderWindow> window; 
  float gravity, frame, space;
  int count;
  sf::Texture bg, flappy, pipe;
  std::shared_ptr<sf::Sprite> background, bird, pipeBottom, pipeTop;
  std::vector<sf::Sprite> pipes; 

  protected:
    void events();
    void draw();

  public:
    FlappyBird();
    void run();
};
flappy.cpp
#include "flappy.hpp"

FlappyBird::FlappyBird(){
  window = std::make_shared<sf::RenderWindow>(
    sf::VideoMode(1000,600),
    "FlappyBird (remix)",
    sf::Style::Titlebar | sf::Style::Close
  );
  window->setFramerateLimit(60);
  window->setPosition(sf::Vector2i(0, 0));

  gravity = frame = {0};
  space = {160.f};
  count = {0};

  bg.loadFromFile("./resources/img/background.png");
  flappy.loadFromFile("./resources/img/flappy.png");
  pipe.loadFromFile("./resources/img/pipe.png");

  background = std::make_shared<sf::Sprite>();
  bird = std::make_shared<sf::Sprite>();
  pipeBottom = std::make_shared<sf::Sprite>();
  pipeTop = std::make_shared<sf::Sprite>();

  background->setTexture(bg);
  bird->setTexture(flappy);
  pipeBottom->setTexture(pipe);
  pipeTop->setTexture(pipe);

  bird->setPosition(
    500.f - flappy.getSize().x / 2.f, 
    300.f - flappy.getSize().y / 2.f 
  );
  bird->setScale(2.f, 2.f);
  bird->setTextureRect(sf::IntRect(0, 0, 34, 24));

  pipeBottom->setScale(1.5f, 1.5f);
  pipeTop->setScale(1.5f, -1.5f);

  // TEMP
  pipeBottom->setPosition(100, 200);
  pipeTop->setPosition(100, 100);
}

void FlappyBird::events(){
  auto e = std::make_shared<sf::Event>(); 
  while(window->pollEvent(*e)){
    if( e->type == sf::Event::Closed){
      window->close();
    }
  }
}

void FlappyBird::draw(){
  window->clear(sf::Color::Black);
  window->draw(*background);
  window->draw(*bird);
  window->draw(*pipeBottom);
  window->draw(*pipeTop);
  window->display();
}

void FlappyBird::run(){
  while(window->isOpen() ){
    events();
    draw();
  }
}
main.cpp
#include "flappy.hpp"

int main(){
  auto flappy = std::make_shared<FlappyBird>(); 
  flappy->run();
  return 0;
}
Makefile
TARGET=a.out
CXX=g++
DEBUG=-g
OPT=-O2
WARN=-Wall
SFML=-lsfml-graphics -lsfml-window -lsfml-system
CXXFLAGS=$(DEBUG) $(OPT) $(WARN) $(SFML)
LD=g++
OBJS=main.o flappy.o
all: $(OBJS)
	$(LD) -o $(TARGET) $(OBJS) $(CXXFLAGS)
	@rm *.o
	@./$(TARGET)

main.o: main.cpp
	$(CXX) -c $(CXXFLAGS) main.cpp -o main.o

flappy.o: flappy.cpp
	$(CXX) -c $(CXXFLAGS) flappy.cpp -o flappy.o

02. MOVE THE PIPES

  1. Remove from constructor::
// TEMP
pipeBottom->setPosition(100, 200);
pipeTop->setPosition(100, 100);
  1. Add to flappy.hpp
void game();
void movePipes();
  1. Add to flappy.cpp
...
window->display();
++count;
}

void FlappyBird::movePipes(){
void FlappyBird::game(){

Add to main.cpp

std::srand(time(0));
  1. Add to movePipes():
void FlappyBird::movePipes(){
  if( count % 150 == 0 ){
    int pos = std::rand() % 275 + 175;

    pipeBottom->setPosition(1000, pos + space);
    pipeTop->setPosition(1000, pos);

    pipes.push_back(*pipeBottom);
    pipes.push_back(*pipeTop);
  }

  for (std::size_t i {}; i < pipes.size(); ++i) {

     if( pipes[i].getPosition().x < -100){
       pipes.erase(pipes.begin() + i);
     }

    pipes[i].move(-3, 0);
  }
}
  1. Add game() to run()

  2. Replace window->draw(*pipeBottom); window->draw(*pipeTop); with:

for(auto &p : pipes){
  window->draw(p); 
}

03. ANIMATE THE FLAPPY BIRD

So he can move his wings

  1. Add to flappy.hpp:
void setAnimeBird();
  1. Add to flappy.cpp:
void FlappyBird::setAnimeBird(){
  frame += 0.15f;

  if( frame > 3 ){
    frame -= 3;
  }

  bird->setTextureRect(sf::IntRect(34 * (int)frame, 0, 34, 24));
}
  1. And add to game():
void FlappyBird::game(){
  setAnimeBird();
  movePipes();
}

04. MOVE THE FLAPPY BIRD

  1. Add to flappy.hpp:
void moveBird();
  1. Add to flappy.cpp
void FlappyBird::moveBird(){
  bird->move(0, gravity);
  gravity += 0.5f;
}
  1. Add to game():
setAnimeBird();
moveBird();
movePipes();
  1. Add to movePipes() before if..:
if( sf::Mouse::isButtonPressed(sf::Mouse::Left)){
  gravity = -8.f;
}
  1. So that the bird appears to have its head tilted up and a slight movement as if it is swinging
if( sf::Mouse::isButtonPressed(sf::Mouse::Left)){
  gravity = -8.f;
  bird->setRotation(-frame - 10.f);
}else{
  bird->setRotation(frame - 10.f);
}

05. COLLIDE WITH THE PIPES

  1. Add to flappy.hpp
bool gameover;
  1. Add to flappy.cpp(constructor):
gameover = {false};
  1. Add to movePipes():
...
for (std::size_t i {}; i < pipes.size(); ++i) {

  if(pipes[i].getGlobalBounds().intersects(bird->getGlobalBounds())){
    gameover =  true;
  }
...
  1. Change the game():
void FlappyBird::game(){
  if(!gameover){
    setAnimeBird();
    moveBird();
    movePipes();
  }
}

This will freeze the screen

  1. Change the position of window->draw(*bird) to after pipes:
for(auto &p : pipes){
  window->draw(p); 
}
window->draw(*bird);

06. ADD TEXT TO GAME OVER

  1. Add to flappy.hpp
sf::Font font;
sf::Text txt_score, txt_gameover;
  1. Add to flappy.cpp(construtor)
font.loadFromFile("./resources/font/flappy-font.ttf");
txt_gameover.setFont(font);
txt_gameover.setString("Press SPACE to restart");
txt_gameover.setPosition(200, 300);
txt_gameover.setCharacterSize(50);
txt_gameover.setOutlineThickness(3);
  1. Add to draw():
window->draw(*bird);

if(gameover){
  window->draw(txt_gameover);
}
  1. Add score and equal to zero in the constructor.

  2. To restart the game. Add to events():

if( gameover && sf::Keyboard::isKeyPressed(sf::Keyboard::Space) ){
  score = 0;
  pipes.clear();

  bird->setPosition(
      500.f - flappy.getSize().x / 2.f, 
      300.f - flappy.getSize().y / 2.f 
      );
  gameover = false;
}

PARTE 7 - ADD SCORE

  1. flappy.hpp: int count, score;
  2. constructor:
txt_score.setFont(font);
txt_score.setString(std::to_string(score));
txt_score.setPosition(10, 10);
txt_score.setCharacterSize(50);
txt_score.setOutlineThickness(3);
  1. In movePipes() after bird->move(..
...
pipes[i].move(-3, 0);

if( pipes[i].getPosition().x == 298 ){
 txt_score.setString(std::to_string(++score));
}
...
  1. To draw():
window->draw(txt_score);
  1. To not add twice, just:
    • flappy.hppbool gameover, add;
    • flappy.cppadd = {false};
    • And in addition do like this:
if( pipes[i].getPosition().x == 298 && !add ){
  txt_score.setString(std::to_string(score));
  add = true;
}else{
  add = false;
}
  1. Redraw score after gameover in events() after score = 0;:
txt_score.setString(std::to_string(++score));

Take advantage and reset gravity = 0.f; too.


WATCH THE VIDEO

The video is in Portuguese, but you can follow the steps even if you don’t understand the language. You can also enable subtitles and use Youtube’s automatic translation.


Source code on GitHub:

https://github.com/terroo/flappybird


Enjoy!


cpp gamedev sfml


Share



Comments