Meta Description" name="description" />
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>2D Space Shooter</title>
<style>
body {
margin: 0;
padding: 0;
background: #0d0e15;
color: #fff;
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
overflow: hidden;
}
#game-container {
position: relative;
}
canvas {
border: 2px solid #00f0ff;
box-shadow: 0 0 20px rgba(0, 240, 255, 0.3);
background: #05050a;
}
.ui-layer {
position: absolute;
top: 10px;
left: 10px;
font-size: 18px;
font-weight: bold;
pointer-events: none;
}
</style>
</head>
<body>
<div id="game-container">
<div class="ui-layer">
Score: <span id="score">0</span> | Lives: <span id="lives">3</span>
</div>
<canvas id="gameCanvas" width="600" height="700"></canvas>
</div>
<script>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const scoreEl = document.getElementById('score');
const livesEl = document.getElementById('lives');
// Input listener tracking
const keys = {};
window.addEventListener('keydown', e => keys[e.code] = true);
window.addEventListener('keyup', e => keys[e.code] = false);
// Player Object
const player = {
x: canvas.width / 2 - 20,
y: canvas.height - 60,
width: 40,
height: 40,
speed: 6,
score: 0,
lives: 3,
lastShot: 0
};
let bullets = [];
let enemies = [];
let gameOver = false;
// Spawning logic
let enemyTimer = 0;
function spawnEnemy() {
const width = 30 + Math.random() * 20;
enemies.push({
x: Math.random() * (canvas.width - width),
y: -40,
width: width,
height: width,
speed: 2 + Math.random() * 3
});
}
function update() {
if (gameOver) return;
// Player Movement
if ((keys['ArrowLeft'] || keys['KeyA']) && player.x > 0) player.x -= player.speed;
if ((keys['ArrowRight'] || keys['KeyD']) && player.x + player.width < canvas.width) player.x += player.speed;
// Shooting (Spacebar with 150ms cooldown)
if (keys['Space'] && Date.now() - player.lastShot > 150) {
bullets.push({
x: player.x + player.width / 2 - 3,
y: player.y,
width: 6,
height: 15,
speed: 10
});
player.lastShot = Date.now();
}
// Move Bullets
bullets.forEach((bullet, index) => {
bullet.y -= bullet.speed;
if (bullet.y < -10) bullets.splice(index, 1);
});
// Spawn Enemies
enemyTimer++;
if (enemyTimer % 45 === 0) {
spawnEnemy();
}
// Move & Check Enemies
enemies.forEach((enemy, eIndex) => {
enemy.y += enemy.speed;
// Enemy passes bottom line
if (enemy.y > canvas.height) {
enemies.splice(eIndex, 1);
player.lives--;
livesEl.innerText = player.lives;
if (player.lives <= 0) gameOver = true;
}
// Bullet vs Enemy Collision
bullets.forEach((bullet, bIndex) => {
if (
bullet.x < enemy.x + enemy.width &&
bullet.x + bullet.width > enemy.x &&
bullet.y < enemy.y + enemy.height &&
bullet.y + bullet.height > enemy.y
) {
enemies.splice(eIndex, 1);
bullets.splice(bIndex, 1);
player.score += 10;
scoreEl.innerText = player.score;
}
});
// Player vs Enemy Collision
if (
player.x < enemy.x + enemy.width &&
player.x + player.width > enemy.x &&
player.y < enemy.y + enemy.height &&
player.y + player.height > enemy.y
) {
enemies.splice(eIndex, 1);
player.lives--;
livesEl.innerText = player.lives;
if (player.lives <= 0) gameOver = true;
}
});
}
function draw() {
// Clear Screen
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw Player Ship
ctx.fillStyle = '#00f0ff';
ctx.beginPath();
ctx.moveTo(player.x + player.width / 2, player.y);
ctx.lineTo(player.x + player.width, player.y + player.height);
ctx.lineTo(player.x, player.y + player.height);
ctx.closePath();
ctx.fill();
// Draw Bullets
ctx.fillStyle = '#ff0055';
bullets.forEach(bullet => {
ctx.fillRect(bullet.x, bullet.y, bullet.width, bullet.height);
});
// Draw Enemies
ctx.fillStyle = '#ffb703';
enemies.forEach(enemy => {
ctx.fillRect(enemy.x, enemy.y, enemy.width, enemy.height);
});
// Game Over Overlay
if (gameOver) {
ctx.fillStyle = 'rgba(0,0,0,0.75)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#ff0055';
ctx.font = '36px Arial';
ctx.textAlign = 'center';
ctx.fillText('GAME OVER', canvas.width / 2, canvas.height / 2 - 20);
ctx.fillStyle = '#fff';
ctx.font = '18px Arial';
ctx.fillText('Refresh page to restart', canvas.width / 2, canvas.height / 2 + 20);
}
}
// Main Loop
function gameLoop() {
update();
draw();
requestAnimationFrame(gameLoop);
}
gameLoop();
</script>
</body>
</html>canvas.widthcanvas.heightplayer.widthplayer.lastShotlivesEl.innerTextenemy.widthenemy.heightbullet.widthbullet.heightplayer.scoreplayer.heightscoreEl.innerTextplayer.heightctx.fontctx.fillStylectx.textAlign1
1
6KB
6KB
119.0ms
188.0ms
120.0ms