galaxy-defender / index.html
adrish's picture
Add 2 files
9bff983 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Galaxy Defender</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background-color: #000;
overflow: hidden;
font-family: 'Orbitron', sans-serif;
color: white;
}
@import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700&display=swap');
#gameContainer {
position: relative;
width: 100vw;
height: 100vh;
background: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiPgogIDxkZWZzPgogICAgPHBhdHRlcm4gaWQ9InBhdHRlcm4iIHBhdHRlcm5Vbml0cz0idXNlclNwYWNlT25Vc2UiIHBhdHRlcm5UcmFuc2Zvcm09InNjYWxlKDIpIHJvdGF0ZSgwKSI+CiAgICAgIDxyZWN0IHdpZHRoPSIxIiBoZWlnaHQ9IjEiIGZpbGw9IiNmZmZmZmYiIG9wYWNpdHk9IjAuMCIvPgogICAgICA8Y2lyY2xlIGN4PSIwLjUiIGN5PSIwLjUiIHI9IjAuNSIgZmlsbD0iIzIwMjBmZiIgb3BhY2l0eT0iMC4xIi8+CiAgICA8L3BhdHRlcm4+CiAgPC9kZWZzPgogIDxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjcGF0dGVybikiLz4KPC9zdmc+') #000;
}
#gameCanvas {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
#startScreen, #gameOverScreen {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
background-color: rgba(0, 0, 0, 0.8);
z-index: 10;
}
#gameOverScreen {
display: none;
}
h1 {
font-size: 3rem;
color: #0ff;
text-shadow: 0 0 10px #0ff, 0 0 20px #0ff;
margin-bottom: 2rem;
text-align: center;
}
button {
background: none;
border: 2px solid #0ff;
color: #0ff;
padding: 1rem 2rem;
font-size: 1.5rem;
font-family: 'Orbitron', sans-serif;
cursor: pointer;
transition: all 0.3s;
margin-top: 1rem;
text-shadow: 0 0 5px #0ff;
}
button:hover {
background-color: rgba(0, 255, 255, 0.1);
box-shadow: 0 0 10px #0ff;
}
#scoreDisplay, #healthDisplay {
position: absolute;
top: 20px;
right: 20px;
z-index: 5;
font-size: 1.5rem;
color: #0ff;
text-shadow: 0 0 5px #0ff;
}
#healthDisplay {
display: flex;
align-items: center;
}
#healthBar {
width: 100px;
height: 20px;
background-color: #333;
margin-left: 10px;
border: 1px solid #0ff;
overflow: hidden;
}
#healthProgress {
height: 100%;
width: 100%;
background-color: #0f0;
transition: width 0.3s;
}
.controls {
position: absolute;
bottom: 20px;
left: 20px;
color: #0ff;
font-size: 1rem;
text-shadow: 0 0 5px #0ff;
}
#mobileControls {
display: none;
position: absolute;
bottom: 20px;
width: 100%;
justify-content: space-around;
z-index: 5;
}
.mobile-button {
width: 60px;
height: 60px;
background-color: rgba(0, 255, 255, 0.2);
border: 2px solid #0ff;
border-radius: 50%;
color: #0ff;
display: flex;
justify-content: center;
align-items: center;
font-size: 1.5rem;
user-select: none;
}
@media (max-width: 768px) {
#mobileControls {
display: flex;
}
.controls {
display: none;
}
h1 {
font-size: 2rem;
}
button {
padding: 0.8rem 1.5rem;
font-size: 1.2rem;
}
}
#stars {
position: absolute;
width: 100%;
height: 100%;
z-index: -1;
}
.star {
position: absolute;
background-color: white;
border-radius: 50%;
animation: twinkle 1s infinite alternate;
}
@keyframes twinkle {
from { opacity: 0.3; }
to { opacity: 1; }
}
.explosion {
position: absolute;
width: 30px;
height: 30px;
pointer-events: none;
background-image: radial-gradient(circle, #ff0, #f80, #f00);
border-radius: 50%;
transform: scale(0);
opacity: 1;
animation: explode 0.5s forwards;
}
@keyframes explode {
to {
transform: scale(3);
opacity: 0;
}
}
</style>
</head>
<body>
<div id="gameContainer">
<div id="stars"></div>
<canvas id="gameCanvas"></canvas>
<div id="scoreDisplay">Score: 0</div>
<div id="healthDisplay">
Health: <div id="healthBar"><div id="healthProgress"></div></div>
</div>
<div id="startScreen">
<h1>GALAXY DEFENDER</h1>
<p>Defend the sector from enemy invaders!</p>
<button id="startButton">START MISSION</button>
<div class="controls">
Controls: Arrow Keys to Move, Space to Shoot
</div>
</div>
<div id="gameOverScreen">
<h1>MISSION FAILED</h1>
<p id="finalScore">Your score: 0</p>
<button id="restartButton">TRY AGAIN</button>
</div>
<div id="mobileControls">
<div class="mobile-button" id="leftButton"></div>
<div class="mobile-button" id="rightButton"></div>
<div class="mobile-button" id="upButton"></div>
<div class="mobile-button" id="downButton"></div>
<div class="mobile-button" id="shootButton">💥</div>
</div>
</div>
<script>
// Game variables
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const startScreen = document.getElementById('startScreen');
const gameOverScreen = document.getElementById('gameOverScreen');
const startButton = document.getElementById('startButton');
const restartButton = document.getElementById('restartButton');
const scoreDisplay = document.getElementById('scoreDisplay');
const finalScore = document.getElementById('finalScore');
const healthProgress = document.getElementById('healthProgress');
const starsContainer = document.getElementById('stars');
// Mobile controls
const leftButton = document.getElementById('leftButton');
const rightButton = document.getElementById('rightButton');
const upButton = document.getElementById('upButton');
const downButton = document.getElementById('downButton');
const shootButton = document.getElementById('shootButton');
// Set canvas size
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
resizeCanvas();
window.addEventListener('resize', resizeCanvas);
// Create stars
function createStars() {
starsContainer.innerHTML = '';
const starCount = Math.floor(window.innerWidth * window.innerHeight / 1000);
for (let i = 0; i < starCount; i++) {
const star = document.createElement('div');
star.className = 'star';
star.style.width = `${Math.random() * 3}px`;
star.style.height = star.style.width;
star.style.left = `${Math.random() * 100}%`;
star.style.top = `${Math.random() * 100}%`;
star.style.opacity = Math.random();
star.style.animationDelay = `${Math.random() * 2}s`;
star.style.animationDuration = `${0.5 + Math.random() * 1.5}s`;
starsContainer.appendChild(star);
}
}
createStars();
// Game objects
const player = {
x: canvas.width / 2,
y: canvas.height - 100,
width: 50,
height: 50,
speed: 8,
color: '#0ff',
health: 100,
maxHealth: 100,
isShooting: false,
shootCooldown: 0,
shootInterval: 300 // ms
};
const bullets = [];
const enemies = [];
const explosions = [];
let score = 0;
let gameRunning = false;
let enemySpawnTimer = 0;
let enemySpawnInterval = 1500; // ms
let lastTime = 0;
let animationFrameId = null;
// Input handling
const keys = {
ArrowLeft: false,
ArrowRight: false,
ArrowUp: false,
ArrowDown: false,
' ': false
};
window.addEventListener('keydown', (e) => {
if (keys.hasOwnProperty(e.key)) {
keys[e.key] = true;
e.preventDefault();
}
});
window.addEventListener('keyup', (e) => {
if (keys.hasOwnProperty(e.key)) {
keys[e.key] = false;
e.preventDefault();
}
});
// Mobile controls
function setupMobileControls() {
const handlePress = (button, key) => {
const startPress = () => {
keys[key] = true;
button.style.backgroundColor = 'rgba(0, 255, 255, 0.4)';
};
const endPress = () => {
keys[key] = false;
button.style.backgroundColor = 'rgba(0, 255, 255, 0.2)';
};
button.addEventListener('touchstart', (e) => {
e.preventDefault();
startPress();
});
button.addEventListener('touchend', (e) => {
e.preventDefault();
endPress();
});
button.addEventListener('mousedown', startPress);
button.addEventListener('mouseup', endPress);
button.addEventListener('mouseleave', endPress);
};
handlePress(leftButton, 'ArrowLeft');
handlePress(rightButton, 'ArrowRight');
handlePress(upButton, 'ArrowUp');
handlePress(downButton, 'ArrowDown');
handlePress(shootButton, ' ');
}
setupMobileControls();
// Draw player ship
function drawPlayer() {
ctx.save();
// Ship body
ctx.fillStyle = player.color;
ctx.beginPath();
ctx.moveTo(player.x, player.y - player.height/2);
ctx.lineTo(player.x + player.width/2, player.y + player.height/2);
ctx.lineTo(player.x - player.width/2, player.y + player.height/2);
ctx.closePath();
ctx.fill();
// Ship cockpit
ctx.fillStyle = '#80f0f0';
ctx.beginPath();
ctx.arc(player.x, player.y - player.height/6, player.width/6, 0, Math.PI * 2);
ctx.fill();
// Engine glow when moving
if (keys.ArrowUp || keys.ArrowDown || keys.ArrowLeft || keys.ArrowRight) {
ctx.fillStyle = '#ff0';
ctx.beginPath();
ctx.moveTo(player.x - player.width/2 + 5, player.y + player.height/2);
ctx.lineTo(player.x + player.width/2 - 5, player.y + player.height/2);
ctx.lineTo(player.x, player.y + player.height/1.5);
ctx.closePath();
ctx.fill();
}
ctx.restore();
}
// Player shooting
function shoot() {
const bullet = {
x: player.x,
y: player.y - player.height/2, // Start from ship's nose
width: 6,
height: 20,
speed: -10, // Moving upwards (negative y direction)
color: '#0ff'
};
bullets.push(bullet);
}
// Draw and update bullets
function updateBullets() {
for (let i = bullets.length - 1; i >= 0; i--) {
const bullet = bullets[i];
// Draw bullet
ctx.fillStyle = bullet.color;
ctx.fillRect(bullet.x - bullet.width/2, bullet.y - bullet.height/2, bullet.width, bullet.height);
// Add glow effect
ctx.save();
ctx.fillStyle = '#fff';
ctx.globalAlpha = 0.5;
ctx.beginPath();
ctx.arc(bullet.x, bullet.y, bullet.width * 1.5, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
// Move bullet based on its speed (negative for player, positive for enemies)
bullet.y += bullet.speed;
// Remove if off screen
if (bullet.y < -bullet.height || bullet.y > canvas.height + bullet.height) {
bullets.splice(i, 1);
}
}
}
// Spawn enemies
function spawnEnemy() {
const size = 30 + Math.random() * 20;
const enemy = {
x: Math.random() * (canvas.width - size) + size/2,
y: -size,
width: size,
height: size,
speedX: (Math.random() - 0.5) * 2,
speedY: 1 + Math.random() * 2, // Slower enemy speed
color: `hsl(${Math.random() * 60}, 100%, 50%)`,
health: Math.ceil(size / 15), // More balanced health
lastShootTime: 0,
shootInterval: 2500 + Math.random() * 3000,
value: Math.floor(size / 5)
};
enemies.push(enemy);
}
// Draw and update enemies
function updateEnemies() {
for (let i = enemies.length - 1; i >= 0; i--) {
const enemy = enemies[i];
// Draw enemy ship
ctx.save();
ctx.fillStyle = enemy.color;
ctx.beginPath();
ctx.moveTo(enemy.x, enemy.y + enemy.height/2);
ctx.lineTo(enemy.x + enemy.width/2, enemy.y - enemy.height/2);
ctx.lineTo(enemy.x - enemy.width/2, enemy.y - enemy.height/2);
ctx.closePath();
ctx.fill();
// Enemy cockpit
ctx.fillStyle = '#ff0';
ctx.beginPath();
ctx.arc(enemy.x, enemy.y - enemy.height/6, enemy.width/6, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
// Move enemy
enemy.x += enemy.speedX;
enemy.y += enemy.speedY;
// Change direction near edges
if (enemy.x < enemy.width/2 || enemy.x > canvas.width - enemy.width/2) {
enemy.speedX *= -1;
}
// Enemy shooting
const now = Date.now();
if (now - enemy.lastShootTime > enemy.shootInterval) {
enemy.lastShootTime = now;
const enemyBullet = {
x: enemy.x,
y: enemy.y + enemy.height/2,
width: 6,
height: 20,
speed: 5, // Positive speed moves bullets downward
color: enemy.color
};
bullets.push(enemyBullet);
}
// Remove if off screen
if (enemy.y > canvas.height + enemy.height) {
enemies.splice(i, 1);
}
}
}
// Collision detection
function checkCollisions() {
// Player bullets hitting enemies
for (let i = bullets.length - 1; i >= 0; i--) {
const bullet = bullets[i];
// Skip enemy bullets (positive speed)
if (bullet.speed > 0) continue;
for (let j = enemies.length - 1; j >= 0; j--) {
const enemy = enemies[j];
if (
bullet.x + bullet.width/2 > enemy.x - enemy.width/2 &&
bullet.x - bullet.width/2 < enemy.x + enemy.width/2 &&
bullet.y - bullet.height/2 < enemy.y + enemy.height/2 &&
bullet.y + bullet.height/2 > enemy.y - enemy.height/2
) {
// Hit detected
createExplosion(enemy.x, enemy.y, enemy.color);
enemy.health--;
// Remove bullet
bullets.splice(i, 1);
// Remove enemy if health is 0 and add score
if (enemy.health <= 0) {
enemies.splice(j, 1);
score += enemy.value;
scoreDisplay.textContent = `Score: ${score}`;
}
break;
}
}
}
// Enemy bullets hitting player
for (let i = bullets.length - 1; i >= 0; i--) {
const bullet = bullets[i];
// Skip player bullets (negative speed)
if (bullet.speed < 0) continue;
if (
bullet.x + bullet.width/2 > player.x - player.width/2 &&
bullet.x - bullet.width/2 < player.x + player.width/2 &&
bullet.y - bullet.height/2 < player.y + player.height/2 &&
bullet.y + bullet.height/2 > player.y - player.height/2
) {
// Hit detected
createExplosion(bullet.x, bullet.y, '#f00');
player.health -= 10;
updateHealthBar();
// Remove bullet
bullets.splice(i, 1);
// Check if player is dead
if (player.health <= 0) {
gameOver();
}
}
}
// Enemies hitting player
for (let i = enemies.length - 1; i >= 0; i--) {
const enemy = enemies[i];
if (
player.x < enemy.x + enemy.width/2 &&
player.x > enemy.x - enemy.width/2 &&
player.y < enemy.y + enemy.height/2 &&
player.y > enemy.y - enemy.height/2
) {
// Collision detected
createExplosion(enemy.x, enemy.y, enemy.color);
player.health -= 20;
updateHealthBar();
enemies.splice(i, 1);
// Check if player is dead
if (player.health <= 0) {
gameOver();
}
}
}
}
// Create explosion effect
function createExplosion(x, y, color) {
const explosion = document.createElement('div');
explosion.className = 'explosion';
explosion.style.left = `${x - 15}px`;
explosion.style.top = `${y - 15}px`;
explosion.style.backgroundColor = color;
document.getElementById('gameContainer').appendChild(explosion);
// Remove after animation
setTimeout(() => {
explosion.remove();
}, 500);
}
// Update health bar display
function updateHealthBar() {
const percent = (player.health / player.maxHealth) * 100;
healthProgress.style.width = `${percent}%`;
// Change color based on health
if (percent > 60) {
healthProgress.style.backgroundColor = '#0f0';
} else if (percent > 30) {
healthProgress.style.backgroundColor = '#ff0';
} else {
healthProgress.style.backgroundColor = '#f00';
}
}
// Game over
function gameOver() {
gameRunning = false;
finalScore.textContent = `Your score: ${score}`;
gameOverScreen.style.display = 'flex';
if (animationFrameId) {
cancelAnimationFrame(animationFrameId);
}
}
// Reset game
function resetGame() {
player.x = canvas.width / 2;
player.y = canvas.height - 100;
player.health = player.maxHealth;
bullets.length = 0;
enemies.length = 0;
score = 0;
scoreDisplay.textContent = `Score: ${score}`;
updateHealthBar();
enemySpawnInterval = 1500;
enemySpawnTimer = 0;
}
// Game loop
function gameLoop(timestamp) {
if (!gameRunning) return;
if (!lastTime) lastTime = timestamp;
const deltaTime = timestamp - lastTime;
lastTime = timestamp;
// Clear canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Update player position
if (keys.ArrowLeft && player.x > player.width/2) {
player.x -= player.speed;
}
if (keys.ArrowRight && player.x < canvas.width - player.width/2) {
player.x += player.speed;
}
if (keys.ArrowUp && player.y > player.height/2) {
player.y -= player.speed;
}
if (keys.ArrowDown && player.y < canvas.height - player.height/2) {
player.y += player.speed;
}
// Handle shooting
if (keys[' '] && player.shootCooldown <= 0) {
shoot();
player.shootCooldown = player.shootInterval;
}
player.shootCooldown -= deltaTime;
// Spawn enemies
enemySpawnTimer += deltaTime;
if (enemySpawnTimer >= enemySpawnInterval) {
enemySpawnTimer = 0;
spawnEnemy();
// Slightly increase spawn rate over time
enemySpawnInterval = Math.max(1000, 1500 - score * 0.5);
}
// Update game objects
drawPlayer();
updateBullets();
updateEnemies();
checkCollisions();
// Continue game loop
animationFrameId = requestAnimationFrame(gameLoop);
}
// Start game
startButton.addEventListener('click', () => {
resetGame();
startScreen.style.display = 'none';
gameRunning = true;
lastTime = 0; // Reset lastTime
animationFrameId = requestAnimationFrame(gameLoop);
});
// Restart game
restartButton.addEventListener('click', () => {
resetGame();
gameOverScreen.style.display = 'none';
gameRunning = true;
lastTime = 0; // Reset lastTime
animationFrameId = requestAnimationFrame(gameLoop);
});
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <a href="https://enzostvs-deepsite.hf.space" style="color: #fff;" target="_blank" >DeepSite</a> <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;"></p></body>
</html>