/*
Min Jun Kim & Han Yu
minjunki && hyu1
15104
Final Project
*/
//This Project is a spaceship fighting game.
var px = 0; //x location of ship
var py = 240; //starting y location of our ship
var psize = 50; //size of ourship
var keys = []; //for controlling sihp
var Selfprojectiles = []; //array of our projectiles
var projectileCounter = 100; //for controlling flow of projectiles
var SOE = []; //array of enemy ships
var shipSpeed = 3; //our speed for moving ship
var distanceOne = []; //distance between projectile and enemy
var distanceTwo = []; //distance between projectile and enemy
var SOEnumb = 2; //number of enemies
var timer = 0; //for controlling flow of game
var gameStage = 1; //starting stage of the game
var timerIncrement = 1; //another variable for controlling flow of game
var enemyProjectiles = []; //array of enemy bullets
var EPdistance = []; //distance between enemy bullets and our ship
var ourHealth = 10; //how much health we have left
var gameOver = 0; //activated when user dies
var clickStart = 0; //for controlling start of game
var clickCount = 0; //for making sure no double clicks
var stars = []; //array of stars in background
var imageOne; //image
var imageTwo; //image
var imageThree; //image
//making background stars
function makeStars(x, y, size) {
return {x: x, y: y, size: size, draw: starRender, update: starUpdate}
}
//draws stars as ellipse
function starRender() {
fill(255);
ellipse(this.x, this.y, this.size);
}
//scrolling speed of stars
function starUpdate() {
this.x -= 3;
}
//making our bullets
function makeOurProjectile(x, y, fast, powerful) {
return {x: x, y: y, speed: fast, power: powerful,
drawIt: OPrender, updateIt: OPupdate}
}
//making enemy bullets
function makeEnemyProjectile(x,y, speedo,status) {
return {x: x, y: y, speed: speedo, boss: status,
updir: random(-5,5), drawIt: EPrender, updateIt: EPupdate}
}
//draws bullets, depending on type of enemy
function EPrender() {
//if boss, have balls instead of lines
if (this.boss == true) {
fill(0);
stroke(250,0,0);
ellipse(this.x, this.y, 10, 10);
}
//standard line bullets from enemy
else {
stroke(250, 0, 0);
strokeWeight(3);
line(this.x, this.y, this.x-30, this.y);
}
}
//controls the movement of enemy bullets depends on enemy type
function EPupdate() {
//if boss, then make bullets bounce and have random speed
if (this.boss == true) {
this.y += this.updir;
this.x -= this.speed*random(1,2);
//bounces bullets
if (this.y > height || this.y < 0) {
this.updir = -this.updir;
}
}
//all other enemies have one dimensional attacks
else {
this.x -= this.speed;
}
}
//draws our bullets as green lines
function OPrender() {
stroke(0,200,0);
strokeWeight(this.power);
line(this.x, this.y, this.x+30, this.y);
}
//moves the bullet across map
function OPupdate() {
this.x += this.speed;
}
//the specs of enemy ships
function makeSOEnemy(x, y, fast, sizing) {
return {x: x, y: y, big: sizing, speed: fast,
drawIt: SOErender, update: SOEupdate, DMG: 0}
}
//controls the movement of enemy ships depending on size and location
function SOEupdate() {
//moves the enemy along the map
if (gameStage == 1) {
this.y += this.speed;
}
if (gameStage == 2) {
this.y += this.speed;
}
//if 2nd round 1st enemy, move back as game goes on
if (gameStage == 2 & this.x < 500) {
this.x += 0.1;
}
if (this.y > height) {
this.speed = -this.speed;
}
if (this.y < 0) {
this.speed = -this.speed;
}
//first round enemy with easy projectiles
if (gameStage == 1) {
if (random(1) > 0.98 & this.y < 600) {
enemyProjectiles.push(makeEnemyProjectile(this.x, this.y,5));
}
}
//2nd round enemy, with more frequent bullets
if (gameStage == 2 & this.big < 90) {
if (random(1) > 0.93 && this.y < 600) {
enemyProjectiles.push(makeEnemyProjectile(this.x, this.y, 5));
}
}
//pulls our ship closer and make projectiles more frequently
if (gameStage == 2 & this.big > 90) {
if (random(1) > 0.95 && this.y < 600) {
enemyProjectiles.push(makeEnemyProjectile(this.x, this.y,
random(4,6,1), true));
}
px+=0.3;
}
}
//draws the enemies depending on the size input.
function SOErender() {
fill(100)
//draw the boss and health bar
if (gameStage == 2 & this.big > 90) {
push();
fill(250,0,0);
rectMode(CORNER);
rect(5,5,width-this.DMG*6.4,20);
pop();
push();
translate(this.x-85, this.y-60);
scale(0.37, 0.37);
image(imageThree,0, 0);
pop();
}
//the standard enemy
else {
//health bar
push();
fill(220, 0, 0);
rect(this.x, this.y - this.big/1.5,
this.big - this.DMG*this.big/20, 10);
pop();
//standard enemy
push();
translate(this.x-this.big*1.4, this.y-this.big*0.5);
scale(0.0035*this.big, 0.0035*this.big);
image(imageTwo, 0, 0);
pop();
}
}
//==============================================================
//sets up the keys and the initial stars
function setup() {
createCanvas(640,480)
keys = [false, false, false, false, false]
for (i = 0; i < 200; i++) {
stars[i]=makeStars(random(width), random(height), random(5));
}
imageOne = loadImage("https://i.imgur.com/GHtg2vU.png");
imageTwo = loadImage("https://i.imgur.com/svK8OkI.png");
imageThree = loadImage("https://i.ibb.co/n0G0yGC/eae.png");
}
function draw() {
//basic initial specs
background(17,21,61);
rectMode(CENTER);
noStroke();
fill(100);
//projectile counter increases by 1
projectileCounter += 1;
//increase timer by timer increment
timer += timerIncrement;
//makes first round of enemies when timer is 1
if (timer == 1 & gameStage == 1) {
for (i = 0; i < SOEnumb; i ++) {
SOE[i] = makeSOEnemy(500,random(height), random(5), random(40,60));
}
timer += 1;
}
//states the first round
if (timer > 1 & timer < 40) {
textAlign(CENTER);
push();
stroke(255);
fill(255);
textSize(20);
text("Round One!", width/2, height/2-100);
pop();
}
//pauses the increase in timer
if (timer > 41) {
timerIncrement = 0;
}
//resumes timer once 1st round is finished
if (gameStage == 2) {
timerIncrement = 1;
}
//once timer has started moving again, write text stating the stage
if (timer > 50 & timer < 150) {
push();
noStroke();
fill(255);
textSize(20);
text("Final Stage!", width/2, height/2-100);
pop();
}
//makes 2 more enemies once timer reaches 60
if (timer == 60 & gameStage == 2) {
SOE[0] = makeSOEnemy(400,random(height), 6.5, 30);
SOE[1] = makeSOEnemy(550, height/2, 3, 100);
timer += 1;
}
//move right if rightarrow is pressed
if (keys[0] == true) {
px += shipSpeed;
}
//move left if leftarrow is pressed
if (keys[1] == true) {
px -= shipSpeed;
}
//move up if uparrow is pressed
if (keys[2] == true) {
py -= shipSpeed;
}
//move down if downarrow is pressed
if (keys[3] == true) {
py += shipSpeed;
}
//add projectiles once shift is pressed and reset counter
if (keys[4] == true & projectileCounter > 5) { //8
Selfprojectiles.push(makeOurProjectile(px+50, py, 10, 3))
projectileCounter = 0;
}
//for teleporting up and down once limit is reached
if (py > height) {
py = 0;
}
if (py < 0) {
py = height;
}
//prevents user from going behind map by pushing back
if (px < -5) {
px += 3;
}
//prevents user from moving too forward by pushing back
if (px > width/2) {
px -= 3.3;
}
//makes random star objects, and initializes the functions
noStroke()
for (i = 0; i < 1; i++) {
stars.push(makeStars(width, random(height), random(5)));
}
for (i = 0; i < stars.length; i++) {
stars[i].draw();
stars[i].update();
//if stars if too far behind map, shift the array
if (stars[i].x < -500) {
stars.shift();
}
}
//initializes the enemoies
for (i = 0; i < SOEnumb; i ++) {
SOE[i].drawIt();
SOE[i].update();
}
//initializes the enemy projectiles
for (i = 0; i < enemyProjectiles.length; i++) {
enemyProjectiles[i].drawIt();
enemyProjectiles[i].updateIt();
}
//initializes our projectiles
for (i = 0; i < Selfprojectiles.length; i++) {
Selfprojectiles[i].drawIt();
Selfprojectiles[i].updateIt();
//calculates distance if the array is nonzero and projectiles
//are still on map
if (Selfprojectiles.length > 0 & Selfprojectiles[i].x < 700) {
//calculate the distance between enemy 1 and projectile
distanceOne[i] = dist(Selfprojectiles[i].x,
Selfprojectiles[i].y, SOE[0].x, SOE[0].y);
//calculate distance between enemy 2 and projectiles
distanceTwo[i] = dist(Selfprojectiles[i].x,
Selfprojectiles[i].y, SOE[1].x, SOE[1].y);
//if off map shift distances and projectiles
if (Selfprojectiles[i].x > width) {
Selfprojectiles.shift();
distanceOne.shift();
distanceTwo.shift();
}
//if distance is smaller than the size of enemy 1 shift
if (distanceOne[i] < SOE[0].big) {
Selfprojectiles.shift();
distanceOne.shift();
distanceTwo.shift();
SOE[0].DMG ++;
}
//if distance is smaller than the size of enemy 2 shift
if (distanceTwo[i] < SOE[1].big) {
Selfprojectiles.shift();
distanceOne.shift();
distanceTwo.shift();
SOE[1].DMG ++;
}
//if enemy 1 reach damage cap, move off map prevent shooting
if (SOE[0].DMG > 20) {
SOE[0].y = 700;
SOE[0].x = 700;
SOE[0].speed = 0;
}
//if enemy 2 reach damage cap, move off map prevent shooting
if (SOE[1].DMG > 20 & gameStage == 1) {
SOE[1].y = 700;
SOE[1].x = 700;
SOE[1].speed = 0;
}
//if boss reach damage cap move off stage
if (SOE[1].DMG > 100 & gameStage == 2) {
SOE[1].y = 700;
SOE[1].x = 700;
SOE[1].speed = 0;
}
//if both enemies are killed during 2nd stage, end game
if (SOE[0].y == 700 & SOE[1].y == 700 && SOE[1].DMG > 99) {
gameStage = 3;
}
//if both enemnies killed during 1st stage, move up stage
if (SOE[0].y == 700 & SOE[1].y == 700 && gameStage == 1) {
gameStage =2;
}
stroke(255);
}
}
for (z = 0; z < enemyProjectiles.length; z++) {
//if enemy projectile array is nonzero, calculate distance
if (enemyProjectiles.length > 0) {
EPdistance[z] = dist(enemyProjectiles[z].x, enemyProjectiles[z].y,
px, py);
//if bullet goes off map, shift distance and bullet
if (enemyProjectiles[z].x < 0) {
enemyProjectiles.shift();
EPdistance.shift();
}
//if projectile too close to ourship, shift arrays and do damage
if (EPdistance[z] < 20 & timer != 0) {
enemyProjectiles.shift();
EPdistance.shift();
ourHealth -= 1;
}
//if health is zero, end game
if (ourHealth === 0) {
gameOver = 1;
}
}
}
//draw the health bar
push();
rectMode(CENTER);
noStroke();
fill(0,200,0);
rect(px+psize/2, py-psize/2, ourHealth*5,5);
pop();
//draw our ship
push();
noStroke();
fill(100);
translate(px-37,py-25);
scale(0.15,0.15);
image(imageOne, 0, 0);
pop();
//initial stage specs. Gives instructions
if (clickStart == 0) {
timer = 0;
timerIncrement = 0;
//black background
push();
fill(0);
rect(width/2, height/2, 1000, 1000);
pop();
//design and title
push();
fill(255, 253, 83, 140);
noStroke();
triangle(150, 100, 280, 600, 550, 500);
triangle(110, 100, 20, 500, 130, 500);
triangle(170, 105, 700, 50, 700, 300);
textFont("futura");
textStyle(ITALIC);
fill(0);
textSize(50);
text("Supreme", 500-85, 130-3);
text("Invasion", 541-85, 180-3);
pop();
//decoration
push();
translate(-150,90);
scale(0.6, 0.6);
rotate(-0.5);
image(imageOne, 0, 100);
pop();
//instructions
push();
noStroke();
fill(0,0,200);
fill(255);
textSize(30);
text("click to begin!", width/2+25, 440);
fill(0);
textSize(20);
text("Arrow Keys to move", width/2+5, 390);
text("Or WASD to move", width/2+5, 410);
text("Shift to shoot", width/2-35, 370);
pop();
}
//once clicked, the opening screen is changed
if (mouseIsPressed & clickCount == 0) {
clickCount += 1;
timerIncrement = 1;
clickStart = 1;
}
//if game over is active, then cover screen with message
if (gameOver > 0) {
fill(0);
rect(0,0, width*2, height*2);
fill(255);
noStroke();
textSize(50);
text("Game Over!", width/2, height/2);
textSize(30);
text("Press 'r' to restart", width/2, height/2+50);
push();
translate(350, 10);
scale(0.5, 0.5);
image(imageTwo, 0, 0);
pop();
}
//if end game is activated then cover screen and say end message
if (gameStage == 3) {
fill(0);
rect(width/2, height/2, 1000,1000);
push();
translate(460, 200);
rotate(0.3);
scale(0.8, 0.8);
image(imageThree, 0, 0);
pop();
push();
noStroke();
fill(255);
textSize(50);
text("You Win! :)", width/2, height/2);
textSize(20);
text("Press 'r' to play again", width/2, height/2+50);
pop();
}
}
//Controls======================================================
//==============================================================
//if key is pressed changes the array for controls
function keyPressed() {
if (key == "ArrowRight" || key == "d" || key == "D") {
keys[0] = true;
}
if (key == "ArrowLeft" || key == "a" || key == "A") {
keys[1] = true;
}
if (key == "ArrowUp" || key == "w" || key == "W") {
keys[2] = true;
}
if (key == "ArrowDown" || key == "s" || key == "S") {
keys[3] = true;
}
if (key == "Shift") {
keys[4] = true;
}
//resets game if press r
if (key == "r" || key == "R") {
gameOver = 0;
gameStage = 1;
timer = 0;
timerIncrement = 1;
clickCount = 0;
clickStart = 0;
ourHealth = 10;
px = 0;
py = 240;
}
}
//releases the keys
function keyReleased() {
if (key == "ArrowRight" || key == "d" || key == "D") {
keys[0] = false;
}
if (key == "ArrowLeft" || key == "a" || key == "A") {
keys[1] = false;
}
if (key == "ArrowUp" || key == "w" || key == "W") {
keys[2] = false;
}
if (key == "ArrowDown" || key == "s" || key == "S") {
keys[3] = false;
}
if (key == "Shift") {
keys[4] = false;
}
}
Instructions:
To play the game, click the initial screen to start the game. You can use either the arrow keys or “w” to move up, “a” to move left, “s” to move down, and “d” to move right (WASD). To shoot projectiles press SHIFT. The point of the game is to defeat all enemies. If you hit the enemy with your projectiles their health will go down, and once the enemy’s health hits zero, the enemy is considered defeated. On the second stage, the boss will pull the user towards the right side of the map, so be mindful. Lastly, the user cannot go horizontally beyond certain limits, and once the user reaches the top of the map the user will be teleported to the bottom of the map.
Statements:
For this final project, I focused a lot on the structure and gameplay (the backbone of the game), while my partner focused on the design aspect and some functionality of the game. My partner personally drew all the characters, which made the game look more unique. The code spans around 600 lines, and in my perspective, it was certainly difficult because of the number of objects (4) and their interaction with each other. I made sure to limit the number in arrays to make the gameplay run more smoothly and cleanly.
The gameplay is slightly different from the initial proposal, but for good reason. We initially planned to have three stages in the game, however that either made the game extremely long or difficult. We also didn’t implement a sound source for this game, because we wanted the users to be able to play the game directly in the browser. Despite these minute changes, the main concept and paradigms are true to the original.
All in all, this project was extremely fun to program. It certainly did take hours and hours to get it right, but in the end it all paid off because of the aesthetics and fun gameplay.