Midterm Refined

Inspiration

For this week’s project, I wanted to use Arduino controls to refine what I did in my midterm. To refresh your memory, my midterm was a two-player game where the players ran around the map competing for candies. The game was ok, but the main bug was that I couldn’t have the two players moving at one time. This was due to the limitations of “Keypressed function”, as it wasn’t able to differentiate which player was pressing the keys.

By introducing Arduino into the game, I could simply create another “console” for player 2, so the player controls won’t be mixed up in the game.

Limitations

There were two limitations to using Arduino as a “console.” One is more obvious, which is the fact that the kit buttons aren’t that responsive, and aren’t really stationary. While I was playing the game with it, the buttons would slip off a lot of the times.

The second limitation is the problem of the breadboard. There are four buttons in the kit, so just enough for the four directions. However, the breadboard didn’t have the horizontal space to put an “up” and “down” button next to each other. As a compromise, I had to put the four buttons in a row, with the middle right being “up” and middle left “down”. Here is the final layout:

Challenges

The main challenge I ran into for this project was trying to integrate Arduino into my midterm project code. My midterm code was rather chaotic with a lot of functions, so it took a lot of time trying to piece together where the Arduino parts of the code would fit into the logic.

Full code

Arduino:

int button1 = 2;
int button2 = 12;
int button3 = 6;
int button4 = 8;

void setup() {
  // put your setup code here, to run once:
  pinMode(button1, INPUT);
  pinMode(button2, INPUT);
  pinMode(button3, INPUT);
  pinMode(button4, INPUT);
  Serial.begin(9600);
}

void loop() {
  int buttonState1 = digitalRead(button1);
  int buttonState2 = digitalRead(button2);
  int buttonState3 = digitalRead(button3);
  int buttonState4 = digitalRead(button4);
  char inByte=Serial.read();
  Serial.print(buttonState1);
  Serial.print(',');
  Serial.print(buttonState2);
  Serial.print(',');
  Serial.print(buttonState3);
  Serial.print(',');
  Serial.println(buttonState4);
}

Main Code:

PFont font;                              

import processing.serial.*;
Serial myPort;
boolean left = false;
boolean right = false;
boolean up = false;
boolean down = false;

import processing.sound.*;            //all the sounds in this game
SoundFile eat;
SoundFile die;
SoundFile music;

Ball b1;                              //initializing the two players
Ball b2;
PImage ninja, warrior, bg;
PImage[][] ninjawalk;
PImage[][] warriorwalk;
int ndirection = 0;
int wdirection = 0;
int nstep = 0;
int wstep = 0;
int speed = 5;

Candy[] candy;                        //initializing all the candy
int count;
color chosen;
int livecount = 0;                    //how many edible candy there are

boolean go = false;                   //start game value

void setup() {
  size(1200, 800);
  bg = loadImage("bg.jpg");
  
  printArray(Serial.list());
  String portname=Serial.list()[3];
  println(portname);
  myPort = new Serial(this,portname,9600);
  myPort.clear();
  myPort.bufferUntil('\n');
  
  //sounds
  eat = new SoundFile(this, "eat.mp3");
  die = new SoundFile(this, "die.mp3");
  music = new SoundFile(this, "music.mp3");
  music.loop();
  
  //slicing the two sprite sheets for player 1 and 2
  ninja = loadImage("ninja.png");
  ninjawalk = new PImage[4][3];
  int w = ninja.width/3;
  int h = ninja.height/4;
  for(int iy = 0; iy < 4; iy++){
    for(int ix = 0; ix<3; ix++){
      ninjawalk[iy][ix] = ninja.get(ix*w, iy *h, w, h);
    }
  }
  warrior = loadImage("warrior.png");
  warriorwalk = new PImage[4][3];
  int ww = warrior.width/3;
  int wh = warrior.height/4;
  for(int wy = 0; wy < 4; wy++){
    for(int wx = 0; wx<3; wx++){
      warriorwalk[wy][wx] = warrior.get(wx*ww, wy *wh, ww, wh);
    }
  }
  //use this function to restart the game
  reset();
}

void draw() {
  menu();
  if (go == true){
    begin();
  }
}

