🎮ArcadeLab

3D Mini Space Shooter

by ThunderPhoenix57
221 lines6.3 KB🛠️ Three.js (3D graphics)
▶ Play
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>3D Mini Space Shooter</title>
  <style>
    body {
      margin: 0;
      overflow: hidden;
      background: #000;
      font-family: sans-serif;
    }
    #ui {
      position: absolute;
      top: 20px;
      left: 20px;
      color: #00ffcc;
      font-size: 20px;
      font-weight: bold;
      text-shadow: 0 0 5px #00ffcc;
      pointer-events: none;
    }
    #instructions {
      position: absolute;
      bottom: 20px;
      left: 50%;
      transform: translateX(-50%);
      color: #ffffff;
      font-size: 14px;
      letter-spacing: 1px;
      pointer-events: none;
      opacity: 0.8;
    }
  </style>
  <!-- Load Three.js library -->
  <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
</head>
<body>

  <div id="ui">Score: <span id="score">0</span></div>
  <div id="instructions">Move: MOUSE | Shoot: CLICK / SPACEBAR</div>

  <script>
    // 1. Scene Setup
    const scene = new THREE.Scene();
    scene.fog = new THREE.FogExp2(0x000000, 0.015);

    const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
    camera.position.set(0, 3, 10);

    const renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setSize(window.innerWidth, window.innerHeight);
    renderer.shadowMap.enabled = true;
    document.body.appendChild(renderer.domElement);

    // 2. Lighting
    const ambientLight = new THREE.AmbientLight(0xffffff, 0.3);
    scene.add(ambientLight);

    const dirLight = new THREE.DirectionalLight(0xffffff, 1);
    dirLight.position.set(5, 10, 7);
    scene.add(dirLight);

    // 3. Game Objects
    // Player Ship
    const playerGroup = new THREE.Group();
    const bodyGeo = new THREE.ConeGeometry(0.6, 2, 8);
    const bodyMat = new THREE.MeshStandardMaterial({ color: 0x00ffcc, roughness: 0.3, metalness: 0.8 });
    const shipMesh = new THREE.Mesh(bodyGeo, bodyMat);
    shipMesh.rotation.x = Math.PI / 2;
    playerGroup.add(shipMesh);

    // Ship Wings
    const wingGeo = new THREE.BoxGeometry(2, 0.1, 0.8);
    const wingMat = new THREE.MeshStandardMaterial({ color: 0xff0055 });
    const wings = new THREE.Mesh(wingGeo, wingMat);
    wings.position.set(0, 0, 0.3);
    playerGroup.add(wings);

    scene.add(playerGroup);

    // Starfield Background
    const starsGeo = new THREE.BufferGeometry();
    const starCount = 800;
    const starPositions = new Float32Array(starCount * 3);

    for (let i = 0; i < starCount * 3; i++) {
      starPositions[i] = (Math.random() - 0.5) * 200;
    }
    starsGeo.setAttribute('position', new THREE.BufferAttribute(starPositions, 3));
    const starMat = new THREE.PointsMaterial({ color: 0xffffff, size: 0.5 });
    const starField = new THREE.Points(starsGeo, starMat);
    scene.add(starField);

    // Game Arrays & State
    const bullets = [];
    const asteroids = [];
    let score = 0;
    const mouse = { x: 0, y: 0 };
    const targetPos = new THREE.Vector3();

    // 4. Input Handlers
    window.addEventListener('mousemove', (e) => {
      mouse.x = (e.clientX / window.innerWidth) * 2 - 1;
      mouse.y = -(e.clientY / window.innerHeight) * 2 + 1;
    });

    function shoot() {
      const laserGeo = new THREE.CylinderGeometry(0.05, 0.05, 1, 8);
      const laserMat = new THREE.MeshBasicMaterial({ color: 0x00ffff });
      const laser = new THREE.Mesh(laserGeo, laserMat);
      
      laser.rotation.x = Math.PI / 2;
      laser.position.copy(playerGroup.position);
      laser.position.z -= 1;
      
      scene.add(laser);
      bullets.push(laser);
    }

    window.addEventListener('click', shoot);
    window.addEventListener('keydown', (e) => {
      if (e.code === 'Space') shoot();
    });

    // Handle Window Resizing
    window.addEventListener('resize', () => {
      camera.aspect = window.innerWidth / window.innerHeight;
      camera.updateProjectionMatrix();
      renderer.setSize(window.innerWidth, window.innerHeight);
    });

    // 5. Spawning Asteroids
    function spawnAsteroid() {
      const radius = 0.5 + Math.random() * 0.8;
      const geo = new THREE.DodecahedronGeometry(radius, 1);
      const mat = new THREE.MeshStandardMaterial({ color: 0x888888, roughness: 0.9 });
      const asteroid = new THREE.Mesh(geo, mat);

      asteroid.position.x = (Math.random() - 0.5) * 16;
      asteroid.position.y = (Math.random() - 0.5) * 10;
      asteroid.position.z = -60;

      asteroid.userData = {
        rotX: (Math.random() - 0.5) * 0.05,
        rotY: (Math.random() - 0.5) * 0.05,
        speed: 0.2 + Math.random() * 0.3,
        radius: radius
      };

      scene.add(asteroid);
      asteroids.push(asteroid);
    }

    setInterval(spawnAsteroid, 600);

    // 6. Main Game Loop
    function animate() {
      requestAnimationFrame(animate);

      // Smooth Ship Movement toward mouse
      targetPos.set(mouse.x * 8, mouse.y * 5, 0);
      playerGroup.position.lerp(targetPos, 0.1);
      
      // Ship banking/tilt effect based on mouse
      playerGroup.rotation.z = -mouse.x * 0.5;
      playerGroup.rotation.x = mouse.y * 0.2;

      // Update Bullets
      for (let i = bullets.length - 1; i >= 0; i--) {
        const b = bullets[i];
        b.position.z -= 1.2;

        if (b.position.z < -70) {
          scene.remove(b);
          bullets.splice(i, 1);
        }
      }

      // Update Asteroids & Collision Detection
      for (let i = asteroids.length - 1; i >= 0; i--) {
        const a = asteroids[i];
        a.position.z += a.userData.speed;
        a.rotation.x += a.userData.rotX;
        a.rotation.y += a.userData.rotY;

        // Check laser hits
        for (let j = bullets.length - 1; j >= 0; j--) {
          const b = bullets[j];
          const dist = a.position.distanceTo(b.position);

          if (dist < a.userData.radius + 0.5) {
            scene.remove(a);
            scene.remove(b);
            asteroids.splice(i, 1);
            bullets.splice(j, 1);

            score += 10;
            document.getElementById('score').innerText = score;
            break;
          }
        }

        // Remove off-screen asteroids
        if (a && a.position.z > 15) {
          scene.remove(a);
          asteroids.splice(i, 1);
        }
      }

      // Scroll background stars
      starField.rotation.z += 0.0005;

      renderer.render(scene, camera);
    }

    animate();
  </script>
</body>
</html>

Game Source: 3D Mini Space Shooter

Creator: ThunderPhoenix57

Libraries: three

Complexity: complex (221 lines, 6.3 KB)

The full source code is displayed above on this page.

Remix Instructions

To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: 3d-mini-space-shooter-thunderphoenix57" to link back to the original. Then publish at arcadelab.ai/publish.