Shreya’s Farmland – An OOP Game

This week’s assignment was to create a game using Object-Oriented Programming. My idea for the game was inspired by one of my favorite childhood computer games in which we had to collect apples to gain points and beware of the bombs which reduced points. For this project, I wanted to recreate this game.

Object-Oriented Programming was ideal to create this game because  I could create the falling objects and the basket as separate classes and then manipulate each one of those in a much easier manner by just varying a few parameters than having to deal with each object individually.

I started with first creating a falling object class in which I had apples and my first goal was to make them move as required – changing their x position each time they came again onto the screen for the next round and even increasing their speed the longer the game progresses to increase difficulty (see below). Then came the basket class and making it move according to user input.

With the basket:

Once I managed to move the apple and the basket as I desired, the next step was to check whether the user actually caught the fruit in the basket or not. Checking for this and increasing the score for it was relatively easy, however, creating a sense that the fruit was actually being captured in the basket was quite difficult. See my first attempt to check for collision:

Clearly, the score is increasing, but no user would know they caught the fruit unless they look at it. This is not user-friendly at all because you cannot concentrate not the game if you have to look at the score again and again. so, it was important that the fruits disappeared once they collided with the basket. However, the problem with this was that I was displaying the fruit image in the run() method of the class but I was checking for collision outside the class. So, if I used tint() to make the opacity of the object zero, everything on the canvas would go transparent. If I put this in the class function, the image in the run() would still draw the fruit, and the purpose of making it invisible failed.

I managed to solve this problem by first rearranging a few lines in the code so that the layering would be appropriate for what I was going to do next. I then created another class member (variable) which kept track if the object collided or not. I would only display the image in the run() method of the class if visible was true (see code below).

DETAILING

Once I got the semantics of the game in place, it was time for some diversity! I first added a scenic background to make it interface pleasing and then I decided to have different fruits; each amounting to different points. I also decided to have a stone (equivalent to the bomb in my childhood game) to make it more interesting. The game would finish when the score would go less than zero.

USER EXPERIENCE

What is a game if you do not know what to do or see how your game went? So, along with creating an illusion of fruits falling in the basket by making my fruits disappear after collision, I decided to have an initial instructions menu and a “Game Over” comment in the end with the display of the user’s highest score achieved during the game for enhanced user experience. I learned how to display text on the screen for this project!

See a video of my final game in progress:

The speed of the falling fruits increases the longer the game progresses to increase the difficulty:

CODE

My class Obj for the falling objects (the different fruits and stones):

class Obj {
  float posX, posY;
  float objWidth, objHeight;
  float speed;
  PImage objImg;
  int points;
  int imgCode;
  boolean scored; //to not keep adding score for basket height
  boolean visible; //to make it dissapear when fals in the basket

  //constructor
  Obj(int _imgCode, PImage _img) {
    objWidth = random(30, 60); 
    objHeight = objWidth*780/801; //original pic ratio = 801x780
    posX = random(width-objWidth)+objWidth/2; 
    posY = 0-objHeight/2;
    speed = random(1, 3);
    imgCode= _imgCode;
    objImg = _img;
    //points = 10;
    scored = false;
    visible = true;
    assign();
    println(speed);
  }

  void fallObj() {
    posY += speed;
    if (frameCount%300==0 && speed<25) //cap on speed so that it doesnt get too fast
    {
      speed*=1.2; //speed of game keeps increasing to increase difficulty
      //println(frameCount);
      //println(speed);
    }
  }

  void checkEdge() {
    if (posY > height+objHeight/2) {
      posX = random(width-objWidth)+objWidth/2;
      posY = 0-objHeight/2;
      scored = false;
      visible = true;
    }
  }

  void run() {
    fallObj();

    if (visible == true)
      image(objImg, posX, posY, objWidth, objHeight);

    checkEdge();
  }

  void assign()
  {
    if (imgCode == 0)
    {
      points = -50;
      speed++; //icrease the speed of rocks a bit
    }
    else if (imgCode == 1)
      points = 5;
    else if (imgCode == 2)
      points = 15;
    else if (imgCode == 3)
      points = 20;
  }
}

My class Basket:

class Basket 
{
  float posX, posY;
  float bWidth, bHeight;
  float speed;
  PImage bImg;
  //int points;

  //constructor
  Basket(PImage _img) 
  {
    bWidth = 180; 
    bHeight = bWidth/2; //original pic ratio = 
    posX = width/2; 
    posY = height-bHeight/2;
    speed=15;
    bImg = _img;
  }