void serialEvent(Serial myPort){
  String s=myPort.readStringUntil('\n');
  s=trim(s);
  if (s!=null){
    int values[]=int(split(s,','));
    if (values.length==4){
      println(values);
      if (values[0] == 1){
        right = true;
      }
      else if (values[0] == 0){
        right = false;
      }
      if (values[1] == 1){
        left = true;
      }
      else if (values[1] == 0){
        left = false;
      }
      if (values[2] == 1){
        up = true;
      }
      else if (values[2] == 0){
        up = false;
      }
      if (values[3] == 1){
        down = true;
      }
      else if (values[3] == 0){
        down = false;
      }
    }
  }
}

//this function restarts the game from the gaming interface directly
void reset(){
  float unit = 200;
  b1 = new Ball(width/4, height - 20, 1);          //player 1 character
  b2 = new Ball(width*3/4, height - 20, 2);        //player 2 character
  
  int wideCount = width / int(unit);               //calling the candies, initializing different candy colors
  int highCount = (height*3/4) / int(unit);
  count = wideCount * highCount;
  candy = new Candy[count];
  int index = 0;
  color[] colors = new color[6];
  colors[0] = #FF0011;
  colors[1] = #BA00FF;
  colors[2] = #2500FF;
  colors[3] = #00FFE8;
  colors[4] = #00FF01;
  colors[5] = #FFAF00;
  chosen = colors[int(random(0,5))];
  for (int y = 0; y < highCount; y++) {
    for (int x = 0; x < wideCount; x++) {
      candy[index++] = new Candy(x * unit + unit/2, y * unit + unit/2, 20, 20, colors[int(random(0,5))]);
    }
  }
  
  for (Candy candy : candy) {                      //counting how many edible candies there are to know the win condition
    if (chosen == candy.c){
      livecount ++;
    }
  }
  
  for (int i =0; i<count;i++){                     //candy speed initializing
     candy[i].xSpeed = random(-2,2);
     candy[i].ySpeed = random(-2,2);
  }
}

//this is the starting menu
void menu(){
  background(bg);
  fill(255);
  font = createFont("BreatheFire-65pg.ttf", 60);
  textFont(font);
  textAlign(CENTER);
  text("CANDY RUSH \n even heros like candy", width/2, 185);
  font = createFont("Georgia", 40);
  textFont(font, 30);
  text("This is a two player game. Ninja and Warrior wants to take a break \n on their quest, so they are going to catch candy. Some candies are \n poisonous, and only the candy indicator can tell you which candies are edible. \n The first player to catch all the edible candy wins. \n NINJA: WASD, WARRIOR: direction keys", width /2, 370);
  stroke(0);
  fill(0);
  rectMode(CENTER);
  rect(width/2, 680, 200, 72);
  fill(255);
  textFont(font, 60);
  font = createFont("BreatheFire-65pg.ttf", 42);
  textFont(font);
  textAlign(CENTER);
  text("PLAY", width/2, 700);
  if (mouseX>=(width/2 - 100)  & mouseX<=(width/2 + 100) & mouseY>=650 & mouseY<=720){
    stroke(255);
    noFill();
    rect(width/2, 680, 200, 72);
    if (mousePressed){
        go = true;
    }
  }
}

//this is the game starting function. the actual game is ran here
void begin(){
  background(bg);
  textSize(24);
  text(b1.score, width/4, height - 50);
  text(b2.score, width*3/4, height - 50);
  
  //calling candy functions
  for (Candy candy : candy) {
    candy.display();
    candy.collision();
    candy.update();
    candy.checkEdges();
  }
  
  //this is the candy indicator
  stroke(0);
  fill(chosen);
  ellipse(width/2, height - b1.diameter, b1.diameter, b1.diameter);
  
  //calling character functions
  b1.display();
  b1.move();
  b2.display();
  b2.move();
  checkwin();
}

