Spaces:
Running
Running
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Space Defender</title> | |
<style> | |
body { | |
margin: 0; | |
overflow: hidden; | |
font-family: 'Orbitron', sans-serif; | |
} | |
canvas { | |
display: block; | |
} | |
#info { | |
position: absolute; | |
top: 10px; | |
width: 100%; | |
text-align: center; | |
color: white; | |
font-size: 20px; | |
text-shadow: 0 0 5px #00ff00; | |
pointer-events: none; | |
} | |
#gameOver { | |
position: absolute; | |
top: 50%; | |
left: 50%; | |
transform: translate(-50%, -50%); | |
color: white; | |
background: rgba(0,0,0,0.8); | |
padding: 30px; | |
border-radius: 15px; | |
text-align: center; | |
font-size: 24px; | |
display: none; | |
border: 2px solid #00ff00; | |
} | |
button { | |
background: #000; | |
color: #00ff00; | |
border: 2px solid #00ff00; | |
padding: 10px 25px; | |
font-size: 18px; | |
margin-top: 20px; | |
font-family: 'Orbitron', sans-serif; | |
cursor: pointer; | |
transition: all 0.3s; | |
} | |
button:hover { | |
background: #00ff00; | |
color: #000; | |
} | |
@import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700&display=swap'); | |
</style> | |
</head> | |
<body> | |
<div id="info"> | |
SCORE: <span id="score">0</span> | | |
HEALTH: <span id="health">100</span>% | | |
LEVEL: <span id="level">1</span> | |
</div> | |
<div id="gameOver"> | |
<h1>MISSION FAILED</h1> | |
<p>YOUR SCORE: <span id="finalScore">0</span></p> | |
<button onclick="resetGame()">RETRY MISSION</button> | |
</div> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> | |
<script> | |
// Game variables | |
let score = 0; | |
let health = 100; | |
let level = 1; | |
let gameOver = false; | |
let lastSpawnTime = 0; | |
let enemies = []; | |
let lasers = []; | |
// Three.js components | |
let scene, camera, renderer, ship, space, stars = []; | |
const clock = new THREE.Clock(); | |
// Initialize game | |
init(); | |
animate(); | |
function init() { | |
// Create scene | |
scene = new THREE.Scene(); | |
// Create camera | |
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); | |
camera.position.z = 30; | |
// Create renderer | |
renderer = new THREE.WebGLRenderer({ antialias: true }); | |
renderer.setSize(window.innerWidth, window.innerHeight); | |
renderer.shadowMap.enabled = true; | |
document.body.appendChild(renderer.domElement); | |
// Add lighting | |
const ambientLight = new THREE.AmbientLight(0x111111); | |
scene.add(ambientLight); | |
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5); | |
directionalLight.position.set(0, 0, 1); | |
scene.add(directionalLight); | |
const pointLight = new THREE.PointLight(0x00ff00, 5, 50); | |
pointLight.position.set(0, -5, 0); | |
scene.add(pointLight); | |
// Create player ship | |
const shipGeometry = new THREE.ConeGeometry(2, 5, 4); | |
const shipMaterial = new THREE.MeshPhongMaterial({ | |
color: 0x00ff00, | |
emissive: 0x00ff00, | |
emissiveIntensity: 0.5 | |
}); | |
ship = new THREE.Mesh(shipGeometry, shipMaterial); | |
ship.position.z = -15; | |
ship.rotation.x = Math.PI / 2; | |
scene.add(ship); | |
// Create space background | |
const spaceGeometry = new THREE.SphereGeometry(1000, 32, 32); | |
const spaceMaterial = new THREE.MeshBasicMaterial({ | |
color: 0x000000, | |
side: THREE.BackSide | |
}); | |
space = new THREE.Mesh(spaceGeometry, spaceMaterial); | |
scene.add(space); | |
// Create stars | |
for (let i = 0; i < 1000; i++) { | |
const starGeometry = new THREE.SphereGeometry(0.1, 8, 8); | |
const starMaterial = new THREE.MeshBasicMaterial({ color: 0xffffff }); | |
const star = new THREE.Mesh(starGeometry, starMaterial); | |
star.position.x = (Math.random() - 0.5) * 2000; | |
star.position.y = (Math.random() - 0.5) * 2000; | |
star.position.z = (Math.random() - 0.5) * 2000; | |
scene.add(star); | |
stars.push(star); | |
} | |
// Event listeners | |
window.addEventListener('resize', onWindowResize); | |
window.addEventListener('keydown', onKeyDown); | |
window.addEventListener('mousemove', onMouseMove); | |
window.addEventListener('click', fireLaser); | |
} | |
function onWindowResize() { | |
camera.aspect = window.innerWidth / window.innerHeight; | |
camera.updateProjectionMatrix(); | |
renderer.setSize(window.innerWidth, window.innerHeight); | |
} | |
function onKeyDown(e) { | |
const speed = 10; | |
if (gameOver) return; | |
switch(e.key) { | |
case 'ArrowLeft': ship.position.x = Math.max(ship.position.x - speed, -20); break; | |
case 'ArrowRight': ship.position.x = Math.min(ship.position.x + speed, 20); break; | |
case 'ArrowUp': ship.position.y = Math.min(ship.position.y + speed, 10); break; | |
case 'ArrowDown': ship.position.y = Math.max(ship.position.y - speed, -10); break; | |
case ' ': fireLaser(); break; | |
} | |
} | |
function onMouseMove(e) { | |
if (gameOver) return; | |
const x = (e.clientX / window.innerWidth) * 2 - 1; | |
const y = -(e.clientY / window.innerHeight) * 2 + 1; | |
ship.position.x = x * 20; | |
ship.position.y = y * 10; | |
} | |
function fireLaser() { | |
if (gameOver) return; | |
const laserGeometry = new THREE.CylinderGeometry(0.1, 0.1, 3, 8); | |
const laserMaterial = new THREE.MeshPhongMaterial({ | |
color: 0x00ffff, | |
emissive: 0x00ffff, | |
emissiveIntensity: 1 | |
}); | |
const laser = new THREE.Mesh(laserGeometry, laserMaterial); | |
laser.position.copy(ship.position); | |
laser.position.z += 5; | |
laser.rotation.x = Math.PI / 2; | |
scene.add(laser); | |
lasers.push(laser); | |
// Play sound | |
const audio = new Audio('https://assets.codepen.io/21542/hit-sound.mp3'); | |
audio.volume = 0.2; | |
audio.play().catch(e => console.log("Audio error:", e)); | |
} | |
function createEnemy() { | |
if (gameOver) return; | |
const types = ['cube', 'sphere', 'torus']; | |
const type = types[Math.floor(Math.random() * types.length)]; | |
let geometry; | |
switch(type) { | |
case 'cube': geometry = new THREE.BoxGeometry(2, 2, 2); break; | |
case 'sphere': geometry = new THREE.SphereGeometry(1.5, 16, 16); break; | |
case 'torus': geometry = new THREE.TorusGeometry(1, 0.5, 16, 32); break; | |
} | |
const color = new THREE.Color( | |
Math.random() * 0.5 + 0.5, | |
Math.random() * 0.2, | |
Math.random() * 0.2 | |
); | |
const material = new THREE.MeshPhongMaterial({ | |
color: color, | |
emissive: color, | |
emissiveIntensity: 0.2 | |
}); | |
const enemy = new THREE.Mesh(geometry, material); | |
enemy.position.x = (Math.random() - 0.5) * 30; | |
enemy.position.y = (Math.random() * 15) - 5; | |
enemy.position.z = 50; | |
scene.add(enemy); | |
enemies.push({ | |
mesh: enemy, | |
speed: 5 + Math.random() * level * 0.5, | |
health: 1 + level * 0.2 | |
}); | |
} | |
function updateLasers(delta) { | |
for (let i = lasers.length - 1; i >= 0; i--) { | |
const laser = lasers[i]; | |
laser.position.z += delta * 100; | |
if (laser.position.z > 50) { | |
scene.remove(laser); | |
lasers.splice(i, 1); | |
} else { | |
checkLaserCollision(laser, i); | |
} | |
} | |
} | |
function checkLaserCollision(laser, laserIndex) { | |
for (let j = enemies.length - 1; j >= 0; j--) { | |
const enemy = enemies[j]; | |
if (laser.position.distanceTo(enemy.mesh.position) < 3) { | |
enemy.health -= 1; | |
if (enemy.health <= 0) { | |
score += Math.floor(10 * enemy.speed); | |
document.getElementById('score').textContent = score; | |
// Explosion effect | |
const explosionGeometry = new THREE.SphereGeometry(2, 8, 8); | |
const explosionMaterial = new THREE.MeshBasicMaterial({ | |
color: 0xff9900, | |
wireframe: true | |
}); | |
const explosion = new THREE.Mesh(explosionGeometry, explosionMaterial); | |
explosion.position.copy(enemy.mesh.position); | |
scene.add(explosion); | |
setTimeout(() => scene.remove(explosion), 200); | |
scene.remove(enemy.mesh); | |
enemies.splice(j, 1); | |
} | |
scene.remove(laser); | |
lasers.splice(laserIndex, 1); | |
break; | |
} | |
} | |
} | |
function updateEnemies(delta) { | |
for (let i = enemies.length - 1; i >= 0; i--) { | |
const enemy = enemies[i]; | |
enemy.mesh.position.z -= delta * enemy.speed; | |
enemy.mesh.rotation.x += delta * 0.5; | |
enemy.mesh.rotation.y += delta * 0.7; | |
if (enemy.mesh.position.z < -20) { | |
scene.remove(enemy.mesh); | |
enemies.splice(i, 1); | |
health -= 5 + level; | |
document.getElementById('health').textContent = health; | |
if (health <= 0) { | |
endGame(); | |
} | |
} | |
} | |
} | |
function updateStars() { | |
stars.forEach(star => { | |
star.position.z += 1; | |
if (star.position.z > 1000) { | |
star.position.z = -1000; | |
} | |
}); | |
} | |
function checkLevelUp() { | |
const newLevel = Math.floor(score / 1000) + 1; | |
if (newLevel > level) { | |
level = newLevel; | |
document.getElementById('level').textContent = level; | |
// Play level up sound | |
const audio = new Audio('https://assets.codepen.io/21542/level-up.mp3'); | |
audio.volume = 0.2; | |
audio.play().catch(e => console.log("Audio error:", e)); | |
} | |
} | |
function endGame() { | |
gameOver = true; | |
document.getElementById('finalScore').textContent = score; | |
document.getElementById('gameOver').style.display = 'block'; | |
// Remove all enemies and lasers | |
enemies.forEach(enemy => scene.remove(enemy.mesh)); | |
lasers.forEach(laser => scene.remove(laser)); | |
enemies = []; | |
lasers = []; | |
} | |
function resetGame() { | |
score = 0; | |
health = 100; | |
level = 1; | |
gameOver = false; | |
document.getElementById('score').textContent = score; | |
document.getElementById('health').textContent = health; | |
document.getElementById('level').textContent = level; | |
document.getElementById('gameOver').style.display = 'none'; | |
ship.position.set(0, 0, -15); | |
} | |
function animate() { | |
requestAnimationFrame(animate); | |
const delta = clock.getDelta(); | |
const time = clock.getElapsedTime(); | |
if (!gameOver) { | |
// Spawn enemies with increasing frequency | |
if (time - lastSpawnTime > 1 / (level * 0.5 + 0.5)) { | |
createEnemy(); | |
lastSpawnTime = time; | |
} | |
updateLasers(delta); | |
updateEnemies(delta); | |
updateStars(); | |
checkLevelUp(); | |
// Camera shake when damaged | |
if (health < 30) { | |
camera.position.x = (Math.random() - 0.5) * 0.5; | |
camera.position.y = (Math.random() - 0.5) * 0.5; | |
} else { | |
camera.position.x = 0; | |
camera.position.y = 0; | |
} | |
// Ship pulsing effect | |
ship.material.color.offsetHSL(0, 0, delta * 0.1); | |
} | |
renderer.render(scene, camera); | |
} | |
</script> | |
</body> | |
</html> |