Your browser does not support the canvas tag.

previous        Show / Hide Source        Download        next
/*
Designer: Mosang Liu
Instructor: Nicolas Hesler
Oct.3,2018
*/

//////////////////////////////////////////////////////////////////////////////////////////////////////////
///////Controls///////////////////////////////////////////////////////////////////////////////////////////
///////Press any key to start/////////////////////////////////////////////////////////////////////////////
///////Click mosue button to fire/////////////////////////////////////////////////////////////////////////
///////The charactor will following the cursor which is insteaded by an aim icon//////////////////////////
///////The farther the aim is, the charactor moves faster/////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////
///////Game play//////////////////////////////////////////////////////////////////////////////////////////
///////Zombies will chase you and respawn after you kill them/////////////////////////////////////////////
///////Kill them or avoid them////////////////////////////////////////////////////////////////////////////
///////Get score by kill zombie and survive longer////////////////////////////////////////////////////////
///////Difficulty will increase based on servive time/////////////////////////////////////////////////////
///////There are 7 difficulties///////////////////////////////////////////////////////////////////////////
///////No one can survive in difficulty 7/////////////////////////////////////////////////////////////////
///////Let's try your limit///////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////


//interface variables
int waitTime;
int serviveTime = 0;
int serviveRank = 0;
int score = 0;
int highScore = 0;
int difficulty;
boolean gameStart = false;
//player's variables 
float playerX = 200;
float playerY = 200;
float playerSpeed = 4;
float playerMoveX = 0;
float playerMoveY = 0;
float distance = sqrt(sq(mouseX-playerX)+sq(mouseY-playerY));
float directionRadian = sqrt(1-sq(cos((mouseY-playerY)/distance)));

//zombie's variables
//zombie1
float zombie1X = -25;
float zombie1Y = random(-25, 425);
float zombie1MoveX = 0;
float zombie1MoveY = 0;
float zombie1Distance = sqrt(sq(playerX-zombie1X)+sq(playerY-zombie1Y));
float zombie1DirectionRadian = sqrt(1-sq(cos((playerY-zombie1Y)/zombie1Distance)));
float zombie1Speed;
//zombie2
float zombie2X = 425;
float zombie2Y = random(-25, 425);
float zombie2MoveX = 0;
float zombie2MoveY = 0;
float zombie2Distance = sqrt(sq(playerX-zombie2X)+sq(playerY-zombie2Y));
float zombie2DirectionRadian = sqrt(1-sq(cos((playerY-zombie2Y)/zombie2Distance)));
float zombie2Speed;

//shooting variables
float fireX, fireY;
float aimX, aimY;
float bulletX, bulletY;
float bulletSpeed = 30;

void setup() {
  size(400, 400);
  frameRate(50);
  ellipseMode(CENTER);
  rectMode(CENTER);
  noCursor();
}

void draw() {
  //draw//
  background(0);
  drawTexture();
  drawAim();
  drawPlayer();
  drawZombie1();
  drawZombie2();
  scoreCard();
  //update//
  playerAction();
  zombie1Move();
  zombie2Move();
  collisionTest();
  hitTest1();
  hitTest2();
  timeUpdate();
  updateDifficulty();

  //if game is not started, run game menu  
  if (gameStart == false) {
    gameStartScreen();
  }
}

//Press any key to start
void gameStartScreen() {
  waitTime = millis();
  textSize(30);
  fill(125, 255, 43);
  text("Use mouse to move", 60, 100);
  fill(55, 12, 232);
  text("Click to fire", 115, 150);
  fill(255);
  text("Press any key to start", 45, 300);
  if (keyPressed) {
    gameStart = true;
  }
}

//update servive time
//servive time = millis() - wait time in manu interface
void timeUpdate() {

  if (gameStart==true) {
    serviveTime = (millis() - waitTime)/1000;
  }
}

//score card include scores, high scores and difficulty
void scoreCard() {
  fill(255);
  textSize (15);
  text ( "Servive time:" + serviveTime, 10, 16);
  text ( "Score:" + (score + serviveTime), 160, 16);
  text ( "Difficulty:" + difficulty, 300, 16);
  text ("Highscores:         Suervive time "  + serviveRank + "          " + "Score" + highScore, 10, 386);
}

