Processing Flappy Bird Game Development

Processing Flappy Bird Game Development

 Hello friends, lately I have gotten into Processing due to taking a course at school, and since our teacher also gave homework, I coded a Flappy Bird-style game in Processing language and wanted to share these codes with you as well. It looks like this will be quite a long article since the codes are about 200 lines, so let’s get straight to coding without making it any longer.

Processing Flappy Bird Game Development

Processing Flappy Bird Game Development

Processing Flappy Bird Game Development

How the Game Works

First, we visually create our character in the game as a circle, then we create rectangles for the obstacles. 

We make our circular character move up and down to avoid the obstacles, and we continuously slide the rectangle objects created for obstacles to the left.

The gameplay part of the game is made with this logic; of course, in addition, we add a start screen, a game over screen, and a point system to let the player know when they have advanced after avoiding the obstacles.

We also code a collision detection system by calculating the positions of the blocks and comparing them to the ball’s position, so the game ends when the character hits an obstacle.

Note: When I coded the game, I slid the obstacles to the left because it was easier for me, but if you want, you can make the character move to the right instead of the obstacles. There is no single way to do anything in software.

Variables Used While Making the Game and Their Explanations

/** Variables **/ 
/** Scene Variable **/ 
// 0 Start screen
// 1 Game screen
// 2 Game over screen
int oyunEkrani = 0; // Variable showing which game screen we are on 
/** 1. Scene 0 control for repeatability **/
boolean classCagir = true; // For control of scene reset if played more than once.
/** Points **/
float puan; // Variable holding the score
/** Bird Variables **/
Kus k; // We call the Bird class as k.
boolean zipladimi = false; // We control whether the bird jumped.
float baslangicZamani; // Variable keeping the millis when the bird starts jumping.
/** Wall Variables **/
int duvarSayisi = 100, duvarlarArasiAralik; // Number of top and bottom walls in the game (must be even), variable for calculating the distance between walls
float duvarlarArasiYukseklik; // Height difference between the bottom and top walls
Duvar[] duvarlar = new Duvar[duvarSayisi]; // Array in which we call the Wall class

Regarding which variable we used for what purpose, 
  • With oyunEkrani, we switch between scenes using this variable.
  • With classCagir, we allow the player to play again without reopening the game and reset the game with a bool check.
  • With the puan variable, we record the player’s score.
  • With Kus k and Duvar[] variables, we call the bird class and the wall class.
  • With zipladimi, we check whether the character has jumped.
  • With baslangicZamani, we detect when the bird jumps and keep it in the air accordingly.
  • duvarSayisi shows the total number of obstacles to be displayed in the game (if you make it 100, since there will be top and bottom, you will avoid 50 obstacles).
  • duvarlarArasiAralik is for the horizontal distance between the walls, duvarlarArasiYukseklik is for the vertical distance between the walls.

Constant Functions and Their Explanations

/** Constant functions **/
void setup() {
  size(300, 600);
}
void draw() {
  if (oyunEkrani == 0) {
    GirisEkrani();
  } else if (oyunEkrani == 1) {
    OyunEkrani();
  } else if (oyunEkrani == 2) {
    KaybettinEkrani();
  }
}

Here, start() is the place to write codes that run once when the application is opened. We determined the screen size with size(x, y).

draw() is the standard function to write codes that run continuously; all events that will take place in the game are written here. We included the scenes here by using an if check.

Explanations of Mechanics and Other Functions

/** Functions **/
/** Collision Detection Function **/
void CarpismaAlgila(Duvar duvar, Kus kus) {
  if (duvar.poz.x < 120 && duvar.poz.x > 60) {
    if ((duvar.poz.y <= 0) && kus.poz.y < (duvar.poz.y + 410)) {
      println("you lost, hit the top");
      KaybettinEkraniGec();
    } else if ((duvar.poz.y > 0) && kus.poz.y > (duvar.poz.y -10)) {
      println("You lost, hit the bottom");   
      KaybettinEkraniGec();
    } else if(duvar.poz.x == 116){
        puan+= 0.5;
    }
  }
}

