Really cool game

This commit is contained in:
Eric Ratliff
2025-06-25 22:14:38 -05:00
commit 694cc903c8
7 changed files with 425 additions and 0 deletions

68
asteroid.js Normal file
View File

@@ -0,0 +1,68 @@
class Asteroid {
constructor(pos, r, isGolden = random() < 0.1) {
if (pos) {
this.pos = pos.copy();
this.r = r ? r / 2 : random(30, 50);
} else {
this.pos = createVector(random(width), random(height));
this.r = random(30, 50);
while (dist(this.pos.x, this.pos.y, ship.pos.x, ship.pos.y) < 100) {
this.pos = createVector(random(width), random(height));
}
}
this.vel = p5.Vector.random2D();
this.vel.mult(random(1, 3));
this.total = floor(random(5, 15));
this.offset = [];
for (let i = 0; i < this.total; i++) {
this.offset[i] = random(-this.r * 0.5, this.r * 0.5);
}
this.isGolden = isGolden;
this.hitsLeft = isGolden ? 3 : 1;
}
update() {
this.pos.add(this.vel);
}
edges() {
if (this.pos.x > width + this.r) this.pos.x = -this.r;
else if (this.pos.x < -this.r) this.pos.x = width + this.r;
if (this.pos.y > height + this.r) this.pos.y = -this.r;
else if (this.pos.y < -this.r) this.pos.y = height + this.r;
}
hits(bullet) {
let d = dist(this.pos.x, this.pos.y, bullet.pos.x, bullet.pos.y);
return d < this.r;
}
breakup() {
let newA = [];
this.hitsLeft--;
if (this.hitsLeft > 0) {
newA.push(this);
} else if (this.r > 20) {
newA.push(new Asteroid(this.pos, this.r, this.isGolden));
newA.push(new Asteroid(this.pos, this.r, this.isGolden));
}
return newA;
}
show() {
push();
translate(this.pos.x, this.pos.y);
noFill();
stroke(this.isGolden ? color(255, 215, 0) : 255); // Golden or white
beginShape();
for (let i = 0; i < this.total; i++) {
let angle = map(i, 0, this.total, 0, TWO_PI);
let r = this.r + this.offset[i];
let x = r * cos(angle);
let y = r * sin(angle);
vertex(x, y);
}
endShape(CLOSE);
pop();
}
}

24
bullet.js Normal file
View File

@@ -0,0 +1,24 @@
class Bullet {
constructor(pos, angle) {
this.pos = pos.copy();
this.vel = p5.Vector.fromAngle(angle);
this.vel.mult(10);
this.r = 4;
}
update() {
this.pos.add(this.vel);
}
show() {
push();
fill(255);
noStroke();
ellipse(this.pos.x, this.pos.y, this.r * 2);
pop();
}
offscreen() {
return (this.pos.x < 0 || this.pos.x > width || this.pos.y < 0 || this.pos.y > height);
}
}

30
greenOrb.js Normal file
View File

@@ -0,0 +1,30 @@
class GreenOrb {
constructor() {
this.pos = createVector(random(width), random(height));
this.r = 10;
this.vel = p5.Vector.random2D();
this.vel.mult(random(1, 2));
while (dist(this.pos.x, this.pos.y, ship.pos.x, ship.pos.y) < 100) {
this.pos = createVector(random(width), random(height));
}
}
update() {
this.pos.add(this.vel);
}
edges() {
if (this.pos.x > width + this.r) this.pos.x = -this.r;
else if (this.pos.x < -this.r) this.pos.x = width + this.r;
if (this.pos.y > height + this.r) this.pos.y = -this.r;
else if (this.pos.y < -this.r) this.pos.y = height + this.r;
}
show() {
push();
fill(0, 255, 0); // Green
noStroke();
ellipse(this.pos.x, this.pos.y, this.r * 2);
pop();
}
}

15
index.html Normal file
View File

@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html>
<head>
<title>Asteroids Game</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.2/p5.min.js"></script>
<script src="ship.js"></script>
<script src="asteroid.js"></script>
<script src="bullet.js"></script>
<script src="shieldOrb.js"></script>
<script src="greenOrb.js"></script>
<script src="sketch.js"></script>
</head>
<body>
</body>
</html>