//increase zombie speed based on servive time
void updateDifficulty() {
  if (serviveTime<=10) {
    zombie1Speed = 1200/zombie1Distance;
    zombie2Speed = 1200/zombie2Distance; 
    difficulty = 1;
  } else if (serviveTime>10 && serviveTime<=20) {
    zombie1Speed = 1500/zombie1Distance;
    zombie2Speed = 1500/zombie2Distance;
    difficulty = 2;
  } else if (serviveTime>20 && serviveTime<=30) {
    zombie1Speed = 1800/zombie1Distance;
    zombie2Speed = 1800/zombie2Distance;
    difficulty = 3;
  } else if (serviveTime>30 && serviveTime<=60) {
    zombie1Speed = 2100/zombie1Distance;
    zombie2Speed = 2100/zombie2Distance;
    difficulty = 4;
  } else if (serviveTime>60 && serviveTime<=120) {
    zombie1Speed = 2400/zombie1Distance;
    zombie2Speed = 2400/zombie2Distance;
    difficulty = 5;
  } else if (serviveTime>120 && serviveTime<=180) {
    zombie1Speed = 2700/zombie1Distance;
    zombie2Speed = 2700/zombie2Distance;
    difficulty = 6;
  }else {
    zombie1Speed = 3200/zombie1Distance;
    zombie2Speed = 3200/zombie2Distance;
    difficulty = 7; 
  }
}

//background texture
//full fill screen with 20*20 rectangles filled by random() function.
void drawTexture() {
  int size = 20;
  for (int x = 0; x < width/size+1; ++x) {
    for (int y = 0; y <height; ++y) {
      noStroke();
      fill(random(80, 130), 8, 10);
      rect(x*size, y*size, size, size);
    }
  }
}

//Aim icon instead of cursor
void drawAim() {
  noFill();
  stroke(0);
  strokeWeight(2);
  ellipse(mouseX, mouseY, 25, 25);
  point(mouseX, mouseY);
  line(mouseX, mouseY-8, mouseX, mouseY-17);
  line(mouseX, mouseY+8, mouseX, mouseY+17);
  line(mouseX-8, mouseY, mouseX-17, mouseY);
  line(mouseX+8, mouseY, mouseX+17, mouseY);
}

//drawPlayer features and bullet
//hand and gun will turn to the direction of the aim 
void drawPlayer() {
  //bullet
  stroke(150, 250, 0);
  strokeWeight(5);
  point(bulletX, bulletY);
  
  //gun
  stroke(0);
  line(playerX, playerY, playerX+(mouseX-playerX)*50/distance, playerY+(mouseY-playerY)*50/distance);

  //Body
  noStroke();
  fill(248, 197, 116);
  ellipse(playerX, playerY, 25, 25);
  
  //hand
  stroke(0);
  strokeWeight(1);
  ellipse(playerX+(mouseX-playerX)*25/distance, playerY+(mouseY-playerY)*25/distance, 10, 10);
  
  //halmet
  fill(102, 62, 42);
  ellipse(playerX+(mouseX-playerX)*2/distance, playerY+(mouseY-playerY)*2/distance, 18, 18);
  fill(229, 206, 223);
  ellipse(playerX+(mouseX-playerX)*2/distance, playerY+(mouseY-playerY)*2/distance, 15, 15);
}

//record fire position coordinates and aim coordinates when mouse is pressed
void mousePressed() {
  fireX=playerX+(mouseX-playerX)*50/distance;
  fireY=playerY+(mouseY-playerY)*50/distance;
  aimX=mouseX;
  aimY=mouseY;
}