/** Controller Functions **/
void mousePressed() {
  if (oyunEkrani == 0) {
    OyunuBaslatEkraniGec();
  }
  if (oyunEkrani == 2) {
    GirisEkraniGec();
  }
}
void keyPressed() {
  if (key == ' ') {
    k.Ziplat();
  }
}
/** Scene Changing Functions **/
void GirisEkraniGec() {
  classCagir = true;
  oyunEkrani = 0;
}
void OyunuBaslatEkraniGec() {
  oyunEkrani = 1;
}
void KaybettinEkraniGec() {
  oyunEkrani = 2;
}

/** Game Screen Functions **/
/** Start Screen Scene **/
void GirisEkrani() {
  background(0, 255, 0);
  textAlign(CENTER);
  textSize(28);
  text("Click and start the game", width/2, height/2);
  if (classCagir) {
    duvarlarArasiAralik = 0;
    duvarlarArasiYukseklik = 0;
    puan = 0;
    k = new Kus();
    for (int i = 0; i < duvarSayisi; i++) {
      duvarlar[i] = new Duvar();


      if (i%2 == 0) {
        duvarlar[i].Yerlestir(duvarlarArasiYukseklik, duvarlarArasiAralik);
      } else {
        duvarlar[i].Yerlestir((duvarlarArasiYukseklik + random(550, 600)), duvarlarArasiAralik);
        float randomAralik = random(200,300);
        if(int(randomAralik) % 2 != 0){
        randomAralik += 1;
        }
        duvarlarArasiAralik += randomAralik;
        duvarlarArasiYukseklik = random(-300, 0);
      }
    }
    classCagir = false;
  }
}
/** Point System **/
void PuanGoster(){
fill(255,255,255);
textAlign(CENTER);
text("Score : " + int(puan), width/2, 100);
}
/** Game Area Scene **/
void OyunEkrani() {
  background(155, 55, 55);
  k.Goster();
  k.Guncelle();
  for (int i = 0; i < duvarSayisi/2; i++) {
    duvarlar[i].HareketEttir();
    duvarlar[i].Goster();
    CarpismaAlgila(duvarlar[i], k);
  }
  for (int i = duvarSayisi/2; i < duvarSayisi; i++) {
    duvarlar[i].HareketEttir();
    duvarlar[i].Goster();
    CarpismaAlgila(duvarlar[i], k);
  }
  PuanGoster();
}
/** Scene that appears after losing **/
void KaybettinEkrani() {
  background(255, 0, 0);
  textAlign(CENTER);
  fill(255, 255, 255);
  text("Score : "+ int(puan) + "\nClick to return to start", width/2, height/2);
}

This is one of the two core parts of our game, the functions here form the foundation of the game, the second core is found in the classes.

  • The CarpismaAlgila() function computes the positions of the walls because we know their sizes and then ensures that when the bird enters those positions, the game is lost. If it does not enter that area, it earns a point. We give the score as 0.5 float because our function runs only once per obstacle, and since there are two obstacles (top and bottom), it gets 0.5 points from each, which sums to 1 point.
  • With mousePressed function, when the mouse is clicked (left) in scene0 it moves to scene1, and clicking on the game over screen moves to scene0.
  • With keyPressed(), when the space bar is hit, we call the Ziplat() function from the bird class for the character to jump.
  • GirisEkraniGec(), OyunuBaslatEkraniGec(), KaybettinEkraniGec() change the scenes.
  • GirisEkrani() handles the initial screen when the game first opens and the process of resetting variables for a replay. It also handles bird creation, wall interval adjustments for the second game, etc.
  • PuanGoster() handles showing the user’s score on the game screen.
  • OyunEkrani() is where we set up the scene the player sees during the game, showing the character, handling movement, creating and sliding the walls to the left, and detecting collision.
  • KaybettinEkrani() contains the codes for the scene that opens when the player hits an obstacle.

Classes and Their Explanations

/** Classes **/
/** Walls **/
class Duvar {
  PVector poz;
  PVector boyut;
  float hiz;

  Duvar() {
    poz = new PVector(500, random(height));  
    hiz = 2;
    boyut = new PVector(50, 400);
  }
  void Yerlestir(float yukseklik, float aralik) {
    poz.y = yukseklik;
    poz.x += aralik;
  }
  void HareketEttir() {
    poz.x -= hiz;
  }
  void Goster() {
    fill(0, 102, 0);
    rect(poz.x, poz.y, boyut.x, boyut.y);
  }
}


