xxxxxxxxxx
882
const serial = new p5.WebSerial();
let portButton;
let inData;
let player;
let zombies = [];
let gameState = 'start';
let playerScore = 0;
let zombieKillCount = 0;
let zombieTypeKillCount = {
'GreenZombie': 0,
'DarkBlueZombie': 0,
'PurpleZombie': 0,
'BrownZombie': 0,
'SmallBrownZombie': 0,
'DarkGrayZombie': 0,
};
let xMin, xMax, yMin, yMax;
let replayTextHovered = false;
let powerUp;
let powerUpActive = false;
let powerUpDuration = 30;
let shieldPowerUp;
let shieldPowerUpActive = false;
let bulletSound;
let gameOverSound;
let gameOverSoundPlayed = false;
let backgroundImage;
function preload() {
bulletSound = loadSound('bulletnoise.mp3');
powerUpSound = loadSound('powerupsound.mp3');
shieldPowerUpSound = loadSound('powerupsound.mp3'); gameOverSound = loadSound('deathsound.mp3');
backgroundImage = loadImage('background.png');
}
function setup() {
createCanvas(1200, 800);
player = new Player();
xMin = 50;
xMax = width - 50;
yMin = 50;
yMax = height - 50;
if (!navigator.serial) {
alert("WebSerial is not supported in this browser. Try Chrome or MS Edge.");
}
navigator.serial.addEventListener("connect", portConnect);
navigator.serial.addEventListener("disconnect", portDisconnect);
// check for any ports that are available:
serial.getPorts();
// if there's no port chosen, choose one:
serial.on("noport", makePortButton);
// open whatever port is available:
serial.on("portavailable", openPort);
// handle serial errors:
serial.on("requesterror", portError);
// handle any incoming serial data:
serial.on("data", serialEvent);
serial.on("close", makePortButton);
textSize(40);
}
////////////
// DRAW ///
////////////
function draw() {
background(220);
background(backgroundImage);
drawSquare(300, 200, 50);
drawSquare(900, 200, 50);
drawSquare(300, 600, 50);
drawSquare(900, 600, 50);
if (gameState === 'start') {
displayStartScreen();
} else if (gameState === 'playing') {
textAlign(CENTER, TOP);
textSize(24);
fill(0);
text(`YOUR SCORE: ${playerScore}`, width / 2, 10);
text(`ZOMBIE KILLS: ${zombieKillCount}`, width / 2, 40);
textAlign(CENTER, BOTTOM);
textSize(26);
fill(0);
text(`${player.bulletCount}/${player.maxBulletCount} rounds`, width / 2, height - 10);
if (powerUpActive || shieldPowerUpActive) {
textSize(20);
let elapsedTimePowerUp = powerUpActive ? millis() - powerUp.activationTime : 0;
let elapsedTimeShield = shieldPowerUpActive ? millis() - shieldPowerUp.activationTime : 0;
if (shieldPowerUpActive) {
let remainingTimeShield = Math.max(0, Math.ceil((15000 - elapsedTimeShield) / 1000));
fill(255, 0, 0);
text(`Shield Power-Up: ${remainingTimeShield}s`, width / 2, height - 40);
}
if (powerUpActive) {
let remainingTimePowerUp = Math.max(0, Math.ceil((powerUpDuration * 1000 - elapsedTimePowerUp) / 1000));
fill(255, 0, 0);
text(`Infinite Ammo Power-Up: ${remainingTimePowerUp}s`, width / 2, height - 40);
}
}
for (let i = zombies.length - 1; i >= 0; i--) {
if (zombies[i]) {
zombies[i].update();
zombies[i].display();
for (let j = player.bullets.length - 1; j >= 0; j--) {
if (player.bullets[j].collide(zombies[i])) {
zombies[i].health--;
if (zombies[i].health <= 0) {
handleZombieKill(zombies[i]);
zombies.splice(i, 1);
}
player.bullets.splice(j, 1);
break;
}
}
if (player.collide(zombies[i])) {
gameState = 'gameOver';
resetPowerUp();
resetShieldPowerUp();
}
if (zombies[i] && zombies[i].offScreen()) {
zombies.splice(i, 1);
}
}
}
if (frameCount % 60 === 0) {
let rand = random();
if (rand < 0.3) {
zombies.push(new GreenZombie());
} else if (rand < 0.5) {
zombies.push(new PurpleZombie());
} else if (rand < 0.7) {
zombies.push(new DarkBlueZombie());
} else if (rand < 0.9) {
zombies.push(new BrownZombie());
} else {
zombies.push(new DarkGrayZombie());
}
}
player.update();
player.display();
if (powerUp && !powerUp.active) {
powerUp.update();
powerUp.display();
if (player.collidePowerUp(powerUp)) {
player.activatePowerUp();
powerUp.activate();
}
}
if (shieldPowerUp && !shieldPowerUp.active) {
shieldPowerUp.update();
shieldPowerUp.display();
if (player.collidePowerUp(shieldPowerUp)) {
player.activateShieldPowerUp();
shieldPowerUp.activate();
}
}
} else if (gameState === 'gameOver') {
displayGameOverScreen();
resetPowerUp();
resetShieldPowerUp();
}
if (gameState === 'gameOver' && !gameOverSoundPlayed) {
displayGameOverScreen();
}
}
/////////////////////////////
// SEND AND RECEIVE DATA ///
/////////////////////////////
let startRestartSensorPressed = false;
function serialEvent() {
let inString = serial.readStringUntil("\r\n");
if (inString) {
let sensors = split(inString, ",");
player.isMoving.up = sensors[0];
player.isMoving.down = sensors[0];
player.isMoving.left = sensors[1];
player.isMoving.right = sensors[1];
let sensor2Pressed = sensors[2];
if (sensor2Pressed) {
player.shoot();
}
player.rotate(sensors[3]);
let sensor4Value = sensors[4];
if (sensor4Value) {
if (!startRestartSensorPressed) {
startGame();
startRestartSensorPressed = true;
}
} else {
startRestartSensorPressed = false;
}
serial.write("x");
}
}
/////////////////////////////////////////////
// UTILITY FUNCTIONS TO MAKE CONNECTIONS ///
/////////////////////////////////////////////
// if there's no port selected,
// make a port select button appear:
function makePortButton() {
// create and position a port chooser button:
portButton = createButton("choose port");
portButton.position(10, 10);
// give the port button a mousepressed handler:
portButton.mousePressed(choosePort);
}
// make the port selector window appear:
function choosePort() {
serial.requestPort();
}
// open the selected port, and make the port
// button invisible:
function openPort() {
// wait for the serial.open promise to return,
// then call the initiateSerial function
serial.open().then(initiateSerial);
// once the port opens, let the user know:
function initiateSerial() {
console.log("port open");
serial.write("x");
}
// hide the port button once a port is chosen:
if (portButton) portButton.hide();
}
// pop up an alert if there's a port error:
function portError(err) {
alert("Serial port error: " + err);
}
// try to connect if a new serial port
// gets added (i.e. plugged in via USB):
function portConnect() {
console.log("port connected");
serial.getPorts();
}
// if a port is disconnected:
function portDisconnect() {
serial.close();
console.log("port disconnected");
}
function drawSquare(x, y, size) {
fill(255, 165, 0);
rectMode(CENTER);
rect(x, y, size, size);
}
function keyPressed() {
if (gameState === 'playing') {
player.keyPressed();
}
}
function keyReleased() {
if (gameState === 'playing') {
player.keyReleased();
}
}
function mouseMoved() {
if (gameState === 'playing') {
player.rotate(mouseX, mouseY);
} else if (gameState === 'gameOver') {
let replayTextX = width / 2;
let replayTextY = height / 2 + 60;
let replayTextWidth = textWidth('Replay');
let replayTextHeight = 24;
replayTextHovered =
mouseX > replayTextX - replayTextWidth / 2 &&
mouseX < replayTextX + replayTextWidth / 2 &&
mouseY > replayTextY - replayTextHeight / 2 &&
mouseY < replayTextY + replayTextHeight / 2;
}
}
function mousePressed() {
if (gameState === 'playing') {
player.shoot();
} else if (gameState === 'start') {
startGame();
} else if (gameState === 'gameOver') {
let replayTextX = width / 2;
let replayTextY = height / 2 + 60;
let replayTextWidth = textWidth('Replay');
let replayTextHeight = 24;
if (
mouseX > replayTextX - replayTextWidth / 2 &&
mouseX < replayTextX + replayTextWidth / 2 &&
mouseY > replayTextY - replayTextHeight / 2 &&
mouseY < replayTextY + replayTextHeight / 2
) {
resetGame();
}
}
}
function startGame() {
gameState = 'playing';
}
function resetGame() {
player = new Player();
zombies = [];
playerScore = 0;
zombieKillCount = 0;
zombieTypeKillCount = {
'GreenZombie': 0,
'DarkBlueZombie': 0,
'PurpleZombie': 0,
'BrownZombie': 0,
'SmallBrownZombie': 0,
'DarkGrayZombie': 0,
};
gameState = 'playing';
powerUp = null;
}
function displayStartScreen() {
textAlign(CENTER, CENTER);
textSize(32);
fill(255);
text('Click to Play', width / 2, height / 2);
}
function displayGameOverScreen() {
textAlign(CENTER, CENTER);
textSize(48);
fill(255,0,0);
text('DEAD', width / 2, height / 2 - 50);
textSize(24);
fill(255);
text(`YOUR SCORE: ${playerScore}`, width / 2, height / 2 - 10);
textSize(24);
fill(255);
text(`ZOMBIE KILLS: ${zombieKillCount}`, width / 2, height / 2 + 30);
textSize(30);
fill(replayTextHovered ? color(0, 255, 0) : 255);
text('Click to Replay', width / 2, height / 2 + 70);
if (!gameOverSoundPlayed) {
gameOverSound.play();
gameOverSoundPlayed = true;
}
}
function handleZombieKill(zombie) {
let points = 0;
if (zombie instanceof GreenZombie) {
points = 1;
} else if (zombie instanceof DarkBlueZombie) {
points = 2;
} else if (zombie instanceof PurpleZombie) {
points = 3;
} else if (zombie instanceof BrownZombie) {
points = 4;
} else if (zombie instanceof SmallBrownZombie) {
points = 2;
} else if (zombie instanceof DarkGrayZombie) {
points = 5;
}
playerScore += points;
zombieKillCount++;
zombieTypeKillCount[zombie.constructor.name]++;
if (zombieKillCount % 5 === 0) {
respawnPowerUp();
}
if (zombieKillCount === 10) {
respawnShieldPowerUp();
}
}
function respawnPowerUp() {
if (!powerUpActive) {
powerUp = new PowerUp();
}
}
function resetPowerUp() {
if (powerUpActive) {
powerUpActive = false;
player.bulletCount = player.maxBulletCount;
powerUp = null;
}
}
function respawnShieldPowerUp() {
if (!shieldPowerUpActive) {
shieldPowerUp = new ShieldPowerUp();
}
}
function resetShieldPowerUp() {
shieldPowerUpActive = false;
shieldPowerUp = null;
}
class Player {
constructor() {
this.x = width / 2;
this.y = height / 2;
this.radius = 20;
this.angle = 0;
this.speed = 5;
this.isMoving = {
up: false,
down: false,
left: false,
right: false,
};
this.bullets = [];
this.maxBulletCount = 12;
this.bulletCount = this.maxBulletCount;
this.reloading = false;
this.shieldPowerUpActive = false;
this.shieldPowerUp = null;
}
update() {
if (this.isMoving.up) this.move(0, -1);
if (this.isMoving.down) this.move(0, 1);
if (this.isMoving.left) this.move(-1, 0);
if (this.isMoving.right) this.move(1, 0);
console.log("Player position:", this.x, this.y);
for (let bullet of this.bullets) {
bullet.update();
}
if (this.shieldPowerUpActive && this.shieldPowerUp) {
let elapsedTime = millis() - this.shieldPowerUp.activationTime;
if (elapsedTime > 15000) {
this.deactivateShieldPowerUp();
} else {
return;
}
}
if (keyIsPressed && key === "R" && !this.reloading) {
this.reload();
}
for (let i = zombies.length - 1; i >= 0; i--) {
if (zombies[i] && this.collide(zombies[i])) {
if (!this.shieldPowerUpActive) {
gameState = 'gameOver';
resetPowerUp();
resetShieldPowerUp();
} else {
handleZombieKill(zombies[i]);
zombies.splice(i, 1);
}
}
}
}
display() {
fill(0, 0, 255);
ellipse(this.x, this.y, this.radius * 2);
for (let bullet of this.bullets) {
bullet.display();
}
}
move(xdir, ydir) {
let newX = this.x + xdir * this.speed;
let newY = this.y + ydir * this.speed;
if (newX > xMin && newX < xMax && newY > yMin && newY < yMax) {
this.x = newX;
this.y = newY;
}
}
rotate(targetX, targetY) {
this.angle = atan2(targetY - this.y, targetX - this.x);
}
shoot() {
if (!this.reloading && (powerUpActive || this.bulletCount > 0)) {
this.bullets.push(new Bullet(this.x, this.y, this.angle));
if (!powerUpActive) {
this.bulletCount--;
}
if (this.bulletCount === 0 && !powerUpActive) {
this.reload();
}
}
}
reload() {
this.reloading = true;
setTimeout(() => {
this.bulletCount = this.maxBulletCount;
this.reloading = false;
}, 1000);
}
collide(other) {
if (this && other) {
let d = dist(this.x, this.y, other.x, other.y);
return d < this.radius + other.radius;
} else {
return false;
}
}
collidePowerUp(powerUp) {
if (powerUp) {
let d = dist(this.x, this.y, powerUp.x, powerUp.y);
return d < this.radius + powerUp.size / 2;
} else {
return false;
}
}
activatePowerUp() {
powerUpActive = true;
this.bulletCount = Infinity;
powerUpSound.play();
}
deactivatePowerUp() {
powerUpActive = false;
this.bulletCount = this.maxBulletCount;
}
activateShieldPowerUp() {
this.shieldPowerUpActive = true;
shieldPowerUpSound.play();
}
deactivateShieldPowerUp() {
this.shieldPowerUpActive = false;
}
keyPressed() {
if (keyCode === 87) this.isMoving.up = true;
if (keyCode === 83) this.isMoving.down = true;
if (keyCode === 65) this.isMoving.left = true;
if (keyCode === 68) this.isMoving.right = true;
if (key === " ") this.shoot();
}
keyReleased() {
if (keyCode === 87) this.isMoving.up = false;
if (keyCode === 83) this.isMoving.down = false;
if (keyCode === 65) this.isMoving.left = false;
if (keyCode === 68) this.isMoving.right = false;
}
}
class Zombie {
constructor() {
let spawnX, spawnY;
let edge = floor(random(4));
if (edge === 0) {
spawnX = random(width);
spawnY = 0;
}
else if (edge === 1) {
spawnX = width;
spawnY = random(height);
}
else if (edge === 2) {
spawnX = random(width);
spawnY = height;
}
else if (edge === 3) {
spawnX = 0;
spawnY = random(height);
}
this.x = spawnX;
this.y = spawnY;
this.radius = 15;
this.speed = 2;
this.health = 1;
}
update() {
let angle = atan2(player.y - this.y, player.x - this.x);
let noiseFactor = random(0.5, 1.5);
this.x += this.speed * noiseFactor * cos(angle);
this.y += this.speed * noiseFactor * sin(angle);
}
display() {
fill(0, 255, 0);
ellipse(this.x, this.y, this.radius * 2);
}
offScreen() {
return this.x < 0 || this.x > width || this.y < 0 || this.y > height;
}
}
class GreenZombie extends Zombie {
constructor() {
super();
this.radius = 15;
this.speed = 2;
this.health = 1;
}
display() {
fill(0, 255, 0);
ellipse(this.x, this.y, this.radius * 2);
}
}
class PurpleZombie extends Zombie {
constructor() {
super();
this.radius = 25;
this.speed = 1.5;
this.health = 2;
}
display() {
fill(128, 0, 128);
ellipse(this.x, this.y, this.radius * 2);
}
}
class DarkBlueZombie extends Zombie {
constructor() {
super();
this.radius = 12;
this.speed = 3;
this.health = 1;
}
display() {
fill(0, 0, 128);
ellipse(this.x, this.y, this.radius * 2);
}
}
class BrownZombie extends Zombie {
constructor() {
super();
this.radius = 15;
this.speed = 2;
this.health = 2;
}
display() {
fill(139, 69, 19);
ellipse(this.x, this.y, this.radius * 2);
}
update() {
super.update();
for (let j = player.bullets.length - 1; j >= 0; j--) {
if (player.bullets[j].collide(this)) {
this.health--;
if (this.health <= 0) {
zombies.push(new SmallBrownZombie(this.x, this.y));
zombies.push(new SmallBrownZombie(this.x, this.y));
playerScore += 4;
zombies.splice(zombies.indexOf(this), 1);
}
player.bullets.splice(j, 1);
break;
}
}
}
}
class SmallBrownZombie extends Zombie {
constructor(x, y) {
super();
this.radius = 10;
this.speed = 3;
this.health = 1;
this.x = x;
this.y = y;
}
display() {
fill(81, 9, 9);
ellipse(this.x, this.y, this.radius * 2);
}
}
class DarkGrayZombie extends Zombie {
constructor() {
super();
this.radius = 30;
this.speed = 1;
this.health = 4;
}
display() {
fill(64, 64, 64);
ellipse(this.x, this.y, this.radius * 2);
}
}
class Bullet {
constructor(x, y, angle) {
this.x = x;
this.y = y;
this.speed = 5;
this.angle = angle;
bulletSound.play();
}
update() {
this.x += this.speed * cos(this.angle);
this.y += this.speed * sin(this.angle);
}
display() {
fill(255, 0, 0);
ellipse(this.x, this.y, 10, 10);
}
collide(other) {
let d = dist(this.x, this.y, other.x, other.y);
return d < 5 + other.radius;
}
}
class PowerUp {
constructor() {
this.size = 20;
this.spawnRandomly();
this.active = false;
this.activationTime = 0;
}
update() {
if (this.active) {
let elapsedTime = millis() - this.activationTime;
if (elapsedTime > powerUpDuration * 1000) {
this.active = false;
this.spawnRandomly();
}
}
}
display() {
fill(255, 255, 0);
triangle(
this.x,
this.y - this.size / 2,
this.x - this.size / 2,
this.y + this.size / 2,
this.x + this.size / 2,
this.y + this.size / 2
);
}
spawnRandomly() {
let squarePositions = [
{ x: 300, y: 200 },
{ x: 900, y: 200 },
{ x: 300, y: 600 },
{ x: 900, y: 600 },
];
let randomSquare = random(squarePositions);
this.x = randomSquare.x;
this.y = randomSquare.y;
}
activate() {
this.active = true;
this.activationTime = millis();
}
}
class ShieldPowerUp {
constructor() {
this.size = 20;
this.x = 0;
this.y = 0;
this.spawnRandomly();
this.active = false;
this.activationTime = 0;
}
update() {
if (this.active) {
let elapsedTime = millis() - this.activationTime;
if (elapsedTime > 15000) {
this.active = false;
this.spawnRandomly();
}
}
}
display() {
fill(135, 206, 250);
triangle(
this.x,
this.y - this.size / 2,
this.x - this.size / 2,
this.y + this.size / 2,
this.x + this.size / 2,
this.y + this.size / 2
);
}
spawnRandomly() {
let squarePositions = [
{ x: 300, y: 200 },
{ x: 900, y: 200 },
{ x: 300, y: 600 },
{ x: 900, y: 600 },
];
let randomSquare = random(squarePositions);
this.x = randomSquare.x;
this.y = randomSquare.y;
}
activate() {
this.active = true;
this.activationTime = millis();
}
}