How to Make Flappy Bird with C++
Step by step code and also video tutorial
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
- Remove from constructor::
// TEMP
pipeBottom->setPosition(100, 200);
pipeTop->setPosition(100, 100);
- Add to
flappy.hpp
void game();
void movePipes();
- Add to
flappy.cpp
...
window->display();
++count;
}
void FlappyBird::movePipes(){
void FlappyBird::game(){
Add to main.cpp
std::srand(time(0));
- 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);
}
}
-
Add
game()
torun()
-
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
- Add to
flappy.hpp
:
void setAnimeBird();
- 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));
}
- And add to
game()
:
void FlappyBird::game(){
setAnimeBird();
movePipes();
}
04. MOVE THE FLAPPY BIRD
- Add to
flappy.hpp
:
void moveBird();
- Add to
flappy.cpp
void FlappyBird::moveBird(){
bird->move(0, gravity);
gravity += 0.5f;
}
- Add to
game()
:
setAnimeBird();
moveBird();
movePipes();
- Add to
movePipes()
beforeif..
:
if( sf::Mouse::isButtonPressed(sf::Mouse::Left)){
gravity = -8.f;
}
- 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
- Add to
flappy.hpp
bool gameover;
- Add to
flappy.cpp
(constructor):
gameover = {false};
- Add to
movePipes()
:
...
for (std::size_t i {}; i < pipes.size(); ++i) {
if(pipes[i].getGlobalBounds().intersects(bird->getGlobalBounds())){
gameover = true;
}
...
- Change the
game()
:
void FlappyBird::game(){
if(!gameover){
setAnimeBird();
moveBird();
movePipes();
}
}
This will freeze the screen
- 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
- Add to
flappy.hpp
sf::Font font;
sf::Text txt_score, txt_gameover;
- 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);
- Add to
draw()
:
window->draw(*bird);
if(gameover){
window->draw(txt_gameover);
}
-
Add
score
and equal to zero in the constructor. -
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
flappy.hpp
:int count, score;
- 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);
- In
movePipes()
afterbird->move(..
...
pipes[i].move(-3, 0);
if( pipes[i].getPosition().x == 298 ){
txt_score.setString(std::to_string(++score));
}
...
- To
draw()
:
window->draw(txt_score);
- To not add twice, just:
flappy.hpp
→bool gameover, add;
flappy.cpp
→add = {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;
}
- Redraw
score
aftergameover
inevents()
afterscore = 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!
Comments