Midterm Project

Overview:

As mentioned before, for my midterm project, I was inspired by the mechanics of the Chrome Dino Runner game and tried to create a newer version with more features that I called “Knight Runner”. To avoid obstacles, the avatar can either jump over the fire, or jump/crouch when it’s a bird.

Process & Features:
Parallax:

To give the game a realistic aspect, I added a Parallax effect in which the far-away clouds and mountains seem to move more slowly than the closer ones, by changing each layer’s (6 layers) position by a different amount (between 1 and 5).

// Parallax effect
void update(){
  x6--; x6_2--;
  x5-=2; x5_2-=2;
  x4-=3; x4_2-=3;
  x3-=3; x3_2-=3;
  x2-=4; x2_2-=4;
  x1-=5; x1_2-=5;
}
Infinite Side-scrolling:

Instead of making the character 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 achieve that, I used two images placed next to each other that reappear on the right side once they get out of the display window.

  // Infinite scrolling
  if (x6<=-width){x6=width;} if (x6_2<=-width){x6_2=width;}
  if (x5<=-width){x5=width;} if (x5_2<=-width){x5_2=width;}
  if (x4<=-width){x4=width;} if (x4_2<=-width){x4_2=width;}
  if (x3<=-width){x3=width;} if (x3_2<=-width){x3_2=width;}
  if (x2<=-width){x2=width;} if (x2_2<=-width){x2_2=width;}
  if (x1<=-width){x1=width;} if (x1_2<=-width){x1_2=width;}
Spritesheet:

To animate the character, I am using 3 sprite sheets stored in a 2D array (running, jumping, sliding, dying), each row has 10 images.

To animate the obstacles, I am using 2 other sprite sheets, one for fire (64 images), and the other for birds (9 images).

I am using frameCount to loop over the sprites

// Upload all the sprites
void loadsprites(){
  // Running
  for (int i=0; i<sprites.length;i++){
    sprites[i][0]=loadImage("assets/run/run"+i+".png");
    sprites[i][0].resize(53,74);
  }
  // Jumping
  for (int i=0; i<sprites.length;i++){
    sprites[i][1]=loadImage("assets/jump/jump"+i+".png");
    sprites[i][1].resize(53,74);
  }
  // Sliding
  for (int i=0; i<sprites.length;i++){
    sprites[i][2]=loadImage("assets/slide/slide"+i+".png");
    sprites[i][2].resize(53,57);
  }
  // Dying
  for (int i=0; i<sprites.length;i++){
    sprites[i][3]=loadImage("assets/dying/Dead__00"+i+".png");
    sprites[i][3].resize(73,77);
  }
  // Fire
  for (int i=0; i<firesprites.length;i++){
    firesprites[i]=loadImage("assets/fire/tile0"+i+".png");
    firesprites[i].resize(60,60);
  }
  // Bird
  for (int i=0; i<birdsprites.length;i++){
    birdsprites[i]=loadImage("assets/bird/tile00"+i+".png");
    birdsprites[i].resize(80,80);
  }
}
Gravity:

I am using a gravity effect for both jumping and sliding, to give the animation a realistic aspect. When jumping, the speed is continuously decreased by the amount of gravity, however, when crouching, it gets increased.

void move(){
  ycoord -= speed;
  // gravity if jumps
  if (ycoord<425){
    speed -= gravity;
  }
  // gravity if crouches
  else if (ycoord>425){
    ycoord += speed;
    speed += gravity;
  }
  // remain same when running
  else{
    state=0;
    speed=0;
    ycoord=425;
  }
}
Jump and Crouch:

The user cannot jump and crouch at the same time. Both the jump and crouch are done using ycoord, speed and the gravity.

// jump
void jump(){
  if (ycoord==425 && crouching==false){
    state=1;
    gravity=1;
    speed= 16;
  }
}

// crouch
void crouch(){
  if (crouching==true && ycoord==425){
    state=2;
    ycoord=442;
    gravity=1;
    speed= -20;
  }
}
Generating obstacles:

