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.5 f , 1.5 f );
pipeTop -> setScale ( 1.5 f , - 1.5 f );
// 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()
to run()
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.15 f ;
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.5 f ;
}
Add to game()
:
setAnimeBird ();
moveBird ();
movePipes ();
Add to movePipes()
before if..
:
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()
after bird->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
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.
VIDEO
Source code on GitHub:
Enjoy!
cpp
gamedev
sfml
Marcos Oliveira
Software developer