<title>Mini 3D Battle Royale</title>
<style>
body { margin: 0; overflow: hidden; font-family: sans-serif; }
#ui {
position: absolute; top: 20px; left: 20px; color: white;
text-shadow: 2px 2px 4px #000; pointer-events: none;
}
#crosshair {
position: absolute; top: 50%; left: 50%;
width: 20px; height: 20px; border: 2px solid white;
border-radius: 50%; transform: translate(-50%, -50%);
}
#instructions {
position: absolute; bottom: 20px; width: 100%;
text-align: center; color: white; background: rgba(0,0,0,0.5);
}
</style>
<div id="ui">
<h1>BATTLE ZONE</h1>
<p>Enemies Down: <span id="score">0</span></p>
</div>
<div id="crosshair"></div>
<div id="instructions">WASD to Move | Mouse to Aim | Click to Shoot</div>
<script type="importmap">
{
"imports": {
"three": "https://unpkg.com/three@0.160.0/build/three.module.js"
}
}
</script>
<script type="module">
import * as THREE from 'three';
// --- Scene Setup ---
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x87ceeb); // Sky blue
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);
// --- Lights ---
const light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(5, 10, 7.5);
scene.add(light);
scene.add(new THREE.AmbientLight(0x404040));
// --- Ground (The Island) ---
const groundGeo = new THREE.PlaneGeometry(100, 100);
const groundMat = new THREE.MeshStandardMaterial({ color: 0x228b22 }); // Grass Green
const ground = new THREE.Mesh(groundGeo, groundMat);
ground.rotation.x = -Math.PI / 2;
scene.add(ground);
// --- Player Setup ---
const playerGroup = new THREE.Group();
scene.add(playerGroup);
playerGroup.add(camera);
camera.position.set(0, 1.6, 0); // Eye level
// --- Enemies (Red Cubes) ---
const enemies = [];
function createEnemy() {
const geo = new THREE.BoxGeometry(1, 2, 1);
const mat = new THREE.MeshStandardMaterial({ color: 0xff0000 });
const enemy = new THREE.Mesh(geo, mat);
enemy.position.set(Math.random() * 40 - 20, 1, Math.random() * 40 - 20);
scene.add(enemy);
enemies.push(enemy);
}
for(let i=0; i<10; i++) createEnemy();
// --- Movement Logic ---
let moveForward = false, moveBackward = false, moveLeft = false, moveRight = false;
let score = 0;
document.addEventListener('keydown', (e) => {
if(e.code === 'KeyW') moveForward = true;
if(e.code === 'KeyS') moveBackward = true;
if(e.code === 'KeyA') moveLeft = true;
if(e.code === 'KeyD') moveRight = true;
});
document.addEventListener('keyup', (e) => {
if(e.code === 'KeyW') moveForward = false;
if(e.code === 'KeyS') moveBackward = false;
if(e.code === 'KeyA') moveLeft = false;
if(e.code === 'KeyD') moveRight = false;
});
// Mouse Lock & Look
document.body.requestPointerLock = document.body.requestPointerLock || document.body.mozRequestPointerLock;
document.addEventListener('click', () => {
document.body.requestPointerLock();
shoot();
});
document.addEventListener('mousemove', (e) => {
if (document.pointerLockElement === document.body) {
playerGroup.rotation.y -= e.movementX * 0.002;
camera.rotation.x -= e.movementY * 0.002;
camera.rotation.x = Math.max(-Math.PI/2, Math.min(Math.PI/2, camera.rotation.x));
}
});
// --- Shooting Logic ---
const raycaster = new THREE.Raycaster();
function shoot() {
raycaster.setFromCamera(new THREE.Vector2(0,0), camera);
const intersects = raycaster.intersectObjects(enemies);
if (intersects.length > 0) {
const target = intersects[0].object;
target.position.set(Math.random() * 40 - 20, 1, Math.random() * 40 - 20); // Respawn
score++;
document.getElementById('score').innerText = score;
}
}
// --- Game Loop ---
const velocity = new THREE.Vector3();
function animate() {
requestAnimationFrame(animate);
if (document.pointerLockElement === document.body) {
const speed = 0.1;
if (moveForward) playerGroup.translateZ(-speed);
if (moveBackward) playerGroup.translateZ(speed);
if (moveLeft) playerGroup.translateX(-speed);
if (moveRight) playerGroup.translateX(speed);
}
renderer.render(scene, camera);
}
animate();
// Handle Resize
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
</script>