astroids / index.html
Tripheist's picture
讛诪讙谉 讗诪讜专 诇讛转讞讚砖 - Initial Deployment
83439d5 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Asteroids Game</title>
<script src="https://cdn.tailwindcss.com"></script>
<style>
body {
margin: 0;
overflow: hidden;
background-color: #000;
font-family: 'Arial', sans-serif;
}
canvas {
display: block;
}
#gameUI {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
}
#score, #lives, #gameOver, #level {
color: white;
position: absolute;
font-size: 1.5rem;
}
#score {
top: 20px;
left: 20px;
}
#lives {
top: 20px;
right: 20px;
}
#gameOver {
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 3rem;
text-align: center;
display: none;
}
#startScreen {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.8);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: white;
}
#startButton {
margin-top: 20px;
padding: 10px 30px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 5px;
font-size: 1.5rem;
cursor: pointer;
pointer-events: auto;
}
#controls {
margin-top: 20px;
text-align: center;
font-size: 1rem;
}
</style>
</head>
<body>
<canvas id="gameCanvas"></canvas>
<div id="gameUI">
<div id="score">Score: 0</div>
<div id="lives">Lives: 3</div>
<div id="level" style="top: 50px; left: 20px;">Level: 1</div>
<div id="gameOver">
Game Over<br>
<button id="restartButton" class="mt-4 px-6 py-2 bg-green-500 text-white rounded hover:bg-green-600">Play Again</button>
</div>
</div>
<div id="startScreen">
<h1 class="text-4xl font-bold mb-4">ASTEROIDS</h1>
<button id="startButton">START GAME</button>
<div id="controls" class="mt-8">
<p class="mb-2"><span class="font-bold">Controls:</span></p>
<p>WASD or Arrow Keys to move</p>
<p>Space to shoot</p>
<p>Arrow Down or F to activate shield (4 sec max)</p>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const scoreElement = document.getElementById('score');
const livesElement = document.getElementById('lives');
const gameOverElement = document.getElementById('gameOver');
const startScreen = document.getElementById('startScreen');
const startButton = document.getElementById('startButton');
const restartButton = document.getElementById('restartButton');
// Set canvas size
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
// Game variables
let score = 0;
let lives = 3;
let gameRunning = false;
let animationId;
let level = 1;
let asteroidsToNextLevel = 0;
let shieldActive = false;
let shieldDuration = 0;
let shieldMaxDuration = 4; // 4 seconds max
let shieldCooldown = 0;
let shieldCooldownMax = 4; // 4 seconds cooldown (matches max duration)
let shieldRegenRate = 1; // 1 second per second of shield
let shieldRadius = 40;
// Player ship
const player = {
x: canvas.width / 2,
y: canvas.height / 2,
radius: 15,
angle: 0,
rotation: 0,
thrusting: false,
thrust: {
x: 0,
y: 0
},
velocity: {
x: 0,
y: 0
},
speed: 0.1,
friction: 0.98,
rotationSpeed: 0.05,
cooldown: 0,
cooldownMax: 10
};
// Bullets
const bullets = [];
const bulletSpeed = 5;
const bulletRadius = 2;
const bulletLife = 60;
// Asteroids
const asteroids = [];
let asteroidCount = 5;
let asteroidSpeed = 1;
const asteroidRadius = 30;
const asteroidLevels = 3; // How many times an asteroid can break
// Key states
const keys = {
ArrowUp: false,
ArrowDown: false,
ArrowLeft: false,
ArrowRight: false,
w: false,
a: false,
s: false,
d: false,
' ': false,
'f': false,
'ArrowDown': false
};
// Event listeners
window.addEventListener('keydown', (e) => {
if (keys.hasOwnProperty(e.key)) {
keys[e.key] = true;
}
});
window.addEventListener('keyup', (e) => {
if (keys.hasOwnProperty(e.key)) {
keys[e.key] = false;
}
});
startButton.addEventListener('click', startGame);
restartButton.addEventListener('click', startGame);
window.addEventListener('resize', () => {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
if (gameRunning) {
player.x = canvas.width / 2;
player.y = canvas.height / 2;
}
});
// Game functions
function startGame() {
score = 0;
lives = 3;
level = 1;
asteroidCount = 5;
asteroidSpeed = 1;
bullets.length = 0;
asteroids.length = 0;
player.x = canvas.width / 2;
player.y = canvas.height / 2;
player.velocity.x = 0;
player.velocity.y = 0;
player.thrusting = false;
player.angle = 0;
startScreen.style.display = 'none';
gameOverElement.style.display = 'none';
gameRunning = true;
// Create initial asteroids
for (let i = 0; i < asteroidCount; i++) {
createAsteroid();
}
asteroidsToNextLevel = asteroidCount;
updateScore();
updateLives();
updateLevel();
animate();
}
function gameOver() {
gameRunning = false;
gameOverElement.style.display = 'block';
cancelAnimationFrame(animationId);
}
function updateScore() {
scoreElement.textContent = `Score: ${score}`;
}
function updateLives() {
livesElement.textContent = `Lives: ${lives}`;
}
function updateLevel() {
document.getElementById('level').textContent = `Level: ${level}`;
}
function createAsteroid(x, y, level = asteroidLevels) {
if (x === undefined || y === undefined) {
// Position asteroid outside viewport but not too close to player
if (Math.random() < 0.5) {
x = Math.random() < 0.5 ? -asteroidRadius : canvas.width + asteroidRadius;
y = Math.random() * canvas.height;
} else {
x = Math.random() * canvas.width;
y = Math.random() < 0.5 ? -asteroidRadius : canvas.height + asteroidRadius;
}
// Ensure asteroid is not too close to player
while (Math.sqrt(Math.pow(x - player.x, 2) + Math.pow(y - player.y, 2)) < 200) {
x = Math.random() * canvas.width;
y = Math.random() * canvas.height;
}
}
const angle = Math.random() * Math.PI * 2;
const velocity = {
x: Math.cos(angle) * asteroidSpeed * (1 + (asteroidLevels - level) * 0.5),
y: Math.sin(angle) * asteroidSpeed * (1 + (asteroidLevels - level) * 0.5)
};
const radius = (asteroidRadius * level / asteroidLevels) + Math.random() * 10;
asteroids.push({
x,
y,
radius,
velocity,
rotation: Math.random() * Math.PI * 2,
rotationSpeed: (Math.random() - 0.5) * 0.02,
level
});
}
function shoot() {
if (player.cooldown <= 0) {
bullets.push({
x: player.x,
y: player.y,
velocity: {
x: Math.cos(player.angle) * bulletSpeed,
y: Math.sin(player.angle) * bulletSpeed
},
life: bulletLife
});
player.cooldown = player.cooldownMax;
}
}
function update() {
// Player rotation
if (keys.ArrowLeft || keys.a) {
player.rotation = -player.rotationSpeed;
} else if (keys.ArrowRight || keys.d) {
player.rotation = player.rotationSpeed;
} else {
player.rotation = 0;
}
player.angle += player.rotation;
// Player thrust
player.thrusting = false;
if (keys.ArrowUp || keys.w) {
player.thrusting = true;
player.thrust.x = Math.cos(player.angle) * player.speed;
player.thrust.y = Math.sin(player.angle) * player.speed;
player.velocity.x += player.thrust.x;
player.velocity.y += player.thrust.y;
}
// Apply friction
player.velocity.x *= player.friction;
player.velocity.y *= player.friction;
// Update player position
player.x += player.velocity.x;
player.y += player.velocity.y;
// Screen wrapping for player
if (player.x < 0) player.x = canvas.width;
if (player.x > canvas.width) player.x = 0;
if (player.y < 0) player.y = canvas.height;
if (player.y > canvas.height) player.y = 0;
// Shooting
if (keys[' ']) {
shoot();
}
if (player.cooldown > 0) {
player.cooldown--;
}
// Shield activation (F key)
if ((keys['f'] || keys['ArrowDown']) && shieldDuration > 0 && !shieldActive) {
shieldActive = true;
}
// Shield duration and cooldown
if (shieldActive) {
shieldDuration -= 1/60; // Decrease by 1 frame (assuming 60fps)
if (shieldDuration <= 0) {
shieldActive = false;
shieldCooldown = shieldMaxDuration; // Full cooldown after use
}
} else if (shieldDuration < shieldMaxDuration) {
// Regenerate shield when not active
shieldDuration = Math.min(shieldDuration + shieldRegenRate/60, shieldMaxDuration);
}
// Shield collision avoidance
if (shieldActive) {
for (let i = asteroids.length - 1; i >= 0; i--) {
const asteroid = asteroids[i];
const dist = Math.sqrt(Math.pow(player.x - asteroid.x, 2) + Math.pow(player.y - asteroid.y, 2));
if (dist < shieldRadius + asteroid.radius) {
// Push asteroid away from player
const angle = Math.atan2(asteroid.y - player.y, asteroid.x - player.x);
const pushForce = 2;
asteroid.velocity.x = Math.cos(angle) * pushForce;
asteroid.velocity.y = Math.sin(angle) * pushForce;
}
}
}
// Update bullets
for (let i = bullets.length - 1; i >= 0; i--) {
const bullet = bullets[i];
bullet.x += bullet.velocity.x;
bullet.y += bullet.velocity.y;
bullet.life--;
// Screen wrapping for bullets
if (bullet.x < 0) bullet.x = canvas.width;
if (bullet.x > canvas.width) bullet.x = 0;
if (bullet.y < 0) bullet.y = canvas.height;
if (bullet.y > canvas.height) bullet.y = 0;
// Remove dead bullets
if (bullet.life <= 0) {
bullets.splice(i, 1);
}
}
// Update asteroids
for (let i = asteroids.length - 1; i >= 0; i--) {
const asteroid = asteroids[i];
asteroid.x += asteroid.velocity.x;
asteroid.y += asteroid.velocity.y;
asteroid.rotation += asteroid.rotationSpeed;
// Screen wrapping for asteroids
if (asteroid.x < -asteroid.radius) asteroid.x = canvas.width + asteroid.radius;
if (asteroid.x > canvas.width + asteroid.radius) asteroid.x = -asteroid.radius;
if (asteroid.y < -asteroid.radius) asteroid.y = canvas.height + asteroid.radius;
if (asteroid.y > canvas.height + asteroid.radius) asteroid.y = -asteroid.radius;
// Check collision with player
const dist = Math.sqrt(Math.pow(player.x - asteroid.x, 2) + Math.pow(player.y - asteroid.y, 2));
if (dist < player.radius + asteroid.radius && !shieldActive) {
lives--;
updateLives();
if (lives <= 0) {
gameOver();
return;
}
// Reset player position
player.x = canvas.width / 2;
player.y = canvas.height / 2;
player.velocity.x = 0;
player.velocity.y = 0;
// Remove the asteroid that hit the player
asteroids.splice(i, 1);
createAsteroid();
continue;
}
// Check collision with bullets
for (let j = bullets.length - 1; j >= 0; j--) {
const bullet = bullets[j];
const dist = Math.sqrt(Math.pow(bullet.x - asteroid.x, 2) + Math.pow(bullet.y - asteroid.y, 2));
if (dist < asteroid.radius) {
// Remove bullet
bullets.splice(j, 1);
// Increase score based on asteroid size
score += 10 * asteroid.level;
updateScore();
// Break asteroid into smaller pieces or remove it
if (asteroid.level > 1) {
// Create 2-3 smaller asteroids
const pieces = 2 + Math.floor(Math.random() * 2);
for (let k = 0; k < pieces; k++) {
createAsteroid(asteroid.x, asteroid.y, asteroid.level - 1);
}
}
// Remove the original asteroid
asteroids.splice(i, 1);
asteroidsToNextLevel--;
// Check if all asteroids are destroyed
if (asteroids.length === 0) {
level++;
asteroidCount = 5 + Math.floor(level * 1.5);
asteroidSpeed = 1 + level * 0.2;
// Create asteroids for next level
for (let i = 0; i < asteroidCount; i++) {
createAsteroid();
}
asteroidsToNextLevel = asteroidCount;
}
break;
}
}
}
}
function draw() {
// Clear canvas
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Draw stars
ctx.fillStyle = 'white';
for (let i = 0; i < 100; i++) {
const x = Math.random() * canvas.width;
const y = Math.random() * canvas.height;
const size = Math.random() * 2;
ctx.fillRect(x, y, size, size);
}
// Draw shield if active
if (shieldActive) {
ctx.save();
ctx.translate(player.x, player.y);
ctx.beginPath();
ctx.arc(0, 0, shieldRadius, 0, Math.PI * 2);
ctx.strokeStyle = 'rgba(0, 150, 255, 0.7)';
ctx.lineWidth = 3;
ctx.stroke();
// Draw shield energy indicator
const shieldPercentage = shieldDuration / shieldMaxDuration;
ctx.beginPath();
ctx.arc(0, 0, shieldRadius + 5, -Math.PI/2, -Math.PI/2 + shieldPercentage * Math.PI * 2);
ctx.strokeStyle = 'rgba(0, 255, 255, 0.9)';
ctx.lineWidth = 2;
ctx.stroke();
ctx.restore();
}
// Draw player
ctx.save();
ctx.translate(player.x, player.y);
ctx.rotate(player.angle);
ctx.strokeStyle = 'white';
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(player.radius, 0);
ctx.lineTo(-player.radius, -player.radius / 2);
ctx.lineTo(-player.radius / 2, 0);
ctx.lineTo(-player.radius, player.radius / 2);
ctx.closePath();
ctx.stroke();
// Draw thrust flame
if (player.thrusting) {
ctx.fillStyle = 'orange';
ctx.beginPath();
ctx.moveTo(-player.radius, -player.radius / 3);
ctx.lineTo(-player.radius * 1.5, 0);
ctx.lineTo(-player.radius, player.radius / 3);
ctx.closePath();
ctx.fill();
}
ctx.restore();
// Draw bullets
ctx.fillStyle = 'white';
for (const bullet of bullets) {
ctx.beginPath();
ctx.arc(bullet.x, bullet.y, bulletRadius, 0, Math.PI * 2);
ctx.fill();
}
// Draw asteroids
ctx.strokeStyle = 'white';
ctx.lineWidth = 2;
for (const asteroid of asteroids) {
ctx.save();
ctx.translate(asteroid.x, asteroid.y);
ctx.rotate(asteroid.rotation);
ctx.beginPath();
for (let i = 0; i < 8; i++) {
const angle = (i / 8) * Math.PI * 2;
const radius = asteroid.radius * (0.7 + Math.random() * 0.3);
const x = Math.cos(angle) * radius;
const y = Math.sin(angle) * radius;
if (i === 0) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
}
ctx.closePath();
ctx.stroke();
ctx.restore();
}
}
function animate() {
if (!gameRunning) return;
update();
draw();
animationId = requestAnimationFrame(animate);
}
});
</script>
</body>
</html>