Spaces:
Running
Running
<html> | |
<head> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> | |
<title>π Cosmic Dodger!</title> | |
<style> | |
* { margin: 0; padding: 0; touch-action: manipulation; } | |
body { overflow: hidden; background: #000; } | |
#hud { | |
position: fixed; | |
top: 15px; | |
left: 15px; | |
color: #fff; | |
font-family: 'Courier New', monospace; | |
font-size: 24px; | |
text-shadow: 0 0 10px #00ffff; | |
z-index: 100; | |
} | |
#gameOver { | |
display: none; | |
position: fixed; | |
top: 50%; | |
left: 50%; | |
transform: translate(-score50%, -50%); | |
text-align: center; | |
color: #fff; | |
background: rgba(0,0,0,0.9); | |
padding: 20px; | |
border-radius: 15px; | |
border: 2px solid #00ff88; | |
} | |
#restart { | |
display: none; | |
position: fixed; | |
bottom: 20px; | |
left: 50%; | |
transform: translateX(-50%); | |
padding: 12px 25px; | |
background: #00ff88; | |
color: #000; | |
border: none; | |
border-radius: 25px; | |
font-size: 18px; | |
cursor: pointer; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="hud"> | |
<div>SCORE: <span id="score">0</span></div> | |
<div>LIVES: <span id="lives">3</span></div> | |
</div> | |
<div id="gameOver"> | |
<h1 style="color: #ff0066">GAME OVER</h1> | |
<p>Final Score: <span id="finalscore">0</span></p> | |
</div> | |
<button id="restart" onclick="location.reload()">PLAY AGAIN</button> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> | |
<script> | |
const CONFIG = { | |
PLAYER_SPEED: 0.3, | |
ASTEROID_SPAWN: 0.02, | |
POWERUP_CHANCE: 0.25, | |
INITIAL_LIVES: 3 | |
}; | |
let score = 0, lives = CONFIG.INITIAL_LIVES, isGameOver = false; | |
let touchStartX = 0, currentX = 0, speedMultiplier = 1; | |
const scene = new THREE.Scene(); | |
const camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000); | |
const renderer = new THREE.WebGLRenderer({ antialias: true }); | |
renderer.setSize(window.innerWidth, window.innerHeight); | |
document.body.appendChild(renderer.domElement); | |
// Player (Glowing Cube) | |
const player = new THREE.Mesh( | |
new THREE.BoxGeometry(0.8, 0.8, 0.8), | |
new THREE.MeshStandardMaterial({ | |
color: 0x00ff88, | |
emissive: 0x00ff88, | |
emissiveIntensity: 0.5 | |
}) | |
); | |
player.position.z = 3; | |
scene.add(player); | |
// Shield Effect | |
const shield = new THREE.Mesh( | |
new THREE.SphereGeometry(1.2, 16, 16), | |
new THREE.MeshStandardMaterial({ | |
color: 0x00ffff, | |
transparent: true, | |
opacity: 0.3 | |
}) | |
); | |
shield.visible = false; | |
player.add(shield); | |
// Starfield | |
const stars = new THREE.Points( | |
new THREE.BufferGeometry().setFromPoints( | |
Array(1500).fill().map(() => new THREE.Vector3( | |
(Math.random() - 0.5) * 100, | |
(Math.random() - 0.5) * 100, | |
(Math.random() - 0.5) * 100 | |
)) | |
), | |
new THREE.PointsMaterial({ | |
size: 0.1, | |
color: 0xffffff, | |
transparent: true, | |
opacity: 0.8 | |
}) | |
); | |
scene.add(stars); | |
// Lighting | |
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5); | |
scene.add(ambientLight); | |
// Controls | |
document.addEventListener('touchstart', e => touchStartX = e.touches[0].clientX); | |
document.addEventListener('touchmove', e => { | |
if(isGameOver) return; | |
currentX = (e.touches[0].clientX - touchStartX) * 0.025; | |
player.position.x = THREE.MathUtils.clamp(currentX, -7, 7); | |
}); | |
// Game Loop | |
function animate() { | |
if(isGameOver) return; | |
// Spawning | |
if(Math.random() < CONFIG.ASTEROID_SPAWN + score/50000) spawnObject(); | |
// Difficulty increase | |
speedMultiplier = 1 + score/5000; | |
// Visual Updates | |
stars.rotation.x += 0.0005; | |
stars.rotation.y += 0.0005; | |
player.rotation.y += 0.02; | |
// Collision Detection | |
scene.children.forEach(obj => { | |
if(obj.userData?.isHazard && obj.position.distanceTo(player.position) < 1.2) { | |
if(obj.userData.isPowerUp) { | |
lives = Math.min(5, lives + 1); | |
score += 100; | |
activateShield(); | |
} else { | |
if(!shield.visible) lives--; | |
if(lives <= 0) endGame(); | |
} | |
scene.remove(obj); | |
updateHUD(); | |
} | |
}); | |
renderer.render(scene, camera); | |
requestAnimationFrame(animate); | |
} | |
function spawnObject() { | |
const isPowerUp = Math.random() < CONFIG.POWERUP_CHANCE; | |
const geometry = isPowerUp ? | |
new THREE.IcosahedronGeometry(0.6) : | |
new THREE.OctahedronGeometry(1); | |
const obj = new THREE.Mesh( | |
geometry, | |
new THREE.MeshStandardMaterial({ | |
color: isPowerUp ? 0x00ffff : 0xff4444, | |
emissive: isPowerUp ? 0x00ffff : 0xff4444, | |
emissiveIntensity: 0.2 | |
}) | |
); | |
obj.position.set( | |
(Math.random() - 0.5) * 14, | |
(Math.random() - 0.5) * 8, | |
-50 | |
); | |
obj.userData = { isHazard: true, isPowerUp }; | |
// Animation | |
function move() { | |
obj.position.z += 0.15 * speedMultiplier; | |
obj.rotation.x += 0.02; | |
obj.rotation.y += 0.02; | |
if(obj.position.z > 5) scene.remove(obj); | |
else requestAnimationFrame(move); | |
} | |
scene.add(obj); | |
move(); | |
} | |
function activateShield() { | |
shield.visible = true; | |
setTimeout(() => shield.visible = false, 2000); | |
} | |
function updateHUD() { | |
document.getElementById('score').textContent = score; | |
document.getElementById('lives').textContent = lives; | |
score += speedMultiplier; | |
} | |
function endGame() { | |
isGameOver = true; | |
document.getElementById('gameOver').style.display = 'block'; | |
document.getElementById('restart').style.display = 'block'; | |
localStorage.setItem('highScore', Math.max(score, localStorage.getItem('highScore') || 0)); | |
} | |
// Init | |
camera.position.z = 5; | |
animate(); | |
updateHUD(); | |
// Restart | |
document.addEventListener('touchend', () => isGameOver && location.reload()); | |
</script> | |
</body> | |
</html> |