To generate obstacles I am both using frameCount and two random functions, the first one is used to choose when to generate the obstacle, whereas the second one is used to choose what to generate (fire or bird). The obstacles are automatically stored in an array to keep track of their position and to display them continuously. If the obstacles disappear from the screen (go over the edge) they get immediately remove from the array.

The bird obstacles have a higher speed than fire because technically fire is static so it should have the same speed as the scrolling, whereas the bird is flying.

// add a new obstacle
void addObstacle(){
  if (frameCount % 60 == 0 && random(1)<0.5){
    // choose randomly if its fire or a bird
    if (random(2)<1){
      // add it to the list
      firelist.add(new Fire());
    }
    else {
      birdlist.add(new Bird());
    }
  }
}
Collisions:

To flag when the avatar touches one of the obstacles, I first used a rectangle to limit the areas of both the obstacle and avatar, then checked if those areas overlapped. Meaning that the x and y coordinates of the avatar would be inside the obstacle area. The avatar should jump when there is fire, but can either jump or crouch when there is a bird, to avoid collisions with obstacles.

// Bird
boolean checkfail(float ycoord){
  // if crouching, avatar is safe
 if (avatar.state==2){
      return false;
    }
    // check if avatar touches the obstacle
    return xcoord+25>=100 && xcoord+25<=150 && ycoord>=400 && ycoord<=400+30 || xcoord+40+25>=100 && xcoord+40+25<=150 && ycoord+70>=400 && ycoord<=400+30;
}
// fire
boolean checkfail(float ycoord){
  // check if avatar touches the obstacle
  return xcoord>=100 && xcoord<=150 && ycoord+70>=540-60-30 || xcoord+40>=100 && xcoord+40<=150 && ycoord+70>=540-60-30;
}
Menu: 

Similarly, to check which button the user clicked, I used mouseClicked(), mouseX, and mouseY and checked whether the x and y coordinates are inside the area of that specific button. The menu (lobby) is displayed first, then the user has a choice to either start the game, or read the instructions.

To switch between lobby, game, and instructions pages, I am overlapping backgrounds over each other.

draw():

Inside my draw() function, I mainly check the status of the game using boolean variables (avatar died, menu, reset, game ongoing…), then proceed with displaying the right images, and the right text.

Boolean variables:

Start: flags when the user clicks on the start button, if start is false, then the menu is displayed

Help: flags when the user clicks on the help button, the instructions are then displayed

Dead: flags when the avatar touches an obstacle, the game then ends, and the user is given a choice to replay

Reset: flags when the user chooses to replay, all the games settings are reset, and the arrays get cleared