void checkwin(){
  if (livecount == 0){                      //win condition
    for (Candy candy : candy) {
      candy.xSpeed = 0;
      candy.ySpeed = 0;
    }
    rectMode(CENTER);
    if (b1.score > b2.score){
      fill(0);
      rect(width/2, height/2, width, height);
      fill(255, 255, 255);
      textSize(32);
      text("Ninja Wins! \n (Press r to restart)", width/2, height/2); 
    }
    else if (b2.score > b1.score){
      fill(0);
      rect(width/2, height/2, width, height);
      fill(255, 255, 255);
      textSize(32);
      text("Warrior Wins! \n (Press r to restart)", width/2, height/2);
    }
    else if (b2.score == b1.score){
      fill(0);
      rect(width/2, height/2, width, height);
      fill(255, 255, 255);
      textSize(32);
      text("Tie! \n (Press r to restart)", width/2, height/2);
    }
    
    //restart button
    if (keyPressed) {                            
       if (key == 'r' || key == 'R') {
          reset();
       }
    }
  }
}

Player Class:

class Ball {

  float x;
  float y;
  float diameter;
  int score;
  int player;
  int w = ninja.width/3;
  int h = ninja.height/4;
  int ww = warrior.width/3;
  int wh = warrior.height/4;

  Ball(float tempX, float tempY, int tempPlayer) {
    x = tempX;
    y = tempY;
    diameter = 45;
    score = 0;
    player = tempPlayer;
  }

  void display() {
    imageMode(CENTER);                                    //displaying player 1
    if (player == 1) {
      image(ninjawalk[ndirection][nstep], x, y, w, h);
    }
    if (player == 2) {                                     //displaying player 2
      image(warriorwalk[wdirection][wstep], x, y, ww, wh);
    }
  }

  void move() {  
    if (player == 1) {
      if (keyPressed) {
        if (key == 'w' || key == 'W') {
          y = y - 5;
          ndirection = 0;
        }
        if (key == 'a' || key == 'A') {
          x = x - 5;
          ndirection = 3;
        }
        if (key == 's' || key == 'S') {
          y = y + 5;
          ndirection = 2;
        }
        if (key == 'd' || key == 'D') {
          x = x + 5;
          ndirection = 1;
        }
        if (frameCount%speed == 0) {
          nstep = (nstep + 1) %3;
        }
      }
    }

    if (player == 2) {
      if (up == true) {
        y = y - 5;
        wdirection = 0;
      }
      if (left == true) {
        x = x - 5;
        wdirection = 3;
      }
      if (down == true) {
        y = y + 5;
        wdirection = 2;
      }
      if (right == true) {
        x = x + 5;
        wdirection = 1;
      }
      if (frameCount%speed == 0) {
        wstep = (wstep + 1) %3;
      }
    }
  }
}

Candy Class:

class Candy {
  float x;
  float y;
  float w;
  float h;
  float xSpeed, ySpeed;
  boolean caught = false;
  color c;

  Candy(float tempX, float tempY, float tempW, float tempH, color tempC) {
    x = tempX;
    y = tempY;
    w = tempW;
    h = tempH;
    c = tempC;
    xSpeed = ySpeed = 0;
  }  

  void display() {

    ellipseMode(CENTER);
    if (caught == false){
      fill(c);
      ellipse(x, y, w, h);
    }
  }
  
  //collision function for when players catch the right and wrong candy
  void collision() {
    if ((b1.x - b1.w *3/4 < x && x < b1.x + b1.w*3/4) & (b1.y - b1.h*3/4 < y && y < b1.y + b1.h*3/4)){
      if (c == chosen){
        x = -50;
        y = -50;
        caught = true;
        b1.score ++;
        livecount --;
        eat.play();
      }
      else if (c != chosen){
        b1.x = width/4;
        b1.y = height - b1.diameter;
        die.play();
      }
    }
    if ((b2.x - b2.w*3/4 < x && x < b2.x + b2.w*3/4) & (b2.y - b2.h*3/4 < y && y < b2.y + b2.h*3/4)){
      if (c == chosen){
        x = -50;
        y = -50;
        caught = true;
        b2.score ++;
        livecount --;
        eat.play();
      }
      else if (c != chosen){
        b2.x = width*3/4;
        b2.y = height - 20;
        die.play();
      }
    }
  }
  
  void update(){
    x += xSpeed;
    y += ySpeed;
  }
  
  void checkEdges() {
    if (y>(height*3)/4) {
      ySpeed = -ySpeed;
    }
    if (y<0) {
      ySpeed = -ySpeed;
    }
    if (x>width) {
      xSpeed = -xSpeed;
    }
    if (x<0) {
      xSpeed = -xSpeed;
    }
  }
}

Demonstration

Leave a Reply