30
shieldOrb.js Normal file
View File

@@ -0,0 +1,30 @@
class ShieldOrb {
constructor() {
this.pos = createVector(random(width), random(height));
this.r = 10;
this.vel = p5.Vector.random2D();
this.vel.mult(random(1, 2));
while (dist(this.pos.x, this.pos.y, ship.pos.x, ship.pos.y) < 100) {
this.pos = createVector(random(width), random(height));
}
}
update() {
this.pos.add(this.vel);
}
edges() {
if (this.pos.x > width + this.r) this.pos.x = -this.r;
else if (this.pos.x < -this.r) this.pos.x = width + this.r;
if (this.pos.y > height + this.r) this.pos.y = -this.r;
else if (this.pos.y < -this.r) this.pos.y = height + this.r;
}
show() {
push();
fill(0, 255, 255); // Cyan
noStroke();
ellipse(this.pos.x, this.pos.y, this.r * 2);
pop();
}
}

91
ship.js Normal file
View File

@@ -0,0 +1,91 @@
class Ship {
constructor() {
this.pos = createVector(width / 2, height / 2);
this.r = 20;
this.heading = 0;
this.rotation = 0;
this.vel = createVector(0, 0);
this.isBoosting = false;
this.shieldActive = false;
this.shieldEndTime = 0;
this.quadShotActive = false;
this.quadShotEndTime = 0;
}
update() {
if (this.isBoosting) {
this.boost();
}
this.pos.add(this.vel);
this.vel.mult(0.99); // Friction
this.heading += this.rotation;
if (this.shieldActive && millis() > this.shieldEndTime) {
this.shieldActive = false;
}
if (this.quadShotActive && millis() > this.quadShotEndTime) {
this.quadShotActive = false;
}
}
boost() {
let force = p5.Vector.fromAngle(this.heading);
force.mult(0.1);
this.vel.add(force);
}
hits(obj) {
let d = dist(this.pos.x, this.pos.y, obj.pos.x, obj.pos.y);
return d < this.r + obj.r;
}
edges() {
if (this.pos.x > width + this.r) this.pos.x = -this.r;
else if (this.pos.x < -this.r) this.pos.x = width + this.r;
if (this.pos.y > height + this.r) this.pos.y = -this.r;
else if (this.pos.y < -this.r) this.pos.y = height + this.r;
}
setRotation(a) {
this.rotation = a;
}
boosting(b) {
this.isBoosting = b;
}
activateShield() {
this.shieldActive = true;
this.shieldEndTime = millis() + 10000; // 10 seconds
}
activateQuadShot() {
this.quadShotActive = true;
this.quadShotEndTime = millis() + 10000; // 10 seconds
}
show() {
push();
translate(this.pos.x, this.pos.y);
rotate(this.heading + PI / 2);
if (this.shieldActive) {
// Pulsing shield effect in last 2 seconds
let timeLeft = (this.shieldEndTime - millis()) / 1000;
let scaleFactor = this.shieldActive && timeLeft < 2 ? 1 + 0.1 * sin(millis() / 100) : 1;
scale(scaleFactor);
noFill();
stroke(0, 255, 255); // Cyan shield
ellipse(0, 0, this.r * 2.5);
}
// Ship fill: green if quad-shot active, else black
if (this.quadShotActive) {
let timeLeft = (this.quadShotEndTime - millis()) / 1000;
let alpha = this.quadShotActive && timeLeft < 2 ? map(sin(millis() / 100), -1, 1, 100, 255) : 255;
fill(0, 255, 0, alpha);
} else {
fill(0);
}
stroke(255);
triangle(-this.r, this.r, this.r, this.r, 0, -this.r);
pop();
}
}

167
sketch.js Normal file
View File