/** Bird Class **/
class Kus {
  PVector poz;
  float yerCekimi, ivme, ziplamaGucu, boyut, kutle;
  Kus() {
    poz = new PVector(100, 100);
    boyut = 40;
    yerCekimi = 3;
    ivme = 0;
    ziplamaGucu = -6;
  }
  void Goster() {
    fill(255, 51, 51);
    ellipse(poz.x, poz.y, boyut, boyut);
  }

  void Guncelle() {
    if(zipladimi){
    ivme += ziplamaGucu;
     //print("current time" + millis() + " start+ " + (baslangicZamani+0.5));
    if(millis() > baslangicZamani+300){
     
    zipladimi = false;
    }
    
    }else{
    ivme += yerCekimi;
    }
 
    poz.y = ivme;
    if (poz.y>height) {
      poz.y = height;
      ivme = 0;
    }
  }
  void Ziplat() {
    baslangicZamani = millis();
  
    zipladimi = true;
  }
}
This is where the codes that build and control our character are found, as well as the wall codes that allow us to create many walls with little code by using classes. Let’s explain these codes.
  • class Duvarlar: Here, instead of writing the same code for walls hundreds of times, we create a class and just write the wall code once and utilize it by calling this class. Now, let’s look into the class.
  • With PVector poz and boyut, we create class-scoped variables to control the wall’s position and size. With hiz, we control at what speed the wall slides to the left.
  • Duvar() is where we set the speed, size, and position values when the class is called.
  • Yerlestir() is the function where we set the wall’s position when called with given values.
  • Hareket Ettir() is the function where we slide the wall to the left.
  • Goster() is the code that redraws the wall on the screen every time its position changes.

  • class Kus: Here is where we build and control the bird just as we did for the wall class with class internal functions.
  • With PVector poz we determine the bird’s position, and with float yerCekimi, ivme, ziplamaGucu, boyut, etc., we control things like fall speed to the ground, jump speed upwards, etc.
  • With Kus(), we set initial position, size, gravity power, value of acceleration, and jump power for the bird.
  • With Goster(), we ensure that the bird is redrawn on the screen and visible to the user during movement.
  • With Guncelle(), we handle actions such as flying up and falling to the ground when the bird jumps.
  • With Ziplat(), we activate the jump function for the bird.
Note: I set the jump to go up for 300 ms using the millis function, so the bird glides upward instead of teleporting.

All Code

/** Variables **/
/** Scene Variable **/
// 0 Start screen
// 1 Game screen
// 2 Game over screen
int oyunEkrani = 0; // Variable showing which game screen we are on 
/** 1. Scene 0 control for repeatability **/
boolean classCagir = true; // For control of scene reset if played more than once.
/** Points **/
float puan; // Variable holding the score
/** Bird Variables **/
Kus k; // We call the Bird class as k.
boolean zipladimi = false; // We control whether the bird jumped.
float baslangicZamani; // Variable keeping the millis when the bird starts jumping.
/** Wall Variables **/
int duvarSayisi = 100, duvarlarArasiAralik; // Number of top and bottom walls in the game (must be even), variable for calculating the distance between walls
float duvarlarArasiYukseklik; // Height difference between the bottom and top walls
Duvar[] duvarlar = new Duvar[duvarSayisi]; // Array in which we call the Wall class

/** Constant functions **/
void setup() {
  size(300, 600);
}
void draw() {
  if (oyunEkrani == 0) {
    GirisEkrani();
  } else if (oyunEkrani == 1) {
    OyunEkrani();
  } else if (oyunEkrani == 2) {
    KaybettinEkrani();
  }
}


/** Functions **/
/** Collision Detection Function **/
void CarpismaAlgila(Duvar duvar, Kus kus) {
  if (duvar.poz.x < 120 && duvar.poz.x > 60) {
    if ((duvar.poz.y <= 0) && kus.poz.y < (duvar.poz.y + 410)) {
      println("you lost, hit the top");
      KaybettinEkraniGec();
    } else if ((duvar.poz.y > 0) && kus.poz.y > (duvar.poz.y -10)) {
      println("You lost, hit the bottom");   
      KaybettinEkraniGec();
    } else if(duvar.poz.x == 116){
        puan+= 0.5;
    }
  }
}

