# Extra-credit assignment: Flappy Bird

##### Description:

For this assignment, I tried to recreate an Arduino version of flappy bird using pushbuttons as primary controllers.  The main idea is that the avatar (bird), which continuously moves to the right, has to move through the equally sized gaps (between the pairs of obstacles aka pipes) that are placed at random heights. The bird is affected by gravity and is always pulled down to the bottom, however, the push button allows it to make a sort of jump in the air and therefore move a bit to the top. After every successful passing through the gap, the score is incremented by one. When the avatar hits the obstacle, the game ends, the score is displayed and the user can restart the game by clicking on the second push button.

##### Obstacles:

To generate obstacles I have used a class that includes three parameters (float xcoord for the x-coordinate, float first and second for the y-coordinates of the obstacles) and a couple of functions. Generate() generates random values for the y-coordinate of the first obstacle, then adds 140 (for the gap) and gets the y-coordinate of the second obstacle. Create() draws the rectangles on the display window, and whenever the obstacle goes out of the screen, it gets generated again on the other side with different y-coordinates.

```class Obstacle {

float xcoord; // x coordinate
float first = random(150,450); // top
float second = first+140; // bottom

void generate(){
first = random(150,450);
second = first+140;
}

void create(){
fill(#4caf50);
noStroke();
rect(xcoord, 0, 80, first);
rect(xcoord, second, 80, height);
fill(#9bf09b);
rect(xcoord+5, 0, 80-10, first-5);
rect(xcoord+5, second+5, 80-10, height);

if(xcoord < -80) { // if out of screen
xcoord = width+230;
generate();
score++;
}
}
}```
##### Movement:

Instead of making the bird move inside the display window, I used an infinite side-scrolling in which the character is static whereas the background moves from the right to the left.

To add movement to the obstacles, I am decreasing their x-coordinate by a constant amount (speed) inside the draw() loop.

```Obstacle obstacle1= new Obstacle(); // generate first obstacle
Obstacle obstacle2= new Obstacle(); // generate second obstacle
Obstacle obstacle3= new Obstacle(); // generate third obstacle
Obstacle obstacle4= new Obstacle(); // generate fourth obstacle

obstacle1.xcoord=width+(width/3)/2-40; // x coordinate of the first obstacle
obstacle2.xcoord=width+((width/3)/2-40)+(width/3);  // x coordinate of the second obstacle
obstacle3.xcoord=width+((width/3)/2-40)+(width/3)*2;  // x coordinate of the third obstacle
obstacle4.xcoord=width+((width/3)/2-40)+(width/3)*3;

// create all obstacles
obstacle1.create();
obstacle2.create();
obstacle3.create();
obstacle4.create();
// move obstacles
obstacle1.xcoord -= speed;
obstacle2.xcoord -= speed;
obstacle3.xcoord -= speed;
obstacle4.xcoord -= speed;```
##### Collisions:

To flag when the avatar touches one of the obstacles, I first used an ellipse to limit the area of the avatar, then checked if it overlapped with the obstacle. Meaning that the x and y coordinates of the avatar would be inside the obstacle area.

```// check if avatar touches the obstacles
if((avatar_ycoord-25<=obstacle1.first && obstacle1.xcoord<=avatar_xcoord+25) || (avatar_ycoord+25>=obstacle1.second&& obstacle1.xcoord<=avatar_xcoord+25)){
stop=true;
}
if((avatar_ycoord-25<=obstacle2.first && obstacle2.xcoord<=avatar_xcoord+25) || (avatar_ycoord+25>=obstacle2.second && obstacle2.xcoord<=avatar_xcoord+25)){
stop=true;
}
if((avatar_ycoord-25<=obstacle3.first && obstacle3.xcoord<=avatar_xcoord+25) || (avatar_ycoord+25>=obstacle3.second && obstacle3.xcoord<=avatar_xcoord+25)){
stop=true;
}
if((avatar_ycoord-25<=obstacle4.first && obstacle4.xcoord<=avatar_xcoord+25) || (avatar_ycoord+25>=obstacle4.second && obstacle4.xcoord<=avatar_xcoord+25)){
stop=true;
}
// check if avatar goes out of display window
if(avatar_ycoord>height || avatar_ycoord<0){
stop=true;
}```
##### Gravity:

The gravity is set to 3 and pulls the avatar down constantly. But when the user clicks on the button, the avatar makes a quick jump and its y-coordinate is affected.

##### Arduino:

On the Arduino ide, I am reading the state of the buttons, and sending them to processing using Serial.println().

```int inPin = 3;  // green pushbutton
int inPin2 = 4; // yellow pushbutton
int val = 0;
int val2 = 0;

void setup() {
Serial.begin(9600);
Serial.println("0,0");
}
void loop() {
while (Serial.available()) {
val = digitalRead(inPin); // green push button state
val2 = digitalRead(inPin2); // yellow push button state
// send the state of the push button to processing
if (val == HIGH && val2== HIGH) {
Serial.println("1,1");
}
else if (val == HIGH && val2== LOW) {
Serial.println("1,0");
}
else if (val == LOW && val2== LOW) {
Serial.println("0,0");
}
else if (val == LOW && val2== HIGH) {
Serial.println("0,1");
}
}
}
}```
##### End game:

When a collision occurs, or the avatar goes out of the screen, the game ends.

##### Restart game:

To restart the game, the user has to click on the second push button.

View post on imgur.com

