Midterm Project: Dead Student

Overview:

Finally, it’s here.

The purpose of this game is for the student, skeleton in the picture, to collect all the questions and then collect the final paper to get the full grade for their exam.

IDEA(details):

Background:

Originally, I intended to have clouds as the background with the stars, but even when I used images instead of pixels, the game’s frameRate dropped and the game was barely functioning so I settled for a clear starry night look.

I choose this because:

firstly, I have really sensitive eyes, and I find it easier on my eye to look at a dark screen.

Secondly, I am a night person, night always looks better ;).

For the stars, I used overlapping circles to give a faded look, which resembles actual stars more than normal circles would.

Code:
class Bg {
  float[] x = new float[50];
  float[] y = new float[50];
  Bg() {
    for (int i=0; i<50; i++) {
      x[i] = random(0, widthh);
      y[i] = random(0, heightt);
    }
  }

  void drawBG() {
    if (frameCount%50==0) {
      for (int i=0; i<50; i++) {
        x[i] = random(0, width);
        y[i] = random(0, height);
      }
    }
    background(50, 50, 80);
    for (int i=0; i<50; i++) {
      for (int j=0; j<10; j++) {
        noStroke();
        fill(255, 25);
        ellipse(x[i], y[i], 1+j, 1+j);
      }
    }
  }
}

 

Creature:

Instead of making repetitive classes, I used class inheritance, where I made the Superclass, Creatures, for all the live items in the game.

Code:
public class Creature {

  // Attributes
  PVector pos;
  PVector speed;
  boolean alive;
  float ground;
  PVector size;
  PVector imgSize;
  int numFrames;
  PImage imgs;
  PImage[] img;
  int frame;
  boolean dir; //left = 0 right = 1
  boolean secondJump = false;

  public Creature(float x, float  y, float  r, float  g, PImage _img, float  w, float  h, int  num_frames) {
    pos= new PVector(x, y);
    speed= new PVector(0, 0);
    alive = true;
    ground = height-g;
    numFrames = num_frames;
    imgSize = new PVector(_img.width/num_frames, _img.height);
    size = new PVector (r, imgSize.y*(r/imgSize.x));
    imgs = _img;
    img = new PImage[numFrames];
    for (int i=0; i<numFrames; i++) {
      img[i] = imgs.get(
        int((imgSize.x)*i),
        0,
        int(imgSize.x),
        int(imgSize.y));
    }
    // for (int y=0; y < 4; y++) {
    //   for (int x=0; x< 12; x++) {
    //     sprites[y][x] = spritesheet.get(x*w, y*h, w, h);
    //   }
    // }
    frame = 0;
    dir = true;
  }

  void gravity() {
    if (pos.y<ground-size.y+10  ||pos.y<ground-size.y ) {
      speed.y+=0.2;
    } else {
      speed.y=0;
      pos.y = constrain(pos.y, ground-size.y, ground-size.y+10);
      secondJump=false;
    }
    for (int p=game.plats.length-1; p>=0; p--) {
      if ((pos.y+size.y <=game.plats[p].pos.y+15)//height
        && pos.x+size.x/3>=game.plats[p].pos.x//right
        && pos.x-size.x/3  <= game.plats[p].pos.x+game.plats[p].pWidth) {//left
        ground = game.plats[p].pos.y;
        break;
      } else {
        ground = heightt-game.ground;
      }
    }
  }


  void update() {
    gravity();
    pos.x += speed.x;
    pos.y += speed.y;
  }
  void display() {
    update();
    // println(this.getClass().getName());
    if (dir && alive) {
      scale(1, 1);
      image(img[frame], pos.x, pos.y, size.x, size.y);
    } else if (!dir && alive) {
      if (this.getClass().getName()=="Midterm$Player") {
        pushMatrix();
        scale(-1, 1);
        image(img[frame], -pos.x, pos.y, size.x, size.y);
        popMatrix();
      } else if (this.getClass().getName()=="Midterm$Monster") {
        image(img[frame], pos.x, pos.y, size.x, size.y);
      }
    }
  }
}
Player:

For the player, I choose the skeleton, because obviously, we are all dead during midterm and final seasons. That also gave me the idea of the name “dead student“.

Sprite:

With the player, I struggled a little with the jumps. I wanted to only allow double jumps, which I thought was the case until my roommate tried it and I realized it didn’t. I tried to fix it, but that only made it worse, so I left it as it was when my roommate tried it. (I mean what works, works right?)

Code:
class Player extends Creature {
  boolean left;
  boolean up;
  boolean right;
  SoundFile jump;
  int count;
  Player(float x, float  y, float  r, float  g, PImage _img, float  w, float  h, int  num_frames, PApplet sketch) {
    super(x, y, r, g, _img, w, h, num_frames);
    left = false;
    up = false;
    right = false;
    jump = new SoundFile(sketch, "jump.wav");
    count = frameCount;
  }

  void update() {
    gravity();
    //====================
    if (left==true) {
      speed.x=-3;
      dir=false;
    } else if (right==true) {
      speed.x=3;
      dir=true;
    } else {
      speed.x=0;
    }
    //====================
    if (up==true && pos.y>=ground-size.y &&secondJump==false) {
      jump.play();
      speed.y = -6;
    } else if (up==true && speed.y<0 && secondJump==true && pos.y<ground-size.y) {
      jump.play();
      speed.y = -8;
      secondJump=false;
    }
    pos.x +=speed.x;
    pos.y +=speed.y;
    if (frameCount%5 == 0 && speed.x !=0 && speed.y ==0) {
      frame= (frame+1)%numFrames;
    } else if (speed.x==0) {
      frame=8;
    }

    //====================
    if (pos.x<=0) {
      pos.x = 0;
    }
    //====================
    if (pos.x+size.x>= widthh) {
      pos.x = widthh- size.x;
    } else if (pos.x<size.x) {
      pos.x = size.x;
    }
    //====================
    for (int i =0; i<game.myMonsters.length; i++) {
      float numME = pow((pow(size.x, 2)+(pow(size.y, 2))), 0.5);
      float numthem = pow((pow(game.myMonsters[i].size.x, 2)+(pow(game.myMonsters[i].size.y, 2))), 0.5);

      if (game.myMonsters[i].distance(this)<=numME/2.5+numthem/2.5 && game.myMonsters[i].alive) {
        if (speed.y>0) {
          game.myMonsters[i].alive=false;
          game.myMonsters[i].sound2.play();
        } else {
          game.myMonsters[i].sound1.play();
          alive = false;
        }
      }
    }
    for (int i =0; i<game.myQuestions.length; i++) {
      if (game.myQuestions[i].distance(this)<=size.x/2+game.myQuestions[i].size.x/2 && game.myQuestions[i].alive) {
        game.score++;
        game.myQuestions[i].sound.play();
        game.myQuestions[i].alive = false;
      }
    }
    if (game.myfinal.distance(this)<=size.x/2+game.myfinal.size.x/2 && game.myfinal.alive) {
      game.score+=5;
      game.myfinal.sound.play();
      game.myfinal.alive = false;
      game.won= true;
    }
  }
}
Monsters:

Finding Sprites for the monsters was not easy. I was trying to find monsters that represent the distractions we have while studying. So instead of finding different monsters, I made my own using emojis and clip art images.

I used the facial features from the emojis and the body of what I think represents distractions. I made them alternate between an Innocent look and an evil look because distractions usually don’t look so bad.

Here are the sprites:

Monster 1, Phone:

Monster 2, Thoughts:

Monster 3, Anxiety:

Code:
class Monster extends Creature {
  SoundFile sound1, sound2;
  float xleft, xright;
  Monster(float x, float  y, float  r, float  g, PImage _img, float  w, float  h, int  num_frames, float x_left, float x_right, PApplet sketch) {
    super(x, y, r, g, _img, w, h, num_frames);

    speed.x=random(0.3,0.9);
    speed.y=0;
    xleft = x_left;
    xright = x_right;
    sound1 = new SoundFile(sketch, "eat.wav");
    sound2 = new SoundFile(sketch, "kill.wav");
  }

  void update() {
    gravity();
    if (pos.x<=xleft) {
      dir = true;
      frame= (frame+1)%numFrames;
      speed.x*=-1;
    } else if (pos.x>=xright) {
      dir = false;
      frame= (frame+1)%numFrames;
      speed.x*=-1;
    }
    pos.x +=speed.x;
    pos.y +=speed.y;
  }



  float distance(Player target) {
    return dist(this.pos.x+size.x/2, this.pos.y+size.y/2, target.pos.x+target.size.x/2, target.pos.y+target.size.y/2);
  }
}
Questions and Final:

For this one, I thought it was better to have a bouncy still image, as it was the only one that was completely dead. I used the same class for both as they had the same charactaristics.

Sprites:

Questions:

Final:

Code:
class Question extends Creature {
  SoundFile sound;
  Question(float x, float  y, float  r, float  g, PImage _img, float  w, float  h, int  num_frames, PApplet sketch) {
    super(x, y, r, g, _img, w, h, num_frames);
    speed.x=0;
    speed.y=-2;
    sound = new SoundFile(sketch, "token.mp3");
  }

  void update() {
    gravity();
    if (speed.y==0) {
      speed.y=-2;
    }

    pos.x +=speed.x;
    pos.y +=speed.y;
  }

  float distance(Player target) {
    return dist(this.pos.x, this.pos.y, target.pos.x, target.pos.y);
  }
}

 

Game:

For the full game, I made a game class that allows me to create a game object whenever I want to start the game. This helps with restarting and waiting for the user to press a key to start; as I just create a game object if the player is dead and the user presses enter at the welcom screen.

My game class only had the creation of all the game objects and the display call for them.

Code:
class Game {

  //
  int ground = 100;
  public int score = 0;
  boolean won;

  Player myplayer;
  Platforms[] plats = new Platforms[5];
  Platforms groundd;
  int platLocH[] = new int[5];
  int platLocW[] = new int[5];


  Monster[] myMonsters = new Monster[6];

  Question[] myQuestions = new Question[5];

  Question myfinal;



  Game(PApplet sketch, PImage playerImg, PImage[] monsters, PImage questionIMG, PImage finalIMG) {
    // creating ground
    groundd = new Platforms(0, heightt-ground, widthh, ground, 0);

    // creating platforms
    for (int i=0; i<plats.length; i++) {
      platLocH[i] = (heightt-255)-i*155;
      platLocW[i] = int(random(0, widthh-500));
      plats[i]= new Platforms(platLocW[i], platLocH[i], 600, heightt/25, 15);
    }

    // creating Monsters(1@ground & 4 at plats 0-3)
    int n;
    float m;
    for (int i=4; i>-1; i--) {
      n = int(random(0, 3));
      myMonsters[i]= new Monster(platLocW[i]+60, platLocH[i]-60*(monsters[n].width/monsters[n].height)-10, 60, ground, monsters[n], monsters[n].width, monsters[n].height, 2, platLocW[i]+random(40, 50), platLocW[i]+random(160, 220), sketch);
    }
    n = int(random(0, 3));
    m = random(widthh/2, widthh-450);
    myMonsters[5] = new  Monster(m+60, heightt-ground, 60, ground, monsters[n], monsters[n].width, monsters[n].height, 2, m+random(40, 50), m+random(160, 220), sketch);

    // creating Questions
    for (int i=3; i>-1; i--) {
      myQuestions[i]=new Question(platLocW[i]+400, platLocH[i]-60*(questionIMG.width/questionIMG.height)-50, 60, ground, questionIMG, questionIMG.width, questionIMG.height, 1, sketch);
    }
    myQuestions[4]=new Question(m+400, heightt-ground, 60, ground, questionIMG, questionIMG.width, questionIMG.height, 1, sketch);

    // creating final
    myfinal = new Question(platLocW[4]+400, platLocH[4]-80*(finalIMG.width/finalIMG.height)-50, 80, ground, finalIMG, finalIMG.width, finalIMG.height, 1, sketch);
    myfinal.alive=false;

    // creating Player
    myplayer = new Player(20, height-ground-180, 70, ground, playerImg, playerImg.width, playerImg.height, 9, sketch);
    won = false;
  }




  void main() {
    for (int i = 0; i<5; i++) {
      plats[i].display();
      if (myQuestions[i].alive) {
        myQuestions[i].display();
      }
    }
    groundd.display();
    myplayer.display();
    for (int i=0; i<6; i++) {
      if (myMonsters[i].alive) {
        myMonsters[i].display();
      }
    }
    if (score == myQuestions.length) {
      myfinal.alive = true;
      myfinal.display();
    }
  }
}

 

Start and end screens and Key presses:

I made functions to display text at the start and end of each game. This allowed me to easily call either whenever needed.

I reset my game variables in the end screen functions, but that is just to avoid unexpected errors.

void startSc() {
  textFont(f, 80);
  fill(255);
  textAlign(CENTER);
  text("Dead Student", widthh/2, heightt/2-100);
  textFont(f, 40);
  text("press enter to start", widthh/2, heightt/2-10);
  
  textFont(f, 20);
  text("Collect the questions to get to final paper", widthh/2, heightt/2+100);
  text("You can kill monsters by jumping on them", widthh/2, heightt/2+130);
  text("Please use left, right, and up arrows to play", widthh/2, heightt/2+190);
}

void endSc() {
  if (game.won) {
    textFont(f, 40);
    fill(255);
    textAlign(CENTER);
    text("YOU WON!!", widthh/2, heightt/2);
    text("YOUR GRADE IS:", widthh/2, heightt/2+50);
    text(String.valueOf(game.score)+" /10", widthh/2, heightt/2+100);
    text("CONGRATS!!", widthh/2, heightt/2+150);
    textFont(f, 20);
    text("press r to reset", widthh/2, heightt/2+200);
  } else {
    for (int i=0; i<game.myMonsters.length; i++) {
      game.myMonsters[i].alive=true;
    }
    for (int i=0; i<game.myQuestions.length; i++) {
      game.myQuestions[i].alive=true;
    }
    textFont(f, 40);
    fill(255);
    textAlign(CENTER);
    text("YOU DIED", widthh/2, heightt/2);
    text("YOUR GRADE IS:", widthh/2, heightt/2+50);
    text(String.valueOf(game.score)+" /10", widthh/2, heightt/2+100);
    textFont(f, 20);
    text("press r to reset", widthh/2, heightt/2+150);
  }
}
Key Pressed/Released Code:

I created the game when the user presses enter;

void keyPressed() {
  if (keyCode == 10 && started==false) {//enter
    started = true;
    game = new Game(this, playerimg, monsters, question, finall);
  } else if (keyCode==82 && started==true) {//r-> reset
    started=false;
    startSc();
  } else if (started==true && game.won==false) {
    if (keyCode == LEFT) {
      game.myplayer.left = true;
    }
    if (keyCode==RIGHT) {
      game.myplayer.right = true;
    }
    if (keyCode==UP) {
      game.myplayer.up = true;
    }
  }
}

void keyReleased() {
  if (started==true) {
    if (keyCode == LEFT) {
      game.myplayer.left = false;
    }
    if (keyCode==RIGHT) {
      game.myplayer.right = false;
    }
    if (keyCode==UP) {
      game.myplayer.up = false;
      game.myplayer.secondJump=true;
    }
  }
}

Pictures:

Video:

Google Drive Folder:

https://drive.google.com/drive/folders/1cQJcHDHnX_z_WFUf1K8c1MLbl8bZIeUl?usp=sharing

Full Code:

import processing.sound.*;

int heightt= 1000;
int widthh = 1400;
PFont f;

class Platforms {

  PVector pos; //mid point
  float pHeight;
  float pWidth;
  float widths;
  float rad;

  Platforms(float x, float y, float w, float h, float r) {
    pos = new PVector(x, y);
    pHeight = h;
    pWidth = w;
    rad = r;
  }

  void display() {
    noStroke();
    rectMode(CORNER);
    fill(30);
    rect(pos.x, pos.y, pWidth, pHeight, rad);
  }
}

class Bg {
  float[] x = new float[50];
  float[] y = new float[50];
  Bg() {
    for (int i=0; i<50; i++) {
      x[i] = random(0, widthh);
      y[i] = random(0, heightt);
    }
  }

  void drawBG() {
    if (frameCount%50==0) {
      for (int i=0; i<50; i++) {
        x[i] = random(0, width);
        y[i] = random(0, height);
      }
    }
    background(50, 50, 80);
    for (int i=0; i<50; i++) {
      for (int j=0; j<10; j++) {
        noStroke();
        fill(255, 25);
        ellipse(x[i], y[i], 1+j, 1+j);
      }
    }
  }
}