/** Controller Functions **/
void mousePressed() {
  if (oyunEkrani == 0) {
    OyunuBaslatEkraniGec();
  }
  if (oyunEkrani == 2) {
    GirisEkraniGec();
  }
}
void keyPressed() {
  if (key == ' ') {
    k.Ziplat();
  }
}
/** Scene Changing Functions **/
void GirisEkraniGec() {
  classCagir = true;
  oyunEkrani = 0;
}
void OyunuBaslatEkraniGec() {
  oyunEkrani = 1;
}
void KaybettinEkraniGec() {
  oyunEkrani = 2;
}

/** Game Screen Functions **/
/** Start Screen Scene **/
void GirisEkrani() {
  background(0, 255, 0);
  textAlign(CENTER);
  textSize(28);
  text("Click and start the game", width/2, height/2);
  if (classCagir) {
    duvarlarArasiAralik = 0;
    duvarlarArasiYukseklik = 0;
    puan = 0;
    k = new Kus();
    for (int i = 0; i < duvarSayisi; i++) {
      duvarlar[i] = new Duvar();


      if (i%2 == 0) {
        duvarlar[i].Yerlestir(duvarlarArasiYukseklik, duvarlarArasiAralik);
      } else {
        duvarlar[i].Yerlestir((duvarlarArasiYukseklik + random(550, 600)), duvarlarArasiAralik);
        float randomAralik = random(200,300);
        if(int(randomAralik) % 2 != 0){
        randomAralik += 1;
        }
        duvarlarArasiAralik += randomAralik;
        duvarlarArasiYukseklik = random(-300, 0);
      }
    }
    classCagir = false;
  }
}
/** Point System **/
void PuanGoster(){
fill(255,255,255);
textAlign(CENTER);
text("Score : " + int(puan), width/2, 100);
}
/** Game Area Scene **/
void OyunEkrani() {
  background(155, 55, 55);
  k.Goster();
  k.Guncelle();
  for (int i = 0; i < duvarSayisi/2; i++) {
    duvarlar[i].HareketEttir();
    duvarlar[i].Goster();
    CarpismaAlgila(duvarlar[i], k);
  }
  for (int i = duvarSayisi/2; i < duvarSayisi; i++) {
    duvarlar[i].HareketEttir();
    duvarlar[i].Goster();
    CarpismaAlgila(duvarlar[i], k);
  }
  PuanGoster();
}
/** Scene that appears after losing **/
void KaybettinEkrani() {
  background(255, 0, 0);
  textAlign(CENTER);
  fill(255, 255, 255);
  text("Score : "+ int(puan) + "\nClick to return to start", width/2, height/2);
}

/** Classes **/
/** Walls **/
class Duvar {
  PVector poz;
  PVector boyut;
  float hiz;

  Duvar() {
    poz = new PVector(500, random(height));  
    hiz = 2;
    boyut = new PVector(50, 400);
  }
  void Yerlestir(float yukseklik, float aralik) {
    poz.y = yukseklik;
    poz.x += aralik;
  }
  void HareketEttir() {
    poz.x -= hiz;
  }
  void Goster() {
    fill(0, 102, 0);
    rect(poz.x, poz.y, boyut.x, boyut.y);
  }
}


/** Bird Class **/
class Kus {
  PVector poz;
  float yerCekimi, ivme, ziplamaGucu, boyut, kutle;
  Kus() {
    poz = new PVector(100, 100);
    boyut = 40;
    yerCekimi = 3;
    ivme = 0;
    ziplamaGucu = -6;
  }
  void Goster() {
    fill(255, 51, 51);
    ellipse(poz.x, poz.y, boyut, boyut);
  }

  void Guncelle() {
    if(zipladimi){
    ivme += ziplamaGucu;
     //print("current time" + millis() + " start+ " + (baslangicZamani+0.5));
    if(millis() > baslangicZamani+300){
     
    zipladimi = false;
    }
    
    }else{
    ivme += yerCekimi;
    }
 
    poz.y = ivme;
    if (poz.y>height) {
      poz.y = height;
      ivme = 0;
    }
  }
  void Ziplat() {
    baslangicZamani = millis();
  
    zipladimi = true;
  }
}

Video from In-Game