  void run()
  {
    image(bImg, posX, posY, bWidth, bHeight);
    if (frameCount%300==0 && speed<40) //cap on speed so that it doesnt get too fast
    {
      speed*=1.5; //to increase speed of basket as game gets faster
    }
  }

  void shiftR() 
  {
    //if (posX<width-bWidth/2)
    if (posX<width-bWidth/2-speed)
      posX+=speed;
    else
      posX=width-bWidth/2;
    image(bImg, posX, posY, bWidth, bHeight);
  }

  void shiftL() 
  {
    if (posX>0+bWidth/2+speed)
      posX-=speed;
    else
      posX=bWidth/2;
    image(bImg, posX, posY, bWidth, bHeight);
  }
}

The main draw() function:

Obj[] objects;
Basket basket;
PImage[] objImgs;
PImage basketImg;
PImage bg; //background image for game
int score = 0;
int maxScore = 0; //to display in the end
boolean started = false; //to keep track of start screen
color beige = color(225, 198, 153);

//==================================================================================================

void setup() {
  size(1000, 650);
  //fullScreen();

  //loading the background
  bg = loadImage("bg7.jpeg");
  //loading the various object images - fruits, basket and stone
  objImgs = new PImage[4];
  objImgs[1] = loadImage("apple1.png"); //code 1 = apple = 5 points
  objImgs[2] = loadImage("mango1.png"); //code 2 = mango = 15 ponits
  objImgs[3] = loadImage("pear1.png");  //code 3 = pear = 20 ponts
  objImgs[0] = loadImage("rock.png");   //code 0 = enemy = -50 points
  basketImg = loadImage("basket4.png");

  rectMode(CENTER);
  imageMode(CENTER);

  //creating my objects for the game
  basket = new Basket(basketImg);
  //falling objects
  objects = new Obj[9];
  for (int i=0; i<objects.length; i++)
  {
    int imgCode = i%objImgs.length;
    objects[i] = new Obj(imgCode, objImgs[i%objImgs.length]);
  }
}

//==================================================================================================

void draw() 
{
  if (started == false)
    startScreen();

  else 
  {
    //background(bg);
    image(bg, width/2, height/2, width, height);

    basket.run();
    for (int i = 0; i<objects.length; i++) {
      objects[i].run();
    }

    scoring();
  }
}

//==================================================================================================

void keyPressed() 
{
  if (started)
  {
    if (key == 'd' || (key == CODED && keyCode == RIGHT))
      basket.shiftR(); 
    if (key == 'a' || (key == CODED && keyCode == LEFT))
      basket.shiftL();
  }
  if (key == ENTER)
    started = true;
  if (key == BACKSPACE)
    endScreen();
}

//==================================================================================================

void scoring()
{
  fill(255);
  textSize(30);
  textAlign(LEFT);
  text("SCORE: " + score, 20, 40);

  //check if any fruit caught in basket
  for (int i=0; i<objects.length; i++)
  {
    if (objects[i].posX+objects[i].objWidth/2>=basket.posX-basket.bWidth/2 &&
      objects[i].posX-objects[i].objWidth/2<=basket.posX+basket.bWidth/2 &&
      objects[i].posY>=basket.posY-basket.bHeight/3 &&
      objects[i].scored == false) 
    {
      score+=objects[i].points;
      objects[i].scored = true;
      objects[i].visible = false;
    }
  }

  if (maxScore<score) 
    maxScore=score;  

  if (score<0)
    endScreen();
}

//==================================================================================================

void startScreen()
{
  background(beige);
  fill(255);
  textAlign(CENTER);
  textSize(40);
  text("SHREYA'S FARMLAND", width/2, height/4);
  textSize(20);
  text("Collect the fruits in the basket,", width/2, height/3);
  text("BEWARE of the stones!", width/2, height/3+25);
  textSize(15);
  text("Points gained are as follows:", width/2, height/3+80);
  //textAlign(LEFT,CENTER);
  text("Apple :  +05", width/2, height/3+100);
  text("Mango:  +15", width/2, height/3+120);
  text("Pear    :  +20", width/2, height/3+140);
  text("Stone  :  -50", width/2, height/3+160);
  textSize(15);
  text("Press ENTER to start the game, press BACKSPACE to quit", width/2, height*3/4);
  //text("Press BACKSPACE to quit", width/2, height*3/4);
}

//==================================================================================================

void endScreen()
{
  noLoop();
  //tint(255,50);
  background(beige);
  //noTint();
  fill(255);
  textAlign(CENTER);
  textSize(40);
  text("GAME OVER!", width/2, height/2-40);
  textSize(20);
  text("Your Highest Score: " + maxScore, width/2, height/2+20);
}

Leave a Reply