Spaces:
Running
Running
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Space Battle 2D</title> | |
<style> | |
body { | |
margin: 0; | |
overflow: hidden; | |
background-color: #000; | |
font-family: 'Arial', sans-serif; | |
touch-action: none; | |
} | |
#gameContainer { | |
position: relative; | |
width: 100vw; | |
height: 100vh; | |
} | |
#gameCanvas { | |
background-color: #000; | |
display: block; | |
background-image: radial-gradient(circle at center, #111 0%, #000 100%); | |
position: absolute; | |
top: 0; | |
left: 0; | |
} | |
#uiContainer { | |
position: absolute; | |
top: 0; | |
left: 0; | |
width: 100%; | |
height: 100%; | |
pointer-events: none; | |
} | |
.health-bar { | |
position: absolute; | |
top: 20px; | |
height: 30px; | |
border: 2px solid #fff; | |
border-radius: 5px; | |
overflow: hidden; | |
} | |
#player1Health { | |
left: 20px; | |
width: 200px; | |
} | |
#player2Health { | |
right: 20px; | |
width: 200px; | |
} | |
.health-fill { | |
height: 100%; | |
transition: width 0.3s; | |
} | |
#player1Health .health-fill { | |
background: linear-gradient(to right, #f00, #f80); | |
} | |
#player2Health .health-fill { | |
background: linear-gradient(to right, #0af, #08f); | |
} | |
#timer { | |
position: absolute; | |
top: 20px; | |
left: 50%; | |
transform: translateX(-50%); | |
color: #fff; | |
font-size: 24px; | |
text-shadow: 0 0 5px #0ff; | |
} | |
#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; | |
z-index: 10; | |
} | |
#startButton { | |
margin-top: 20px; | |
padding: 15px 30px; | |
font-size: 20px; | |
background: linear-gradient(to right, #f80, #f00); | |
border: none; | |
border-radius: 10px; | |
color: white; | |
cursor: pointer; | |
transition: transform 0.2s; | |
} | |
#startButton:hover { | |
transform: scale(1.05); | |
} | |
.controls { | |
margin-top: 30px; | |
text-align: center; | |
background-color: rgba(0, 0, 0, 0.5); | |
padding: 15px; | |
border-radius: 10px; | |
max-width: 80%; | |
} | |
.player-controls { | |
display: inline-block; | |
margin: 0 20px; | |
vertical-align: top; | |
} | |
#gameOverScreen { | |
position: absolute; | |
top: 0; | |
left: 0; | |
width: 100%; | |
height: 100%; | |
background-color: rgba(0, 0, 0, 0.8); | |
display: none; | |
flex-direction: column; | |
justify-content: center; | |
align-items: center; | |
color: white; | |
z-index: 10; | |
} | |
#winnerText { | |
font-size: 36px; | |
margin-bottom: 20px; | |
text-shadow: 0 0 10px #0ff; | |
} | |
#restartButton { | |
margin-top: 20px; | |
padding: 15px 30px; | |
font-size: 20px; | |
background: linear-gradient(to right, #0af, #08f); | |
border: none; | |
border-radius: 10px; | |
color: white; | |
cursor: pointer; | |
transition: transform 0.2s; | |
} | |
#restartButton:hover { | |
transform: scale(1.05); | |
} | |
.mobile-controls { | |
position: absolute; | |
bottom: 20px; | |
width: 100%; | |
display: none; | |
justify-content: space-between; | |
padding: 0 20px; | |
box-sizing: border-box; | |
} | |
.mobile-joystick { | |
width: 100px; | |
height: 100px; | |
background-color: rgba(255, 255, 255, 0.2); | |
border-radius: 50%; | |
position: relative; | |
} | |
.mobile-fire { | |
width: 80px; | |
height: 80px; | |
background-color: rgba(255, 0, 0, 0.5); | |
border-radius: 50%; | |
display: flex; | |
justify-content: center; | |
align-items: center; | |
font-size: 24px; | |
color: white; | |
} | |
@media (max-width: 768px) { | |
.mobile-controls { | |
display: flex; | |
} | |
.controls { | |
display: none; | |
} | |
} | |
</style> | |
</head> | |
<body> | |
<div id="gameContainer"> | |
<canvas id="gameCanvas"></canvas> | |
<div id="uiContainer"> | |
<div id="player1Health" class="health-bar"> | |
<div class="health-fill" style="width: 100%"></div> | |
</div> | |
<div id="player2Health" class="health-bar"> | |
<div class="health-fill" style="width: 100%"></div> | |
</div> | |
<div id="timer">02:00</div> | |
<div class="mobile-controls"> | |
<div class="mobile-joystick" id="player1Joystick"></div> | |
<div class="mobile-fire" id="player1Fire">FIRE</div> | |
</div> | |
</div> | |
<div id="startScreen"> | |
<h1>SPACE BATTLE</h1> | |
<p>Defeat your opponent before time runs out!</p> | |
<div class="controls"> | |
<div class="player-controls"> | |
<h3>Player 1 (Red)</h3> | |
<p>Move: WASD</p> | |
<p>Fire: Space</p> | |
</div> | |
<div class="player-controls"> | |
<h3>Player 2 (Blue)</h3> | |
<p>Move: Arrow Keys</p> | |
<p>Fire: Enter</p> | |
</div> | |
</div> | |
<button id="startButton">START BATTLE</button> | |
</div> | |
<div id="gameOverScreen"> | |
<h1>GAME OVER</h1> | |
<div id="winnerText"></div> | |
<button id="restartButton">PLAY AGAIN</button> | |
</div> | |
</div> | |
<script> | |
// Game setup | |
const canvas = document.getElementById('gameCanvas'); | |
const ctx = canvas.getContext('2d'); | |
const startScreen = document.getElementById('startScreen'); | |
const startButton = document.getElementById('startButton'); | |
const gameOverScreen = document.getElementById('gameOverScreen'); | |
const restartButton = document.getElementById('restartButton'); | |
const winnerText = document.getElementById('winnerText'); | |
const timerElement = document.getElementById('timer'); | |
const player1HealthFill = document.querySelector('#player1Health .health-fill'); | |
const player2HealthFill = document.querySelector('#player2Health .health-fill'); | |
// Set canvas size | |
function resizeCanvas() { | |
canvas.width = window.innerWidth; | |
canvas.height = window.innerHeight; | |
} | |
window.addEventListener('resize', resizeCanvas); | |
resizeCanvas(); | |
// Game state | |
let gameRunning = false; | |
let gameTime = 120; // 2 minutes in seconds | |
let lastTime = 0; | |
// Players | |
const players = { | |
player1: { | |
x: 200, | |
y: canvas.height / 2, | |
width: 40, | |
height: 30, | |
color: '#f80', | |
speed: 5, | |
health: 100, | |
maxHealth: 100, | |
lastShot: 0, | |
shotCooldown: 500, // ms | |
keys: { | |
up: 'w', | |
down: 's', | |
left: 'a', | |
right: 'd', | |
fire: ' ' | |
}, | |
activeKeys: { | |
up: false, | |
down: false, | |
left: false, | |
right: false | |
} | |
}, | |
player2: { | |
x: canvas.width - 200, | |
y: canvas.height / 2, | |
width: 40, | |
height: 30, | |
color: '#08f', | |
speed: 5, | |
health: 100, | |
maxHealth: 100, | |
lastShot: 0, | |
shotCooldown: 500, // ms | |
keys: { | |
up: 'ArrowUp', | |
down: 'ArrowDown', | |
left: 'ArrowLeft', | |
right: 'ArrowRight', | |
fire: 'Enter' | |
}, | |
activeKeys: { | |
up: false, | |
down: false, | |
left: false, | |
right: false | |
} | |
} | |
}; | |
// Projectiles | |
const projectiles = []; | |
const projectileSpeed = 10; | |
const projectileSize = 5; | |
// Stars for background | |
const stars = []; | |
for (let i = 0; i < 100; i++) { | |
stars.push({ | |
x: Math.random() * canvas.width, | |
y: Math.random() * canvas.height, | |
size: Math.random() * 2, | |
opacity: Math.random() | |
}); | |
} | |
// Input handling | |
const keysPressed = {}; | |
window.addEventListener('keydown', (e) => { | |
keysPressed[e.key] = true; | |
// Check player 1 controls | |
if (e.key === players.player1.keys.up) players.player1.activeKeys.up = true; | |
if (e.key === players.player1.keys.down) players.player1.activeKeys.down = true; | |
if (e.key === players.player1.keys.left) players.player1.activeKeys.left = true; | |
if (e.key === players.player1.keys.right) players.player1.activeKeys.right = true; | |
// Check player 2 controls | |
if (e.key === players.player2.keys.up) players.player2.activeKeys.up = true; | |
if (e.key === players.player2.keys.down) players.player2.activeKeys.down = true; | |
if (e.key === players.player2.keys.left) players.player2.activeKeys.left = true; | |
if (e.key === players.player2.keys.right) players.player2.activeKeys.right = true; | |
// Fire controls | |
if (gameRunning) { | |
const now = Date.now(); | |
if (e.key === players.player1.keys.fire && now - players.player1.lastShot > players.player1.shotCooldown) { | |
fireProjectile(players.player1); | |
players.player1.lastShot = now; | |
} | |
if (e.key === players.player2.keys.fire && now - players.player2.lastShot > players.player2.shotCooldown) { | |
fireProjectile(players.player2); | |
players.player2.lastShot = now; | |
} | |
} | |
}); | |
window.addEventListener('keyup', (e) => { | |
keysPressed[e.key] = false; | |
// Check player 1 controls | |
if (e.key === players.player1.keys.up) players.player1.activeKeys.up = false; | |
if (e.key === players.player1.keys.down) players.player1.activeKeys.down = false; | |
if (e.key === players.player1.keys.left) players.player1.activeKeys.left = false; | |
if (e.key === players.player1.keys.right) players.player1.activeKeys.right = false; | |
// Check player 2 controls | |
if (e.key === players.player2.keys.up) players.player2.activeKeys.up = false; | |
if (e.key === players.player2.keys.down) players.player2.activeKeys.down = false; | |
if (e.key === players.player2.keys.left) players.player2.activeKeys.left = false; | |
if (e.key === players.player2.keys.right) players.player2.activeKeys.right = false; | |
}); | |
// Mobile touch controls | |
const player1Joystick = document.getElementById('player1Joystick'); | |
const player1Fire = document.getElementById('player1Fire'); | |
let joystickActive = false; | |
let joystickStartX = 0; | |
let joystickStartY = 0; | |
let joystickX = 0; | |
let joystickY = 0; | |
player1Joystick.addEventListener('touchstart', (e) => { | |
const rect = player1Joystick.getBoundingClientRect(); | |
joystickStartX = rect.left + rect.width / 2; | |
joystickStartY = rect.top + rect.height / 2; | |
joystickActive = true; | |
handleJoystickMove(e.touches[0]); | |
e.preventDefault(); | |
}); | |
document.addEventListener('touchmove', (e) => { | |
if (joystickActive) { | |
handleJoystickMove(e.touches[0]); | |
e.preventDefault(); | |
} | |
}); | |
document.addEventListener('touchend', () => { | |
joystickActive = false; | |
players.player1.activeKeys.up = false; | |
players.player1.activeKeys.down = false; | |
players.player1.activeKeys.left = false; | |
players.player1.activeKeys.right = false; | |
}); | |
player1Fire.addEventListener('touchstart', (e) => { | |
const now = Date.now(); | |
if (now - players.player1.lastShot > players.player1.shotCooldown) { | |
fireProjectile(players.player1); | |
players.player1.lastShot = now; | |
} | |
e.preventDefault(); | |
}); | |
function handleJoystickMove(touch) { | |
if (!joystickActive) return; | |
const dx = touch.clientX - joystickStartX; | |
const dy = touch.clientY - joystickStartY; | |
const distance = Math.sqrt(dx * dx + dy * dy); | |
const maxDistance = 50; | |
if (distance > maxDistance) { | |
joystickX = (dx / distance) * maxDistance; | |
joystickY = (dy / distance) * maxDistance; | |
} else { | |
joystickX = dx; | |
joystickY = dy; | |
} | |
// Update player controls based on joystick position | |
const threshold = 20; | |
players.player1.activeKeys.up = joystickY < -threshold; | |
players.player1.activeKeys.down = joystickY > threshold; | |
players.player1.activeKeys.left = joystickX < -threshold; | |
players.player1.activeKeys.right = joystickX > threshold; | |
} | |
// Projectile functions | |
function fireProjectile(player) { | |
const direction = player === players.player1 ? 1 : -1; | |
projectiles.push({ | |
x: player.x + (player.width / 2 * direction), | |
y: player.y, | |
width: projectileSize, | |
height: projectileSize, | |
speed: projectileSpeed * direction, | |
color: player.color, | |
owner: player === players.player1 ? 'player1' : 'player2' | |
}); | |
} | |
// Collision detection | |
function checkCollisions() { | |
for (let i = projectiles.length - 1; i >= 0; i--) { | |
const proj = projectiles[i]; | |
// Check if projectile is out of bounds | |
if (proj.x < 0 || proj.x > canvas.width) { | |
projectiles.splice(i, 1); | |
continue; | |
} | |
// Check collision with player 1 | |
if (proj.owner === 'player2' && | |
proj.x < players.player1.x + players.player1.width && | |
proj.x + proj.width > players.player1.x && | |
proj.y < players.player1.y + players.player1.height && | |
proj.y + proj.height > players.player1.y) { | |
players.player1.health -= 10; | |
player1HealthFill.style.width = `${(players.player1.health / players.player1.maxHealth) * 100}%`; | |
projectiles.splice(i, 1); | |
if (players.player1.health <= 0) { | |
endGame('Player 2 (Blue)'); | |
} | |
continue; | |
} | |
// Check collision with player 2 | |
if (proj.owner === 'player1' && | |
proj.x < players.player2.x + players.player2.width && | |
proj.x + proj.width > players.player2.x && | |
proj.y < players.player2.y + players.player2.height && | |
proj.y + proj.height > players.player2.y) { | |
players.player2.health -= 10; | |
player2HealthFill.style.width = `${(players.player2.health / players.player2.maxHealth) * 100}%`; | |
projectiles.splice(i, 1); | |
if (players.player2.health <= 0) { | |
endGame('Player 1 (Red)'); | |
} | |
} | |
} | |
} | |
// Game control functions | |
function startGame() { | |
gameRunning = true; | |
gameTime = 120; | |
lastTime = Date.now(); | |
startScreen.style.display = 'none'; | |
gameOverScreen.style.display = 'none'; | |
// Reset players | |
players.player1.x = 200; | |
players.player1.y = canvas.height / 2; | |
players.player1.health = 100; | |
player1HealthFill.style.width = '100%'; | |
players.player2.x = canvas.width - 200; | |
players.player2.y = canvas.height / 2; | |
players.player2.health = 100; | |
player2HealthFill.style.width = '100%'; | |
// Clear projectiles | |
projectiles.length = 0; | |
// Start game loop | |
requestAnimationFrame(gameLoop); | |
} | |
function endGame(winner) { | |
gameRunning = false; | |
winnerText.textContent = `${winner} Wins!`; | |
gameOverScreen.style.display = 'flex'; | |
} | |
function updateTimer() { | |
const now = Date.now(); | |
const delta = (now - lastTime) / 1000; | |
lastTime = now; | |
if (gameRunning) { | |
gameTime -= delta; | |
if (gameTime <= 0) { | |
gameTime = 0; | |
gameRunning = false; | |
if (players.player1.health > players.player2.health) { | |
endGame('Player 1 (Red)'); | |
} else if (players.player2.health > players.player1.health) { | |
endGame('Player 2 (Blue)'); | |
} else { | |
endGame('Draw!'); | |
} | |
} | |
const minutes = Math.floor(gameTime / 60); | |
const seconds = Math.floor(gameTime % 60); | |
timerElement.textContent = `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`; | |
} | |
} | |
// Drawing functions | |
function drawBackground() { | |
ctx.fillStyle = '#000'; | |
ctx.fillRect(0, 0, canvas.width, canvas.height); | |
// Draw stars | |
ctx.save(); | |
stars.forEach(star => { | |
ctx.fillStyle = `rgba(255, 255, 255, ${star.opacity})`; | |
ctx.beginPath(); | |
ctx.arc(star.x, star.y, star.size, 0, Math.PI * 2); | |
ctx.fill(); | |
// Move stars for parallax effect | |
star.y += 0.5; | |
if (star.y > canvas.height) { | |
star.y = 0; | |
star.x = Math.random() * canvas.width; | |
} | |
}); | |
ctx.restore(); | |
} | |
function drawPlayers() { | |
// Player 1 | |
ctx.save(); | |
ctx.fillStyle = players.player1.color; | |
ctx.translate(players.player1.x, players.player1.y); | |
ctx.beginPath(); | |
ctx.moveTo(players.player1.width, players.player1.height / 2); | |
ctx.lineTo(0, 0); | |
ctx.lineTo(0, players.player1.height); | |
ctx.closePath(); | |
ctx.fill(); | |
ctx.restore(); | |
// Player 2 | |
ctx.save(); | |
ctx.fillStyle = players.player2.color; | |
ctx.translate(players.player2.x, players.player2.y); | |
ctx.beginPath(); | |
ctx.moveTo(0, players.player2.height / 2); | |
ctx.lineTo(players.player2.width, 0); | |
ctx.lineTo(players.player2.width, players.player2.height); | |
ctx.closePath(); | |
ctx.fill(); | |
ctx.restore(); | |
} | |
function drawProjectiles() { | |
projectiles.forEach(proj => { | |
ctx.fillStyle = proj.color; | |
ctx.fillRect(proj.x, proj.y, proj.width, proj.height); | |
}); | |
} | |
// Game loop | |
function gameLoop() { | |
if (!gameRunning) return; | |
// Update game state | |
updateTimer(); | |
updatePlayers(); | |
updateProjectiles(); | |
checkCollisions(); | |
// Draw everything | |
drawBackground(); | |
drawPlayers(); | |
drawProjectiles(); | |
// Continue the game loop | |
requestAnimationFrame(gameLoop); | |
} | |
function updatePlayers() { | |
// Player 1 movement | |
if (players.player1.activeKeys.up) players.player1.y -= players.player1.speed; | |
if (players.player1.activeKeys.down) players.player1.y += players.player1.speed; | |
if (players.player1.activeKeys.left) players.player1.x -= players.player1.speed; | |
if (players.player1.activeKeys.right) players.player1.x += players.player1.speed; | |
// Player 2 movement | |
if (players.player2.activeKeys.up) players.player2.y -= players.player2.speed; | |
if (players.player2.activeKeys.down) players.player2.y += players.player2.speed; | |
if (players.player2.activeKeys.left) players.player2.x -= players.player2.speed; | |
if (players.player2.activeKeys.right) players.player2.x += players.player2.speed; | |
// Keep players within bounds | |
players.player1.x = Math.max(0, Math.min(canvas.width - players.player1.width, players.player1.x)); | |
players.player1.y = Math.max(0, Math.min(canvas.height - players.player1.height, players.player1.y)); | |
players.player2.x = Math.max(0, Math.min(canvas.width - players.player2.width, players.player2.x)); | |
players.player2.y = Math.max(0, Math.min(canvas.height - players.player2.height, players.player2.y)); | |
} | |
function updateProjectiles() { | |
for (let i = 0; i < projectiles.length; i++) { | |
projectiles[i].x += projectiles[i].speed; | |
} | |
} | |
// Event listeners for buttons | |
startButton.addEventListener('click', startGame); | |
restartButton.addEventListener('click', startGame); | |
</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> |