##### Code:
###### Processing:
```import processing.serial.*;
Serial myPort;
PImage img; // avatar img

int speed = 3; // speed of game
int avatar_xcoord = 70; // avatar's x coordinate
int avatar_ycoord = 0;  // avatar's y coordinate
int gravity = 3; // gravity effect
boolean stop = false; // boolean var to end game
int score; // score
boolean clicked= false; // boolean var to restard game

Obstacle obstacle1= new Obstacle(); // generate first obstacle
Obstacle obstacle2= new Obstacle(); // generate second obstacle
Obstacle obstacle3= new Obstacle(); // generate third obstacle
Obstacle obstacle4= new Obstacle(); // generate fourth obstacle

void setup(){
size(900,600);
obstacle1.xcoord=width+(width/3)/2-40; // x coordinate of the first obstacle
obstacle2.xcoord=width+((width/3)/2-40)+(width/3);  // x coordinate of the second obstacle
obstacle3.xcoord=width+((width/3)/2-40)+(width/3)*2;  // x coordinate of the third obstacle
obstacle4.xcoord=width+((width/3)/2-40)+(width/3)*3;  // x coordinate of the fourth obstacle
// Get response from Arduino
printArray(Serial.list());
String portname=Serial.list()[1];
myPort = new Serial(this,portname,9600);
myPort.clear();
}

void serialEvent(Serial myPort){
s=trim(s);
if (s!=null){
int values[]=int(split(s,','));
// jump effect for the avatar
if (stop==false && values[0]==1) {avatar_ycoord -= gravity*0.6;}
// restart game
else if (stop==true && values[1]==1) {clicked=true; print("hi");}
}
myPort.write("\n");
}

void draw(){
// check if avatar touches the obstacles
if((avatar_ycoord-25<=obstacle1.first && obstacle1.xcoord<=avatar_xcoord+25) || (avatar_ycoord+25>=obstacle1.second&& obstacle1.xcoord<=avatar_xcoord+25)){
stop=true;
}
if((avatar_ycoord-25<=obstacle2.first && obstacle2.xcoord<=avatar_xcoord+25) || (avatar_ycoord+25>=obstacle2.second && obstacle2.xcoord<=avatar_xcoord+25)){
stop=true;
}
if((avatar_ycoord-25<=obstacle3.first && obstacle3.xcoord<=avatar_xcoord+25) || (avatar_ycoord+25>=obstacle3.second && obstacle3.xcoord<=avatar_xcoord+25)){
stop=true;
}
if((avatar_ycoord-25<=obstacle4.first && obstacle4.xcoord<=avatar_xcoord+25) || (avatar_ycoord+25>=obstacle4.second && obstacle4.xcoord<=avatar_xcoord+25)){
stop=true;
}
// check if avatar goes out of display window
if(avatar_ycoord>height || avatar_ycoord<0){
stop=true;
}

background(#cdeefd);
// if game ends
if (stop==true){
background(#cdeefd);
textSize(25);
stroke(#9bf09b);
noFill();
strokeWeight(8);
rect(width/2-200, height/2-150, 400, 300);
strokeWeight(4);
// display score
text("Game Over!", width/2-60, height/2-70);
text("Score: " + score, width/2-40, height/2-70 + 40);
text("Click on yellow", width/2-75, height/2-70 + 80);
text("button to replay!", width/2-85, height/2-70 + 120);
// restart game and reset all parameters
if (clicked){
stop=false;  // reset boolean var
clicked=false; // reset boolean var
score=0; // reset score
obstacle1.xcoord=width+(width/3)/2-40; // x coordinate of he first obstacle
obstacle2.xcoord=width+((width/3)/2-40)+(width/3); // x coordinate of he second obstacle
obstacle3.xcoord=width+((width/3)/2-40)+(width/3)*2; // x coordinate of he third obstacle
obstacle4.xcoord=width+((width/3)/2-40)+(width/3)*3; // x coordinate of he fourth obstacle
avatar_xcoord= 70; // x coordinate of avatar
avatar_ycoord= 0; // y coordinate of avatar
}
}
// if game starts
if (stop==false){
// create all obstacles
obstacle1.create();
obstacle2.create();
obstacle3.create();
obstacle4.create();
// move obstacles
obstacle1.xcoord -= speed;
obstacle2.xcoord -= speed;
obstacle3.xcoord -= speed;
obstacle4.xcoord -= speed;
// display the avatar
image(img, avatar_xcoord-30, avatar_ycoord-35, 80, 70);
avatar_ycoord += gravity;
fill(0);
textSize(12);
// display the score
text("Score: " + score, 820, 20);
}
}

class Obstacle {

float xcoord; // x coordinate
float first = random(150,450); // top
float second = first+140; // bottom

void generate(){
first = random(150,450);
second = first+140;
}

void create(){
fill(#4caf50);
noStroke();
rect(xcoord, 0, 80, first);
rect(xcoord, second, 80, height);
fill(#9bf09b);
rect(xcoord+5, 0, 80-10, first-5);
rect(xcoord+5, second+5, 80-10, height);

if(xcoord < -80) { // if out of screen
xcoord = width+230;
generate();
score++;
}
}
}```
###### Arduino:
```int inPin = 3;  // green pushbutton
int inPin2 = 4; // yellow pushbutton
int val = 0;
int val2 = 0;

void setup() {
Serial.begin(9600);
Serial.println("0,0");
}
void loop() {
while (Serial.available()) {
val = digitalRead(inPin); // green push button state
val2 = digitalRead(inPin2); // yellow push button state
// send the state of the push button to processing
if (val == HIGH && val2== HIGH) {
Serial.println("1,1");
}
else if (val == HIGH && val2== LOW) {
Serial.println("1,0");
}
else if (val == LOW && val2== LOW) {
Serial.println("0,0");
}
else if (val == LOW && val2== HIGH) {
Serial.println("0,1");
}
}
}
}```