//player actions
//player move towards aim
//assign bullet coordinates by fire position.
void playerAction() {
  if (gameStart) {
    //move
    playerMoveX=(mouseX-playerX)/distance * playerSpeed;
    playerMoveY=(mouseY-playerY)/distance * playerSpeed;
    playerX+=playerMoveX;
    playerY+=playerMoveY;
    //mouse click to shoot
    if (mousePressed) {
      bulletX = fireX;
      bulletY = fireY;
    }



 /* shooting system debug notes
 At first the way I calculate bulletSpeed in Y derection by this function:     float bulletDistance = sqrt(sq(aimX-fireX)+sq(aimY-fireY));
                                                                               float bulletDirectionRadian = sqrt(1-sq(cos((aimY-fireY)/bulletDistance)));
 However, the problem is: by this way bulletY can not larger                   if (shoot==true){
 than bulletX. It Caused player can't shootingin a huge area.                    if(fireX<aimX){
                                                                                   if(fireY>aimY){
 so I changed to the mathematic method, which lead to another bug:                   bulletX += bulletSpeed*cos(bulletDirectionRadian);
 player can't shoot up and down. I speculated two reasons:                           bulletY -= bulletSpeed*sin(bulletDirectionRadian);
                                                                                     }else{
     1. bulletSpeedX is a constant. So if aimY very close to fireY,                  bulletX += bulletSpeed*cos(bulletDirectionRadian);
     bulletSpeedY will become super large and beyong the limits of human             bulletY += bulletSpeed*sin(bulletDirectionRadian);
     vison.                                                                          }
                                                                                   }
     2. because float is not precise, so if two values are two close, they     if(fireX>aimX){
     are abbreviated into the same number.                                        if(fireY>aimY){
                                                                                     bulletX -= bulletSpeed*cos(bulletDirectionRadian);
I can't sovle this problem, but I find a nother way to make up.                      bulletY -= bulletSpeed*sin(bulletDirectionRadian);
I defined a small area up and down. so if aimX is in this area,                      }else{
it shoot straight up or down.                                                        bulletX -= bulletSpeed*cos(bulletDirectionRadian);
                                                                                     bulletY += bulletSpeed*sin(bulletDirectionRadian);
                                                                                     }
                                                                                   }
                                                                                 }
     */

    //bullet movement logic
    if (fireX<=aimX+25 && fireX>=aimX-25) {
      if (fireY>=aimY) {
        bulletY -= bulletSpeed;
      }
      if (fireY<aimY) {
        bulletY += bulletSpeed;
      }
    }
    if (fireX<aimX-25) {

      bulletX += bulletSpeed;
      bulletY += bulletSpeed*(aimY-fireY)/(aimX-fireX);
    }
    if (fireX>aimX+25) {
      bulletX -= bulletSpeed;
      bulletY -= bulletSpeed*(aimY-fireY)/(aimX-fireX);
    }
  }
}

//zombie functions
//if game started, zombies are moving toward player
//hands have 4 directions follow player's position

//draw zombie1
void drawZombie1() {
  float z1Hand1X = cos(zombie1DirectionRadian-PI/6)*15;
  float z1Hand1Y = sin(zombie1DirectionRadian-PI/6)*15;
  float z1Hand2X = cos(zombie1DirectionRadian+PI/6)*15;
  float z1Hand2Y = sin(zombie1DirectionRadian+PI/6)*15;
  noStroke();
  //zombie1 body
  fill(133, 119, 40);
  ellipse(zombie1X, zombie1Y, 25, 25);
  
  //zombie1 hands
  //hand1
  stroke(0);
  fill(133, 119, 40);
  if (zombie1X > playerX) {
    if (zombie1Y > playerY) {
      ellipse(zombie1X-z1Hand1X, zombie1Y-z1Hand1Y, 10, 10 );
    }
    if (zombie1Y < playerY) {
      ellipse(zombie1X-z1Hand1X, zombie1Y+z1Hand1Y, 10, 10 );
    }
  }
  if (zombie1X < playerX) {
    if (zombie1Y > playerY) {
      ellipse(zombie1X+z1Hand1X, zombie1Y-z1Hand1Y, 10, 10 );
    }
    if (zombie1Y < playerY) {
      ellipse(zombie1X+z1Hand1X, zombie1Y+z1Hand1Y, 10, 10 );
    }
  }
  //hand2  
  if (zombie1X > playerX) {
    if (zombie1Y > playerY) {
      ellipse(zombie1X-z1Hand2X, zombie1Y- z1Hand2Y, 10, 10 );
    }
    if (zombie1Y < playerY) {
      ellipse(zombie1X-z1Hand2X, zombie1Y+ z1Hand2Y, 10, 10 );
    }
  }
  if (zombie1X < playerX) {
    if (zombie1Y > playerY) {
      ellipse(zombie1X+z1Hand2X, zombie1Y- z1Hand2Y, 10, 10 );
    }
    if (zombie1Y < playerY) {
      ellipse(zombie1X+z1Hand2X, zombie1Y+ z1Hand2Y, 10, 10 );
    }
  }
}

//zombie1 Movement logics
void zombie1Move() {
  if (gameStart) {
    zombie1MoveX=(playerX-zombie1X)/distance * zombie1Speed;
    zombie1MoveY=(playerY-zombie1Y)/distance * zombie1Speed;
    zombie1X+=zombie1MoveX;
    zombie1Y+=zombie1MoveY;
  }
}