public class Creature {

  // Attributes
  PVector pos;
  PVector speed;
  boolean alive;
  float ground;
  PVector size;
  PVector imgSize;
  int numFrames;
  PImage imgs;
  PImage[] img;
  int frame;
  boolean dir; //left = 0 right = 1
  boolean secondJump = false;

  public Creature(float x, float  y, float  r, float  g, PImage _img, float  w, float  h, int  num_frames) {
    pos= new PVector(x, y);
    speed= new PVector(0, 0);
    alive = true;
    ground = height-g;
    numFrames = num_frames;
    imgSize = new PVector(_img.width/num_frames, _img.height);
    size = new PVector (r, imgSize.y*(r/imgSize.x));
    imgs = _img;
    img = new PImage[numFrames];
    for (int i=0; i<numFrames; i++) {
      img[i] = imgs.get(
        int((imgSize.x)*i),
        0,
        int(imgSize.x),
        int(imgSize.y));
    }
    // for (int y=0; y < 4; y++) {
    //   for (int x=0; x< 12; x++) {
    //     sprites[y][x] = spritesheet.get(x*w, y*h, w, h);
    //   }
    // }
    frame = 0;
    dir = true;
  }

  void gravity() {
    if (pos.y<ground-size.y+10  ||pos.y<ground-size.y ) {
      speed.y+=0.2;
    } else {
      speed.y=0;
      pos.y = constrain(pos.y, ground-size.y, ground-size.y+10);
      secondJump=false;
    }
    for (int p=game.plats.length-1; p>=0; p--) {
      if ((pos.y+size.y <=game.plats[p].pos.y+15)//height
        && pos.x+size.x/3>=game.plats[p].pos.x//right
        && pos.x-size.x/3  <= game.plats[p].pos.x+game.plats[p].pWidth) {//left
        ground = game.plats[p].pos.y;
        break;
      } else {
        ground = heightt-game.ground;
      }
    }
  }


  void update() {
    gravity();
    pos.x += speed.x;
    pos.y += speed.y;
  }
  void display() {
    update();
    // println(this.getClass().getName());
    if (dir && alive) {
      scale(1, 1);
      image(img[frame], pos.x, pos.y, size.x, size.y);
    } else if (!dir && alive) {
      if (this.getClass().getName()=="Midterm$Player") {
        pushMatrix();
        scale(-1, 1);
        image(img[frame], -pos.x, pos.y, size.x, size.y);
        popMatrix();
      } else if (this.getClass().getName()=="Midterm$Monster") {
        image(img[frame], pos.x, pos.y, size.x, size.y);
      }
    }
  }
}

class Player extends Creature {
  boolean left;
  boolean up;
  boolean right;
  SoundFile jump;
  int count;
  Player(float x, float  y, float  r, float  g, PImage _img, float  w, float  h, int  num_frames, PApplet sketch) {
    super(x, y, r, g, _img, w, h, num_frames);
    left = false;
    up = false;
    right = false;
    jump = new SoundFile(sketch, "jump.wav");
    count = frameCount;
  }

  void update() {
    gravity();
    //====================
    if (left==true) {
      speed.x=-3;
      dir=false;
    } else if (right==true) {
      speed.x=3;
      dir=true;
    } else {
      speed.x=0;
    }
    //====================
    if (up==true && pos.y>=ground-size.y &&secondJump==false) {
      jump.play();
      speed.y = -6;
    } else if (up==true && speed.y<0 && secondJump==true && pos.y<ground-size.y) {
      jump.play();
      speed.y = -8;
      secondJump=false;
    }
    pos.x +=speed.x;
    pos.y +=speed.y;
    if (frameCount%5 == 0 && speed.x !=0 && speed.y ==0) {
      frame= (frame+1)%numFrames;
    } else if (speed.x==0) {
      frame=8;
    }

    //====================
    if (pos.x<=0) {
      pos.x = 0;
    }
    //====================
    if (pos.x+size.x>= widthh) {
      pos.x = widthh- size.x;
    } else if (pos.x<size.x) {
      pos.x = size.x;
    }
    //====================
    for (int i =0; i<game.myMonsters.length; i++) {
      float numME = pow((pow(size.x, 2)+(pow(size.y, 2))), 0.5);
      float numthem = pow((pow(game.myMonsters[i].size.x, 2)+(pow(game.myMonsters[i].size.y, 2))), 0.5);

      if (game.myMonsters[i].distance(this)<=numME/2.5+numthem/2.5 && game.myMonsters[i].alive) {
        if (speed.y>0) {
          game.myMonsters[i].alive=false;
          game.myMonsters[i].sound2.play();
        } else {
          game.myMonsters[i].sound1.play();
          alive = false;
        }
      }
    }
    for (int i =0; i<game.myQuestions.length; i++) {
      if (game.myQuestions[i].distance(this)<=size.x/2+game.myQuestions[i].size.x/2 && game.myQuestions[i].alive) {
        game.score++;
        game.myQuestions[i].sound.play();
        game.myQuestions[i].alive = false;
      }
    }
    if (game.myfinal.distance(this)<=size.x/2+game.myfinal.size.x/2 && game.myfinal.alive) {
      game.score+=5;
      game.myfinal.sound.play();
      game.myfinal.alive = false;
      game.won= true;
    }
  }
}

class Monster extends Creature {
  SoundFile sound1, sound2;
  float xleft, xright;
  Monster(float x, float  y, float  r, float  g, PImage _img, float  w, float  h, int  num_frames, float x_left, float x_right, PApplet sketch) {
    super(x, y, r, g, _img, w, h, num_frames);

    speed.x=random(0.3,0.9);
    speed.y=0;
    xleft = x_left;
    xright = x_right;
    sound1 = new SoundFile(sketch, "eat.wav");
    sound2 = new SoundFile(sketch, "kill.wav");
  }

  void update() {
    gravity();
    if (pos.x<=xleft) {
      dir = true;
      frame= (frame+1)%numFrames;
      speed.x*=-1;
    } else if (pos.x>=xright) {
      dir = false;
      frame= (frame+1)%numFrames;
      speed.x*=-1;
    }
    pos.x +=speed.x;
    pos.y +=speed.y;
  }



  float distance(Player target) {
    return dist(this.pos.x+size.x/2, this.pos.y+size.y/2, target.pos.x+target.size.x/2, target.pos.y+target.size.y/2);
  }
}

class Question extends Creature {
  SoundFile sound;
  Question(float x, float  y, float  r, float  g, PImage _img, float  w, float  h, int  num_frames, PApplet sketch) {
    super(x, y, r, g, _img, w, h, num_frames);
    speed.x=0;
    speed.y=-2;
    sound = new SoundFile(sketch, "token.mp3");
  }

  void update() {
    gravity();
    if (speed.y==0) {
      speed.y=-2;
    }

    pos.x +=speed.x;
    pos.y +=speed.y;
  }

  float distance(Player target) {
    return dist(this.pos.x, this.pos.y, target.pos.x, target.pos.y);
  }
}


class Game {

  //
  int ground = 100;
  public int score = 0;
  boolean won;

  Player myplayer;
  Platforms[] plats = new Platforms[5];
  Platforms groundd;
  int platLocH[] = new int[5];
  int platLocW[] = new int[5];


  Monster[] myMonsters = new Monster[6];

  Question[] myQuestions = new Question[5];

  Question myfinal;



  Game(PApplet sketch, PImage playerImg, PImage[] monsters, PImage questionIMG, PImage finalIMG) {
    // creating ground
    groundd = new Platforms(0, heightt-ground, widthh, ground, 0);

    // creating platforms
    for (int i=0; i<plats.length; i++) {
      platLocH[i] = (heightt-255)-i*155;
      platLocW[i] = int(random(0, widthh-500));
      plats[i]= new Platforms(platLocW[i], platLocH[i], 600, heightt/25, 15);
    }

    // creating Monsters(1@ground & 4 at plats 0-3)
    int n;
    float m;
    for (int i=4; i>-1; i--) {
      n = int(random(0, 3));
      myMonsters[i]= new Monster(platLocW[i]+60, platLocH[i]-60*(monsters[n].width/monsters[n].height)-10, 60, ground, monsters[n], monsters[n].width, monsters[n].height, 2, platLocW[i]+random(40, 50), platLocW[i]+random(160, 220), sketch);
    }
    n = int(random(0, 3));
    m = random(widthh/2, widthh-450);
    myMonsters[5] = new  Monster(m+60, heightt-ground, 60, ground, monsters[n], monsters[n].width, monsters[n].height, 2, m+random(40, 50), m+random(160, 220), sketch);

    // creating Questions
    for (int i=3; i>-1; i--) {
      myQuestions[i]=new Question(platLocW[i]+400, platLocH[i]-60*(questionIMG.width/questionIMG.height)-50, 60, ground, questionIMG, questionIMG.width, questionIMG.height, 1, sketch);
    }
    myQuestions[4]=new Question(m+400, heightt-ground, 60, ground, questionIMG, questionIMG.width, questionIMG.height, 1, sketch);

    // creating final
    myfinal = new Question(platLocW[4]+400, platLocH[4]-80*(finalIMG.width/finalIMG.height)-50, 80, ground, finalIMG, finalIMG.width, finalIMG.height, 1, sketch);
    myfinal.alive=false;

    // creating Player
    myplayer = new Player(20, height-ground-180, 70, ground, playerImg, playerImg.width, playerImg.height, 9, sketch);
    won = false;
  }




  void main() {
    for (int i = 0; i<5; i++) {
      plats[i].display();
      if (myQuestions[i].alive) {
        myQuestions[i].display();
      }
    }
    groundd.display();
    myplayer.display();
    for (int i=0; i<6; i++) {
      if (myMonsters[i].alive) {
        myMonsters[i].display();
      }
    }
    if (score == myQuestions.length) {
      myfinal.alive = true;
      myfinal.display();
    }
  }
}

void startSc() {
  textFont(f, 80);
  fill(255);
  textAlign(CENTER);
  text("Dead Student", widthh/2, heightt/2-100);
  textFont(f, 40);
  text("press enter to start", widthh/2, heightt/2-10);
  
  textFont(f, 20);
  text("Collect the questions to get to final paper", widthh/2, heightt/2+100);
  text("You can kill monsters by jumping on them", widthh/2, heightt/2+130);
  text("Please use left, right, and up arrows to play", widthh/2, heightt/2+190);
}

void endSc() {
  if (game.won) {
    textFont(f, 40);
    fill(255);
    textAlign(CENTER);
    text("YOU WON!!", widthh/2, heightt/2);
    text("YOUR GRADE IS:", widthh/2, heightt/2+50);
    text(String.valueOf(game.score)+" /10", widthh/2, heightt/2+100);
    text("CONGRATS!!", widthh/2, heightt/2+150);
    textFont(f, 20);
    text("press r to reset", widthh/2, heightt/2+200);
  } else {
    for (int i=0; i<game.myMonsters.length; i++) {
      game.myMonsters[i].alive=true;
    }
    for (int i=0; i<game.myQuestions.length; i++) {
      game.myQuestions[i].alive=true;
    }
    textFont(f, 40);
    fill(255);
    textAlign(CENTER);
    text("YOU DIED", widthh/2, heightt/2);
    text("YOUR GRADE IS:", widthh/2, heightt/2+50);
    text(String.valueOf(game.score)+" /10", widthh/2, heightt/2+100);
    textFont(f, 20);
    text("press r to reset", widthh/2, heightt/2+150);
  }
}

Game game;
PImage playerimg;
PImage[] monsters = new PImage[3];
PImage question, finall, bgImg;
Bg bckgrnd;
boolean started;
SoundFile backgroundSound;

void loadStuff() {
  playerimg = loadImage("playerog.png");
  monsters[0] = loadImage("phone.png");
  monsters[1] = loadImage("anxiety.png");
  monsters[2] = loadImage("thoughts.png");
  question = loadImage("question.png");
  finall = loadImage("final.png");
  bgImg = loadImage("cloud.png");
  backgroundSound = new SoundFile(this, "bg.wav");
}

void setup() {
  size(1400, 1000);
  loadStuff();
  f = createFont("Courier New", 32);
  started = false;
  bckgrnd = new Bg();
}


void draw() {
  background(100);
  bckgrnd.drawBG();
  if (!backgroundSound.isPlaying()) {
    backgroundSound.play();
  }

  if (started) {
    if (game.myplayer.alive && game.won==false) {
      game.main();
      textFont(f, 28);
      fill(255);
      textAlign(LEFT);
      text("Grade: "+String.valueOf(game.score), widthh-200, 100);
    } else {
      endSc();
    }
  } else {
    startSc();
  }
}

void keyPressed() {
  if (keyCode == 10 && started==false) {//enter
    started = true;
    game = new Game(this, playerimg, monsters, question, finall);
  } else if (keyCode==82 && started==true) {//r-> reset
    started=false;
    startSc();
  } else if (started==true && game.won==false) {
    if (keyCode == LEFT) {
      game.myplayer.left = true;
    }
    if (keyCode==RIGHT) {
      game.myplayer.right = true;
    }
    if (keyCode==UP) {
      game.myplayer.up = true;
    }
  }
}

void keyReleased() {
  if (started==true) {
    if (keyCode == LEFT) {
      game.myplayer.left = false;
    }
    if (keyCode==RIGHT) {
      game.myplayer.right = false;
    }
    if (keyCode==UP) {
      game.myplayer.up = false;
      game.myplayer.secondJump=true;
    }
  }
}

 

 

 

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;
    }
  }
}

 

 

Processing Game : STRESS FREE NYUAD?

Overview

Finally, after all the ranting and hours of crying, the midterm game project is complete and it looks and works great, as intended, who am I kidding even. To summarize the game, the theme is around stress struggles as a student. To make it more personalized, the student is an NYUAD falcon. The game design includes free-falling stressful and stress-relieving activities that the player has to avoid and collect respectively while moving across the screen horizontally. It is a race between sanity and stress. The first total to reach 320 points wins. There are four types of free-falling activities that are worth a different number of points and allows the player certain boosted or dulled abilities. The game starts with the main page that allows the player to either start the game directly or navigate to the instructions screen. The game has three different difficulty levels that the player has to choose from. As the game concludes, a game over screen is displayed with results and the option to restart the game with a mouse press.

 

Production

This production process has been by far the most stressful, ironic right. With the code breaking down so many times, I started the initial game design from scratch with circles as the objects and incorporated the movement. See the initial sketch below.

Once the intended movement was achieved, building upon previous progress, I selected my images to use and incorporated them into the previous sketch. Finally, as shown below, the basic design of the game was complete.

The following steps included working more on the game outlook. Putting my artsy skills to use was real fun while coloring the free-falling objects png files. To have a better outlook, I used two images for the player, flipped left and right, which were chosen based on the movement of the player on the screen.

After completing this, the main landing screen, instructions screen, main gameplay screen, and game over screen were redesigned. For this, I used different background images, fonts, and text placements. Like most sketch screens, I wanted to have a faded image background during the main gameplay screen. However, with png moving down the screen, the background would distort, and also the increased loading time of the screen made the movement of objects weird and much less smooth. Therefore, after trying several different patterns and backgrounds, I just stick to a solid background. This came out looking pretty good design and allowing the moving objects’ color to pop on screen. Below are screenshots of the final designs.

Once all of the design was finalized, the last step was to include sound. There were challenges with different sounds playing at a time, ending up in weird noise. So, I finalized a single background sound looped until the game over screen and whenever a selection is made using mouse press or keypress, a notification sound is played. With this, I finalized my game design, and below is a demo

Code for the game is below (+500 lines) and the entire sketch can be downloaded from here

import processing.sound.*;

//Global variables responsible for various states in game
//That includes: difficulty select, playing game, game over
Game start;
Player player;
String state = "MAIN";
String difficulty = "EASY";

//array to store the different points
Activity[] activityarray;

//all the images and fonts for design 
PImage food;
PImage leisure;
PImage deadlines;
PImage grades;
PImage user;
PImage userr;
PImage bdrop;
PImage miss;
PImage bg;
PImage diff;
PImage inst;
PImage keys;
PImage back;
PFont f;
PFont g;

//global variables for location check and size
float speed = 1;
float previouspos = 0;

//soundfile
SoundFile audio;
SoundFile sel;

//setup the game

