Spaces:
Running
Running
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Moving Brick Attacker</title> | |
<script src="https://cdn.tailwindcss.com"></script> | |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
<style> | |
#gameCanvas { | |
background-color: #1a202c; | |
border-radius: 8px; | |
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); | |
touch-action: none; | |
} | |
.game-container { | |
position: relative; | |
} | |
.game-overlay { | |
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.7); | |
color: white; | |
border-radius: 8px; | |
} | |
.hidden { | |
display: none; | |
} | |
.brick { | |
border-radius: 2px; | |
} | |
.heart-icon { | |
color: #f56565; | |
} | |
.bullet { | |
position: absolute; | |
width: 4px; | |
height: 12px; | |
background-color: #f6e05e; | |
border-radius: 2px; | |
} | |
#attacker { | |
position: absolute; | |
width: 50px; | |
height: 80px; | |
background-color: #4a5568; | |
border-radius: 8px; | |
bottom: 10px; | |
left: 50%; | |
transform: translateX(-50%); | |
z-index: 10; | |
} | |
#gun { | |
position: absolute; | |
width: 30px; | |
height: 10px; | |
background-color: #2d3748; | |
top: -10px; | |
left: 10px; | |
border-radius: 2px; | |
transform-origin: left center; | |
z-index: 20; | |
} | |
#gun-barrel { | |
position: absolute; | |
width: 30px; | |
height: 4px; | |
background-color: #2d3748; | |
top: 3px; | |
left: 30px; | |
border-radius: 2px; | |
} | |
/* Animation for special bricks */ | |
@keyframes pulse { | |
0% { transform: scale(1); } | |
50% { transform: scale(1.05); } | |
100% { transform: scale(1); } | |
} | |
@keyframes spin { | |
0% { transform: rotate(0deg); } | |
100% { transform: rotate(360deg); } | |
} | |
.start-btn { | |
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
border: none; | |
color: white; | |
padding: 15px 32px; | |
text-align: center; | |
text-decoration: none; | |
display: inline-block; | |
font-size: 18px; | |
font-weight: bold; | |
margin: 10px 2px; | |
cursor: pointer; | |
border-radius: 50px; | |
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2); | |
transition: all 0.3s ease; | |
position: relative; | |
overflow: hidden; | |
} | |
.start-btn:hover { | |
transform: translateY(-3px); | |
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3); | |
} | |
.start-btn:active { | |
transform: translateY(1px); | |
} | |
.start-btn::after { | |
content: ""; | |
position: absolute; | |
top: 0; | |
left: 0; | |
width: 100%; | |
height: 100%; | |
background: linear-gradient(135deg, rgba(255,255,255,0.3) 0%, rgba(255,255,255,0) 100%); | |
opacity: 0; | |
transition: opacity 0.3s ease; | |
} | |
.start-btn:hover::after { | |
opacity: 1; | |
} | |
.start-btn i { | |
margin-right: 10px; | |
} | |
</style> | |
</head> | |
<body class="bg-gray-900 min-h-screen flex flex-col items-center justify-center p-4"> | |
<div class="max-w-4xl w-full"> | |
<h1 class="text-3xl font-bold text-center text-white mb-6">Moving Brick Attacker</h1> | |
<div class="flex justify-between items-center mb-4"> | |
<div class="flex items-center space-x-2"> | |
<span class="text-white font-semibold">Score:</span> | |
<span id="score" class="text-yellow-300 font-bold text-xl">0</span> | |
</div> | |
<div class="flex items-center space-x-2"> | |
<span class="text-white font-semibold">Lives:</span> | |
<div id="lives" class="flex space-x-1"> | |
<i class="fas fa-heart heart-icon"></i> | |
<i class="fas fa-heart heart-icon"></i> | |
<i class="fas fa-heart heart-icon"></i> | |
</div> | |
</div> | |
</div> | |
<div class="game-container relative"> | |
<canvas id="gameCanvas" width="800" height="500" class="w-full"></canvas> | |
<div id="startScreen" class="game-overlay"> | |
<h2 class="text-4xl font-bold mb-6 text-yellow-300">Brick Attacker</h2> | |
<p class="text-xl mb-8 text-center max-w-md">Shoot the moving bricks before they reach the bottom!</p> | |
<button id="startButton" class="start-btn"> | |
<i class="fas fa-play"></i> Start Game | |
</button> | |
</div> | |
<div id="gameOverScreen" class="game-overlay hidden"> | |
<h2 class="text-4xl font-bold mb-4 text-red-500">Game Over</h2> | |
<p class="text-2xl mb-2">Your score: <span id="finalScore" class="text-yellow-300">0</span></p> | |
<button id="restartButton" class="start-btn mt-6"> | |
<i class="fas fa-redo"></i> Play Again | |
</button> | |
</div> | |
</div> | |
<div class="mt-6 flex justify-center"> | |
<div class="bg-gray-800 p-4 rounded-lg max-w-md"> | |
<h3 class="text-white font-semibold mb-2">Controls:</h3> | |
<ul class="text-gray-300 space-y-1"> | |
<li><i class="fas fa-mouse-pointer mr-2"></i> Move mouse to aim</li> | |
<li><i class="fas fa-mouse mr-2"></i> Click to shoot</li> | |
<li><i class="fas fa-arrows-alt mr-2"></i> Arrow keys to move</li> | |
</ul> | |
</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 scoreElement = document.getElementById('score'); | |
const finalScoreElement = document.getElementById('finalScore'); | |
const livesElement = document.getElementById('lives'); | |
let gameRunning = false; | |
let score = 0; | |
let lives = 3; | |
let bricks = []; | |
let bullets = []; | |
let attackerX = canvas.width / 2; | |
let attackerWidth = 50; | |
let gunAngle = 0; | |
let mouseX = 0; | |
let mouseY = 0; | |
let animationId; | |
let brickSpeed = 2; | |
let brickSpawnRate = 60; | |
let frameCount = 0; | |
// Attacker element (for visual representation) | |
const attacker = document.createElement('div'); | |
attacker.id = 'attacker'; | |
document.querySelector('.game-container').appendChild(attacker); | |
const gun = document.createElement('div'); | |
gun.id = 'gun'; | |
attacker.appendChild(gun); | |
const gunBarrel = document.createElement('div'); | |
gunBarrel.id = 'gun-barrel'; | |
gun.appendChild(gunBarrel); | |
// Event listeners | |
startButton.addEventListener('click', startGame); | |
restartButton.addEventListener('click', startGame); | |
canvas.addEventListener('mousemove', (e) => { | |
const rect = canvas.getBoundingClientRect(); | |
mouseX = e.clientX - rect.left; | |
mouseY = e.clientY - rect.top; | |
// Calculate gun angle based on mouse position | |
const dx = mouseX - (attackerX + attackerWidth / 2); | |
const dy = mouseY - (canvas.height - 90); | |
gunAngle = Math.atan2(dy, dx); | |
// Update gun position and rotation | |
gun.style.transform = `rotate(${gunAngle}rad)`; | |
}); | |
canvas.addEventListener('click', shoot); | |
document.addEventListener('keydown', (e) => { | |
if (!gameRunning) return; | |
if (e.key === 'ArrowLeft') { | |
attackerX = Math.max(0, attackerX - 20); | |
} else if (e.key === 'ArrowRight') { | |
attackerX = Math.min(canvas.width - attackerWidth, attackerX + 20); | |
} | |
}); | |
// Game functions | |
function startGame() { | |
gameRunning = true; | |
score = 0; | |
lives = 3; | |
bricks = []; | |
bullets = []; | |
brickSpeed = 2; | |
brickSpawnRate = 60; | |
frameCount = 0; | |
scoreElement.textContent = score; | |
updateLives(); | |
startScreen.classList.add('hidden'); | |
gameOverScreen.classList.add('hidden'); | |
// Reset attacker position | |
attackerX = canvas.width / 2 - attackerWidth / 2; | |
// Start game loop | |
animationId = requestAnimationFrame(gameLoop); | |
} | |
function gameLoop() { | |
if (!gameRunning) return; | |
// Clear canvas | |
ctx.clearRect(0, 0, canvas.width, canvas.height); | |
// Update attacker position | |
attacker.style.left = `${attackerX}px`; | |
// Spawn bricks | |
if (frameCount % brickSpawnRate === 0) { | |
spawnBrick(); | |
} | |
// Update bricks | |
updateBricks(); | |
// Update bullets | |
updateBullets(); | |
// Check collisions | |
checkCollisions(); | |
// Increase difficulty | |
if (frameCount % 500 === 0) { | |
brickSpeed = Math.min(brickSpeed + 0.2, 6); | |
brickSpawnRate = Math.max(brickSpawnRate - 5, 20); | |
} | |
frameCount++; | |
animationId = requestAnimationFrame(gameLoop); | |
} | |
function spawnBrick() { | |
const width = Math.random() * 60 + 40; | |
const x = Math.random() * (canvas.width - width); | |
const color = getRandomColor(); | |
// 10% chance for special brick | |
const isSpecial = Math.random() < 0.1; | |
bricks.push({ | |
x, | |
y: 0, | |
width, | |
height: 20, | |
color, | |
isSpecial, | |
health: isSpecial ? 3 : 1 | |
}); | |
} | |
function getRandomColor() { | |
const colors = [ | |
'#f56565', // red | |
'#48bb78', // green | |
'#4299e1', // blue | |
'#ed8936', // orange | |
'#9f7aea', // purple | |
'#ecc94b' // yellow | |
]; | |
return colors[Math.floor(Math.random() * colors.length)]; | |
} | |
function updateBricks() { | |
for (let i = bricks.length - 1; i >= 0; i--) { | |
const brick = bricks[i]; | |
// Move brick down | |
brick.y += brickSpeed; | |
// Draw brick | |
ctx.fillStyle = brick.color; | |
ctx.fillRect(brick.x, brick.y, brick.width, brick.height); | |
// Draw special effects | |
if (brick.isSpecial) { | |
ctx.strokeStyle = '#ffffff'; | |
ctx.lineWidth = 2; | |
ctx.strokeRect(brick.x, brick.y, brick.width, brick.height); | |
// Draw health indicator | |
ctx.fillStyle = '#ffffff'; | |
ctx.font = '10px Arial'; | |
ctx.fillText('★'.repeat(brick.health), brick.x + 5, brick.y + 15); | |
} | |
// Remove bricks that go off screen | |
if (brick.y > canvas.height) { | |
bricks.splice(i, 1); | |
loseLife(); | |
} | |
} | |
} | |
function updateBullets() { | |
for (let i = bullets.length - 1; i >= 0; i--) { | |
const bullet = bullets[i]; | |
// Move bullet | |
bullet.x += bullet.dx * 10; | |
bullet.y += bullet.dy * 10; | |
// Draw bullet | |
const bulletElement = document.createElement('div'); | |
bulletElement.className = 'bullet'; | |
bulletElement.style.left = `${bullet.x}px`; | |
bulletElement.style.top = `${bullet.y}px`; | |
document.querySelector('.game-container').appendChild(bulletElement); | |
// Remove bullet after a short delay to allow animation | |
setTimeout(() => { | |
if (bulletElement.parentNode) { | |
bulletElement.parentNode.removeChild(bulletElement); | |
} | |
}, 16); | |
// Remove bullets that go off screen | |
if (bullet.x < 0 || bullet.x > canvas.width || | |
bullet.y < 0 || bullet.y > canvas.height) { | |
bullets.splice(i, 1); | |
} | |
} | |
} | |
function checkCollisions() { | |
for (let i = bullets.length - 1; i >= 0; i--) { | |
const bullet = bullets[i]; | |
for (let j = bricks.length - 1; j >= 0; j--) { | |
const brick = bricks[j]; | |
if (bullet.x > brick.x && bullet.x < brick.x + brick.width && | |
bullet.y > brick.y && bullet.y < brick.y + brick.height) { | |
// Hit brick | |
brick.health--; | |
if (brick.health <= 0) { | |
// Brick destroyed | |
bricks.splice(j, 1); | |
score += brick.isSpecial ? 5 : 1; | |
scoreElement.textContent = score; | |
} | |
// Remove bullet | |
bullets.splice(i, 1); | |
break; | |
} | |
} | |
} | |
} | |
function shoot() { | |
if (!gameRunning) return; | |
const startX = attackerX + attackerWidth / 2; | |
const startY = canvas.height - 90; | |
bullets.push({ | |
x: startX, | |
y: startY, | |
dx: Math.cos(gunAngle), | |
dy: Math.sin(gunAngle) | |
}); | |
} | |
function loseLife() { | |
lives--; | |
updateLives(); | |
if (lives <= 0) { | |
gameOver(); | |
} | |
} | |
function updateLives() { | |
livesElement.innerHTML = ''; | |
for (let i = 0; i < lives; i++) { | |
const heart = document.createElement('i'); | |
heart.className = 'fas fa-heart heart-icon'; | |
livesElement.appendChild(heart); | |
} | |
} | |
function gameOver() { | |
gameRunning = false; | |
cancelAnimationFrame(animationId); | |
finalScoreElement.textContent = score; | |
gameOverScreen.classList.remove('hidden'); | |
} | |
</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 <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=Mohabedalgani/zaido" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
</html> |