@@ -0,0 +1,167 @@
let ship;
let asteroids = [];
let bullets = [];
let shieldOrb = null;
let greenOrb = null;
let lives = 3;
let score = 0;
let gameOver = false;
let lastShieldSpawn = 0;
let lastGreenSpawn = 0;
function setup() {
createCanvas(800, 600);
ship = new Ship();
for (let i = 0; i < 5; i++) {
asteroids.push(new Asteroid());
}
}
function draw() {
background(0);
if (gameOver) {
textSize(32);
fill(255);
textAlign(CENTER);
text("Game Over! Score: " + score, width / 2, height / 2);
text("Press R to Restart", width / 2, height / 2 + 40);
return;
}
// Display lives and score
textSize(20);
fill(255);
text("Lives: " + lives, 50, 30);
text("Score: " + score, 50, 60);
// Handle continuous key input (WASD)
if (keyIsDown(65)) { // A key
ship.setRotation(-0.1);
}
if (keyIsDown(68)) { // D key
ship.setRotation(0.1);
}
if (keyIsDown(87)) { // W key
ship.boosting(true);
} else {
ship.boosting(false);
}
// Update and show ship
ship.update();
ship.show();
ship.edges();
// Spawn shield orb periodically
if (millis() - lastShieldSpawn > 15000 && !shieldOrb) {
shieldOrb = new ShieldOrb();
lastShieldSpawn = millis();
}
// Spawn green orb periodically
if (millis() - lastGreenSpawn > 20000 && !greenOrb) {
greenOrb = new GreenOrb();
lastGreenSpawn = millis();
}
// Update and show shield orb
if (shieldOrb) {
shieldOrb.update();
shieldOrb.show();
shieldOrb.edges();
if (ship.hits(shieldOrb)) {
ship.activateShield();
shieldOrb = null;
}
}
// Update and show green orb
if (greenOrb) {
greenOrb.update();
greenOrb.show();
greenOrb.edges();
if (ship.hits(greenOrb)) {
ship.activateQuadShot();
greenOrb = null;
}
}
// Update and show bullets
for (let i = bullets.length - 1; i >= 0; i--) {
bullets[i].update();
bullets[i].show();
if (bullets[i].offscreen()) {
bullets.splice(i, 1);
}
}
// Update and show asteroids
for (let i = asteroids.length - 1; i >= 0; i--) {
if (!asteroids[i]) continue; // Skip if asteroid is undefined
asteroids[i].update();
asteroids[i].show();
asteroids[i].edges();
// Check collision with ship
if (ship.hits(asteroids[i]) && !ship.shieldActive) {
lives--;
if (lives <= 0) {
gameOver = true;
} else {
ship = new Ship(); // Reset ship position
}
}
// Check collision with bullets
for (let j = bullets.length - 1; j >= 0; j--) {
if (asteroids[i] && asteroids[i].hits(bullets[j])) {
let isGolden = asteroids[i].isGolden; // Store before removal
let newAsteroids = asteroids[i].breakup();
asteroids.splice(i, 1);
bullets.splice(j, 1);
asteroids.push(...newAsteroids);
score += isGolden ? 20 : 10; // Use stored isGolden value
break; // Exit bullet loop after collision
}
}
}
}
function keyPressed() {
if (keyCode === 32) { // Spacebar to shoot
if (ship.quadShotActive) {
// Fire in four directions: forward, backward, left, right
bullets.push(new Bullet(ship.pos, ship.heading));
bullets.push(new Bullet(ship.pos, ship.heading + PI));
bullets.push(new Bullet(ship.pos, ship.heading - PI / 2));
bullets.push(new Bullet(ship.pos, ship.heading + PI / 2));
} else {
bullets.push(new Bullet(ship.pos, ship.heading));
}
}
if (keyCode === 82 && gameOver) { // R to restart
lives = 3;
score = 0;
asteroids = [];
bullets = [];
shieldOrb = null;
greenOrb = null;
ship = new Ship();
lastShieldSpawn = millis();
lastGreenSpawn = millis();
for (let i = 0; i < 5; i++) {
asteroids.push(new Asteroid());
}
gameOver = false;
}
}
function keyReleased() {
if (keyCode === 65 || keyCode === 68) { // A or D key
ship.setRotation(0);
}
if (keyCode === 87) { // W key
ship.boosting(false);
}
}