<!DOCTYPE html> <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: none; } body { overflow: hidden; background: #000; } #hud { position: fixed; top: 15px; left: 15px; color: #fff; font-family: 'Courier New', monospace; font-size: 26px; text-shadow: 0 0 10px #00ffff; z-index: 100; } #gameOver { display: none; position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); text-align: center; color: #fff; background: rgba(0,0,0,0.95); padding: 25px; border-radius: 15px; border: 2px solid #ff0066; width: 80%; max-width: 300px; } #restart { display: none; position: fixed; bottom: 25px; left: 50%; transform: translateX(-50%); padding: 15px 35px; background: #00ff88; color: #000; border: none; border-radius: 25px; font-size: 20px; cursor: pointer; font-weight: bold; } </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; font-size: 1.8em">GAME OVER</h1> <p style="font-size: 1.2em">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.25, ASTEROID_SPEED: 0.15, PLAYER_SIZE: 0.6, X_BOUNDS: 6, Y_BOUNDS: 4 }; let score = 0, lives = 3, isGameOver = false; let touchStartX = 0, currentX = 0; 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 (Flat Triangle - Mobile Friendly) const player = new THREE.Mesh( new THREE.ConeGeometry(CONFIG.PLAYER_SIZE, 1.2, 3), new THREE.MeshStandardMaterial({ color: 0x00ff88, emissive: 0x00ff88, emissiveIntensity: 0.5 }) ); player.rotation.x = Math.PI/2; player.position.z = 3; scene.add(player); // Player Boundary Markers const boundaryMaterial = new THREE.MeshBasicMaterial({ color: 0x00ffff, transparent: true, opacity: 0.2 }); const leftBoundary = new THREE.Mesh( new THREE.PlaneGeometry(0.5, 10), boundaryMaterial ); const rightBoundary = leftBoundary.clone(); leftBoundary.position.x = -CONFIG.X_BOUNDS; rightBoundary.position.x = CONFIG.X_BOUNDS; scene.add(leftBoundary, rightBoundary); // Asteroids (More Visible) function createAsteroid() { const asteroid = new THREE.Mesh( new THREE.IcosahedronGeometry(0.8), new THREE.MeshStandardMaterial({ color: 0xff4444, emissive: 0xff0000, emissiveIntensity: 0.3, wireframe: true }) ); asteroid.position.set( (Math.random() - 0.5) * 12, (Math.random() - 0.5) * 7, -40 ); return asteroid; } // Starfield (Optimized) const stars = new THREE.Points( new THREE.BufferGeometry().setFromPoints( Array(800).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.2, color: 0xffffff, transparent: true, opacity: 0.7 }) ); scene.add(stars); // Lighting const ambientLight = new THREE.AmbientLight(0xffffff, 0.8); scene.add(ambientLight); // Controls (Improved Mobile Handling) function handleMove(x) { if(isGameOver) return; currentX = THREE.MathUtils.clamp( (x - touchStartX) * 0.03, -CONFIG.X_BOUNDS, CONFIG.X_BOUNDS ); player.position.x = currentX; } document.addEventListener('touchstart', e => { e.preventDefault(); touchStartX = e.touches[0].clientX; }); document.addEventListener('touchmove', e => handleMove(e.touches[0].clientX)); document.addEventListener('mousedown', e => touchStartX = e.clientX); document.addEventListener('mousemove', e => e.buttons === 1 && handleMove(e.clientX)); // Game Loop function animate() { if(isGameOver) return; // Spawn Logic if(Math.random() < 0.03 + score/5000) { const asteroid = createAsteroid(); scene.add(asteroid); // Animate asteroid (function move(obj) { obj.position.z += CONFIG.ASTEROID_SPEED + score/2500; obj.rotation.x += 0.02; obj.rotation.y += 0.02; if(obj.position.z > 5) scene.remove(obj); else requestAnimationFrame(() => move(obj)); })(asteroid); } // Visual Updates stars.rotation.y += 0.0003; player.rotation.z = Math.sin(performance.now()*0.005) * 0.3; // Collision Check scene.children.forEach(obj => { if(obj !== player && obj.position.z > 0) { if(obj.position.distanceTo(player.position) < 1.2) { lives--; scene.remove(obj); updateHUD(); if(lives <= 0) endGame(); } } }); renderer.render(scene, camera); requestAnimationFrame(animate); } function updateHUD() { document.getElementById('score').textContent = Math.floor(score); document.getElementById('lives').textContent = lives; score += 1 + Math.floor(score/1000); } function endGame() { isGameOver = true; document.getElementById('gameOver').style.display = 'block'; document.getElementById('restart').style.display = 'block'; document.getElementById('finalscore').textContent = Math.floor(score); localStorage.setItem('highScore', Math.max(score, localStorage.getItem('highScore') || 0)); } // Initial Setup camera.position.z = 8; animate(); updateHUD(); // Resize Handler window.addEventListener('resize', () => { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); }); // Restart Handler document.addEventListener('touchend', () => isGameOver && location.reload()); document.addEventListener('mouseup', () => isGameOver && location.reload()); </script> </body> </html>