void draw(){
   
  // if instructions icon clicked
  if (help){
    // mute the music
    back.amp(0);
    image(main,0,0);
    imageMode(CENTER);
    image(instructmenu,width/2,height/2);
    image(backmenu,width/2, height/2-200);
    imageMode(CORNER);
    textSize(20);
    text("↑ : Jump",width/2,height/2-60);
    text("↓ : Crouch",width/2,height/2-20);
    text("Try to avoid all obstacles",width/2,height/2+20);
    text("(Fire, birds)",width/2,height/2+60);
    noFill();
    rect(width/2-30,height/2-230,60,60);  
  }
  
  else if (start){
    // unmute the music if alive
    if (dead==false) {back.amp(1);}
    // mute the music if dead
    else {back.amp(0);}
    display(); // display the background images
    update(); // parallax effect and infinite scrolling
    rect(100,425,50,70);
    avatar.show(); // display the avatar
    addObstacle(); // add an obstacle
    imageMode(CENTER);
    // Display the score
    image(scoremenu, width/2, 50); 
    imageMode(CORNER);
    textSize(20);
    textAlign(CENTER);
    text("score:  " + round(score),width/2,55);
    
    // Display the obstacles
    for (int i=0; i<firelist.size(); i++){
      firelist.get(i).show();
      firelist.get(i).move();
      
      // check if avatar touches an obstacle
      if (firelist.get(i).checkfail(avatar.ycoord)){
        dead=true;
      }
      
      // remove the obstacles that are not displayed
      if (firelist.get(i).xcoord <-70){
        firelist.remove(i);
      }
    }
    
    // Display the obstacles
    for (int i=0; i<birdlist.size(); i++){
      birdlist.get(i).show();
      birdlist.get(i).move();
      
      // check if avatar touches an obstacle
      if (birdlist.get(i).checkfail(avatar.ycoord)){
        dead=true;
      }
      
      // remove the obstacles that are not displayed
      if (birdlist.get(i).xcoord <-70){
        birdlist.remove(i);
      }
    }
    
    // If replay button is clicked, reset the game
    if (reset==true){
     back.amp(1); // unmute the music
     dead=false;
     start=true;
     reset=false;
     score=0; // reset the score
     // reset the obstacles list
     firelist = new ArrayList<Fire>();
     birdlist = new ArrayList<Bird>();
    }
   
    if (dead==true){
      // stop the parallax
      x6++; x6_2++;
      x5+=2; x5_2+=2;
      x4+=3; x4_2+=3;
      x3+=3; x3_2+=3;
      x2+=4; x2_2+=4;
      x1+=5; x1_2+=5;
      
      // stop the obstacles animation
      for (int i=0; i<firelist.size(); i++){
        firelist.get(i).xcoord +=5;
      }
      for (int i=0; i<birdlist.size(); i++){
        birdlist.get(i).xcoord +=10;
      }
      // enable the dying animation
      avatar.state=3;
      // display the replay button
      imageMode(CENTER);
      image(startmenu,width/2, height/2-20);
      text("REPLAY",width/2,height/2+7-20);
      imageMode(CORNER);
   }
 }
 
 // display the lobby menu
 else if (start==false){
   // mute the music
   back.amp(0);
   menu();
   image(main,0,0);
   imageMode(CENTER);
   textAlign(CENTER);
   textSize(30);
   // display the ui
   image(startmenu,width/2, height/2-20);
   text("PLAY",width/2,height/2+7-20);
   image(helpmenu,width/2, height/2+100-20);
   text("HELP",width/2,height/2+100+7-20);
   image(title,width/2, 100);
   noFill();
   rect(width/2-75,height/2-55,150,70);
   rect(width/2-90,height/2+40,180,80);
   imageMode(CORNER);
 }
 
}
Shapes:

I have used 4 blinking rectangles to add some aesthetics to the title.

noStroke();
if (frameCount%15==0){
  noFill();}
 else{ fill(0);}
rect(width/2+220, 85,10,30,PI);
rect(width/2+240, 90,10,20,PI);
rect(width/2-225, 85,10,30,PI);
rect(width/2-245, 90,10,20,PI);
fill(255);
User Input:

To detect when the user clicks on a key, I used both keyPressed() and keyReleased() functions for keyboard, and mouseClicked.

void mouseClicked(){
// if player clicks on start
if ((mouseX>width/2-75) && (mouseX<width/2+75) && (mouseY>height/2-55) && (mouseY<height/2+15)){
start=true;
menu.play();
}
// if player clicks on help
if ((start==false) && (mouseX>width/2-90) && (mouseX<width/2+90) && (mouseY>height/2+40) && (mouseY<height/2+120)){
help=true;
menu.play();
}
// if player clicks on back
else if ((help==true) && (mouseX>width/2-30) && (mouseX<width/2+30) && (mouseY>height/2-230) && (mouseY<height-170)){
help=false;
menu.play();
}
// if player clicks on replay
else if ((dead==true) && (mouseX>width/2-75) && (mouseX<width/2+75) && (mouseY>height/2-55) && (mouseY<height/2+15)){
reset=true;
menu.play();
}
}
void keyPressed(){
  // Jump
  if (keyCode==UP && dead==false){
    avatar.jump();
  }
  // Crouch
  if (keyCode==DOWN && dead==false){
    avatar.crouching=true;
    avatar.crouch();
  }
}