void setup(){
  
  fullScreen();
  
  //initializing game and player
  start = new Game(state);
  player = new Player();
  
  //loading images
  food =loadImage("data/food.png");
  leisure = loadImage("data/leisure.png");
  deadlines = loadImage("data/deadline.png");
  grades = loadImage("data/test.png");
  user = loadImage("data/user.png");
  userr = loadImage("data/userr.png");
  bg = loadImage("data/bg.jpg");
  diff = loadImage("data/diff.jpg");
  bdrop = loadImage("data/backdrop.jpg");
  inst = loadImage("data/inst.jpg");
  keys = loadImage("data/key.png");
  miss = loadImage("data/stress.png");
  back = loadImage("data/backdrop.jpg");
  back.resize(width,height);
  
  //loading sound
  audio = new SoundFile(this, "data/game.mp3");
  audio.loop();
  sel = new SoundFile(this, "data/select.mp3");
  
  //font
  f = loadFont("FootlightMTLight-55.vlw");
  g = loadFont("JavaneseText-55.vlw");
  
  //diffculty level
  if(difficulty == "EASY"){
    speed = 2;
  }
  if(difficulty == "MEDIUM"){
    speed = 4;
  }
  if(difficulty == "HARD"){
    speed = 8;
  }

  activityarray = new Activity[250];
  
  //initializing the array of activity object
  fillarray();
  
  
}

void draw(){
  //image(back,0,0,width,height);
  start.display();
}

//fill activity array
void fillarray(){
  //choose random x values and drop activities
  for(int i=0; i<activityarray.length; i++){
    float x_pos = random(0,width-30);
    while(x_pos == previouspos){
      x_pos = random(0,width-30);
    }
    previouspos = x_pos;
    float type = random(1,50);
    String activity_ability = "FOOD";
    
    //randomly choosing  ability based on random number
    if(type < 5){
      activity_ability = "LEISURE";
    }
    else if(type > 45){
      activity_ability = "GOODGRADES";
    }
    if(type > 10 && type <=25){
      activity_ability = "DEADLINE";
    }
    
    //create new activity object
    activityarray[i] = new Activity(x_pos,random(-10000,0),speed,activity_ability);
  }
}


//game class
class Game{
  String mode;
  int score = 0;
  int lostpoints = 0;
  float speed = 0;
  
  Game(String state){
    mode = state;
  }
  
  void display(){
    textAlign(CENTER,CENTER);

    //check for game over
    if(lostpoints >= 320 || score >= 320){
      mode = "OVER";
      audio.stop();
    }
    
    //load the main landing page
    if(mode=="MAIN"){
      
      //background gradient
      background(255);
      loadPixels();
      for(int y=0;y<height;y++){
        for(int x=0;x<width; x++){
          int index = x+y*width;
          pixels[index] = color(map(y,0,height,155,0),0,map(y,0,height,255,0),50);
        }
      }
      updatePixels();
      
      //displaying text
      textFont(f,random(90,97));
      fill(150,120);
      text("STRESS FREE NYUAD?",width/2,height/3);
      fill(255);
      textFont(f,90);
      text("STRESS FREE NYUAD?",width/2,height/3);
      textFont(g,30);
      fill(255);
      text("START GAME - G",width/2,2*height/3 - height/14);
      text("INSTRUCTIONS - I",width/2,2*height/3);
      textFont(f,20);
      text("Press corresponding keys to initiate the game",width/2,height-height/14);
    }
    
    //loading the instructions page
    if(mode=="INSTRUCTIONS"){
      fill(0);
      image(inst,0,0,width,height);
      textFont(f,60);
      text("INSTRUCTIONS",width/2,height/10);
      
      //display images and text
      image(keys,width/4-width/7,height/2.5 - height/8,130,60);
      image(food,width/2-130/2,height/2.5 - height/8,130,60);
      image(grades,3*width/4+width/16,height/2.5 - height/7,130,80);
      image(leisure,width/4-width/7,1.75*height/2.5 - height/8,130,60);
      image(deadlines,width/2-130/2,1.75*height/2.5 - height/8,130,60);
      image(miss,3*width/4+width/16,1.75*height/2.5 - height/8,130,60);
      textFont(g,22);
      text("Use arrow L-R keys to\nmove across the screen",width/4-width/10,height/2.5);
      text("Eat healthy to gain +05 pts",width/2,height/2.5);
      text("Perform well on assignment\n to get +10 pts ",3*width/4+width/10,height/2.5);
      text("Do refresing leisure activities\n to get boosted speed",width/4-width/10,1.75*height/2.5);
      text("Avoid deadline pressure\nto skip decreased speed",width/2,1.75*height/2.5);
      text("Missed pts added to stress level\nFirst to reach 320 pts win",3*width/4+width/10,1.75*height/2.5);
      textFont(g,random(32,34));
      fill(random(255),0,0);
      text("START GAME - G",width/2,9*height/10);
    }
    
    
    //displaying the difficulty selection screen
    if(mode=="DIFFICULTY"){
      image(diff,0,0,width,height);
      //turn the text red and create shadow effect when mouse is hovered
      //over the level selection part
      if(mouseX>0 && mouseX<width/3){
        fill(255,0,0);
        textFont(g,54);
        text("EASY - E",width/4-width/10,height/2);
      }
      else if(mouseX>width/3 && mouseX<2*width/3){
        fill(255,0,0);
        textFont(g,54);
        text("MEDIUM - M",width/2,height/2);
      }
      else if(mouseX>2*width/3 && mouseX<width){
        fill(255,0,0);
        textFont(g,54);
        text("HARD - H",3*width/4+width/10,height/2);
      }
      fill(255);
      textFont(g,50);
      text("EASY - E",width/4-width/10,height/2);
      text("MEDIUM - M",width/2,height/2);
      text("HARD - H",3*width/4+width/10,height/2);
      textFont(f,20);
      text("Press corresponding keys to initiate the game",width/2,height-height/14);
      
    }
    
    
   //game over screen
    if(mode=="OVER"){
      //display the background image
      fill(255);
      image(bg,0,0,width,height);
      
      //display the text
      textFont(f,60);
      text("GAME OVER",width/2, height/3);
      textFont(f,35);
      text("Your Score:",width/2 - width/6, height/3 +height/6);
      text("Stress Level:",width/2 - width/6, height/3 + height/4);
      textFont(g,45);
      text(score,width/2, height/3 +height/6);
      text(lostpoints,width/2, height/3 + height/4);
      textFont(f,35);
      text("points",width/2 + width/6, height/3 +height/6);
      text("points",width/2 +width/6, height/3 + height/4);
      
      //display result string based on scores
      textFont(g,45);
      if(score>=lostpoints){
        if(score==lostpoints){
          text("IT'S A TIE", width/2, 2.25*height/3);
        }
        else{
          text("YOU WON", width/2, 2.25*height/3);
        }
      }
      else{
        text("YOU LOST", width/2, 2.25*height/3);
      }
      textFont(f,20);
      text("Please click on screen to restart game",width/2,height-height/14);     
    }
    
    
    //main game screen
    if(mode=="PLAY"){
      
      //background color
      background(back);
      fill(0);
      
      //score board
      rect(width-width/6,height/14.5,width/8,height/13.5);
      fill(255);
      textFont(g,18);
      text("Your:", width-width/7,height/12);
      text("Stress:", width-width/7,height/8);
      textFont(f,random(24,26));
      fill(random(200,255));
      text(score,width-width/10,height/12);
      text(lostpoints,width-width/10,height/8);
      fill(255);
      textFont(g,18);
      text("points", width-width/16.5,height/12);
      text("points", width-width/16.5,height/8);
      
      //main display player
      player.display();
      
      //display activities
      for(int i =0; i< activityarray.length;i++)
      {
        activityarray[i].display();
        if(activityarray[i].yloc > height){
          lostpoints += activityarray[i].point;
          activityarray[i].point = 0;
        }
        
        //resize the image upon collision to have effect
        //of collecting the activity 
        if(activityarray[i].collisions() == true){
          score += activityarray[i].point;
          activityarray[i].awidth = 0;
          activityarray[i].aheight = 0;
          activityarray[i].point =0;
        }
      }
    }
  } 
}


//player class
class Player{
  float pwidth;
  float pheight;
  float xPos;
  float yPos;
  boolean left;
  boolean right;
  float speed;
  float fast_time;
  float slow_time;
  
  Player(){
    pwidth= 100;
    pheight = 100;
    xPos = width/2 - pwidth;
    yPos = height - pheight;
    left = false;
    right = false;
    speed = 7;
    fast_time = 0;
    slow_time = 0;
  }
  
  void display(){
    
    //tracking the time when boosted speed
    if(speed == 12){
      fast_time += 1;
      //last 100 frames
      if(fast_time == 100){
        fast_time = 0;
        speed = 7;
      }
    }
    
    //tracking the time when slowed speed
    if(speed == 1){
      slow_time += 1;
      //last 100 frames
      if(slow_time == 100){
        slow_time = 0;
        speed = 7;
      }
    }
    
    //update the position on screen
    update();
    
    //draw the player
    if(left==true){
      image(user,xPos,yPos,pwidth,pheight);
    }
    else if(right==true){
      image(userr,xPos,yPos,pwidth,pheight);
    }
    else{
      image(userr,xPos,yPos,pwidth,pheight);
    }
  }
  
  //update the position of the player
  void update(){
    if(left==true && xPos >=0){
      xPos -= speed;
    }
    if(right==true && xPos <= width-pwidth){
      xPos += speed;
    }
  } 
}


//Class of falling activities/ points
class Activity{
  float awidth = 60;
  float aheight = 60;
  //coordinates
  float yloc;
  float xloc;
  float speed;
  String ability;
  //standard point
  int point = 5;
  //image
  PImage activityimg;
  
  Activity(float xpos, float y,float s, String a){
    xloc = xpos;
    speed= s;
    yloc = y;
    ability = a;
    
    //updating point values and image based on type
    if(ability == "GOODGRADES"){
      activityimg = grades;
      point = 10;
    }
    else if(ability == "LEISURE"){
      activityimg = leisure;
    }
    else if(ability == "DEADLINE"){
      point = 0;
      activityimg = deadlines;
    }
    else{
      point =5;
      activityimg = food;
    }
  }
  
  //display the activity object
  void display(){
    update();
    image(activityimg,xloc,yloc,awidth,aheight);
  }
    
  //update the locations
  void update(){
    //move down
    yloc += speed;
  }
    
  //check for collisions
  boolean collisions(){
    if((player.xPos + player.pwidth >= xloc) && (player.xPos <= xloc + awidth)){
      if((yloc + aheight >= player.yPos) && (yloc <= player.pheight + player.yPos)){
        
        //check if it collides with special activity and update speed accordingly
        if(ability == "LEISURE"){
          player.speed = 12;
        }
        if(ability == "DEADLINE"){
          player.speed = 1;
        }
        return true;
      }
    }
    return false;
  }
}


//keep track of key presses on screen
void keyPressed(){
  if(start.mode == "MAIN"){
    if(keyCode == 73){      //73 = 'i'
      sel.play();
      start.mode = "INSTRUCTIONS";
    }
    if(keyCode == 71){        //71 = 'g'
      sel.play();
      start.mode = "DIFFICULTY";
    }
  }
  
  if(start.mode == "INSTRUCTIONS"){
    if(keyCode == 71){        //71 = 'g'
      sel.play();
      start.mode = "DIFFICULTY";
    }
  }
  
  if(start.mode == "DIFFICULTY"){
    if(keyCode == 69){        //71 = 'e'
      sel.play();
      difficulty = "EASY";
      start.mode = "PLAY";
    }
    if(keyCode == 77){        //71 = 'm'
      sel.play();
      difficulty = "MEDIUM";
      start.mode = "PLAY";
    }
    if(keyCode == 72){        //71 = 'h'
      sel.play();
      difficulty = "HARD";
      start.mode = "PLAY";
    }
  }
  
  //move until key pressed
  if(start.mode=="PLAY"){
    if(keyCode == RIGHT){
      player.right = true;
    }
    if(keyCode == LEFT){
      player.left = true;
    }
  }

}


//stop motion when key is released
void keyReleased(){
  if(start.mode == "PLAY"){
    if(keyCode == RIGHT){
      player.right = false;
    }
    if(keyCode == LEFT){
      player.left = false;
    }
  }
}


//replay the game
void mouseClicked(){
  if(start.mode=="OVER"){
    sel.play();
    player.left = false;
    player.right = false;
    fillarray();
    background(0);
    start = new Game("MAIN");
    audio.play();
  }
}

 

Midterm – Adventures of Kathakali and Theyyam

The game that I made for the midterm project was definitely a product of a lot of ideas and concepts that were discussed in the class – not just pertaining to the environment of processing itself. As a CS major, I had done a bit of Processing for my Intro to CS class, but this time around, I approached making a game on Processing differently – and the biggest difference was I was actively thinking about whether the game was designed to make sense to a player – whereas back then I was concentrating on achieving functionality.

My game, in a nutshell, can be thought of as snake and ladders but instead of the snakes and ladders, there are cards that describe the number of steps you move back and forth. The theme I chose was my state in India – Kerala and it can be seen in the tokens – Theyyam and Kathakali (both of which are art forms in Kerala) and the cards. The cards are things that are related to Kerala. Here are the cards:

Implementation

 

I took a step-by-step approach and broke down the game into smaller units and then tied them together. In the last week, I worked on the rolling die, the grid, and the tokens. This week, I started on improving the design of the grid. The one I had before was a grid with 121 squares starting on the top left and ending on the bottom right. I wasn’t satisfied with these specifications – the idea of reaching a 100 had more of a victorious effect than 121. Also, traditionally, most of these games begin at the bottom left and end on the top left. I tried playing through a game with the original specifications, but knowledge of previous games made it hard to play, so I decided to match the specifications. Moreover, the numbers were displayed in n order, so when the next line comes, it starts from the left again, but with the movement of the tokens, this path is very abrupt and cut off.  Making these changes was very confusing, especially alternating between going from left to right and then vice-versa, but finally, I used the (%2==0) condition and alternated to get the left to right and right to left display. Also, I changed the colors of the squares from just black and alternated between shades of black, to provide more definition to the individual squares. 

Then, I worked on picking random squares on each row to have a colored square that would represent a card. This was very easy to implement, and I made an array of colors and randomized which color to pick for each game from this array, and the position of the squares is random for each game as well.

After this, I worked on moving the tokens and recording what the die returned on rolling. Further, I had to add the functionality of moving when arriving on a colored square and then showing the card when the token arrives at the colored square. This was definitely the hardest part to figure out – implementation-wise and design-wise. I went through a lot of ways to implement this and figuring out how the token should move, when should it move, should there be triggers, etc. I picked the version that seemed the simplest of the bunch.  The player rolls the die and the token moves according to the die number, and this is automatic. If they happen to arrive at a colored square, the extra steps are also accounted for in the movement of the token, and the card is displayed on the side to make sense of the movement. In order to understand whose turn it is, the token is displayed on the side of whose turn it is. 

Rolling of the die and coordinating that with the turns of the user was confusing. I had a global variable to keep track of the turns. Figuring out the logic behind it took some time though. Also, I found out that the number of steps the tokens take the first time around is always -1 the number of steps they are supposed to take, so I made an if condition to check if it is the first time they are moving and take that into account as well. The explanation I have for it is that the number displayed starts at 1 and the internal number starts at 0, and I created the program handling both, so I changed that. Finally, the player gets to move, only when a 1 or 6 comes up, so I had a global variable to indicate whether they are on the board yet. 

Then, I designed the beginning and end slides of the game which was fairly easy to implement. 

Welcome page + Instructions
Exit Page

The functionality of the restart button took some time to figure out, the core of the issue was that many of my global variables needed to be reinitialized too, not just those in the setup(). But once I figured that out, all was done. The final addition was the sound! I added an evil laugh effect every time the player enters a colored square that makes them move back and a “woohoo” effect for when they progressed. I added some instructions on the game board as well, just in case the player chooses to skip the instructions on the welcome slide, they’ll not be completely confused during the game. A lot of the things I incorporated in the game design-wise were from feedback from family and friends playing the game. They noticed things that seemed very obvious to me (for example, you have to double click on the dice to stop it). So, yeah, it was a cool learning experience. 

Here’s a demo of the game

 

//sound
import processing.sound.*;
SoundFile[] soundFiles = new SoundFile[2]; 

//images
PImage kathakali, kToken, kathakaliWon;
PImage theyyam, tToken, theyyamWon;
PImage power, filter, daagini, mosquito, dosa, coconut, houseboat, mahabali, traffic, sadya;
PImage welcome;


Rect rect[];
int size = 82;
int diceX = (100*width/8)+25;
int diceY =(60*height/8)+10;
int diceSize = 90;
boolean toggleRun=false;
boolean gameMode = false;
int yOffset = 35;
int xOffset = 40;