//draw zombie2
void drawZombie2() {
  float z2Hand1X = cos(zombie2DirectionRadian-PI/6)*15;
  float z2Hand1Y = sin(zombie2DirectionRadian-PI/6)*15;
  float z2Hand2X = cos(zombie2DirectionRadian+PI/6)*15;
  float z2Hand2Y = sin(zombie2DirectionRadian+PI/6)*15;
  noStroke();
  //zombie2 body
  fill(133, 119, 40);
  ellipse(zombie2X, zombie2Y, 25, 25);
  
  //zombie2 hands
  //hand1
  stroke(0);
  strokeWeight(1);
  fill(133, 119, 40);
  if (zombie2X > playerX) {
    if (zombie2Y > playerY) {
      ellipse(zombie2X-z2Hand1X, zombie2Y-z2Hand1Y, 10, 10 );
    }
    if (zombie2Y < playerY) {
      ellipse(zombie2X-z2Hand1X, zombie2Y+z2Hand1Y, 10, 10 );
    }
  }
  if (zombie2X < playerX) {
    if (zombie2Y > playerY) {
      ellipse(zombie2X+z2Hand1X, zombie2Y-z2Hand1Y, 10, 10 );
    }
    if (zombie2Y < playerY) {
      ellipse(zombie2X+z2Hand1X, zombie2Y+z2Hand1Y, 10, 10 );
    }
  }
  //hand2  
  if (zombie2X > playerX) {
    if (zombie2Y > playerY) {
      ellipse(zombie2X-z2Hand2X, zombie2Y- z2Hand2Y, 10, 10 );
    }
    if (zombie2Y < playerY) {
      ellipse(zombie2X-z2Hand2X, zombie2Y+ z2Hand2Y, 10, 10 );
    }
  }
  if (zombie2X < playerX) {
    if (zombie2Y > playerY) {
      ellipse(zombie2X+z2Hand2X, zombie2Y- z2Hand2Y, 10, 10 );
    }
    if (zombie2Y < playerY) {
      ellipse(zombie2X+z2Hand2X, zombie2Y+ z2Hand2Y, 10, 10 );
    }
  }
}
//zombie2 Movement logics
void zombie2Move() {
  if (gameStart) {
    zombie2MoveX=(playerX-zombie2X)/distance * zombie2Speed;
    zombie2MoveY=(playerY-zombie2Y)/distance * zombie2Speed;
    zombie2X+=zombie2MoveX;
    zombie2Y+=zombie2MoveY;
  }
}


void collisionTest() {
  float zombie1Distance = sqrt(sq(playerX-zombie1X)+sq(playerY-zombie1Y));
  float zombie2Distance = sqrt(sq(playerX-zombie2X)+sq(playerY-zombie2Y));
  if (zombie1Distance < 35 || zombie2Distance < 35 ) {
    reset();
  }
}

//hit tests
void hitTest1() {
  float bulletDistance1 = sqrt(sq(zombie1X-bulletX)+sq(zombie1Y-bulletY));
  if (bulletDistance1 <25 ) {
    killZombie1();
  }
}
void hitTest2() {
  float bulletDistance2 = sqrt(sq(zombie2X-bulletX)+sq(zombie2Y-bulletY));
  if ( bulletDistance2 <25) {
    killZombie2();
  }
}

//reset zombies
void killZombie1() {
  zombie1X = -25;
  zombie1Y = random(-25, 425);
  score+=1;
  resetBullet();
}
void killZombie2() {
  zombie2X = 425;
  zombie2Y = random(-25, 425);
  score+=1;
  resetBullet();
}

//reset bullet
void resetBullet() {
  bulletX = playerX;
  bulletY = playerY;
}

//reset game and record highscores
void reset() {
  gameStart = false;
  playerX = 200;
  playerY = 200;
  zombie1X = -25;
  zombie1Y = random(-25, 425);
  zombie2X = 425;
  zombie2Y = random(-25, 425);
  resetBullet();
  zombie1Speed = 1200/zombie1Distance;
  zombie2Speed = 1200/zombie2Distance;
  if (highScore < score+serviveTime) {
    highScore = score+serviveTime;
  }
  score = 0;
  if (serviveRank < serviveTime) {
    serviveRank = serviveTime;
  }
  serviveTime = 0;
  resetBullet();
}