void keyReleased(){
  // stop crouching
  if (keyCode==DOWN && dead==false){
    avatar.crouching=false;
  }
}
SFX:

Concerning the sound effects, I have only used two main ones (the background music, and the click sound effect). Instead of stopping the music, I mute it, then unmute it when the game starts.

SoundFile menu; // Click sound effect
SoundFile back; // background music 

// Load the sound effects
menu = new SoundFile(this, "assets/menu.wav");
back = new SoundFile(this, "assets/back.mp3");
// Play the background music
back.play();
// Loop the background music
back.loop();

// unmute the music if alive
if (dead==false) {back.amp(1);}
// mute the music if dead
else {back.amp(0);}
Score:

The score gets incremented when the avatar is moving by O.O5 per frame and is displayed on the top-center of the display window. It gets reset when the game restarts.

// increment the score
if (!dead) {score+=0.05;}
End of the game:

When the avatar touches an obstacle, the dying animation is enabled and freezes at the last sprite, and all the other animations/motions are stopped. The score is displayed on top, and the replay button appears.

 

View post on imgur.com

FULL CODE:

import processing.sound.*;
PImage bg1, bg2, bg3, bg4, bg5, bg6, bg7, platform, main, startmenu, helpmenu, title, instructmenu, scoremenu; // Background images
PImage backmenu;
int x1=0, x1_2=960, x2=0, x2_2=960, x3=0, x3_2=960; // X-coordinates of the images
int x4=0, x4_2=960, x5=0, x5_2=960, x6=0, x6_2=960; // X-coordinates of the images
PImage[][] sprites = new PImage[10][4]; // Store the sprites for the avatar
PImage [] firesprites = new PImage[64]; // Store the sprites for the fire
PImage [] birdsprites = new PImage[9]; // Store the sprites for the birds
ArrayList<Fire> firelist = new ArrayList<Fire>(); // Store all the fire objects
ArrayList<Bird> birdlist = new ArrayList<Bird>(); // store all the bird objects
boolean start=false;
boolean dead= false; 
boolean help= false;
boolean reset= false;
float score=0; // Keep track of the score
SoundFile menu; // Click sound effect
SoundFile back; // background music 

// Create a new avatar
Avatar avatar = new Avatar(0,425);

// Upload all the sprites
void loadsprites(){
  // Running
  for (int i=0; i<sprites.length;i++){
    sprites[i][0]=loadImage("assets/run/run"+i+".png");
    sprites[i][0].resize(53,74);
  }
  // Jumping
  for (int i=0; i<sprites.length;i++){
    sprites[i][1]=loadImage("assets/jump/jump"+i+".png");
    sprites[i][1].resize(53,74);
  }
  // Sliding
  for (int i=0; i<sprites.length;i++){
    sprites[i][2]=loadImage("assets/slide/slide"+i+".png");
    sprites[i][2].resize(53,57);
  }
  // Dying
  for (int i=0; i<sprites.length;i++){
    sprites[i][3]=loadImage("assets/dying/Dead__00"+i+".png");
    sprites[i][3].resize(73,77);
  }
  // Fire
  for (int i=0; i<firesprites.length;i++){
    firesprites[i]=loadImage("assets/fire/tile0"+i+".png");
    firesprites[i].resize(60,60);
  }
  // Bird
  for (int i=0; i<birdsprites.length;i++){
    birdsprites[i]=loadImage("assets/bird/tile00"+i+".png");
    birdsprites[i].resize(80,80);
  }
}

// Load the background images
void load(){
  bg7 = loadImage("assets/bg.png");
  // Resize the images
  bg7.resize(960,540);
  bg6 = loadImage("assets/bg6.png");
  bg6.resize(960,540);
  bg5 = loadImage("assets/bg5.png");
  bg5.resize(960,540);
  bg4 = loadImage("assets/bg2.png");
  bg4.resize(960,540);
  bg3 = loadImage("assets/bg4.png");
  bg3.resize(960,540);
  bg2 = loadImage("assets/bg1.png");
  bg2.resize(960,540);
  bg1 = loadImage("assets/bg3.png");
  bg1.resize(960,540);
  platform = loadImage("assets/platform.png");
  platform.resize(960,610);
}

// menu loading
void menu(){
  // load ui images
  startmenu = loadImage("assets/b_3.png");
  helpmenu = loadImage("assets/b_4.png");
  // resize the images
  startmenu.resize(150,70);
  helpmenu.resize(180,80);
  title = loadImage("assets/title.png");
  instructmenu = loadImage("assets/b_5.png");
  scoremenu = loadImage("assets/bar_1.png");
  scoremenu.resize(250,40);
  instructmenu.resize(350,300);
  backmenu = loadImage("assets/b_6.png");
  backmenu.resize(60,60);
}

// Display the background
void display(){
  image(bg7,0,0);
  image(bg6,x6,0);
  image(bg6,x6_2,0);
  image(bg5,x5,0);
  image(bg5,x5_2,0);
  image(bg4,x4,0);
  image(bg4,x4_2,0);
  image(bg3,x3,0);
  image(bg3,x3_2,0);
  image(bg2,x2,0);
  image(bg2,x2_2,0);
  image(bg1,x1,0);
  image(bg1,x1_2,0);
  // Add a tint to match the background
  tint(#7cdfd2);
  image(platform,x1,0);
  image(platform,x1_2,0);
  noTint();
}

// Parallax effect
void update(){
  x6--; x6_2--;
  x5-=2; x5_2-=2;
  x4-=3; x4_2-=3;
  x3-=3; x3_2-=3;
  x2-=4; x2_2-=4;
  x1-=5; x1_2-=5;
  
  // Infinite scrolling
  if (x6<=-width){x6=width;} if (x6_2<=-width){x6_2=width;}
  if (x5<=-width){x5=width;} if (x5_2<=-width){x5_2=width;}
  if (x4<=-width){x4=width;} if (x4_2<=-width){x4_2=width;}
  if (x3<=-width){x3=width;} if (x3_2<=-width){x3_2=width;}
  if (x2<=-width){x2=width;} if (x2_2<=-width){x2_2=width;}
  if (x1<=-width){x1=width;} if (x1_2<=-width){x1_2=width;} 
}

// add a new obstacle
void addObstacle(){
  if (frameCount % 60 == 0 && random(1)<0.5){
    // choose randomly if its fire or a bird
    if (random(2)<1){
      // add it to the list
      firelist.add(new Fire());
    }
    else {
      birdlist.add(new Bird());
    }
  }
}

void setup(){
  size(960,540);
  load(); // load the background images
  loadsprites(); // load the sprites
  main = loadImage("assets/main.png");
  main.resize(960,540);
  // Load the sound effects
  menu = new SoundFile(this, "assets/menu.wav");
  back = new SoundFile(this, "assets/back.mp3");
  // Play the background music
  back.play();
  // Loop the background music
  back.loop();
}

void draw(){
   
  // if instructions icon clicked
  if (help){
    // mute the music
    back.amp(0);
    image(main,0,0);
    imageMode(CENTER);
    image(instructmenu,width/2,height/2);
    image(backmenu,width/2, height/2-200);
    imageMode(CORNER);
    textSize(20);
    text("↑ : Jump",width/2,height/2-60);
    text("↓ : Crouch",width/2,height/2-20);
    text("Try to avoid all obstacles",width/2,height/2+20);
    text("(Fire, birds)",width/2,height/2+60);
    noFill();
    //rect(width/2-30,height/2-230,60,60);  
  }
  
  else if (start){
    // unmute the music if alive
    if (dead==false) {back.amp(1);}
    // mute the music if dead
    else {back.amp(0);}
    display(); // display the background images
    update(); // parallax effect and infinite scrolling
    //rect(100,425,50,70);
    avatar.show(); // display the avatar
    addObstacle(); // add an obstacle
    imageMode(CENTER);
    // Display the score
    image(scoremenu, width/2, 50); 
    imageMode(CORNER);
    textSize(20);
    textAlign(CENTER);
    text("score:  " + round(score),width/2,55);
    
    // Display the obstacles
    for (int i=0; i<firelist.size(); i++){
      firelist.get(i).show();
      firelist.get(i).move();
      
      // check if avatar touches an obstacle
      if (firelist.get(i).checkfail(avatar.ycoord)){
        dead=true;
      }
      
      // remove the obstacles that are not displayed
      if (firelist.get(i).xcoord <-70){
        firelist.remove(i);
      }
    }
    
    // Display the obstacles
    for (int i=0; i<birdlist.size(); i++){
      birdlist.get(i).show();
      birdlist.get(i).move();
      
      // check if avatar touches an obstacle
      if (birdlist.get(i).checkfail(avatar.ycoord)){
        dead=true;
      }
      
      // remove the obstacles that are not displayed
      if (birdlist.get(i).xcoord <-70){
        birdlist.remove(i);
      }
    }
    
    // If replay button is clicked, reset the game
    if (reset==true){
     back.amp(1); // unmute the music
     dead=false;
     start=true;
     reset=false;
     score=0; // reset the score
     // reset the obstacles list
     firelist = new ArrayList<Fire>();
     birdlist = new ArrayList<Bird>();
    }
   
    if (dead==true){
      // stop the parallax
      x6++; x6_2++;
      x5+=2; x5_2+=2;
      x4+=3; x4_2+=3;
      x3+=3; x3_2+=3;
      x2+=4; x2_2+=4;
      x1+=5; x1_2+=5;
      
      // stop the obstacles animation
      for (int i=0; i<firelist.size(); i++){
        firelist.get(i).xcoord +=5;
      }
      for (int i=0; i<birdlist.size(); i++){
        birdlist.get(i).xcoord +=10;
      }
      // enable the dying animation
      avatar.state=3;
      // display the replay button
      imageMode(CENTER);
      image(startmenu,width/2, height/2-20);
      text("REPLAY",width/2,height/2+7-20);
      imageMode(CORNER);
   }
 }
 
 // display the lobby menu
 else if (start==false){
   // mute the music
   back.amp(0);
   menu();
   image(main,0,0);
   imageMode(CENTER);
   textAlign(CENTER);
   textSize(30);
   // display the ui
   image(startmenu,width/2, height/2-20);
   text("PLAY",width/2,height/2+7-20);
   image(helpmenu,width/2, height/2+100-20);
   text("HELP",width/2,height/2+100+7-20);
   image(title,width/2, 100);
   noFill();
   //rect(width/2-75,height/2-55,150,70);
   //rect(width/2-90,height/2+40,180,80);
   imageMode(CORNER);
   noStroke();
   if (frameCount%15==0){
     noFill();}
    else{ fill(0);}
   rect(width/2+220, 85,10,30,PI);
   rect(width/2+240, 90,10,20,PI);
   rect(width/2-225, 85,10,30,PI);
   rect(width/2-245, 90,10,20,PI);
   fill(255);
 }
 
}

void mouseClicked(){
  // if player clicks on start
  if ((mouseX>width/2-75) && (mouseX<width/2+75) && (mouseY>height/2-55) && (mouseY<height/2+15)){
    start=true;
    menu.play();
  }
  // if player clicks on help
  if ((start==false) && (mouseX>width/2-90) && (mouseX<width/2+90) && (mouseY>height/2+40) && (mouseY<height/2+120)){
     help=true;
     menu.play();
  }
  // if player clicks on back
  else if ((help==true) && (mouseX>width/2-30) && (mouseX<width/2+30) && (mouseY>height/2-230) && (mouseY<height-170)){
     help=false;
     menu.play();
  }
  // if player clicks on replay
  else if ((dead==true) && (mouseX>width/2-75) && (mouseX<width/2+75) && (mouseY>height/2-55) && (mouseY<height/2+15)){
    reset=true;
    menu.play();
  }
}

void keyPressed(){
  // Jump
  if (keyCode==UP && dead==false){
    avatar.jump();
  }
  // Crouch
  if (keyCode==DOWN && dead==false){
    avatar.crouching=true;
    avatar.crouch();
  }
}

void keyReleased(){
  // stop crouching
  if (keyCode==DOWN && dead==false){
    avatar.crouching=false;
  }
}

class Bird{
  //bird's x-coordinate
  float xcoord;
  
  Bird(){
    // generate the bird outside the screen
    xcoord = 40 + 960;
  }
  
  // display the bird
  void show(){
    // stop the animation
    if (dead){image(birdsprites[6],xcoord,380);}
    else{
    // play the animation
    image(birdsprites[frameCount/2%birdsprites.length],xcoord,380);}
  }
  
  // move the bird
  void move(){
    xcoord -= 10;
    noFill();
    //rect(xcoord+25, 400, 48,30);
  }
  
  boolean checkfail(float ycoord){
    // if crouching, avatar is safe
    if (avatar.state==2){
      return false;
    }
    // check if avatar touches the obstacle
    return xcoord+25>=100 && xcoord+25<=150 && ycoord>=400 && ycoord<=400+30 || xcoord+40+25>=100 && xcoord+40+25<=150 && ycoord+70>=400 && ycoord<=400+30;
  }
}

class Fire{
  //fire's x-coordinate
  float xcoord;
  
  Fire(){
    // generate the fire outside the screen
    xcoord = 40 + 960;
  }
  
  // display the fire
  void show(){
    // stop the animation
    if (dead){image(firesprites[10],xcoord,435);}
    else{
    // play the animation
    image(firesprites[frameCount/2%firesprites.length],xcoord,435);}
  }

  // move the fire
  void move(){
    xcoord -= 5;
  }
  
  boolean checkfail(float ycoord){
    // check if avatar touches the obstacle
    return xcoord>=100 && xcoord<=150 && ycoord+70>=540-60-30 || xcoord+40>=100 && xcoord+40<=150 && ycoord+70>=540-60-30;
  }
}

class Avatar{
  float xcoord=100; // xcoordinate of avatar
  float ycoord; // ycoordinate of avatar
  float gravity=1; // gravity of avatar
  float speed= 0; // speed of avatar
  int state=0; // state of avatar (jumping, crouchin, dying)
  boolean crouching =false; // flag if crouching
   // used for dying animation
  int k=0; 
  int cnt;
  
  // constructor
  Avatar(int state, float ycoord){
    this.state= state;
    this.ycoord= ycoord;
  }
  
  void show(){
    // play the dying animation
    if (state==3 && cnt<9){
      // freeze at last sprite
      ycoord=427;
      image(sprites[k*frameCount/2%sprites.length][state],xcoord,ycoord);
      k=1;
      cnt++;
    }
    // display animation
    else if (state==1 || state==2 || state==0){
      image(sprites[frameCount/2%sprites.length][state],xcoord,ycoord);
    }
    else {
      // freeze at last sprite
      ycoord=427;
      image(sprites[9][state],xcoord,ycoord);
    }
    move();
  }
  
  // move the player
  void move(){
    // increment the score
    if (!dead) {score+=0.05;}
    ycoord -= speed;
    // gravity if jumps
    if (ycoord<425){
      speed -= gravity;
    }
    // gravity if crouches
    else if (ycoord>425){
      ycoord += speed;
      speed += gravity;
    }
    // remain same when running
    else{
      state=0;
      speed=0;
      ycoord=425;
    }
  }
  
  // jump
  void jump(){
    if (ycoord==425 && crouching==false){
      state=1;
      gravity=1;
      speed= 16;
    }
  }
  
  // crouch
  void crouch(){
    if (crouching==true && ycoord==425){
      state=2;
      ycoord=442;
      gravity=1;
      speed= -20;
    }
  }
}

 

 

Leave a Reply