//color palette
color c1=#ffc097;
color c2=#ffee93;
color c3=#fcf5c7;
color c4=#aeced9;
color c5=#adf7b6;
int die = 0;
color[] palette = {c1, c2, c3, c4, c5};


//game logisitics
int turn=-1;
int token=0;
Token token1, token2;
boolean card=false;
int cardButton=0;
int start1;
int start2;
boolean celebrate = false;
boolean start=false;

void setup() {
  fullScreen();

  soundFiles[0] = new SoundFile(this, "evil.wav");
  soundFiles[1] = new SoundFile(this, "woohoo.wav");
  theyyam = loadImage("theyyam.png");
  tToken = loadImage("theyyam.png");
  theyyam.resize(220, 260);
  tToken.resize(88, 104);
  theyyamWon = loadImage("theyyamWon.jpg");


  kathakali = loadImage("kathakali.png");
  kToken  = loadImage("kathakali.png");
  kathakali.resize(180, 224);
  kToken.resize(72, 90);
  kathakaliWon = loadImage("kathakaliWon.jpg");
  
  
  
  

  power = loadImage("power.jpg");
  filter = loadImage("filter.jpg");
  daagini = loadImage("daagini.jpg");
  mosquito = loadImage("mosquito.jpg");
  dosa = loadImage("dosa.jpg");
  coconut = loadImage("coconut.jpg");
  houseboat = loadImage("houseboat.jpg");
  mahabali = loadImage("mahabali.jpg");
  traffic = loadImage("traffic.jpg");
  sadya = loadImage("sadya.jpg");

  welcome = loadImage("WELCOME.jpg");

  background(0);
  image(welcome, 0, 40);
}

