Spaces:
Running
Running
| <html> | |
| <head> | |
| <title>Space Shooter with Controls</title> | |
| <style> | |
| body { margin: 0; overflow: hidden; } | |
| #hud { | |
| position: fixed; | |
| top: 10px; | |
| left: 10px; | |
| color: #0ff; | |
| font-family: monospace; | |
| font-size: 16px; | |
| text-shadow: 0 0 5px #00f; | |
| pointer-events: none; | |
| } | |
| .controls { | |
| position: fixed; | |
| bottom: 20px; | |
| width: 100%; | |
| display: flex; | |
| justify-content: center; | |
| gap: 20px; | |
| touch-action: none; | |
| } | |
| .control-btn { | |
| width: 60px; | |
| height: 60px; | |
| background: rgba(0,255,255,0.2); | |
| border: 2px solid #0ff; | |
| border-radius: 50%; | |
| color: #0ff; | |
| font-size: 24px; | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| cursor: pointer; | |
| user-select: none; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div id="hud"> | |
| SCORE: <span id="score">0</span><br> | |
| HEALTH: <div id="health" style="width: 100px; height: 5px; background: #333;"> | |
| <div style="height: 100%; width: 100%; background: #0f0;"></div> | |
| </div> | |
| </div> | |
| <div class="controls"> | |
| <div class="control-btn" id="leftBtn">←</div> | |
| <div class="control-btn" id="shootBtn">●</div> | |
| <div class="control-btn" id="rightBtn">→</div> | |
| </div> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> | |
| <script> | |
| let scene, camera, renderer, player; | |
| let score = 0, health = 100, lastFire = 0; | |
| const enemies = []; | |
| const bullets = []; | |
| const controls = { left: false, right: false }; | |
| function init() { | |
| // Scene setup | |
| scene = new THREE.Scene(); | |
| camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000); | |
| renderer = new THREE.WebGLRenderer(); | |
| renderer.setSize(window.innerWidth, window.innerHeight); | |
| document.body.appendChild(renderer.domElement); | |
| // Generate starfield | |
| const stars = new THREE.BufferGeometry(); | |
| const starPositions = []; | |
| for(let i = 0; i < 1000; i++) { | |
| starPositions.push( | |
| (Math.random() - 0.5) * 2000, | |
| (Math.random() - 0.5) * 2000, | |
| (Math.random() - 0.5) * 2000 | |
| ); | |
| } | |
| stars.setAttribute('position', new THREE.Float32BufferAttribute(starPositions, 3)); | |
| scene.add(new THREE.Points(stars, | |
| new THREE.PointsMaterial({size: 0.2, color: 0xFFFFFF}))); | |
| // Create player | |
| player = new THREE.Mesh( | |
| new THREE.ConeGeometry(0.8, 2, 8), | |
| new THREE.MeshStandardMaterial({ | |
| color: 0x00FF00, | |
| metalness: 0.5, | |
| emissive: 0x003300 | |
| }) | |
| ); | |
| player.position.z = 5; | |
| scene.add(player); | |
| // Create initial enemies | |
| for(let i = 0; i < 5; i++) createEnemy(); | |
| // Lighting | |
| const light = new THREE.PointLight(0xFFFFFF, 1, 100); | |
| light.position.set(0, 0, 50); | |
| scene.add(light); | |
| setupControls(); | |
| animate(); | |
| } | |
| function createEnemy() { | |
| const enemy = new THREE.Mesh( | |
| new THREE.IcosahedronGeometry(1), | |
| new THREE.MeshStandardMaterial({ | |
| color: 0xFF0000, | |
| metalness: 0.3, | |
| emissive: 0x330000 | |
| }) | |
| ); | |
| enemy.position.set( | |
| (Math.random() - 0.5) * 30, | |
| (Math.random() - 0.5) * 30, | |
| -50 - Math.random() * 50 | |
| ); | |
| enemies.push(enemy); | |
| scene.add(enemy); | |
| } | |
| function setupControls() { | |
| // Mobile touch events | |
| document.getElementById('leftBtn').addEventListener('touchstart', () => controls.left = true); | |
| document.getElementById('leftBtn').addEventListener('touchend', () => controls.left = false); | |
| document.getElementById('rightBtn').addEventListener('touchstart', () => controls.right = true); | |
| document.getElementById('rightBtn').addEventListener('touchend', () => controls.right = false); | |
| document.getElementById('shootBtn').addEventListener('touchstart', shoot); | |
| // Desktop mouse events | |
| document.getElementById('leftBtn').addEventListener('mousedown', () => controls.left = true); | |
| document.getElementById('leftBtn').addEventListener('mouseup', () => controls.left = false); | |
| document.getElementById('leftBtn').addEventListener('mouseleave', () => controls.left = false); | |
| document.getElementById('rightBtn').addEventListener('mousedown', () => controls.right = true); | |
| document.getElementById('rightBtn').addEventListener('mouseup', () => controls.right = false); | |
| document.getElementById('rightBtn').addEventListener('mouseleave', () => controls.right = false); | |
| document.getElementById('shootBtn').addEventListener('click', shoot); | |
| // Keyboard controls | |
| window.addEventListener('keydown', e => { | |
| if(e.key === 'ArrowLeft') controls.left = true; | |
| if(e.key === 'ArrowRight') controls.right = true; | |
| if(e.key === ' ') shoot(); | |
| }); | |
| window.addEventListener('keyup', e => { | |
| if(e.key === 'ArrowLeft') controls.left = false; | |
| if(e.key === 'ArrowRight') controls.right = false; | |
| }); | |
| } | |
| function shoot() { | |
| if(Date.now() - lastFire < 250) return; | |
| lastFire = Date.now(); | |
| const bullet = new THREE.Mesh( | |
| new THREE.SphereGeometry(0.2), | |
| new THREE.MeshStandardMaterial({ | |
| color: 0xFFFF00, | |
| emissive: 0x444400 | |
| }) | |
| ); | |
| bullet.position.copy(player.position); | |
| bullet.velocity = new THREE.Vector3(0, 0, -0.3); | |
| bullets.push(bullet); | |
| scene.add(bullet); | |
| } | |
| function update() { | |
| // Player movement | |
| if(controls.left) player.rotation.z += 0.05; | |
| if(controls.right) player.rotation.z -= 0.05; | |
| // Update bullets | |
| bullets.forEach((bullet, index) => { | |
| bullet.position.add(bullet.velocity); | |
| if(bullet.position.z < -100) { | |
| scene.remove(bullet); | |
| bullets.splice(index, 1); | |
| } | |
| }); | |
| // Update enemies | |
| enemies.forEach((enemy, index) => { | |
| enemy.position.z += 0.1; | |
| if(enemy.position.z > 10) { | |
| enemy.position.set( | |
| (Math.random() - 0.5) * 30, | |
| (Math.random() - 0.5) * 30, | |
| -50 - Math.random() * 50 | |
| ); | |
| } | |
| }); | |
| // Collisions | |
| bullets.forEach((bullet, bIndex) => { | |
| enemies.forEach((enemy, eIndex) => { | |
| if(bullet.position.distanceTo(enemy.position) < 2) { | |
| scene.remove(bullet); | |
| scene.remove(enemy); | |
| bullets.splice(bIndex, 1); | |
| enemies.splice(eIndex, 1); | |
| score += 100; | |
| document.getElementById('score').textContent = score; | |
| createEnemy(); | |
| } | |
| }); | |
| }); | |
| } | |
| function animate() { | |
| requestAnimationFrame(animate); | |
| update(); | |
| renderer.render(scene, camera); | |
| } | |
| window.onresize = () => { | |
| camera.aspect = window.innerWidth / window.innerHeight; | |
| camera.updateProjectionMatrix(); | |
| renderer.setSize(window.innerWidth, window.innerHeight); | |
| }; | |
| init(); | |
| </script> | |
| </body> | |
| </html> |