xxxxxxxxxx
326
let x1,
x2,
scrollSpeed = 4; //values for scrolling background
let startTime,
countdown,
timeLimit = 60; //countdown timer
let atkCharge = 0,
score = 0; //initial values for attack charge and score
var bestScore = 0; //high score
let gameState = "start"; //current game state
let serial;
let protagImg, copImg, copCarImg, vinylImg, cassetteImg, startScreenImg;
let gameBG; //background image
let bgm;
let powerUpSound;
let gameFont;
let cops = [],
collectibles = [],
projectiles = []; //arrays to store enemies, power-up collectibles and projectile attacks
let protag;
let enemySpawnTimer = 0,
collectibleSpawnTimer = 0; //timer for spawning enemies and power-ups
let START = 0,
ATTACK = 0,
JUMP = 0; //input flags for actions from arduino
function preload() {
//load images and music
gameBG = loadImage("bg.png");
protagImg = loadImage("protag.png");
copImg = loadImage("cop.png");
copCarImg = loadImage("copcar.png");
vinylImg = loadImage("vinyl.png");
cassetteImg = loadImage("cassette.png");
startScreenImg = loadImage("startscreen.jpg");
bgm = loadSound("fire-ex.mp3");
gameFont = loadFont("Slackey-Regular.ttf");
}
function setup() {
createCanvas(600, 400);
x1 = 0; //intial position of first background segment
x2 = width; //initial position of second background segment
protag = new Protag(); //create protagonist object
let savedBestScore = localStorage.getItem("bestScore"); //retrieve best saved score from previous games
if (savedBestScore !== null) {
//set best score
bestScore = int(savedBestScore);
}
}
function draw() {
//display respective screens depending on game state
if (gameState === "start") {
drawStartScreen();
} else if (gameState === "instruction") {
drawInstructionScreen();
} else if (gameState === "playing") {
runGame();
} else if (gameState === "end") {
drawEndScreen();
}
//play background music
bgm.playMode("untilDone");
bgm.play();
}
function runGame() {
// set up countdown timer
let currentTime = millis();
let elapsedTime = (currentTime - startTime) / 1000;
countdown = timeLimit - floor(elapsedTime);
if (countdown < 0) {
gameState = "end";
}
// load background
image(gameBG, x1, 0, width, height);
image(gameBG, x2, 0, width, height);
fill("rgb(71,97,113)");
rect(0, height - height / 3.2, width, height / 3.2);
// background scrolling
x1 -= scrollSpeed;
x2 -= scrollSpeed;
if (x1 < -width) x1 = x2 + width;
if (x2 < -width) x2 = x1 + width;
// display protagonist
protag.display();
protag.move();
spawnEntities(); //spawn enemies and power-ups
// handle collisions between objects
handleCollisions();
// handle attacks
handleAttacks();
// draw attack charge bar and timer
drawHUD();
}
function drawStartScreen() {
image(startScreenImg, 0, 0, width, height);
if (START === 1) {
gameState = "instruction";
START = 0; // reset START to prevent immediate state transition
}
}
function drawInstructionScreen() {
//display game instructions
background("rgb(255,131,40)");
fill(255);
textFont(gameFont);
textSize(16);
textAlign(CENTER, CENTER);
text("INSTRUCTIONS", width / 2, 50);
textSize(14);
text(
"You wake up as a Taiwanese guitarist in the 1990s \n" +
" and you're running late to a gig!\n" +
"1. Use the controls to jump and attack.\n" +
"2. Avoid contact with Cops and Cop Cars.\n" +
"3. Collect Vinyl and Cassettes to charge attacks.\n" +
"4. Press ATTACK to fire projectiles.\n" +
"5. Cars take 2 attacks to destroy. Cops take just 1.\n" +
"6. Score points by blasting enemies out of your way.\n" +
"Your band awaits!",
width / 2,
height / 2
);
textSize(20);
text("Press START to begin the game", width / 2, height - 50);
if (START === 1) {
startTime = millis();
gameState = "playing"; //begin main game
START = 0;
}
}
function drawEndScreen() {
background("rgb(253,144,98)");
fill(255);
textAlign(CENTER);
textSize(35);
textSize(20);
text("Your Score: " + score, width / 2, height / 2);
text("High Score: " + bestScore, width / 2, height / 2.5);
text("Press START to return to the start screen", width / 2, height / 2 + 60);
if (START === 1) {
restartGame();
gameState = "start"; // return to start screen
START = 0;
}
}
function restartGame() {
//reset variables for new game
score = 0;
startTime = millis();
countdown = timeLimit;
gameState = "start";
}
function spawnEntities() {
//check if enough time (2 seconds) has passed to spawn an enemy so that they don't overlap
if (millis() - enemySpawnTimer > 2000) {
//spawn either cop or cop car
if (random() > 0.5) {
cops.push(new Cop());
} else {
cops.push(new CopCar());
}
//update spawn timer to current time
enemySpawnTimer = millis();
}
if (millis() - collectibleSpawnTimer > 3000) {
if (random() > 0.5) {
collectibles.push(new Vinyl());
} else {
collectibles.push(new Cassette());
}
collectibleSpawnTimer = millis();
}
}
function handleCollisions() {
// handle collisions with cops and cop cars
for (let i = cops.length - 1; i >= 0; i--) {
cops[i].display();
cops[i].move();
if (protag.hits(cops[i])) {
// decrease score on contact
score = max(0, score - 1); // prevent negative score
if (cops[i] instanceof CopCar) {
cops[i].hitsRemaining--;
if (cops[i].hitsRemaining <= 0) {
cops.splice(i, 1); // remove CopCar if hit enough times
}
} else {
cops.splice(i, 1); // remove Cop
}
}
}
// handle collisions with power-up collectibles
for (let i = collectibles.length - 1; i >= 0; i--) {
collectibles[i].display();
collectibles[i].move();
if (protag.hits(collectibles[i])) {
if (collectibles[i] instanceof Cassette) {
atkCharge = min(3, atkCharge + 1); // cap attack charge at 3
} else if (collectibles[i] instanceof Vinyl) {
atkCharge = min(3, atkCharge + 2);
}
collectibles.splice(i, 1); // remove collectible after contact
}
}
}
function handleAttacks() {
if (ATTACK === 1 && atkCharge > 0) {
projectiles.push(new Projectile(protag.x + 20, protag.y + 10));
atkCharge -= 1; // decrease attack charge when an attack is used
ATTACK = 0;
}
// move and display projectiles
for (let i = projectiles.length - 1; i >= 0; i--) {
projectiles[i].move();
projectiles[i].display();
// check for collisions with enemies
for (let j = cops.length - 1; j >= 0; j--) {
if (projectiles[i].hits(cops[j])) {
if (cops[j] instanceof CopCar) {
cops[j].hitsRemaining--;
if (cops[j].hitsRemaining <= 0) {
score += 2; // increase score for defeating cop car
cops.splice(j, 1);
}
} else {
score += 1; // increase score for defeating cop
cops.splice(j, 1);
}
projectiles.splice(i, 1); // remove projectile upon hit
break; // break out of loop since the projectile is gone
}
}
}
}
function readSerial(data) {
//read input from arduino
if (data != null) {
let fromArduino = split(trim(data), ",");
if (fromArduino.length == 3) {
JUMP = int(fromArduino[0]);
ATTACK = int(fromArduino[1]);
START = int(fromArduino[2]);
}
}
}
function windowResized() {
print("resized to " + windowWidth + "," + windowHeight);
resizeCanvas(windowWidth, windowHeight);
}
function keyPressed() {
//press space bar to establish serial connection
if (key === " ") {
setUpSerial();
}
}
function keyTyped() {
if (key === "f") {
toggleFullscreen();
}
}
function toggleFullscreen() {
let fs = fullscreen(); // Get the current state
fullscreen(!fs); // Flip it!
}
function drawHUD() {
// timer display
fill(0);
textSize(16);
textFont(gameFont);
textAlign(RIGHT, TOP);
text("Time: " + countdown, width - 20, 10);
// attack charge bar display
let barX = width - 110;
let barY = 40;
let barWidth = 100;
let barHeight = 20;
let barFillWidth = (atkCharge / 3) * barWidth; // width based on atkCharge (max 3)
// bar background
noStroke();
fill(200);
rect(barX, barY, barWidth, barHeight);
fill('red');
rect(barX, barY, barFillWidth, barHeight);
// score display
fill(0);
textAlign(LEFT, TOP);
textSize(20);
text("Score: " + score, 20, 10);
}