void draw() {



  if (start)
  {
    
    //when restart, show image of dice, so that player can click on it to begin game
    if (celebrate)
    {
      fill(#FFF3D6);
      rectMode(CENTER);
      rect(diceX, diceY, diceSize, diceSize, diceSize/5);

      //dots
      fill(50);


      ellipse(diceX, diceY, diceSize/5, diceSize/5);
      rectMode(CORNER);
      celebrate=false;
    }


    if (toggleRun)
    {
      die=dice();
    }


    for (int j=0; j<rect.length; j++) {
      rect[j].display();
    }



    if (token1.mode)
    {
      token1.display();
    }
    if (token2.mode)
    {
      token2.display();
    }






    if (((die==1) || (die ==6)) && ((!toggleRun)))
    {
      

      if (token==0 && !token1.mode)
      {

        token1.start();
      }
      if (token==1 && !token2.mode)
      {
        token2.start();
      }
    }




    fill(0);
    noStroke();
    rect(0, 0, 285, 800);
    fill(0);
    noStroke();
    rect(diceX-diceSize, diceY-diceSize, 200, 30);
    fill(255);

    text("Double click on the dice to roll it", diceX-diceSize, diceY-diceSize+20);

    text("Goal: Reach Square 100", 60, 600);
    text("Roll die and move accordingly, movement", 20, 620);
    text(" changes if there's a card involved on the square ", 0, 640);
    text("you land.", 100, 660);
    text("Each player needs to roll a one or a six", 30, 300);
    text("to begin their game ", 80, 320);

    if (turn==-1)
      image(kathakali, 35, height/2-100);
    else
    {
      if (token==0)
        image(theyyam, 25, height/2-100);
      else
        image(kathakali, 35, height/2-100);
    }

    if (token1.num>=99)
    {

      token1.celebrate();
      celebrate = true;
    }

    if (token2.num>=99)
    {

      token2.celebrate();
      celebrate = true;
    }
  }
}

//grid print
void grid() {
  int xlen = (width)/size ;
  int ylen = (height)/size;
  rect = new Rect[(xlen-7)*(ylen)];

  int i=99;
  color clr;


  int card;
  int cardNum=0;
  for (int y=0; y < ylen; y++) {
    cardNum++;
    int rand = int(random(4, xlen-5));
    if (y%2==0)
    {

      for (int x =3; x < xlen-4; x++) {
        card=0;
        PVector p = new PVector(x*size+xOffset, y*size+yOffset);
        int j = y%2;
        if ((i+j)%2==0)
          clr=color(40);
        else
          clr=color(0);
        if (x==rand)
        {
          clr=palette[int(random(5))];

          //clr=color(247,221,123);
          card=1;
        }
        rect[i] = new Rect(p, size, i, clr, card, cardNum);

        i--;
      }
    } else
    {
      for (int x =xlen-4; x >3; x--) {
        card=0;
        PVector p = new PVector(x*size+xOffset-size, y*size+yOffset);
        int j = y%2;
        if ((i+j)%2!=0)
          clr=color(40);
        else
          clr=color(0);
        if (x==rand)
        {

          clr=palette[int(random(5))];

          //clr=color(247,221,123);
          card=1;
        }
        rect[i] = new Rect(p, size, i, clr, card, cardNum);
        i--;
      }
    }
  }
}






int dice() {

  fill(255);

  fill(#FFF3D6);
  rectMode(CENTER);
  rect(diceX, diceY, diceSize, diceSize, diceSize/5);

  //dots
  fill(50);
  int side = int(random(1, 7));
  if (side == 1 || side == 3 || side == 5)
    ellipse(diceX, diceY, diceSize/5, diceSize/5);
  if (side == 2 || side == 3 || side == 4 || side == 5 || side == 6) {
    ellipse(diceX - diceSize/4, diceY - diceSize/4, diceSize/5, diceSize/5);
    ellipse(diceX+ diceSize/4, diceY + diceSize/4, diceSize/5, diceSize/5);
  }
  if (side == 4 || side == 5 || side == 6) {

    ellipse(diceX - diceSize/4, diceY + diceSize/4, diceSize/5, diceSize/5);
    ellipse(diceX + diceSize/4, diceY- diceSize/4, diceSize/5, diceSize/5);
  }
  if (side == 6) {
    ellipse(diceX, diceY- diceSize/4, diceSize/5, diceSize/5);
    ellipse(diceX, diceY + diceSize/4, diceSize/5, diceSize/5);
  }
  rectMode(CORNER);




  return side;
}

void mousePressed() {
  color red = color(255, 0, 0);
  color yellow = color(255, 255, 0);
  if (start) {
    if (start1!=-1||start2!=-1)
    {
      fill(0);
      rect(1160, 450, 250, 70, 50 );
    }
    PVector p = new PVector(0, 0);
    if (mouseX>(diceX)-(diceSize/2) && mouseX<(diceX)+(diceSize/2) && mouseY>(diceY)-(diceSize/2) && mouseY<(diceY)+(diceSize/2 ))
    {

      toggleRun=!toggleRun;
      turn+=1;
      fill(0);
      rect(1160, 100, 281, 450);
      if ((turn%2==0))
      {
        token=1-token;
      } else
      {
        if (token==1 && token2.mode)
        {
          if (token2.num>99) {
            token2.num=99;
          }
          if (token2.num<0) {
            token2.num=0;
          }
          token2.num +=die;
          for (int i=0; i<rect.length; i++)
          {
            if (rect[i].num==token2.num)
            {
              if (start1<0)
              {
                p = rect[i-1].position;
                token2.move(p);

                token2.num=rect[i-1].num;
                if (rect[i-1].card==1)
                {
                  showCards(rect[i-1].cardNum, yellow, token2);
                  cardButton=2;
                }
                break;
              }
              p = rect[i].position;
              token2.move(p);

              if (rect[i].card==1)
              {
                showCards(rect[i].cardNum, yellow, token2);
                cardButton=2;
              }

              break;
            }
          }


          start1++;
        }


        if (token==0 && token1.mode)
        {

          token1.num +=die;
          if (token1.num>99) {
            token1.num=99;
          }
          if (token1.num<0) {
            token1.num=0;
          }

          for (int i=0; i<rect.length; i++)
          {
            if (rect[i].num==token1.num)
            {
              if (start2<0)
              {
                p = rect[i-1].position;
                token1.move(p);
                token1.num=rect[i-1].num;
                if (rect[i-1].card==1)
                {

                  showCards(rect[i-1].cardNum, red, token1);
                  cardButton=1;
                }

                break;
              }
              p = rect[i].position;
              token1.move(p);
              if (rect[i].card==1)
              {
                showCards(rect[i-1].cardNum, red, token1);
                cardButton = 1;
              }

              break;
            }
          }

          start2++;
        }
      }
    }

    if ((token2.num>=99||token1.num>=99) && celebrate)
    {
      //reset
      if (mouseX>360 && mouseX<660 && mouseY>459 && mouseY<542)
      {
        restart();
      }

      //exit
      if (mouseX>719 && mouseX<982 && mouseY>459 && mouseY<542)
      {
        exit();
      }
    }
  }
  if (!start)
  {
    if (mouseX>121 && mouseX<1319 && mouseY>742 && mouseY<782)
    {
      start=true;
      restart();
    }
  }
}



void showCards(int cardNum, color c, Token token)
{

  PVector p = new PVector(0, 0);
  switch(cardNum)
  {
  case 1:
    fill(c);
    token.num -=5;
    soundFiles[0].play();
    image(power, 1160, 100);
    break;
  case 2:
    fill(c);
    token.num +=8;
    soundFiles[1].play();
    image(filter, 1160, 100);
    break;
  case 3:
    fill(c);
    token.num -=8;
    image(daagini, 1160, 100);
    soundFiles[0].play();
    break;
  case 4:
    fill(c);
    token.num -=3;
    image(mosquito, 1160, 100);
    soundFiles[0].play();
    break;
  case 5:
    fill(c);
    token.num +=10;
    soundFiles[1].play();

    image(dosa, 1160, 100);
    break;
  case 6:
    fill(c);
    token.num -=10;
    image(coconut, 1160, 100);
    soundFiles[0].play();
    break;
  case 7:
    fill(c);
    token.num +=12;
    soundFiles[1].play();
    image(houseboat, 1160, 100);
    break;
  case 8:
    fill(c);
    token.num +=3;
    soundFiles[1].play();

    image(mahabali, 1160, 100);
    break;
  case 9:
    fill(c);
    token.num -=3;
    image(traffic, 1160, 100);
    soundFiles[0].play();
    break;
  case 10:
    token.num +=5;
    soundFiles[1].play();

    image(sadya, 1160, 100);
    break;
  }


  if (token.num>99) {
    token.num=99;
  }
  if (token.num<0) {
    token.num=0;
  }


  for (int i=0; i<rect.length; i++)
  {
    if (rect[i].num==token.num)
    {

      token.position = rect[i].position;


      break;
    }
  }


 

  
}

void restart()
{
  background(0);



  toggleRun=false;
  gameMode = false;

  die = 0;
  turn=-1;
  token=0;
  card=false;
  cardButton=0;
  start1=-1;
  start2=-1;

  token1 = new Token(tToken, theyyam, theyyamWon);
  token2 = new Token(kToken, kathakali, kathakaliWon);

  grid();

  dice();


}

Disclaimer: The components of the art used in this game are done by other artists and some of them by me, I worked on them on illustrator for the game. 

Credits:

Kathakali – Sonali Meshram

Theyyam – Sinu Rajendran

Comics (Maayavi) – Pradeep Sathe, M. Mohandas, Thyagarajan

Midterm Project: Catching Fallen Stars (Taiwanese Version)

Catching fallen stars(Taiwanese Version)

INSPIRATION:

There is a Taiwanese myth about catching fallen stars to make a wish come true. I intend to create a game that consists of a traditional Taiwanese bamboo woven basket and white stars falling from the night sky.

RULES:

The way to win is to collect 66 stars (white). The players would lose one life if hit by fallen satellite remains (red). They have three lives in total and loses the game when all three lives are wasted. However, players can catch Taiwanese street food (green) to restore lost lives.

OBJECTS:
  • Background: import an image of the Taiwanese famous night sky of lanterns
  • Sound: Background music – summer night time in Taiwanese mountains
  • Object behavior:
    1. Stars: falling with medium speed
    2. Satellite: falling with extremely fast speed
    3. Food: falling with extremely low speed
STORYBOARD:
  1. MENU (START)
  2. STORY
  3. RULES
  4. GAME
  5. Restart
    1. win
    2. lose
CHALLENGES: 
    1. The hardest part was the collision detection. It was hard to wrap my head around the logic. For the satellite remains, player lives should have decrement one when one satellite hits, but for some reason, perhaps the boundary setting or the collision content, player lives decrease ridiculously fast. However, sometimes it works perfectly. I am still in the process of figuring it out.
    2. Slight mistake: not clarifying how to play the game (what key to press)
CODES:
1. Main Function
PImage sky;
PImage stinkyTofu;
PImage sausage;
PImage oysterOmlette; 

//another variable name to create another object
Fall [] fallStars;
Fall [] fallGood;
Fall [] fallBad;

import processing.sound.*;
SoundFile bgMusic;
//SoundFile winMusic;
//SoundFile loseMusic;

//setup the basket
int stage;
float basketWidth = 100;
float basketHeight = 50;
float X = 384;
float Y = 723;
color basketColor = color(188, 123, 25);
int lives = 3;
int stars = 0;
boolean goLeft = false;
boolean goRight = false;


void setup() {
  size (1024, 768);
  
  bgMusic = new SoundFile (this, "/Users/chi-tingtsai/Downloads/summer night.mp3");
  bgMusic.loop();
  //winMusic = new SoundFile (this, "/Users/chi-tingtsai/Documents/Processing/Intro_to_IM_Midterm_ver2/data/win.mp3");
  //loseMusic = new SoundFile (this, "/Users/chi-tingtsai/Documents/Processing/Intro_to_IM_Midterm_ver2/data/lose.mp3");
  stage = 0;
  
  //setup the falling objects
  fallStars = new Fall[10];
  for (int k=0; k<fallStars.length; k++) {
    fallStars[k] = new Fall(random(width), 0, random(2, 7), color(255));
  }
  fallGood = new Fall[5];
  for (int i=0; i<fallGood.length; i++) {
    fallGood[i] = new Fall(random(width), 0, random(1, 5), color(94, 240, 134));
  }
  fallBad = new Fall[15];
  for (int l=0; l<fallBad.length; l++) {
    fallBad[l] = new Fall(random(width), 0, random(6, 10), color(234, 83, 131));
  }
}

//EXECUTION
void draw () {
  if (stage == 0) {
    menu();
  }
  if (stage == 3) {
    playGame();
  }
}

void keyReleased() {
  goLeft = false;
  goRight = false;
  if (key == ' ' && stage == 0) {
    stage = 1;
    story();
  } else if (key == ' ' && stage == 1) {
    stage = 2;
    rules();
  } else if (key == ' ' && stage == 2) {
    stage = 3;
  } else if (key == ' ' && stage == 3) {
    for (int k=0; k<fallStars.length; k++) {
      fallStars[k].restart();
    }

    for (int i=0; i<fallGood.length; i++) {
      fallGood[i].restart();
    }

    for (int l=0; l<fallBad.length; l++) {
      fallBad[l].restart();
    }
  }
}
2. MENU FUNCTION:
color frame = color(237, 168, 17);

void menu() {
  background(color(99, 144, 130));
  fill(255);
  textAlign(CENTER);
  PFont F = createFont("PatuaOne-Regular", 1);

  //explain how to start
  textFont(F, 30);
  text("Press Space Bar to Begin Game", width/2, height*3/4);
  //explain this game
  textFont(F, 50);
  text("CATCHING FALLEN STARS\n(TAIWANESE VERSION)", width/2, height/2-50);

  //frame
  noStroke();
  rectMode(CENTER);
  fill(frame);
  rect(width/2, 10, width, 20);
  rect(width/2, height-10, width, 20);
  rect(10, height/2, 20, height);
  rect(width-10, height/2, 20, height);
}
3. STORY:
int rectWidth = width/10;
int rectHeight = height/10;
float bg, up, mid, down;

void story() {
  background(color(99, 144, 130));
  fill(255);
  textAlign(CENTER);
  PFont F = createFont("PatuaOne-Regular", 1);

  //explain how to start
  textFont(F, 50);
  text("STORY", width/2, height/4);
  textFont(F, 30);
  text("A long time ago,\nDeep in the mountains of Yu and Ali,\nThere was a lake called Sun Moon Lake.\nRumor has it that if you collected enough fallen stars\nDuring Mid Autumn Festival,\nYour fears will disipate,\nInsecurities will calm,\nAnd peace will shine on you.", width/2, height/2-120);

  //frame
  noStroke();
  rectMode(CENTER);
  fill(frame);
  rect(width/2, 10, width, 20);
  rect(width/2, height-10, width, 20);
  rect(10, height/2, 20, height);
  rect(width-10, height/2, 20, height);

  //Next to Rules
  textFont(F, 40);
  text("Press Space Bar to Continue", width/2, height*3/4+75);
}
4. RULES:
int size = 200;
void rules() {
  stinkyTofu = loadImage("stinkytofu.jpeg");
  stinkyTofu.resize(size, size);
  sausage = loadImage("sausage.jpeg");
  sausage.resize(size, size);
  oysterOmlette = loadImage("oysteromlette.jpeg");
  oysterOmlette.resize(size-30, size-30);

  background(color(99, 144, 130));
  fill(255);
  textAlign(CENTER);
  PFont F = createFont("PatuaOne-Regular", 1);

  //display images of food
  image(stinkyTofu, 30, 30);
  image(sausage, 784, 30);
  image(oysterOmlette, width-200, height - 200);

  //explain how to play
  textFont(F, 50);
  text("RULES", width/2, height/4);
  textFont(F, 30);
  text("The way to win is to catch 66 stars(white)\nwith you bamboo woven basket\nYou have three lives in total.\nYou lose one life if hit by a satellite (red).\nYou lost the game when all lives are wasted.\nHowever, you can catch Taiwanese street food(green)\nto restore lost lives.", width/2, height/2-60);

  //frame
  noStroke();
  rectMode(CENTER);
  fill(frame);
  rect(width/2, 10, width, 20);
  rect(width/2, height-10, width, 20);
  rect(10, height/2, 20, height);
  rect(width-10, height/2, 20, height);
}
5. PLAY GAME:
void playGame() {
  sky = loadImage("pinxi sky lantern.jpeg");
  sky.resize(width, height);
  PFont F = createFont("PatuaOne-Regular", 1);
  noStroke();

  //background
  background (255);
  image (sky, 0, 0);

  //objects start falling
  for (int k=0; k<fallStars.length; k++) {
    fallStars[k].runFall();
  }

  for (int i=0; i<fallGood.length; i++) {
    fallGood[i].runFall();
  }

  for (int l=0; l<fallBad.length; l++) {
    fallBad[l].runFall();
  }

  //different kinds of collision
  for (int i=0; i<fallStars.length; i++) {
    fallStars[i].starcollision();
  }

  for (int k=0; k<fallGood.length; k++) {
    fallGood[k].goodcollision();
  }

  for (int l=0; l<fallBad.length; l++) {
    fallBad[l].badcollision();
  }

  //player basket
  rectMode(CENTER);
  fill(basketColor);
  rect(X, Y, basketWidth, basketHeight);
  if (goLeft == true) {
    X -= 15;
  }
  if (goRight == true) {
    X += 15;
  }

  if (stars == 66 && stage == 3) {
    background(color(99, 144, 130));
    textFont(F, 80);
    fill(255);
    text("Congratulations! You have done it!", width/2, height/2);
    textFont(F, 50);
    text("Have you found your peace?\nOr even more chaotic?\nhehe)", width/2, height/2-50);
    textFont(F, 30);
    text("Press Space Bar to Restart", width/2, height/2+50);
  }

  if (lives == 0 && stage == 3) {
    background(color(99, 144, 130));
    textFont(F, 80);
    fill(255);
    text("Game Over...", width/2, height/2);
    textFont(F, 50);
    text("Awwww... Press Space Bar to Restart?", width/2, height/2+50);
  }

  //show lives to player
  textSize(24);
  fill(color(90, 167, 247));
  text("LIVES:", width -120, height - 50);
  text(lives, width - 60, height - 50);

  //show lives to player
  fill(color(255));
  text("STARS:", 90, 60);
  text(stars, 160, 60);

  //frame
  noStroke();
  rectMode(CENTER);
  fill(frame);
  rect(width/2, 10, width, 20);
  rect(width/2, height-10, width, 20);
  rect(10, height/2, 20, height);
  rect(width-10, height/2, 20, height);
}

void keyPressed() {
  if (keyCode == LEFT) {
    goLeft = true;
  } 
  else if (keyCode == RIGHT) {
    goRight = true;
  }
  if (X <= basketWidth/2 + 10) {
    goLeft = false;
    X = basketWidth/2;
  }
  if (X >= width - basketWidth/2 - 10) {
    goRight = false;
    X = width - basketWidth/2;
  }
}
6. CLASS for Fallen Objects:
//VARIABLE
boolean goodtouch = false;
boolean badtouch = false;
boolean startouch = false;

class Fall {
  float posX, posY;
  float objectWidth;
  float objectHeight;
  color fallColor;
  float speed;

  //CONSTRUCTER
  Fall(float _posX, float _posY, float _speed, color _colorFall) {
    posX = _posX;
    posY = _posY;
    objectWidth = objectHeight = 20;
    fallColor = _colorFall;
    speed = _speed;
  }

  //FUNCTIONALITY
  void runFall() {
    colorFall();
    beginFall();
  }

  void colorFall() {
    fill(fallColor);
    noStroke();
    rect(posX, posY, objectWidth, objectHeight);
  }

  void beginFall() {
    posY += speed;
    if (posY > height+objectHeight/2) {
      posY = -objectHeight/2;
      reset();
    }
  }

  void reset() {
    posX = random(0, width);
  }

  void goodcollision() {
    if (posY + (objectHeight+basketHeight)/2 > Y && posY - (objectHeight+basketHeight)/2 < Y &&
      posX - (objectHeight+basketHeight)/2 < X && posX + (objectHeight+basketHeight)/2 > X && !goodtouch) {
      goodtouch = true;
      lives ++;
    }
    if (posY > height && goodtouch ==true) {
      goodtouch = false;
    }
    if (lives > 3) {
      lives = 3;
    }
  }

  void badcollision() {
    if (posY + (objectHeight+basketHeight)/2 > Y && posY - (objectHeight+basketHeight)/2 < Y
      && posX - (objectHeight+basketHeight)/2 < X && posX + (objectHeight+basketHeight)/2 > X && !badtouch) {
      badtouch =true;
      lives --;
    }
    if (posY > height && badtouch ==true) {
      badtouch = false;
    }
    if (lives < 0) {
      lives = 0;
    }
  }

  void starcollision() {
    if (posY + (objectHeight+basketHeight)/2 > Y && posY - (objectHeight+basketHeight)/2 < Y
      && posX - (objectHeight+basketHeight)/2 < X && posX + (objectHeight+basketHeight)/2 > X && !startouch) {
      startouch = true;
      stars ++;
    }
    if (posY > height && startouch ==true) {
      startouch = false;
    }
  }
  
  void restart() {
    stage = 3;
    lives = 3;
    stars = 0;
    posY = 0;
  }
}

 

UNI RUSH- Shamma’s Midterm Project

UNI RUSH

INTRODUCTION & GAME RULES:

Uni Rush is an innovative idea for gaming in processing which have sketched the idea of a student escaping the reality of university course grades. The basic logic behind it is that the student character is moving in all four directions and the grade letters are coming from the right side of the screen. When a grade passes behind the character, the score increases. However, if the character moves out of the screen or touches any grade, whether bad or good, then it is game over. For the character movements, conventional gaming keyboard keys were used: w for up, a for left, s for down, and d for right.

INSPIRATION BEHIND UNI RUSH:

The inspiration behind the game I wanted to create comes from how overprotective I tend to be with my GPA. Since I was a kid, I always would work to achieve the highest grades and even the slightest deduction of grades would make me cry. However, after I came to university, that reality changed, and it’s been like a roller-coaster with all its ups and downs. The game resembles a university student who is running all over to escape the sad reality of university grades. Based on that, the more grades the character is able to escape , the higher the score is.  I have always been keen to understand how gpa calculation works, so I searched it up and decided to base my game on that. Yet, this did not work and the fun of it made me come up with a new game logic, closer to reality.

CHALLENGES & PROBLEMS:

The very first and foremost challenge I have faced was rendering the grades continuously on the screen, in addition to its movement within the specified speed. This has put me in chaos, as I felt that my code is all over the place. A second  difficulty was during the movement of the character. Third, the management of frames and the glitches found during frame rendering. Last, but not least, playing the sound based on the movement of the character and rendering the grade letters as a series of image was an obstacle.

OBJECTS USED :

1- Grade letters

The grade letter objects are moving images of all grade letters 

2- Student Character

This is an NYU Mascot cartoon character given a bigger size as compared to the grade letters and is able to move left- right & up-down based on the key pressing

3- Game Background

This object is a university campus image 

4- GPA Score Prompt

This object is a simple score text prompt which gets added as the character escapes the grade letters.

PROCEDURE:

I have started my game by implementing the player character class. This class was responsible for the actions taken by the character. Later, I have implemented attributes and methods in that class that helps with the movement of the character in all four directions. The rendering of the character is also implemented in that class. I performed the default constructor for the  character class which takes x and y positions of the character.

The die function implemented to check if the player character moves out of the screen.
//function to check if the player character is out screen which means the character died
  //stopping the game background sound and playing main menu sound
  void die() 
  {
  
    this.xPos = -500;
    this.yPos = -500;
    this.CharacterDropped = true;
    BackSoundEffect.stop();
    SkipGradeLetterSound.play();
    MainMenuSoundEffect.play();
  
  }
The player character is moving based on the key pressed events which work on the specified keys for the movement of the player character in upward, downward, left, or right direction.
void keyPressed() 
{

  //If the key is 'w' then moving the character upward
  if(key == 'w') Character.move(0, -1);
  //if the key is 'a' then moving the character left
  if(key == 'a') Character.move(-1, 0);
  //if the key is s then moving the character down
  if(key == 's') Character.move(0, 1);
  //if the key is 'd' then moving the character right
  if(key == 'd') Character.move(1, 0);

  //if the key is 'r' then setting the character to its default state and postion
  if(key == 'r' || Character.CharacterDropped)
  {
    //stop playing background sounds
    BackSoundEffect.stop();
    MainMenuSoundEffect.stop();
    BackSoundEffect.loop();
    //restarting game to its default configuration
    restart();
    gameFrame = 0;
  }
}
For rendering the player character on screen I implemented show() method inside the character class which is loading the player character image on the current frame x and y position.
//for rendering the player character in rectangular box structure on screen
  void show() 
  {
    rectMode(CENTER);
    fill(255);
    noStroke();
    //showing player character image on screen
    image(PlayerCharacter[currentFrame], this.xPos + xRail * screenHeight/4, this.yPos + this.yRail * screenHeight/4);
 
  }
Then I did the implementation letter class which I used later to render grade letters on the screen. The letter class contains attributes for the position of letters and the movement speed of letter images on the screen. Afterwards, I implemented a function to update letters on-screen through frames and another function to check if the letter moves out of the screen. In this case, the letter further drops out from the object array.

The default constructor of the letter class takes the position of the letter as a parameter and sets the default speed and movement direction of the letter on screen.
//default constructor for letter class which takes position of letter as parameter
  Letter(PVector pos) 
  {

    //setting radom speed and velocity of the letters for the movement of letter
    acceleration = new PVector(random(-0.05, 0.05), random(-0.05, 0.05));
    velocity = new PVector(random(-2, 2), random(-2, 2));
    //default position of each letter
    position = pos.copy();
    //random boundry for each letter movement
    MovementSpan = random(100.0, 255.0);  
  }
I implemented the function IsLetterDrops() to check if the letter moves out the screen or not. If the letter moves out of the screen then the letter will be removed further from the letters array.
//for checking if the letters moves out of the screen or not
  boolean IsLetterDrops() 
  {
    //if the letter movement boundry value is less than 0 then letter is out of screen thus returning true else false
    if(MovementSpan < 0.0) 
    {
      return true;
    } 
    else 
    {
      return false;
    }

  }
For loading background image and sound effects, I made a separate function and called that function in the draw and setup functions to render the graphical assets on screen. There are different sound effects used to represent different behaviours. When the character moves, then the sound effect is different. Thus, for the main menu screen and game over screen, a different sound effect is allocated.

I implemented the loadGraphics() function which is loading the images saving in the global image arrays for the player character and grade letters. Furthermore, the background image is also loading into the global background image variable.
// for loading graphics on screen
void loadGraphics() 
{


  PlayerCharacter[0] = loadImage("C1.png");
  PlayerCharacter[1] = loadImage("C2.png");
  PlayerCharacter[2] = loadImage("C3.png");
  PlayerCharacter[3] = loadImage("C4.png");
  

  gradeLetters[0] = loadImage("A.png");
  gradeLetters[1] = loadImage("B.png");
  gradeLetters[2] = loadImage("C.png");
  gradeLetters[3] = loadImage("D.png");
  gradeLetters[4] = loadImage("F.png");
  
  gradeLetters[5] = loadImage("B+.png");
  gradeLetters[6] = loadImage("C+.png");
  gradeLetters[7] = loadImage("D+.png");
 
  

  GPAScoreValues[0] = loadImage("0.png");
  GPAScoreValues[1] = loadImage("1.png");
  GPAScoreValues[2] = loadImage("2.png");
  GPAScoreValues[3] = loadImage("3.png");
  GPAScoreValues[4] = loadImage("4.png");
  

  background = loadImage("back.png");
  KeyControlsImage = loadImage("Controls.png");
  MainPromptText = loadImage("textprompt.png");
  GPAScore = loadImage("gpascore.png");


}
In setup() function I am setting the frame rate by using framerate() builtin function, loading the graphical assets, and playing the main menu sound effect in the loop.
//Main setup function for the configuration of game 
void setup() 
{
  //setting the size of screen by width and height
  size(1080, 720);
  //setting framerate which means how many times screen should be repeated
  frameRate(30);
  //loading sound and background images assets on screen
  loadAssets();
  
  //Put the game in default configuration and game renders from starting position
  restart();
  
  //checking true which shows that character is at its default state
  Character.CharacterDropped = true;
  
  //for playing main menu sound
  MainMenuSoundEffect.loop();

}
In draw() function I am rendering the background image by using image() function and also updating the game assets, for example, repositioning the player character based on the key pressed by the user.
//Main draw function which iterates infinite time and render game screen
void draw() 
{
  image(background,0,0);
  //Updating GPA score point and game configuration
  updateGame();
  //showing graphichs and other assets on screen
  show();

}

FINAL WORK:

CONCLUSION:

For my midterm project, I learned how to implement game logic, render multiple graphics all at the same time, move images from one position to another, and use my knowledge of data structures (for example arrays lists in processing). The game making journey has enhanced my knowledge in regards to different object initialization and creation, and in audio file manipulations from the loading of sound effects that has been done.

GOOGLE DRIVE LINK:

https://drive.google.com/drive/folders/11FKKklnNtAAbftPXK7VQ85PIB9F_IHjX?usp=sharing

COMPLETE CODE OF UNI RUSH GAME 🙂

//importing library for sound
import processing.sound.*;

//Object Creation for Background Sound Effect while game playing
SoundFile BackSoundEffect; 

//Object Creation For Main Menu Background Sound Effect 
SoundFile MainMenuSoundEffect;

//Object Creation For Sound Effect When Character Fails to Eat Any Grade Letter
SoundFile SkipGradeLetterSound; 

//Object Creation For Sound Effect during the movement of character
SoundFile CharacterMovementSoundEffect;

//Image object array creation  for lion character
PImage PlayerCharacter[] = {null, null, null, null};


//Object Creation for grade letters
PImage gradeLetters[] = {null, null, null, null, null, null, null, null};


//Object Creation for the background image
PImage background;

//object creation for key controls for game playing
PImage KeyControlsImage;

//Object creation for main screen text prompt
PImage MainPromptText;

//Object creation for GPA Score Prompt
PImage GPAScore;

//Object creation for GPA score Points
PImage GPAScoreValues[] = {null, null, null, null, null, null, null, null, null, null};

//Game screen width and height
int screenWidth = 1080;
int screenHeight = 720;


//for how many times background should be repeated
int backgroundRepeat[] = {((int)screenWidth/128) + 2, ((int)screenHeight/128) + 2};

//the starting point of the background image
int backgroundOffset = 0;



ArrayList<IntersectionWithLetter> fframes; 
float framespeed; 
float margin;
float marginCounter;

int score; 
int gameFrame = 0;

GradeLetters GradeLetters = new GradeLetters();

Character Character;


//Main setup function for the configuration of game 
void setup() 
{

  //setting the size of screen by width and height
  size(1080, 720);
  //setting framerate which means how many times screen should be repeated
  frameRate(30);
  //loading sound and background images assets on screen
  loadAssets();
  
  //Put the game in default configuration and game renders from starting position
  restart();
  
  //checking true which shows that character is at its default state
  Character.CharacterDropped = true;
  
  //for playing main menu sound
  MainMenuSoundEffect.loop();

}


//Main draw function which iterates infinite time and render game screen
void draw() 
{
   image(background,0,0);
  //Updating game background which include background image,character images and grade letters
 // updateBackground();
  //Updating GPA score point and game configuration
  updateGame();
  //showing graphichs and other assets on screen
  show();

}

//Key pressed function event which occurs when any keyboard key is pressed
void keyPressed() 
{

  //If the key is 'w' then moving the character upward
  if(key == 'w') Character.move(0, -1);
  //if the key is 'a' then moving the character left
  if(key == 'a') Character.move(-1, 0);
  //if the key is s then moving the character down
  if(key == 's') Character.move(0, 1);
  //if the key is 'd' then moving the character right
  if(key == 'd') Character.move(1, 0);

  //if the key is 'r' then setting the character to its default state and postion
  if(key == 'r' || Character.CharacterDropped)
  {
    //stop playing background sounds
    BackSoundEffect.stop();
    MainMenuSoundEffect.stop();
    BackSoundEffect.loop();
    //restarting game to its default configuration
    restart();
    gameFrame = 0;
  }
}


//Main class for Grade letters which inherits the letters class object array
class GradeLetters
{

  //grade letters object array
  ArrayList<Letter> letters;

  //default constructor for grade letters
  GradeLetters() 
  {
    //initializing letters object array in defualt constructor
    letters = new ArrayList<Letter>();
  }
  

  //populating grade letters with different screen positions
  void AddLetter(PVector position, int nletters) 
  {

    //for loop which iterates through number of letters and adding letter with its position
    for(int i = 0; i < nletters; i++) 
    {
      //adding letter with its position
      this.AddLetter(position);
    }
  
  }

  //single paramter add letter function which is for initializing new letter with its position
  void AddLetter(PVector position) 
  {
    //calling letters array add function and adding new letter with it position into the array
    letters.add(new Letter(position));
  }

  //function for updating letters on screen
  void UpdateLettersOnScreen() 
  {
    
    //for loop which runs till letters array size in reverse order
    for (int i = letters.size()-1; i >= 0; i--) 
    {
      //getting the letter from the array from ith index
      Letter l = letters.get(i);
      //rendering the letter on screen
      l.UpdateLettersOnScreen();
      
      //checking if the letter goes out of screen then removing the letter from the array
      if (l.IsLetterDrops()) 
      {
        letters.remove(i);
      }

    }

  }

}


//letter class which represents single letter object
class Letter 
{

  //Position of Letter on screen
  PVector position;
  //Velocity of movement of letter on screen
  PVector velocity; 
  //Accelration of movement of letter on screen
  PVector acceleration;

  //size of the letter which is randomly generated from values 1 to 4
  int size = (int)random(1, 4);
  

  //varibale for limiting the area for the movement of each letter on screen. the letter moves in its setting boundry
  float MovementSpan;


  //default constructor for letter class which takes position of letter as parameter
  Letter(PVector pos) 
  {

    //setting radom speed and velocity of the letters for the movement of letter
    acceleration = new PVector(random(-0.05, 0.05), random(-0.05, 0.05));
    velocity = new PVector(random(-2, 2), random(-2, 2));
    //default position of each letter
    position = pos.copy();
    //random boundry for each letter movement
    MovementSpan = random(100.0, 255.0);  
  }

  //to update the rendering of letter on screen
  void UpdateLettersOnScreen() 
  {
    //updating speed and velocity of letter movement and decresing the movement boundry of letter by value 2.
    velocity.add(acceleration);
    position.add(velocity);
    MovementSpan -= 2.0;
    
    //displaying the letter on screen
    display();
    
  }

  //to display letter on screen
  void display() 
  {
    
    //setting transparent color 
    fill(255, MovementSpan);
    //drawing rectangle which represents the boundry of letters
    rect(position.x, position.y, size, size);
  }


  //for checking if the letters moves out of the screen or not
  boolean IsLetterDrops() 
  {
    //if the letter movement boundry value is less than 0 then letter is out of screen thus returning true else false
    if(MovementSpan < 0.0) 
    {
      return true;
    } 
    else 
    {
      return false;
    }

  }

}

//Player Character Class   
class Character 
{
  //The default x and y position value variables for player character.
  int xPos, yPos;
  //The default x and y direction of the player character
  int xRail = 0, yRail = 0;
  
  //variable for default image frame value for player character
  int currentFrame = 0;
  
  //varibale for checking of the player character is inside screen or not
  boolean CharacterDropped = false;
  
  //default constructor for player character which sets the default position of the player character
  Character(int xPos, int yPos)
  {
    this.xPos = xPos;
    this.yPos = yPos;
  }
  
  //function for the movement of player character accoriding to the x and y direction of the character
  void move(int xDirection, int yDirection)
  {
    
    //checking if the movement of the character is in horizontal direction 
    if(this.xRail + xDirection <= 4 && this.xRail + xDirection >= -1)
    {

      //if the y direction value is zero then adding new letter in the followed direction and playing player character movement sound
      if(yDirection == 0)
      {
        
        GradeLetters.AddLetter(new PVector(this.xPos + xRail * screenHeight/4, this.yPos + this.yRail * screenHeight/4 + 30), 20);
        keyViewerActive(key, 10, screenHeight - 10);
        CharacterMovementSoundEffect.play();
      }

      //moving one step forward the direction of player character in x direction
      this.xRail += xDirection;

    }


    //checking if the movement of the character is in verticle direction 
    if(this.yRail + yDirection <= 1 && this.yRail + yDirection >= -1)
    {

      //if the x direction value is zero then adding new letter in the followed direction and playing player character movement sound
      if(xDirection == 0) 
      {
        
        GradeLetters.AddLetter(new PVector(this.xPos + xRail * screenHeight/4 + 20, this.yPos + this.yRail * screenHeight/4 + 30), 10);
        keyViewerActive(key, 10, screenHeight - 10);
        CharacterMovementSoundEffect.play();
      
      }
      
      //moving one step forward the direction of player character in y direction
      this.yRail += yDirection;
    
    }


  }
  
  //function to check if the player character is out screen which means the character died
  //stopping the game background sound and playing main menu sound
  void die() 
  {
  
    this.xPos = -500;
    this.yPos = -500;
    this.CharacterDropped = true;
    BackSoundEffect.stop();
    SkipGradeLetterSound.play();
    MainMenuSoundEffect.play();
  
  }
  
  //for rendering the player character in rectangular box structure on screen
  void show() 
  {


    rectMode(CENTER);
    fill(255);
    noStroke();
    //showing player character image on screen
    image(PlayerCharacter[currentFrame], this.xPos + xRail * screenHeight/4, this.yPos + this.yRail * screenHeight/4);
 
  }
  
  //for updating grade letters on screen
  void UpdateLettersOnScreen() 
  {

    //if the player character is in screen boundry
    if(!CharacterDropped) 
    {

      //shwoing the player character
      this.show();
      //updating the current frame value to render assests on screen
      if(frameCount % 5 == 0) 
      {
        currentFrame++;
        if(currentFrame == 4) currentFrame = 0;
      }

    }

  }

}


// class for checking if the player character eat or absorb any grade or not
class IntersectionWithLetter 
{

  // variable for the x postion
  int xPos;
  // varibales for the screen frames and its speed initialized by random values
  int currentFrame = (int)random(0, 7), frameSpeed = (int)random(4, 6), empty = (int)random(-2, 2);


  float speed, slowSpeed = 0;
  //check if there is need to add gpa point or not
  boolean canAddToScore = true;

  //default constructor which takes x position of intersecting the grade letter and player character + the speed with which intersection occurs
  IntersectionWithLetter(int xPos, float speed)
  {
    this.xPos = xPos;
    this.speed = speed;
  }
  
  //function to check if the player character eat any grade letter or not
  void Eat() 
  {
   

    if(!Character.CharacterDropped && Character.yRail != this.empty && Character.xPos + Character.xRail * screenHeight/4 >= this.xPos -40 && Character.xPos + Character.xRail * screenHeight/4 <= this.xPos + 70)
    {
      Character.die();
    }

  }
  
  //for showing grade letter on the current rendered frame
  void show() 
  {

    rectMode(CENTER);
    noStroke();
    //for loop which iterates one time and if the ith index is not empty then showing image of grade letter on current frame
    for(int i = -1; i <= 1; i++)
    {
      if(i != empty) image(gradeLetters[currentFrame], this.xPos, screenHeight/2 + i * screenHeight/4);
    }


  }
  
  //for updating letters on current rendered frame
  void UpdateLettersOnScreen() 
  {

    //updating the speed of grade letters
    if(this.slowSpeed == 0) this.xPos -= this.speed;
    else this.xPos -= this.slowSpeed;
    this.Eat();
    this.show();

    // if the rendering is done with all frames then resetting the current frame value to zero
    if(frameCount % frameSpeed == 0) 
    {

      currentFrame++;
      if(currentFrame == 8) 
      {
        currentFrame = 0;
      }

    }

  }


}


//for updating frames on screen after intersection
void UpdateFrames() 
{
  
  //looping to update letters on screen frame after intersection
  for(IntersectionWithLetter IntersectionWithLetter : fframes) 
  {
      IntersectionWithLetter.UpdateLettersOnScreen();
  }

  //checking if the top frame is first frame 
  if(fframes.size() > 0 && fframes.get(0).xPos < -128) 
  {
    //removing the first frame from the screen
    removeFirstFrame();
  }
  //checking if there is space on rendering more frame on screen
  if(marginCounter <= 0 && !(gameFrame >= 2400 && gameFrame <= 3030)) 
  {
    //adding new frame on screen
    addFrame();
  }

  SlowFrameSpeed();

}


//for removing the first frame from the screen
void removeFirstFrame() 
{
  fframes.remove(0);
}

//for adding new frame on screen
void addFrame() 
{
  
  fframes.add(new IntersectionWithLetter(screenWidth + 128, framespeed));
  if(margin > screenWidth/4) margin *= 0.95;
  marginCounter = margin;
  if(framespeed < 20) 
  {
    framespeed *= 1.015;
  }


}

//for slowing the frame rendering speed
void SlowFrameSpeed() 
{

  //if there is space to render more frames on screen then setting the frame speed to 1
  if(gameFrame >= 2400 && gameFrame <= 3030) 
  {
    
    for(IntersectionWithLetter IntersectionWithLetter : fframes) 
    {
      IntersectionWithLetter.slowSpeed = 1;
    }

  } 
  //if there is no space to render more frames on screen then setting the frame speed to 0
  else 
  {

    for(IntersectionWithLetter IntersectionWithLetter : fframes) 
    {
      IntersectionWithLetter.slowSpeed = 0;
    }

  }


}


//for updating background of the game
void updateBackground() 
{
  
  // if the frame vlue is even then updating the background offset position
  if(frameCount%2 == 0) 
  {
    backgroundOffset++;  
  }

  //if the background offset value is maximum then resetting the offset value to 0 and showing the background image
  if(backgroundOffset == 128)
  {

  backgroundOffset = 0;
  showBackground();

  } 

}

//for displaying background on screen
void showBackground() 
{

  //nested for loop to render the background image on each pixel on screen in x and y direction  
  for(int x = 0; x < backgroundRepeat[0]; x++)
  {

    for(int y = 0; y < backgroundRepeat[1]; y++)
    {
      
      image(background, x*background.width - backgroundOffset, y*background.height);
    }

  }


}


// for converting number to images
ArrayList<PImage> numberToArrayOfImages(int number) 
{

  ArrayList<PImage> images = new ArrayList<PImage>();
  String numberS = str(number);
  int len = numberS.length(), buffer;
  
  //iterating till the number length
  for(int index = 0; index < len; index++) 
  {
    //switch for checking for the desired gpa point and if the case match then updating buffer
    switch(numberS.charAt(index)) 
    {
    
     case '0':
       buffer = 0;
       break;
     case '1':
       buffer = 1;
       break;
     case '2':
       buffer = 2;
       break;
     case '3':
       buffer = 3;
       break;
     case '4':
       buffer = 4;
       break;
     case '5':
       buffer = 5;
       break;
     case '6':
       buffer = 6;
       break;
     case '7':
       buffer = 7;
       break;  
     case '8':
       buffer = 8;
       break;  
     case '9':
       buffer = 9;
       break;
     default:
       buffer = 0;
       break;
      }
    images.add(GPAScoreValues[buffer]);
  }
  return images;

}


//showing gpa score
void showScore() 
{

  fill(255);
  textSize(16);
  //rendering gpa score image
  image(GPAScore, 0, 0);
  

  ArrayList<PImage> images = numberToArrayOfImages(score);
  int index = 0;
  
  //iterating all gpa score images
  for(PImage image : images) 
  {
    int yOffset = 0;
    //if the gpa score value matches with the image
    if(image == GPAScoreValues[0]) yOffset = 4;
    //then rendering the image on screen
    image(image, 60 + index * 11, 10 - yOffset);
    index++;
  }



}

// for updating gpa score
void updateScore() 
{
  //checking if player is in screen boundry
  if(!Character.CharacterDropped) 
  {

    //checking if the 
    for(IntersectionWithLetter layer : fframes) 
    {

      if(layer.canAddToScore == true && layer.xPos <= Character.xPos + (Character.xRail * screenHeight/4)) 
      {
        score++;
        layer.canAddToScore = false;
      }

    }

  }
}


//for displaying key viewer on screen
void keyViewer() 
{

  image(KeyControlsImage, 10, screenHeight - 10 - KeyControlsImage.height);

}

//for updating the position based on the active key press
void keyViewerActive(char keyName, int xPos, int yPos)
{

  fill(50, 255, 50);

  //if the key is w then drawing square to new required position
  if(keyName == 'w') 
  {
    square(xPos + 3 * 3 + 44 + 25, yPos + 3 * 3 - 44 * 2, 46);
  } 
  //if the key is a then drawing square to new required position
  else if(keyName == 'a') 
  {
    square(xPos + 3 + 22, yPos - 3 - 22, 46);
  } 
  //if the key is s then drawing square to new required position
  else if(keyName == 's') 
  {
    square(xPos + 3 * 3 + 44 + 25, yPos - 3 - 22, 46);
  } 
  //if the key is d then drawing square to new required position
  else if(keyName == 'd') 
  {
    square(xPos + 3 * 5 + 44 * 2 + 28, yPos - 3 - 22, 46);
  }

  keyViewer();

}



// function for main menu screen
void mainScreen() 
{

  // filling up a rectangle with light color
  fill(50, 150);
  rect(screenWidth/2, screenHeight/2, screenWidth, screenHeight);
  
  textSize(36);
  fill(255);
  
  imageMode(CENTER);
  //prompting text on screen
  image(MainPromptText, screenWidth/2, screenHeight/3);
  
  imageMode(CORNER);

}


// for loading images and sound effect assests on screen
void loadAssets() 
{

  loadGraphics();
  loadSound();

}


// for loading graphics on screen
void loadGraphics() 
{


  PlayerCharacter[0] = loadImage("C1.png");
  PlayerCharacter[1] = loadImage("C2.png");
  PlayerCharacter[2] = loadImage("C3.png");
  PlayerCharacter[3] = loadImage("C4.png");
  

  gradeLetters[0] = loadImage("A.png");
  gradeLetters[1] = loadImage("B.png");
  gradeLetters[2] = loadImage("C.png");
  gradeLetters[3] = loadImage("D.png");
  gradeLetters[4] = loadImage("F.png");
  
  gradeLetters[5] = loadImage("B+.png");
  gradeLetters[6] = loadImage("C+.png");
  gradeLetters[7] = loadImage("D+.png");
 
  

  GPAScoreValues[0] = loadImage("0.png");
  GPAScoreValues[1] = loadImage("1.png");
  GPAScoreValues[2] = loadImage("2.png");
  GPAScoreValues[3] = loadImage("3.png");
  GPAScoreValues[4] = loadImage("4.png");
  

  background = loadImage("back.png");
  KeyControlsImage = loadImage("Controls.png");
  MainPromptText = loadImage("textprompt.png");
  GPAScore = loadImage("gpascore.png");


}


//for loading sound effects 
void loadSound() 
{
  //background sound effect
  BackSoundEffect = new SoundFile(this, "backgroundsound.mp3");
  //main menu sound effect
  MainMenuSoundEffect = new SoundFile(this, "mainmenusound.mp3");
  //skipping grade letter sound
  SkipGradeLetterSound = new SoundFile(this, "dropsound.mp3");
  //player character movement sound effect
  CharacterMovementSoundEffect = new SoundFile(this, "cmovesound.mp3");

}


//for updating game
void updateGame() 
{
  //upodating frames count on screen
  gameFrame++;

  //updating grade letters on screen
  Character.UpdateLettersOnScreen();
  //updating frames on screen
  UpdateFrames();
  //updating gpa score
  updateScore();
  //for updating letters on screen
  GradeLetters.UpdateLettersOnScreen();
  //upodating frame speed
  marginCounter -= framespeed;
  
}



//for showing gpa score and key viewer on game screen
void show() 
{

  //showing gpa score
  showScore();

  //if player character is out of screen then returning to main menu screen
  if(Character.CharacterDropped) 
  {
    mainScreen();
  }

  //for rendering key viewer on screen
  keyViewer();

}

// for restarting the game. 
void restart() 
{
  // the gpa score will be zero
  score = 0;
  
  //frame speed reset to 5 which means 5 frames will be rendered in one second
  framespeed = 2;
  //margin between player character and grade letters
  margin = screenWidth/2;
  marginCounter = margin;


  //initializing player character to its default position
  Character = new Character(230, height/2);
  // resetting frames
  fframes = new ArrayList<IntersectionWithLetter>();
  fframes.add(new IntersectionWithLetter(screenWidth + 128, framespeed));  
  

}

Midterm Final: Save NYUAD!

Building up on the progress I posted a while ago, I am happy the process went smoothly. I did face a few minor issues but the help from professor Aaron and Jack, aided me in getting through them.

Issues and solutions:

First of all an initial issue that I had regarding the game was that I wasn’t able to fix the issue that the thief was above the darkness. Initially I was using load pixels/ image processing with pixels which did make the flashlight effect, but after professor Aaron’s help, the conclusion that in order to make the thief behind the darkness as well I had to switch to using a PGraphic object was reached. I drew the nyu background and the thief image into the pgraphic object and so I can access the pixels of that pgraphic object in the for loops, instead of just the background image.

 

void theGame(){
  
    black = createGraphics(1200, 800);
  black.beginDraw();
  black.background(0);
  black.endDraw();
 
  mask = createGraphics(1200, 800);
  mask.beginDraw();
  mask.background(0);
  mask.endDraw(); 

pg=createGraphics(1200,800);
pg.beginDraw();

 
  pg.image(nyu,0,0);
    pg.image(thief,x,y);
   if(dist(x,y,mouseX,mouseY)<100){
     if(mousePressed==true){
        gameState="WIN";
       file.stop();
       if(!sound.isPlaying());
  sound.play();
     } 
   }
   
  pg.endDraw();
   
  mask.beginDraw();
  mask.background(0);
  mask.noStroke();
  mask.fill(255);
   
  mask.ellipse(mouseX, mouseY, 200, 200);
 
  mask.endDraw();
  
  pg.mask(mask);

Another issue I faced was that using keyPressed after game over to return to the play mode did not work as I had to keep pressing the key, hence in order to fix this I created a new function “reset game”, which helped fix this issue as well as replay the sounds and the reset the time back to countdown from 5.

void resetGame(){
  if (keyPressed==true){
 gameState="PLAY";
 file.loop();
 laugh.stop();
 sound.stop();
  timeLeft=6;
}

Further development:

Developments I mad since the progress post are extensive. First of all I added the start page with instructions and the “win” or “lose” pages as well, with the transitions from each one to the other.

Intro page:
Win page:

Game Over page:

I also made the thief image appear at random locations every time a new game is played, and included the mouse pressed event for the player to be able to press on the thief and win.

Finalizing minor details:

In my opinion the finer details brought the whole project together. The sound effects of the thief laughing when you lose the game as well as the alarm sirens makes the game more interesting. The timer makes the suspenseful mood , making the game much more fun to play. Adding the timer was bit confusing but with the help of this youtube video I was able to do it (https://www.youtube.com/watch?v=JXqukW44Dhs&t=813s) .

The sounds I used were gotten from (https://freesound.org/)

Final Result:

This is the zip code if anyone wants to try out the game.

midterm__work_on_this_

Code:

import processing.sound.*;
SoundFile file;
SoundFile sound;
SoundFile laugh;


PImage nyu;
PImage thief;
PImage introPic;
PImage winningPic;
PImage gameOver;
PImage image;
 float r1 ;
 float r2 ;



int buttonX, buttonY;
int buttonWidth, buttonHeight;

color buttonColor, baseColor;    //colors of play button

PFont f; 
PGraphics pg;
PGraphics black, mask;
String gameState;
float x;
float y;

Timer countdownTimer;
int timeLeft;



void setup() {
  size(1200, 800); 
gameState=("START");

  PFont.list();

 f = createFont("Times New Roman",75);

x = random(-20, 1100);
 y = random(-20, 600);

nyu=loadImage("nyu.png");
thief=loadImage("thief.png");
    
  buttonColor = color(0);
  baseColor = color(102);
  buttonWidth = 200;
  buttonHeight = 80;
  buttonX = width/2+300;
  buttonY = 3*height/4 + 100;     
  
    countdownTimer=new Timer(1000);
    timeLeft=6;
file= new SoundFile(this,"theme.mp3");
sound= new SoundFile(this,"alarm.wav");
laugh= new SoundFile(this,"evil laugh.wav");
}



void draw() {
  background(0);

 
if (gameState=="START"){

  introScreen();
  if (keyPressed==true){
       file.loop();
    gameState="PLAY"; //transition to play state if key pressed
    countdownTimer.start();
  }       
}

else if (gameState=="PLAY"){
  theGame();
}

else if (gameState=="WIN"){
 
 congratsScreen();
 if (keyPressed==true){
    gameState="PLAY";
    resetGame();
    x = random(-20, 1100);
 y = random(-20, 600);
}
}
else if (gameState=="LOSE"){
  gameOver();
    
    resetGame();

    x = random(-20, 1100);
 y = random(-20, 600);
}
}
     
  //code for the actual game
 void theGame(){
  //pgraphics
    black = createGraphics(1200, 800);
  black.beginDraw();
  black.background(0);
  black.endDraw();
 
  mask = createGraphics(1200, 800);
  mask.beginDraw();
  mask.background(0);
  mask.endDraw(); 

pg=createGraphics(1200,800);
pg.beginDraw();

 //code for allowing the players to click on the thief image
  pg.image(nyu,0,0);
    pg.image(thief,x,y);
   if(dist(x,y,mouseX,mouseY)<100){
     if(mousePressed==true){
        gameState="WIN"; //if the player presses on the image the player wins
       file.stop();
       if(!sound.isPlaying());
  sound.play();
     } 
   }
   
  pg.endDraw();
   
  mask.beginDraw();
  mask.background(0);
  mask.noStroke();
  mask.fill(255);
   
  mask.ellipse(mouseX, mouseY, 200, 200);
 
  mask.endDraw();
  
  pg.mask(mask);

  

  image(black,0,0);
   image(pg, 0, 0);
   //countdown timer logic
   if (countdownTimer.complete()==true){
     if(timeLeft>1){
       timeLeft--;
       countdownTimer.start();
   }else{
     gameState="LOSE"; //code for if the timer finishes the player loses
     file.stop();
     sound.stop();
       if(!laugh.isPlaying());
  laugh.play();
   }
   }
   //showing the timer
   String s="Time Left:"+timeLeft;
   textAlign(LEFT);
   textSize(20);
   fill(255,0,0);
   text(s,20,100);
 }
//code for the start/intro screen
void introScreen(){

   background(157,157,165);

  
    
      fill(buttonColor);
    
    imageMode(CORNERS);
     introPic=loadImage("introPic.png");
    image(introPic, 0,0,1200, 650);       
     
   
    stroke(255);
    rect(buttonX, buttonY, buttonWidth, buttonHeight, 20);      //key pressed play button
    
    textFont(f,25); 
    textAlign(CENTER, CENTER);
    fill(255);
textSize(20);
    String play = "Press any key to play";          //text in play button to clarify it
    text(play, buttonX+100 , buttonY+30);
    
    textSize(30);
    String instructions="Find the thief using the flashlight before the time finishes";
     text(instructions, buttonX-500 , buttonY+15);
     String instructionsCont="Click on the thief to capture him,You have 5 seconds!";
    text(instructionsCont,buttonX-500 , buttonY+50);

  }
//code for the congrats screen when the player wins
void congratsScreen(){
  winningPic=loadImage("busted2.png");
image(winningPic, 0,height/5,1200,800);  
textFont(f,40); 
    textAlign(CENTER, CENTER);
    fill(255);
textSize(40);
    String congrats = "Congrats You Busted Him!";         
    text(congrats, width/2,80);
     String replay = "Click any key to play again";          
    text(replay, width/2,130);
}
//code for the gameover screen when a player loses
void gameOver(){
  background(0);
  textSize(80);
String gameover = "GAME OVER!";
String gameovercont="HE GOT AWAY WITH IT";          
    text(gameover, width/2-250,height/4);
    text(gameovercont, width/2-400,height/2);
    textSize(40);
     String replay = "Click on any key to play again";          
    text(replay, width/2-200,height/2+200);
 

}
//code to reset the game, along with the sound and the timer
void resetGame(){
  if (keyPressed==true){
 gameState="PLAY";
 file.loop();
 laugh.stop();
 sound.stop();
  timeLeft=6;
}
}

 

MATCH-GAME Midterm Project – Jade

 

Description:

For my midterm project, I made a mutant version of Match Three game, including elements of Tetris. The patterns of the cubes come from Mahjong, a traditional Chinese tile-based chess game. The player needs to match 3 or more cubes with the same patterns and make combos to eliminate the cubes, and the goal is to eliminate the highlighted cube. I designed a help window, so when you press “H”, you can see some of the combos allowed in this game. There are some more kinds of combos, and I intend to leave the part for the player to figure out.

In the game, the player uses “s”, “a”, “d” to move the cube, and can press “enter” to start, “p” to pause the game, “h” for help and “esc” to exit the game. The players can press “enter” to restart the game once they clear or they lose the game.

I inserted some sound effects, like when the cube falls into the place or the player successfully makes a combo, also when the player wins or loses the game.

There is also one more mechanism that as the score accumulates, the cubes will fall down at a higher speed.

 

Mahjong

 

Challenges:

It is challenging to design the mechanism for the game and also to figure out the logic part of the codes. During this week, I focused on the UI of the game. And using images instead of colors is harder than I thought. I had to modify pretty many codes to replace all the colors with images.

And for the sound part, I had to pay attention to where to put the sound.play code so that it won’t loop over and over. It also takes me a lot of time designing the UI and locating the texts and images.

But the whole process of making the game from scratch is interesting. I learned a lot from this midterm assignment!

import processing.sound.*;
SoundFile same_sound;
SoundFile win_sound;
SoundFile fit_sound;
SoundFile over_sound;

int w = 10;
int h = 10;
int q = 40;
int dt;
int currentTime;

PImage[] cubes;
PImage bg;
PImage bg1;
PImage win;
PImage over;

Grid grid;
Piece piece;
Piece nextPiece;
Pieces pieces;
Score score;
int level = 1;
int nbLines = 0;

int txtSize = 20;
int textColor = 0;
int x1;

Boolean gameOver = false;
Boolean gameOn = false;
Boolean gamePause = false;
Boolean gameWin = false;
Boolean help = false;

void setup()
{
  size(600, 480, P2D);
  textSize(30);
  cubes = new PImage[4];
  cubes[0] = loadImage("dong.png");
  cubes[1] = loadImage("nan.png");
  cubes[2] = loadImage("xi.png");
  cubes[3] = loadImage("bei.png");
  bg = loadImage("bg.png");
  bg1 = loadImage("bg1.png");
  win = loadImage("win.jpeg");
  over = loadImage("over.png");
  
  same_sound = new SoundFile(this, "same.wav");
  win_sound = new SoundFile(this, "win.wav");
  fit_sound = new SoundFile(this, "fit.wav");
  over_sound = new SoundFile(this, "over.wav");
}

void initialize() {
  nbLines = 0;
  dt = 1000;
  currentTime = millis();
  score = new Score();
  grid = new Grid();
  pieces = new Pieces();
  piece = new Piece(-2);
  nextPiece = new Piece(-2);
  grid.generate();
  score = new Score();
  level = 1;
  
  x1 = int(random(0, w)); 
  while (grid.isFree(x1,h-1)) {
    x1 = int(random(0, w));
  }
}

void draw()
{
  background(0);
  image(bg, 0, 0, 600, 480);
  if (grid != null) {
    grid.drawGrid();
    int now = millis();
    if (gameOn) {
      if (now - currentTime > dt) {
        currentTime = now;
        piece.oneStepDown();
      }
    }
    fill(#F8FC08);
    PFont f = createFont("", 20);
    textFont(f);
    text("P - PAUSE", 30, 360);
    text("H - HELP", 30, 400);
    
    PFont pixel = createFont("ARCADECLASSIC.TTF", 40);
    textFont(pixel);
    piece.display(false);
    score.display();
    fill(#DE9F16);
    text("Goal: ", 30, 200);
    if (grid.cells[x1][h-1] != -1) {
      image(cubes[grid.cells[x1][h-1]], 50, 220, 40, 40);
    }    
    stroke(#F8FC08);
    strokeWeight(5);
    line(50, 220, 90, 220);
    line(50, 220, 50, 260);
    line(90, 220, 90, 260);
    line(50, 260, 90, 260);
  }
  
  if (gameOver) {
     noStroke();
     background(255);
     fill(0);
     over.resize(600, 200);
     image(over, 0, 100);
     text("Press 'ENTER' to     restart.", 50, 400);
     over_sound.play(); 
     noLoop();
  }
  
  if (!gameOn) {
    image(bg1, 0, 0, 600, 480);
    noStroke();    
    fill(#A04C13);
    PFont f = createFont("", 30);
    textFont(f);
    text("MOVE LEFT", 70, 140);
    text("MOVE RIGHT", 70, 180);
    text("MOVE DOWN", 70, 220);
    text("PAUSE", 70, 260);
    text("HELP", 465, 390);
    text("EXIT", 500, 430);

    PFont pixel = createFont("ARCADECLASSIC.TTF", 40);
    textFont(pixel);
    text("A", 25, 140);
    text("D", 25, 180);
    text("S", 25, 220);
    text("P", 25, 260);
    text("H", 420, 390);
    text("ESC", 420, 430);
    
    fill(0);
    stroke(0);
    strokeWeight(3);
    line(20, 140, 50, 140);
    line(20, 115, 50, 115);
    line(20, 115, 20, 140);
    line(50, 115, 50, 140);
    
    line(20, 155, 50, 155);
    line(20, 180, 50, 180);
    line(20, 155, 20, 180);
    line(50, 155, 50, 180);   

    line(20, 195, 50, 195);
    line(20, 220, 50, 220);
    line(20, 195, 20, 220);
    line(50, 195, 50, 220); 
    
    line(20, 235, 50, 235);
    line(20, 260, 50, 260);
    line(20, 235, 20, 260);
    line(50, 235, 50, 260);    

    line(415, 390, 445, 390);
    line(415, 365, 445, 365);
    line(415, 365, 415, 390);
    line(445, 365, 445, 390);  
    
    line(415, 430, 490, 430);
    line(415, 405, 490, 405); 
    line(415, 405, 415, 430);
    line(490, 405, 490, 430);  
    
  }
  
  if (gameWin) {
     noStroke();
     image(win, 0, 0, 600, 480);
     fill(#E36917);
     text("Press 'ENTER' to    restart.", 30, 460);
     win_sound.amp(0.5);
     win_sound.play();
     noLoop();
  }
  
  if (help) {
    fill(#EAD0E4);
    rect(0, 0, 600, 480);
    fill(#E36917);
    PFont f = createFont("", 30);
    textFont(f);
    text("Match 3 or more cubes with", 40, 100);
    text("the same pattern to eliminate.", 40, 140);
    image(cubes[3], 60, 180, 50, 50);
    image(cubes[3], 110, 180, 50, 50);
    image(cubes[3], 160, 180, 50, 50);
    
    image(cubes[2], 280, 180, 50, 50);
    image(cubes[2], 330, 180, 50, 50);
    image(cubes[2], 380, 180, 50, 50);
    image(cubes[2], 430, 180, 50, 50);
    image(cubes[2], 480, 180, 50, 50);  
    
    image(cubes[1], 110, 250, 50, 50);
    image(cubes[1], 110, 300, 50, 50);
    image(cubes[1], 110, 350, 50, 50);
    
    image(cubes[0], 280, 250, 50, 50);
    image(cubes[0], 330, 250, 50, 50);
    image(cubes[0], 380, 250, 50, 50);
    image(cubes[0], 380, 300, 50, 50);
    image(cubes[0], 380, 350, 50, 50);
   }
}

void goToNextPiece() {
  piece = new Piece(nextPiece.kind);
  nextPiece = new Piece(-2);
}

void goToNextLevel() {
  level = 1 + int(nbLines / 100);
  dt *= .98;
}

void keyPressed() {
  if (gameOn) {
      piece.inputKey(keyCode);
  }
  if (keyCode == 80) {
    if (gameOn) {
      gamePause = !gamePause;
      
      if (gamePause) {
        fill(0, 60);
        rect(width/2 - 220, height/2 - 50, 450, 100, 20);
        fill(255);
        textSize(25);
        PFont f = createFont("", 30);
        textFont(f);
        stroke(0);
        text("Press  'P'  to restart the game", width/2 - 200, height/2);
        noLoop();
      } else if (!gamePause){
        loop();
      }
    }
    
  } else if (key == ENTER) {
    if (!gameOn || gameWin || gameOver) {
      initialize();
      gameWin = false;
      gameOver = false;
      gameOn = true;
      loop();
    }    
  } 
  
  if (keyCode == 72) {
    help = !help;
    if (gameOn) {
      gamePause = true;
    }
  }
}
class Piece {

  final int[] colors = {0, 1, 2, 3};

  final int[][] pos;
  int x = int(w/2);
  int y = 0;
  int kind;
  int c;

  Piece(int k) {
    if (k == -2) {
      kind = int(random(0, 4));
    } else {
      kind = k;
    }
    c = colors[kind];
    pos = pieces.pos[kind];
  }

  void display(Boolean still) {
    stroke(250);
    //fill(c);
    pushMatrix();
    if (!still) {
      translate(160, 40);
      translate(x*q, y*q);
    }
    image(cubes[c], pos[0][0] * q, pos[0][1] * q, 40, 40);
    popMatrix();
  }

  // returns true if the piece can go one step down
  void oneStepDown() {
    y += 1;
    if(!grid.pieceFits()){
      piece.y -= 1;
      grid.addPieceToGrid();
    }
  }

  // try to go one step left
  void oneStepLeft() {
    x --;
  }

  // try to go one step right
  void oneStepRight() {
    x ++;
  }

  void goToBottom() {
    grid.setToBottom();
  }

  void inputKey(int k) {
    switch(k) {
    case 65:
      x --;
      if(grid.pieceFits()){
      }else {
         x++; 
      }
      break;
    case 68:
      x ++;
      if(grid.pieceFits()){
      }else{
         x--; 
      }
      break;
    case 83:
      oneStepDown();
      break;
    }
  }

}



class Pieces {
  int[][][] pos = new int [4][1][2];
  
  Pieces() {
    pos[0][0][0] = -1;
    pos[0][0][1] = 0;
    pos[1][0][0] = -1;
    pos[1][0][1] = 0;
    pos[2][0][0] = -1;
    pos[2][0][1] = 0;
    pos[3][0][0] = -1;
    pos[3][0][1] = 0;
  }
}
class Grid {
  int [][] cells = new int[w][h];

  Grid() {
    for (int i = 0; i < w; i ++) {
      for (int j = 0; j < h; j ++) {
        cells[i][j] = -1;
      }
    }
  }

  Boolean isFree(int x, int y) {
    if (x > -1 && x < w && y > -1 && y < h) {
      return cells[x][y] == -1;
    } else if (y < 0) {
      return true;
    }
    return false;
  }

  Boolean pieceFits() {
    int x = piece.x;
    int y = piece.y;
    int[][] pos = piece.pos;
    Boolean pieceOneStepDownOk = true;
    int tmpx = pos[0][0]+x;
    int tmpy = pos[0][1]+y;
    if (tmpy >= h || !isFree(tmpx, tmpy)) {
      fit_sound.play();
      pieceOneStepDownOk = false;
     }
    return pieceOneStepDownOk;
  }

  void addPieceToGrid() {
    int x = piece.x;
    int y = piece.y;
    int[][] pos = piece.pos;
      if(pos[0][1]+y >= 0){
        cells[pos[0][0]+x][pos[0][1]+y] = piece.c;
      }else{
        gameOn = false;
        gameOver = true;
        return;
      }
    checkSame();
    
    
    goToNextPiece();
    checkSame();
    drawGrid();
    checkGame();
    
  }
  

  void checkSame() {
    int nb = 0;
    int mark_x = 0;
    int mark_y = 0;
    boolean same = false;
    
    // check horizontal
    for (int j = 0; j < h; j ++) {
      for (int i = 0; i < w; i++) {
        int c1 = cells[i][j];
        if ((i+2 < w) && (!isFree(i, j))) {
          if ((cells[i+1][j] == c1) &&
            (cells[i+2][j] == c1)) {
              int mark_num = 2;
              mark_x = i;
              mark_y = j;
              if ((i+3 < w) && (cells[i+3][j] == c1)) {
                mark_num = 3;
              }
              if ((i+4 < w) && (cells[i+4][j] == c1)) {
                mark_num = 4;
              }            
              nb = nb + mark_num - 1;
              same_sound.play();
              for (int k = mark_y; k > 0; k--) {
                for (int m = mark_x;  m <= mark_x + mark_num; m++) {
                  if (m==x1 && j == h-1) {
                    cells[x1][h-1] = -1;
                    checkGame();
                  }
                  if ((j+2 < h) && (cells[m][j+1] == c1) &&
                  (cells[m][j+2] == c1)) {       
                    cells[m][j+1] = cells[m][j];
                    cells[m][j+2] = cells[m][j+1];
                    checkGame();
                  }
                  cells[m][k] = cells[m][k-1];
                  cells[m][0] = -1;
                  checkGame();
              continue;
             }           
            }
           }
      }
      if ((j+2 < h) && (!isFree(i, j))) {
          if ((cells[i][j+1] == c1) &&
          (cells[i][j+2] == c1)) {
            same = true;
            mark_x = i;
            mark_y = j;         
      if (same) {
        nb++;
        same_sound.play();
        for (int k = mark_y; k < mark_y + 3; k++) {
          try{       
          cells[mark_x][k] = -1;
          checkGame();
          } catch(Exception e) {           
          }
        }
        for (int k = mark_y; k > 2; k--){
          cells[mark_x][k] = cells[mark_x][k-2];
          checkGame();
        }
     
      } 
      }
      }
      }
    }
    checkGame();
    deleteLines(nb);
    }
    
    
  Boolean checkWin() {
    if (isFree(x1, h-1)) {
      return true;
    }
    return false;
  }

  void deleteLines(int nb) {
    nbLines += nb;
    if (int(score.points / 100) > level) {
      goToNextLevel();
    }
    score.addLinePoints(nb);
  }

  void setToBottom() {
    int j = 0;
    for (j = 0; j < h; j ++) {
      if (!pieceFits()) {
        break;
      } else {
        piece.y++;
      }
    }
    piece.y--;
    delay(1500);
    addPieceToGrid();
  }

  void drawGrid() {
    stroke(150);
    pushMatrix();
    translate(160, 40);
    line(-10, 0, -10, h*q);

    for (int i = 0; i < w; i ++) {
      for (int j = 0; j < h; j ++) {
        if (cells[i][j] != -1) {
          image(cubes[cells[i][j]], i * q, j * q, 40, 40);
        }
      }
    }
    pick(x1, h-1);
    popMatrix();
  }
  
  void generate() {
     for (int i = 0; i < w; i ++) {
      for (int j = h * 1/2 ; j < h ; j ++) {
        int kind = int(random(0, 4));
        cells[i][j] = kind;
        image(cubes[kind], i * q, j * q, 40, 40);
      }
      checkSame();
    }
    checkSame();
  }

  
  void checkGame() {
    for (int i=0; i<w; i++) {
      if (!isFree(i, 0)) {
        gameOver = true;    
      }
      for (int j=0; j<h; j++) {
        if (!isFree(i, j) && isFree(i,j+1)) {
          cells[i][j] = -1;
        }
      } 
    }
    if (checkWin()) {
      gameWin = true;
    }
  }
  
  void pick(int x, int y) {
    stroke(#F8FC08);
    strokeWeight(5);
    if (cells[x][y] != -1) {
      image(cubes[cells[x][y]], x * q, y * q, 40, 40);
    }
    line(x*q, y*q, (x+1)*q, y*q);
    line(x*q, y*q, x*q, (y+1)*q);
    line((x+1)*q, y*q, (x+1)*q, (y+1)*q);
    line(x*q, (y+1)*q, (x+1)*q, (y+1)*q);
  }

}
class Score {
  int points = 0;

  void addLinePoints(int nb) {
    points += level * nb * 10;
  }


  void display() {
    pushMatrix();
    translate(30, 60);

    //score
    fill(#DE9F16);
    text("score: ", 0, 0);
    fill(230, 230, 12);
    text(""+formatPoint(points), 0, txtSize + 10);
    
    popMatrix();

  }

  String formatPoint(int p) {
    String txt = "";
    int qq = int(p/1000000);
    if (qq > 0) {
      txt += qq + ",";
      p -= qq * 1000000;
    }

    qq = int(p/1000);
    if (txt != "") {
      if (qq == 0) {
        txt += "000";
      } else if (qq < 10) {
        txt += "00";
      } else if (qq < 100) {
        txt += "0";
      }
    }
    if (qq > 0) {
      txt += qq;
      p -= qq * 1000;
    }
    if (txt != "") {
      txt += ",";
    }

    if (txt != "") {
      if (p == 0) {
        txt += "000";
      } else if (p < 10) {
        txt += "00" + p;
      } else if (p < 100) {
        txt += "0" + p;
      } else {
        txt += p;
      }
    } else {
      txt += p;
    }
    return txt;
  }
}

 

Staying in Orbit: Midterm Video Game Project

My midterm project turned out closely similar to what I imagined it would be when I started. I used two images and one audio. The character sprite and the audio file were both downloaded from https://www.nyan.cat/

Game Premise: 

The game is a one player game with the goal of keeping the character within the screen (in orbit). If the character moves out of screen (falls out of orbit), the game ends. The player interacts with the game using the Space key. Whenever the Space key is pressed, the character accelerates horizontally and vertically. If the key is not pressed, the character loses horizontal speed and falls to the bottom of the screen.

Challenges:

The audio file used in the game is too big (about 13 mb), so the game takes a while to load. However, the game runs smoothly after loading. Another issue I faced is adjusting the game difficulty. I adjusted the timing of releasing the objects using the random function. This makes the difficulty of the game somewhat inconsistent, but I think I have adjusted it sufficiently.

Short Clip:

The code and assets are available in this Drive folder (accessible only within the NYU network).

Midterm Progress – deadStudent

Inspiration:

There is a game played a while back that I really liked, and as recreating that game will take much longer than the time I have now, I got an idea that is similar in a way, but simpler.

The game I got inspired by is called HUE.

Hue review: Fiddlesticks' colourful PS4 and Xbox platformer

In this game, hue needs to save his mom by collecting pieces of the color ring in the picture above.

My Idea:

My idea, similarly, the student needs to collect a few questions and the exam to pass.

Game Parts:

– The player/student needs to collect all the questions on the screen and get to the final before the timer ends.

– If the student collects all the questions, they can collect the final exam paper and get the full grade on the exam. Otherwise, they get points for the questions they collected.

-each question is guarded by a monster(monsters are inspired by the monsters that face students like anxiety, procrastination, and distractions).

This is a rough draft of what the game is supposed to look like:

Progress:

I started with the background. Instead of using a still image, I used loadPixels() to make the background look something like a night sky.

Code:

 

class Bg {
  float[] x = new float[50];
  float[] y = new float[50];
  Bg() {
    for (int i=0; i<50; i++) {
      x[i] = random(0, widthh);
      y[i] = random(0, heightt);
    }
  }
  void drawBG() {
    loadPixels();
    for (int y=0; y<heightt; y++) {
      for (int x=0; x<widthh; x++) {
        float n = noise(x*.002, y*.005);
        //n = frameCount%width+n;
        //n= map(n,0,width,0,255);
        pixels[x+y*width]=color(n*150);
      }
    }
    updatePixels();

    if (frameCount%5==0) {
      for (int i=0; i<50; i++) {
        x[i] = random(0, width);
        y[i] = random(0, height);
      }
    }
    for (int i=0; i<50; i++) {
      for (int j=0; j<10; j++) {
        noStroke();
        fill(255, 25);
        ellipse(x[i], y[i], 1+j, 1+j);
      }
    }
  }
}

 

I added platforms, which the questions and monsters will be on at random, saved, x-locations:

Code:
class Platforms {

  PVector pos; //mid point
  float pHeight;
  float pWidth;
  float widths;

  Platforms(float x, float y, float w, float h) {
    pos = new PVector(x, y);
    //println(pos.x, pos.y);
    pHeight = h;
    pWidth = w;
  }

  void display() {
    stroke(0);
    //noStroke();
    rectMode(CORNER);
    fill(1);
    rect(pos.x, pos.y, pWidth, pHeight);
  }
  // float distance(Objects other){

  // }
}

My code is mainly classes for each thing on the screen and one for the whole game that controls everything.

I made one creature’s class that will be a superclass for the player, monsters, questions, and the final.

The class still has a few errors that I am working on.

class Creature {

 // Attributes
 PVector pos;
 PVector speed;
 boolean alive;
 float ground;
 float size;
 PVector imgSize;
 int numFrames;
 PImage img;
 int frame;
 boolean dir; //left = 0 right = 1

 //Player
 Creature(float x,float  y,float  r,float  g,float  img,float  w,float  h,int  num_frames,float  xl,float  xr) {
   pos= new PVector(x,y);
   speed=new PVector(0,0);
   alive = true;
   ground = g;
   size = r;
   imgSize = new PVector(w,h);
   numFrames = num_frames;
   img = new PImage(loadImage("player.png"));
   frame = 0;
   dir = true;
 }

 void gravity() {
   if (pos.y+size >= ground) {
     speed.y=0;
   } else {
     speed.y +=0.3;
     if (pos.y+size+speed.y>ground) {
       speed.y = ground-(pos.y+size);
     }
   }

   for (int p=0; p<game.plats.length; p++) {
     if (game.plats[p].pos.y + size <= game.plats[p].pos.y && pos.x+size>=game.plats[p].pos.x && pos.x-size <= game.plats[p].pos.x+game.plats[p].pos.x) {
       ground = game.plats[p].pos.y;
       break;
     } else {
       ground = game.ground;
     }
   }
 }
 void update(){
     gravity();
     pos.x += speed.x;
     pos.y += speed.y;
 }
 void display(){
     update();

     if (dir && alive){
         image(img, pos.x-imgSize.x/2 , pos.y-imgSize.y/2, frame*imgSize.x,0,(frame+1)*imgSize.x,imgSize,imgSize.y);
     }
     else if(alive){
         image(img, pos.x-imgSize.x/2 , pos.y-imgSize.y/2, frame*imgSize.x,(frame+1)*imgSize.x,0,imgSize,imgSize.y);
     }
 }
}
void startSc(){

}

void endSc(){

}

Finally, my game class and setup and draw functions that control the whole progress of the game.

the game being in a class allows me to easily restart the game if the player fails by calling a new game object again.

class Game {

  //
  int ground = 100;

  //   Player _player;
  Platforms[] plats = new Platforms[5];
  Platforms ground;
  int platLocH[] = new int[5];
  int platLocW[] = new int[5];


  // Objects[] _monsters = new Objects[10];
  // Objects _final;
  // Objects[] _questions = new Objects[10];

  // Bullets _bullets;

  Game() {
    ground = new Platforms(0, heightt-ground, widthh, ground);
    for (int i=0; i<plats.length; i++) {
      platLocH[i] = (heightt-250)-i*130;
      platLocW[i] = int(random(0, widthh-500));
      plats[i]= new Platforms(platLocW[i], platLocH[i], 500, heightt/20);
      println((heightt)-i*150);
    }
  }




  void main() {
    for (int i = 0; i<5; i++) {
      plats[i].display();
    }
    ground.display();
  }
}


Bg bckgrnd = new Bg();
Game game = new Game();



void draw() {
  bckgrnd.drawBG();

  game.main();
  //rect(height/2, width/2, 100,100);
}

void keyPressed() {
  //   if (_start.getflag==True) {
  //   }
}

 

Character Sprites:

These are my character sprites and draft sprites for the final exam and the questions:

Character(dead student):

Questions:

Final Paper: