/*
Katherine Hua, Min Lee
Section A
khua@andrew.cmu.edu, mslee@andrew.cmu.edu
Final Project
*/
var characters = []; // images of the characters are loaded into this array
var mushroom; // image of mushroom that represents # of lives left is loaded into this variable
var cloud; // image of cloud is loaded into this variable
var cloudsX = []; //cloud's randomized x positions will be loaded into this array
var cloudsY = []; //cloud's randomized y positions will be loaded into this array
var cloudsSpeed = []; // cloud's randomized speed will be loaded into this array
var gameMode = 0; // determines which screen you see; title screen = 0, game = 1, game over = 2, instructions = 3
var lives = 3; // you start out with 3 lives
var score = 0; // you begin with a score of 0
var characterIndex; //assigns a number value to a character
var pipeNumber; // //assigns each pipe a number value
var characterX; //keeps track of the character's X values
var characterY = 180; //keeps track of the character's Y values
var characterSpeed = -3; // //keeps track of the character's speed
var pipesOccupied; //the pipe number that's occupied by a character
var newIndex; //new number value assigned to character
function preload() {
//links of character images
var characterImages = [];
mushroom = loadImage("https://i.imgur.com/V1vjvug.png"); //mushroom that is meant to represent # of lives
cloud = loadImage("https://i.imgur.com/7PCTEzk.png"); //clouds that will be floating by in the background
characterImages[0] = "https://i.imgur.com/Bf9U7OE.png"; //mario
characterImages[1] = "https://i.imgur.com/s7o5h2m.png"; //peach
characterImages[2] = "https://i.imgur.com/xQqZiZb.png"; //toad
characterImages[3] = "https://i.imgur.com/0bcsmDZ.png"; //bowser
characterImages[4] = "https://i.imgur.com/Wv9Dkzj.png"; //wario
characterImages[5] = "https://i.imgur.com/AkWIiIw.png"; //boo
//initial cloud positions
for (var i = 0; i < 4; i++) {
//setting the clouds to random initial positions
cloudsX[i] = width + i * 50;
cloudsY[i] = random(0, 130);
cloudsSpeed[i] = random(0.25, 1); // moving the clouds at a randomized speed between 0.25 and 1
};
//load images of characters into characters array
//allows for object to change characters
characters[0] = loadImage(characterImages[0]);
characters[1] = loadImage(characterImages[1]);
characters[2] = loadImage(characterImages[2]);
characters[3] = loadImage(characterImages[3]);
characters[4] = loadImage(characterImages[4]);
characters[5] = loadImage(characterImages[5]);
}
function setup() {
createCanvas(600, 400);
//random positions of character
pipeNumber = round(random(0, 5)); //randomly chooses which pipe the character will appear at
pipesOccupied = pipeNumber; //corresponding pipe number
characterX = width / 7 * (pipeNumber + 1) + 60; //character's x position that will determine their corresponding pipe
characterIndex = round(random(0, 5)); //randomly chooses which character will appear
}
function draw() {
background(21, 30, 99); // setting background to the blue
//creating the clouds; just loading the image onto the canvas; they do not move here yet
for (var i = 0; i < 4; i++) {
image(cloud, cloudsX[i], cloudsY[i]);
};
displayHill(); // creating the hill landscape moving in the background
//part of the Title Screen
//images of characters spread out across title screen
if (gameMode === 0) {
for (var x = 0; x < 6; x++) {
image(characters[x], (width/7 * (x+1)) - (width / 7 / 2) + 10, 110);
}
}
gameScreen(); // this is the actual game
displayChutes(); // creates the green chutes/pipes
displayGround(); // creates the criss-crossed red brick ground
titleScreen(); // this is the starting, default screen
drawScoreTally(); // keeps track of the score in gameScreen and gameOverScreen
instructionsScreen(); // instructions screen
displayButtons(); // creates the buttons in the actual game part
gameOverScreen(); // the game over screen after you lose all your lices
//clouds are made to move here at randomized speeds
for (var i = 0; i < cloudsX.length; i++) {
cloudsX[i] -= cloudsSpeed[i];
if (cloudsX[i] < -60) {
cloudsX[i] = width;
};
};
}
function drawScoreTally() {
if (gameMode === 1) { // we only want this visual element to appear in the gameScreen
push();
//making the yellow rectangle where the score will be kept
fill(181, 141, 38);
strokeWeight(2);
stroke(255, 229, 163);
rectMode(CENTER);
rect(width / 2, 35, 70, 20, 3);
noStroke();
//adding the actual text on top of the rectangle
fill(255,229, 163);
textAlign(CENTER);
text("Score:" + " " + score, width / 2, 40)
//text saying that if user wishes to restart the game at any point, the user can just press the key 'x' to restart the game
text("Press 'x' to restart game", width/2, 60);
pop();
};
}
//creating the generative hill landscape in the background of the game
function displayHill() {
var hill = 0.006
var hillSec = 0.0007;
push();
stroke(25, 83, 19);
beginShape();
for (i = 0; i < width; i ++) {
var h = (millis() * hillSec) + (i * hill);
var y = map(noise(h), 0, 1, 200, 100);
line(i, y, i, height);
}
endShape();
pop();
}
// creating the brick ground
function displayGround() {
push();
strokeWeight(2);
stroke(211, 71, 71);
fill(181, 38, 38);
rectMode(CORNER);
rect(0, 260, 600, 400-260);
for (var k = 0; k < 60; k ++) {
line(k * 10, 260, k * 10, 400);
line(0, 260 + k * 10, 600, 260 + k * 10);
}
pop();
}
//creating the green chutes/pipes
function displayChutes() {
if (gameMode === 0 || gameMode === 1 || gameMode === 3) { //we want the chutes/pipes to appear in all screens except the game over screen
push();
//creating 6 chutes/pipes
for (var j = 1; j < 7; j++) {
rectMode(CENTER); //drawing rectangles from the center rather than the corner
fill(43, 130, 58);
strokeWeight(3);
stroke(81, 163, 95);
rect((width/7) * j, 220, 65, 80, 2); //body of the chute
rect((width/7) * j, 200, 75, 40, 2); //head of the chute
strokeWeight(7);
//giving the pipes the highlight on their left side
line(55 + (width/7 * (j-1)), 186, 55 + (width/7 * (j-1)), 215);
line(60 + (width/7 * (j-1)), 225, 60 + (width/7 * (j-1)), 255);
}
pop();
}
}
function titleScreen() {
push();
if (gameMode === 0 || gameMode === 3) { // we want the title screen to appear in title screen and instructions screen
//creating title of the game: "Pipe it Up"
stroke(155, 115, 0);
strokeWeight(8);
fill(255, 203, 57);
textAlign(CENTER);
textSize(70);
textFont('arial')
text("PIPE IT UP", width / 2, (height / 3 * 2) + 60);
push();
//creating the two yellow buttons on bottom of screen
fill(155, 115, 0);
strokeWeight(2);
stroke(255, 230, 57);
rect(150, 350, 125, 35, 4); //left button
rect(325, 350, 125, 35, 4); // right button
textSize(15);
//creating the text on top of buttons
textFont('Arial');
strokeWeight(1);
stroke(255, 203, 57);
fill(255, 203, 57);
text("START GAME", 213, 373);
text("INSTRUCTIONS", 388, 373);
pop();
}
pop();
//changes cursor to hand when hovering over start button and instructions button
if (mouseX > 150 & mouseX < 275 && mouseY < 385 && mouseY > 350) {
cursor(HAND);
if (mouseIsPressed) {
gameMode = 1;
};
} else if (mouseX > 325 & mouseX < 450 && mouseY > 350 && mouseY < 385) {
cursor(HAND);
if (mouseIsPressed) {
gameMode = 3;
};
} else {
cursor(ARROW);
}
}
function instructionsScreen() {
if (gameMode === 3) { //we only want the instructions screen to occur during instructions Screen time
push();
//creating an lowered opacity blanket of black across the title screen
fill(0, 0, 0, 200);
rect(0,0,600,400);
//title of the instructions screen: "Instructions"
textSize(30);
strokeWeight(1);
stroke(255, 188, 0);
fill(255, 188, 0);
textAlign(CENTER);
text("INSTRUCTIONS", width/2, 80);
//purpose of the game
fill(255);
strokeWeight(1);
stroke(255);
textSize(18);
text("Keep the Mushroom Kingdom safe!", width/2, 125);
//instructions of the game
textSize(15);
noStroke();
textAlign(LEFT);
text("There are 6 pipes leading into the kingdom, each one assigned to a button \n on your keyboard : S, D, F, J, K, L. \n \n It is your job as royal guard to delegate who enters and who does not. \n Unfortunately, some baddies will try to infiltrate the kingdom \n and make you lose your job. But luckily, with the tap of a button, \n you can immediately prevent them from entering. \n But be careful! Reject a civilian and you'll get a demerit. \n Three demerits is a disadulation.", 50, 160);
textAlign(CENTER);
strokeWeight(1);
stroke(255);
text("Press 'x' to return to Start page", width/2, 350);
pop();
}
}
function gameScreen() {
if (gameMode === 1) {
//----------------------------- get random character to pop up at different location -------
//places character down
image(characters[characterIndex], characterX, characterY);
//if character reaches peak, move in opposite direction
if (characterY < 100) {
characterSpeed = characterSpeed * -1
//if character reaches bottom of the pipe, changes character randomly and goes to new pipe
} else if (characterY > 200) {
characterSpeed = characterSpeed * -1
characterIndex = round(random(0, 5))
pipesOccupied = round(random(0, 5))
characterX = width / 7 * (pipesOccupied) + 60
};
characterY += characterSpeed;
//----------------------------- correct answer / wrong answer ------------------------------
//draws lives as mushrooms
for (var i = 0; i < lives; i++) {
image(mushroom, 60 + (40 * i), 20)
};
//once lives reaches 0, game ends
if (lives === 0) {
gameMode = 2
};
}
}
function displayButtons() {
//draws each of the buttons
var txt = ["S", "D", "F", "J", "K", "L"];
if (gameMode === 1) {
push();
stroke(137, 144, 200);
strokeWeight(5);
//changing color of button if respective key is pressed
//if 's' key is pressed
if (keyIsPressed) {
if (key === 's') {
fill(39, 49, 124);
} else {
fill(91, 100, 168);
}
} else {
fill(91, 100, 168);
}
ellipse((width/7), height - height/7, 40, 40);
// if 'd' key is pressed
if (keyIsPressed) {
if (key === 'd') {
fill(39, 49, 124);
} else {
fill(91, 100, 168);
}
} else {
fill(91, 100, 168);
}
ellipse((width / 7) * 2, height - (height / 7), 40, 40);
// if 'f' key is pressed
if (keyIsPressed) {
if (key === 'f') {
fill(39, 49, 124);
} else {
fill(91, 100, 168);
}
} else {
fill(91, 100, 168);
}
ellipse((width / 7) * 3, height - (height / 7), 40, 40);
// if 'j' key is pressed
if (keyIsPressed) {
if (key === 'j') {
fill(39, 49, 124);
} else {
fill(91, 100, 168);
}
} else {
fill(91, 100, 168);
}
ellipse((width / 7) * 4, height - (height / 7), 40, 40);
// if 'k' key is pressed
if (keyIsPressed) {
if (key === 'k') {
fill(39, 49, 124);
} else {
fill(91, 100, 168);
}
} else {
fill(91, 100, 168);
}
ellipse((width / 7) * 5, height - (height / 7), 40, 40);
// if 'l' key is pressed
if (keyIsPressed) {
if (key === 'l') {
fill(39, 49, 124);
} else {
fill(91, 100, 168);
}
} else {
fill(91, 100, 168);
}
ellipse((width / 7) * 6, height - (height / 7), 40, 40);
// adding the text on top of the button
pop();
for (var j = 1; j < 7; j++) {
push();
fill(200, 184, 220);
noStroke();
textAlign(CENTER);
textSize(19);
text(txt[j - 1], (width / 7) * j , height - (height / 7) + 5);
pop();
}
}
}
function gameOverScreen() {
//press 'r' to restart
if (gameMode === 2) {
//title of the gameOverScreen: "game over"
push();
fill(11, 16, 90, 200);
rect(0, 0, 600, 400);
textSize(50);
fill(239, 0, 42);
strokeWeight(5);
stroke(145, 0, 26);
textAlign(CENTER);
text("GAME OVER", width/2, height/2 - 20);
//displays your score
textSize(30);
strokeWeight(2);
text("Score:" + " " + score, width/2, height/2 + 20);
//tells you how you can play again and restart the game
textSize(20);
strokeWeight(1);
fill(239, 0, 42);
text("Press 'r' to restart game", width/2, height/2 + 70);
pop();
};
}
function keyPressed() {
//if game hasn't started yet, press S to start
//if game started, press X to go to home screen
//if game over, press r to restart
if (gameMode === 1) {
if (key === 'x' || key === 'X') {
gameMode = 0;
level = 1;
score = 0;
// if character is bad guy, add point for pressing; else subtract life
//if character is good guy, subtract life for pressing and add point for not pressing
} else {
for (var i = 0; i < characters.length; i++) {
var keys = ["s", "d", "f", "j", "k", "l"]
if (pipesOccupied === i) {
//if good guy,
if (characterIndex === 0 || characterIndex === 1 || characterIndex === 2) {
if (key === keys[i]) {
lives -= 1
}
} else if (characterIndex === 3 || characterIndex === 4 || characterIndex === 5 & key === keys[i]) {
score += 1
} else if (characterIndex === 3 || characterIndex === 4 || characterIndex === 5 && key != keys[i]) {
lives -= 1
}
};
};
}
//GAMEOVER - press R to restart the game
} else if (gameMode === 2) {
if (key === 'r' || key === 'R') {
gameMode = 0;
level = 1;
score = 0;
lives = 3;
};
//INSTRUCTIONS - press X to go to title screen
} else if (gameMode === 3) {
if (key === 'x' || key === 'X') {
gameMode = 0;
};
};
}
Instructions:
Keep the Mushroom Kingdom safe! There are 6 pipes leading into the Mushroom Kingdom and each one is assigned to a respective button on your keyboard : S, D, F, J, K, L. It is your job as a royal guard of the kingdom to delegate who is able to enter and who is not. Unfortunately, some villains will try to infiltrate the kingdom and make you lose your job. Luckily, with the tap of a button, you can immediately prevent them from entering. But be careful! Reject a civilian and you’ll lose a demerit (life). “Three demerits is a disadulation” – Jim Halpert from The Office. haha
Statements:
What we have produced for our final project is a completely different direction than what we had proposed to do in our Proposal. After lots of thought, we decided to change the topic of our final project because we believed this was something that could best reflect our creativity and have a more set goal (and also in celebration of Nintendo’s release of Super Smash Bros Ultimate today).
In regards to the distribution of work, Min and I worked in person together throughout the whole process. In the beginning, we would just sit next to each other, create a visual element, and then come together to merge whatever the two of us came up with. After the visual aspects were basically all done and we had to get to the interactive component of the game, that is when things started to get a little more difficult. We had to communicate a lot and constantly merge our codes to make sure that we are using the same variables, going towards the same direction, and following the same general train of thought. In the end, I focused more on the game design and structure while Min focused more on gameplay and functionality.
In the beginning, we had wanted characters to be coming out of all six chutes randomly, but at all times. But, we couldn’t figure out how to prevent characters from appearing in pipes that another character already occupies. So, we shifted our goals to stick to one pipe. We also could not figure out how to debug our code so that if no key is pressed to capture a “bad” character, a life is lost, or vice versa, if no key is pressed to let a “good character remain free, points are added to the score. However other than those two problems, the rest of the game came out quite successfully.