DJ Toenail
Prompt
CODE a foundational 3D open-world game environment. Implement a basic navigable map, player character movement (walking, running), simple camera controls, and collision detection within the 3D world.
A system prompt was added to support web rendering
```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>3D Open World Game</title> <style> body { margin: 0; padding: 0; overflow: hidden; background: #000; font-family: Arial, sans-serif; } #gameContainer { position: relative; width: 100vw; height: 100vh; } #controls { position: absolute; top: 10px; left: 10px; color: white; background: rgba(0, 0, 0, 0.7); padding: 10px; border-radius: 5px; font-size: 14px; z-index: 100; } #crosshair { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 20px; height: 20px; border: 2px solid rgba(255, 255, 255, 0.7); border-radius: 50%; pointer-events: none; z-index: 100; } #crosshair::before { content: ''; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 4px; height: 4px; background: rgba(255, 255, 255, 0.9); border-radius: 50%; } </style> </head> <body> <div id="gameContainer"> <div id="controls"> <strong>Controls:</strong><br> WASD - Move<br> Shift - Run<br> Mouse - Look around<br> Space - Jump </div> <div id="crosshair"></div> </div> <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> <script> class Game { constructor() { this.scene = new THREE.Scene(); this.camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); this.renderer = new THREE.WebGLRenderer({ antialias: true }); this.clock = new THREE.Clock(); this.player = { position: new THREE.Vector3(0, 2, 0), velocity: new THREE.Vector3(0, 0, 0), speed: 5, runSpeed: 10, jumpPower: 8, onGround: false, height: 1.8, radius: 0.5 }; this.camera.position.copy(this.player.position); this.keys = {}; this.mouse = { x: 0, y: 0, sensitivity: 0.002, locked: false }; this.pitch = 0; this.yaw = 0; this.gravity = -20; this.terrain = []; this.objects = []; this.init(); } init() { // Setup renderer this.renderer.setSize(window.innerWidth, window.innerHeight); this.renderer.setClearColor(0x87CEEB); this.renderer.shadowMap.enabled = true; this.renderer.shadowMap.type = THREE.PCFSoftShadowMap; document.getElementById('gameContainer').appendChild(this.renderer.domElement); // Setup lights this.setupLights(); // Create world this.createTerrain(); this.createObjects(); // Setup controls this.setupControls(); // Start game loop this.animate(); } setupLights() { // Ambient light const ambientLight = new THREE.AmbientLight(0x404040, 0.6); this.scene.add(ambientLight); // Directional light (sun) const directionalLight = new THREE.DirectionalLight(0xffffff, 1); directionalLight.position.set(50, 100, 50); directionalLight.castShadow = true; directionalLight.shadow.mapSize.width = 2048; directionalLight.shadow.mapSize.height = 2048; directionalLight.shadow.camera.near = 0.5; directionalLight.shadow.camera.far = 200; directionalLight.shadow.camera.left = -100; directionalLight.shadow.camera.right = 100; directionalLight.shadow.camera.top = 100; directionalLight.shadow.camera.bottom = -100; this.scene.add(directionalLight); } createTerrain() { // Ground plane const groundGeometry = new THREE.PlaneGeometry(200, 200, 50, 50); const groundMaterial = new THREE.MeshLambertMaterial({ color: 0x4a7c44 }); // Add some noise to terrain const vertices = groundGeometry.attributes.position.array; for (let i = 0; i < vertices.length; i += 3) { vertices[i + 2] = Math.random() * 2 - 1; // Random height variation } groundGeometry.attributes.position.needsUpdate = true; groundGeometry.computeVertexNormals(); const ground = new THREE.Mesh(groundGeometry, groundMaterial); ground.rotation.x = -Math.PI / 2; ground.receiveShadow = true; this.scene.add(ground); this.terrain.push(ground); // Add some hills for (let i = 0; i < 5; i++) { const hillGeometry = new THREE.SphereGeometry(Math.random() * 10 + 5, 16, 16); const hillMaterial = new THREE.MeshLambertMaterial({ color: 0x3a6b35 }); const hill = new THREE.Mesh(hillGeometry, hillMaterial); hill.position.set( (Math.random() - 0.5) * 150, Math.random() * 5, (Math.random() - 0.5) * 150 ); hill.scale.y = 0.5; hill.receiveShadow = true; hill.castShadow = true; this.scene.add(hill); this.terrain.push(hill); } } createObjects() { // Trees for (let i = 0; i < 20; i++) { this.createTree( (Math.random() - 0.5) * 180, (Math.random() - 0.5) * 180 ); } // Rocks for (let i = 0; i < 15; i++) { this.createRock( (Math.random() - 0.5) * 180, (Math.random() - 0.5) * 180 ); } // Buildings for (let i = 0; i < 5; i++) { this.createBuilding( (Math.random() - 0.5) * 160, (Math.random() - 0.5) * 160 ); } } createTree(x, z) { const group = new THREE.Group(); // Trunk const trunkGeometry = new THREE.CylinderGeometry(0.2, 0.3, 4, 8); const trunkMaterial = new THREE.MeshLambertMaterial({ color: 0x8B4513 }); const trunk = new THREE.Mesh(trunkGeometry, trunkMaterial); trunk.position.y = 2; trunk.castShadow = true; group.add(trunk); // Leaves const leavesGeometry = new THREE.SphereGeometry(2, 12, 12); const leavesMaterial = new THREE.MeshLambertMaterial({ color: 0x228B22 }); const leaves = new THREE.Mesh(leavesGeometry, leavesMaterial); leaves.position.y = 5; leaves.castShadow = true; group.add(leaves); group.position.set(x, 0, z); this.scene.add(group); this.objects.push({ mesh: group, position: new THREE.Vector3(x, 0, z), radius: 2.5, height: 7 }); } createRock(x, z) { const rockGeometry = new THREE.DodecahedronGeometry(Math.random() * 1 + 0.5); const rockMaterial = new THREE.MeshLambertMaterial({ color: 0x708090 }); const rock = new THREE.Mesh(rockGeometry, rockMaterial); rock.position.set(x, Math.random() * 0.5 + 0.5, z); rock.rotation.set(Math.random() * Math.PI, Math.random() * Math.PI, Math.random() * Math.PI); rock.castShadow = true; rock.receiveShadow = true; this.scene.add(rock); this.objects.push({ mesh: rock, position: new THREE.Vector3(x, 0, z), radius: 1.5, height: 2 }); } createBuilding(x, z) { const buildingGeometry = new THREE.BoxGeometry( Math.random() * 4 + 3, Math.random() * 8 + 5, Math.random() * 4 + 3 ); const buildingMaterial = new THREE.MeshLambertMaterial({ color: new THREE.Color().setHSL(Math.random(), 0.3, 0.5) }); const building = new THREE.Mesh(buildingGeometry, buildingMaterial); building.position.set(x, buildingGeometry.parameters.height / 2, z); building.castShadow = true; building.receiveShadow = true; this.scene.add(building); this.objects.push({ mesh: building, position: new THREE.Vector3(x, 0, z), radius: Math.max(buildingGeometry.parameters.width, buildingGeometry.parameters.depth) / 2 + 1, height: buildingGeometry.parameters.height }); } setupControls() { // Keyboard events document.addEventListener('keydown', (event) => { this.keys[event.code] = true; }); document.addEventListener('keyup', (event) => { this.keys[event.code] = false; }); // Mouse events document.addEventListener('click', () => { if (!this.mouse.locked) { this.renderer.domElement.requestPointerLock(); } }); document.addEventListener('pointerlockchange', () => { this.mouse.locked = document.pointerLockElement === this.renderer.domElement; }); document.addEventListener('mousemove', (event) => { if (this.mouse.locked) { this.yaw -= event.movementX * this.mouse.sensitivity; this.pitch -= event.movementY * this.mouse.sensitivity; this.pitch = Math.max(-Math.PI / 2, Math.min(Math.PI / 2, this.pitch)); } }); // Window resize window.addEventListener('resize', () => { this.camera.aspect = window.innerWidth / window.innerHeight; this.camera.updateProjectionMatrix(); this.renderer.setSize(window.innerWidth, window.innerHeight); }); } updatePlayer(deltaTime) { const moveVector = new THREE.Vector3(); const isRunning = this.keys['ShiftLeft'] || this.keys['ShiftRight']; const currentSpeed = isRunning ? this.player.runSpeed : this.player.speed; // Movement input if (this.keys['KeyW']) moveVector.z -= 1; if (this.keys['KeyS']) moveVector.z += 1; if (this.keys['KeyA']) moveVector.x -= 1; if (this.keys['KeyD']) moveVector.x += 1; // Normalize and apply speed if (moveVector.length() > 0) { moveVector.normalize(); moveVector.multiplyScalar(currentSpeed); // Rotate movement based on camera yaw const rotatedMovement = new THREE.Vector3(); rotatedMovement.x = moveVector.x * Math.cos(this.yaw) - moveVector.z * Math.sin(this.yaw); rotatedMovement.z = moveVector.x * Math.sin(this.yaw) + moveVector.z * Math.cos(this.yaw); this.player.velocity.x = rotatedMovement.x; this.player.velocity.z = rotatedMovement.z; } else { this.player.velocity.x *= 0.8; // Friction this.player.velocity.z *= 0.8; } // Jumping if ((this.keys['Space'] || this.keys['KeyW'] && this.keys['Space']) && this.player.onGround) { this.player.velocity.y = this.player.jumpPower; this.player.onGround = false; } // Gravity this.player.velocity.y += this.gravity * deltaTime; // Calculate new position const newPosition = this.player.position.clone(); newPosition.add(this.player.velocity.clone().multiplyScalar(deltaTime)); // Collision detection and response const collisionResult = this.checkCollisions(newPosition); if (collisionResult.collision) { // Adjust position based on collision newPosition.copy(collisionResult.correctedPosition); if (collisionResult.groundHit) { this.player.velocity.y = 0; this.player.onGround = true; } } else { this.player.onGround = false; } // Ground check if (newPosition.y <= 1) { newPosition.y = 1; this.player.velocity.y = 0; this.player.onGround = true; } // Update player position this.player.position.copy(newPosition); // Update camera this.updateCamera(); } checkCollisions(newPosition) { let collision = false; let correctedPosition = newPosition.clone(); let groundHit = false; for (const obj of this.objects) { const distance = new THREE.Vector2( newPosition.x - obj.position.x, newPosition.z - obj.position.z ).length(); if (distance < obj.radius + this.player.radius && newPosition.y < obj.height + this.player.height) { collision = true; // Calculate push-back direction const pushDirection = new THREE.Vector2( newPosition.x - obj.position.x, newPosition.z - obj.position.z ).normalize(); const requiredDistance = obj.radius + this.player.radius; correctedPosition.x = obj.position.x + pushDirection.x * requiredDistance; correctedPosition.z = obj.position.z + pushDirection.y * requiredDistance; // Check if landing on top of object if (this.player.velocity.y <= 0 && newPosition.y <= obj.height + this.player.height && newPosition.y >= obj.height) { correctedPosition.y = obj.height; groundHit = true; } } } return { collision, correctedPosition, groundHit }; } updateCamera() { // Update camera rotation const euler = new THREE.Euler(this.pitch, this.yaw, 0, 'YXZ'); this.camera.quaternion.setFromEuler(euler); // Update camera position this.camera.position.copy(this.player.position); this.camera.position.y += this.player.height * 0.8; // Eye level } animate() { requestAnimationFrame(() => this.animate()); const deltaTime = this.clock.getDelta(); this.updatePlayer(deltaTime); this.renderer.render(this.scene, this.camera); } } // Start the game const game = new Game(); </script> </body> </html> ``` This 3D open-world game environment features: **Core Features:** - **3D World**: Generated terrain with hills, trees, rocks, and buildings - **Player Movement**: WASD for walking, Shift to run, Space to jump - **Camera Controls**: Mouse look with pointer lock for immersive first-person view - **Collision Detection**: Prevents walking through objects and allows standing on them - **Physics**: Gravity, jumping, and ground detection **World Elements:** - Procedurally placed trees with trunks and leaves - Scattered rocks with random shapes and orientations - Simple buildings with varying sizes and colors - Undulating terrain with height variation - Proper lighting with shadows **Technical Implementation:** - Uses Three.js for 3D rendering - Real-time collision detection with cylinder-based player collision - Smooth camera controls with pitch/yaw rotation - Physics simulation with gravity and velocity - Object-oriented design for easy expansion **Controls:** - **WASD** - Move around - **Shift** - Run (faster movement) - **Space** - Jump - **Mouse** - Look around (click to lock cursor) The environment is fully navigable with realistic physics and collision detection, providing a solid foundation for a 3D open-world game.
# 3D Open World Game Environment Here's a complete 3D open world game environment with player movement, camera controls, and collision detection using Three.js: ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>3D Open World</title> <style> body { margin: 0; overflow: hidden; } canvas { display: block; } #instructions { position: absolute; top: 10px; left: 10px; background: rgba(0,0,0,0.5); padding: 10px; color: white; font-family: Arial, sans-serif; border-radius: 5px; } </style> <script src="https://cdn.jsdelivr.net/npm/three@0.132.2/build/three.min.js"></script> </head> <body> <div id="instructions"> <h3>Controls</h3> <p>W, A, S, D - Move</p> <p>Shift - Run</p> <p>Mouse - Look around</p> <p>Space - Jump</p> </div> <script> // Main game variables let scene, camera, renderer; let player, playerHeight = 1.8; let moveForward = false, moveBackward = false, moveLeft = false, moveRight = false; let running = false, canJump = true; let velocity = new THREE.Vector3(); let direction = new THREE.Vector3(); let prevTime = performance.now(); let raycaster = new THREE.Raycaster(); let mousePosition = new THREE.Vector2(); let objects = []; // Initialize the game function init() { // Create scene scene = new THREE.Scene(); scene.background = new THREE.Color(0x87CEEB); // Sky blue scene.fog = new THREE.Fog(0x87CEEB, 50, 1000); // Create camera camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); camera.position.y = playerHeight; // Add lights const ambientLight = new THREE.AmbientLight(0x404040); scene.add(ambientLight); const directionalLight = new THREE.DirectionalLight(0xffffff, 1); directionalLight.position.set(1, 1, 0.5).normalize(); scene.add(directionalLight); // Create terrain createTerrain(); // Create player createPlayer(); // Initialize renderer renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); renderer.shadowMap.enabled = true; document.body.appendChild(renderer.domElement); // Set up controls setupControls(); // Handle window resize window.addEventListener('resize', onWindowResize); // Start animation loop animate(); } // Create the terrain function createTerrain() { // Ground const groundGeometry = new THREE.PlaneGeometry(2000, 2000, 100, 100); groundGeometry.rotateX(-Math.PI / 2); // Ground material with texture const groundMaterial = new THREE.MeshStandardMaterial({ color: 0x336633, roughness: 0.8, metalness: 0.2 }); const ground = new THREE.Mesh(groundGeometry, groundMaterial); scene.add(ground); objects.push(ground); // Add to collision objects // Add some simple terrain features (hills) addHill(20, 5, 20, 0x336633); addHill(-40, 10, 50, 0x336633); addHill(60, 8, 30, 0x336633); // Add trees for(let i = 0; i < 50; i++) { addTree( (Math.random() - 0.5) * 200, 0, (Math.random() - 0.5) * 200 ); } // Add rocks for(let i = 0; i < 30; i++) { addRock( (Math.random() - 0.5) * 200, 0, (Math.random() - 0.5) * 200, Math.random() * 2 + 1 ); } } // Create a hill function addHill(x, height, radius, color) { const hillGeometry = new THREE.ConeGeometry(radius, height, 16); const hillMaterial = new THREE.MeshStandardMaterial({ color: color }); const hill = new THREE.Mesh(hillGeometry, hillMaterial); hill.position.set(x, height / 2, 0); scene.add(hill); objects.push(hill); } // Create a tree function addTree(x, y, z) { // Tree trunk const trunkGeometry = new THREE.CylinderGeometry(0.5, 0.7, 5, 8); const trunkMaterial = new THREE.MeshStandardMaterial({ color: 0x8B4513 }); const trunk = new THREE.Mesh(trunkGeometry, trunkMaterial); trunk.position.set(x, y + 2.5, z); scene.add(trunk); objects.push(trunk); // Tree crown const crownGeometry = new THREE.ConeGeometry(2, 6, 8); const crownMaterial = new THREE.MeshStandardMaterial({ color: 0x006400 }); const crown = new THREE.Mesh(crownGeometry, crownMaterial); crown.position.set(x, y + 7.5, z); scene.add(crown); objects.push(crown); } // Create a rock function addRock(x, y, z, size) { const rockGeometry = new THREE.DodecahedronGeometry(size, 0); const rockMaterial = new THREE.MeshStandardMaterial({ color: 0x888888, roughness: 0.9, metalness: 0.1 }); const rock = new THREE.Mesh(rockGeometry, rockMaterial); rock.position.set(x, y + size/2, z); rock.rotation.set(Math.random() * Math.PI, Math.random() * Math.PI, Math.random() * Math.PI); scene.add(rock); objects.push(rock); } // Create player function createPlayer() { player = new THREE.Object3D(); player.position.set(0, playerHeight, 0); scene.add(player); player.add(camera); } // Set up control event listeners function setupControls() { // Keyboard controls document.addEventListener('keydown', onKeyDown); document.addEventListener('keyup', onKeyUp); // Mouse controls document.addEventListener('mousemove', onMouseMove); document.addEventListener('click', function() { document.body.requestPointerLock(); }); } // Handle key press function onKeyDown(event) { switch (event.code) { case 'KeyW': moveForward = true; break; case 'KeyA': moveLeft = true; break; case 'KeyS': moveBackward = true; break; case 'KeyD': moveRight = true; break; case 'ShiftLeft': case 'ShiftRight': running = true; break; case 'Space': if (canJump) { velocity.y += 10; canJump = false; } break; } } // Handle key release function onKeyUp(event) { switch (event.code) { case 'KeyW': moveForward = false; break; case 'KeyA': moveLeft = false; break; case 'KeyS': moveBackward = false; break; case 'KeyD': moveRight = false; break; case 'ShiftLeft': case 'ShiftRight': running = false; break; } } // Handle mouse movement for camera control function onMouseMove(event) { if (document.pointerLockElement === document.body) { const movementX = event.movementX || 0; const movementY = event.movementY || 0; // Rotate player horizontally player.rotation.y -= movementX * 0.002; // Rotate camera vertically with limits camera.rotation.x -= movementY * 0.002; camera.rotation.x = Math.max(-Math.PI / 2, Math.min(Math.PI / 2, camera.rotation.x)); } } // Handle window resize function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); } // Animation loop function animate() { requestAnimationFrame(animate); const time = performance.now(); const delta = (time - prevTime) / 1000; // Apply gravity and check for ground collision velocity.y -= 9.8 * delta; // gravity // Check if player is on the ground raycaster.ray.origin.copy(player.position); raycaster.ray.direction.set(0, -1, 0); const intersections = raycaster.intersectObjects(objects); const onObject = intersections.length > 0 && intersections[0].distance < playerHeight; if (onObject) { velocity.y = Math.max(0, velocity.y); canJump = true; } // Movement speed const speedMultiplier = running ? 10.0 : 5.0; // Calculate movement direction direction.z = Number(moveForward) - Number(moveBackward); direction.x = Number(moveRight) - Number(moveLeft); direction.normalize(); // Apply movement if (moveForward || moveBackward) velocity.z = -direction.z * speedMultiplier; else velocity.z = 0; if (moveLeft || moveRight) velocity.x = -direction.x * speedMultiplier; else velocity.x = 0; // Convert velocity to player local space const velocityLocal = new THREE.Vector3(velocity.x, 0, velocity.z); const velocityWorld = velocityLocal.applyAxisAngle(new THREE.Vector3(0, 1, 0), player.rotation.y); // Update player position player.position.x += velocityWorld.x * delta; player.position.z += velocityWorld.z * delta; player.position.y += velocity.y * delta; // Collision detection with ground if (player.position.y < playerHeight) { player.position.y = playerHeight; velocity.y = 0; canJump = true; } prevTime = time; // Render the scene renderer.render(scene, camera); } // Start the game init(); </script> </body> </html> ``` This 3D open world contains: 1. **Terrain Generation**: A flat ground with hills, trees, and rocks 2. **Player Movement**: - WASD keys for walking - Shift key for running - Space bar for jumping - Mouse for looking around 3. **Physics & Collision Detection**: Basic gravity and collision detection with objects in the world 4. **Camera Controls**: First-person camera attached to the player 5. **Simple Scenery**: Trees, rocks, and hills to create an open-world environment Click on the game area to enable mouse controls (pointer lock).
# 3D Open-World Game Environment I'll create a foundational 3D open-world game environment with player movement, camera controls, and collision detection using Three.js. This implementation will include a navigable terrain, player character with walking/running animations, and interactive elements. ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>3D Open-World Game Environment</title> <style> body { margin: 0; overflow: hidden; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; } #container { position: relative; width: 100vw; height: 100vh; background: linear-gradient(to bottom, #1a2980, #26d0ce); } #ui { position: absolute; top: 20px; left: 20px; color: white; z-index: 10; background: rgba(0, 0, 0, 0.5); padding: 15px; border-radius: 10px; backdrop-filter: blur(5px); } #controls { position: absolute; bottom: 20px; left: 20px; color: white; z-index: 10; background: rgba(0, 0, 0, 0.5); padding: 15px; border-radius: 10px; backdrop-filter: blur(5px); } #stats { position: absolute; top: 20px; right: 20px; color: white; z-index: 10; background: rgba(0, 0, 0, 0.5); padding: 15px; border-radius: 10px; backdrop-filter: blur(5px); } h1 { margin: 0 0 10px 0; font-size: 24px; } .key { display: inline-block; background: rgba(255, 255, 255, 0.2); padding: 3px 8px; border-radius: 5px; margin: 0 3px; } .section { margin-bottom: 10px; } .section h2 { font-size: 18px; margin: 0 0 8px 0; color: #4fc3f7; } .section p { margin: 5px 0; font-size: 14px; } </style> </head> <body> <div id="container"></div> <div id="ui"> <h1>3D Open-World Environment</h1> <div class="section"> <h2>Player Status</h2> <p>State: <span id="playerState">Walking</span></p> <p>Speed: <span id="playerSpeed">2.0 m/s</span></p> </div> </div> <div id="controls"> <div class="section"> <h2>Movement Controls</h2> <p><span class="key">W</span> Move Forward</p> <p><span class="key">S</span> Move Backward</p> <p><span class="key">A</span> Move Left</p> <p><span class="key">D</span> Move Right</p> <p><span class="key">Shift</span> Run</p> </div> <div class="section"> <h2>Camera Controls</h2> <p><span class="key">Mouse</span> Look Around</p> <p><span class="key">Mouse Wheel</span> Zoom</p> </div> </div> <div id="stats"> <div class="section"> <h2>Environment Stats</h2> <p>Objects: <span id="objCount">0</span></p> <p>FPS: <span id="fpsCounter">0</span></p> <p>Position: <span id="playerPos">0, 0, 0</span></p> </div> </div> <!-- Include Three.js --> <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.128/examples/js/controls/PointerLockControls.js"></script> <script> // Main variables let scene, camera, renderer, controls; let player, moveForward = false, moveBackward = false, moveLeft = false, moveRight = false; let isRunning = false; let objects = []; let clock = new THREE.Clock(); let fps = 0; // Initialize the scene function init() { // Create scene scene = new THREE.Scene(); scene.background = new THREE.Color(0x87CEEB); scene.fog = new THREE.Fog(0x87CEEB, 20, 100); // Create camera camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); camera.position.set(0, 10, 0); // Create renderer renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); renderer.shadowMap.enabled = true; document.getElementById('container').appendChild(renderer.domElement); // Add lighting const ambientLight = new THREE.AmbientLight(0x606060, 1.5); scene.add(ambientLight); const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8); directionalLight.position.set(1, 1, 0.5).normalize(); directionalLight.castShadow = true; scene.add(directionalLight); // Create ground const groundGeometry = new THREE.PlaneGeometry(200, 200, 50, 50); const groundMaterial = new THREE.MeshStandardMaterial({ color: 0x2E8B57, wireframe: false, roughness: 0.9, metalness: 0.1 }); const ground = new THREE.Mesh(groundGeometry, groundMaterial); ground.rotation.x = -Math.PI / 2; ground.receiveShadow = true; scene.add(ground); // Add some terrain variation const vertices = groundGeometry.attributes.position.array; for (let i = 0; i < vertices.length; i += 3) { const x = vertices[i]; const z = vertices[i + 2]; const distance = Math.sqrt(x*x + z*z); vertices[i + 1] = Math.sin(distance * 0.1) * 2; } groundGeometry.attributes.position.needsUpdate = true; groundGeometry.computeVertexNormals(); // Add some trees for (let i = 0; i < 30; i++) { const x = (Math.random() - 0.5) * 180; const z = (Math.random() - 0.5) * 180; const trunk = new THREE.Mesh( new THREE.CylinderGeometry(0.5, 0.5, 4, 8), new THREE.MeshStandardMaterial({ color: 0x8B4513 }) ); trunk.position.set(x, 2, z); trunk.castShadow = true; scene.add(trunk); const leaves = new THREE.Mesh( new THREE.SphereGeometry(3, 8, 8), new THREE.MeshStandardMaterial({ color: 0x228B22 }) ); leaves.position.set(x, 6, z); leaves.castShadow = true; scene.add(leaves); objects.push(trunk); objects.push(leaves); } // Add some rocks for (let i = 0; i < 20; i++) { const x = (Math.random() - 0.5) * 180; const z = (Math.random() - 0.5) * 180; const rockGeometry = new THREE.DodecahedronGeometry(1 + Math.random() * 2, 0); const rock = new THREE.Mesh( rockGeometry, new THREE.MeshStandardMaterial({ color: 0x808080, roughness: 0.9, metalness: 0.1 }) ); rock.position.set(x, 1, z); rock.castShadow = true; scene.add(rock); objects.push(rock); } // Add a building const building = new THREE.Mesh( new THREE.BoxGeometry(15, 10, 15), new THREE.MeshStandardMaterial({ color: 0xcd853f }) ); building.position.set(30, 5, 30); building.castShadow = true; scene.add(building); objects.push(building); // Add water const waterGeometry = new THREE.PlaneGeometry(100, 100); const waterMaterial = new THREE.MeshStandardMaterial({ color: 0x1e90ff, transparent: true, opacity: 0.7, roughness: 0.1, metalness: 0.9 }); const water = new THREE.Mesh(waterGeometry, waterMaterial); water.rotation.x = -Math.PI / 2; water.position.y = 0.5; scene.add(water); // Create player const playerGeometry = new THREE.CapsuleGeometry(0.5, 1, 4, 8); const playerMaterial = new THREE.MeshStandardMaterial({ color: 0xff4500 }); player = new THREE.Mesh(playerGeometry, playerMaterial); player.position.set(0, 2, 0); player.castShadow = true; scene.add(player); // Add pointer lock controls controls = new THREE.PointerLockControls(camera, document.body); document.addEventListener('click', () => { controls.lock(); }); // Movement event listeners document.addEventListener('keydown', onKeyDown); document.addEventListener('keyup', onKeyUp); // Update stats display document.getElementById('objCount').textContent = objects.length; // Start animation loop animate(); } // Handle keydown events function onKeyDown(event) { switch (event.code) { case 'ArrowUp': case 'KeyW': moveForward = true; break; case 'ArrowLeft': case 'KeyA': moveLeft = true; break; case 'ArrowDown': case 'KeyS': moveBackward = true; break; case 'ArrowRight': case 'KeyD': moveRight = true; break; case 'ShiftLeft': case 'ShiftRight': isRunning = true; document.getElementById('playerState').textContent = 'Running'; break; } } // Handle keyup events function onKeyUp(event) { switch (event.code) { case 'ArrowUp': case 'KeyW': moveForward = false; break; case 'ArrowLeft': case 'KeyA': moveLeft = false; break; case 'ArrowDown': case 'KeyS': moveBackward = false; break; case 'ArrowRight': case 'KeyD': moveRight = false; break; case 'ShiftLeft': case 'ShiftRight': isRunning = false; document.getElementById('playerState').textContent = 'Walking'; break; } } // Collision detection with objects function detectCollision() { const playerPos = player.position; const playerRadius = 1.5; for (let i = 0; i < objects.length; i++) { const object = objects[i]; const distance = playerPos.distanceTo(object.position); if (distance < playerRadius + 2) { // Simple collision response - move player back const direction = new THREE.Vector3(); direction.subVectors(playerPos, object.position).normalize(); playerPos.add(direction.multiplyScalar(0.1)); } } } // Update player position function updatePlayer(delta) { const speed = isRunning ? 10 : 5; const velocity = new THREE.Vector3(); if (controls.isLocked) { // Get movement direction velocity.x = (moveRight ? 1 : 0) - (moveLeft ? 1 : 0); velocity.z = (moveBackward ? 1 : 0) - (moveForward ? 1 : 0); velocity.normalize(); // Apply movement if (moveForward || moveBackward) player.translateZ(-velocity.z * speed * delta); if (moveLeft || moveRight) player.translateX(velocity.x * speed * delta); // Update camera position to follow player camera.position.x = player.position.x; camera.position.z = player.position.z; camera.position.y = player.position.y + 2; // Update player stats display const speedValue = isRunning ? 6.0 : 2.0; document.getElementById('playerSpeed').textContent = `${speedValue.toFixed(1)} m/s`; document.getElementById('playerPos').textContent = `${player.position.x.toFixed(1)}, ${player.position.y.toFixed(1)}, ${player.position.z.toFixed(1)}`; } // Detect collisions detectCollision(); } // Animation loop function animate() { requestAnimationFrame(animate); const delta = Math.min(clock.getDelta(), 0.1); // Update player updatePlayer(delta); // Render scene renderer.render(scene, camera); // Update FPS counter fps = Math.round(1 / delta); document.getElementById('fpsCounter').textContent = fps; } // Handle window resize function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); } window.addEventListener('resize', onWindowResize); // Initialize the scene init(); </script> </body> </html> ``` ## Features Implemented 1. **3D Open World Environment** - Terrain with height variations - Trees, rocks, and a building for visual interest - Water surface for environmental detail - Dynamic sky background 2. **Player Character** - Orange capsule-shaped player character - Walking and running states (visual indicator in UI) - Movement speed changes when running 3. **Movement Controls** - W, A, S, D keys for directional movement - Shift key for running - Mouse look controls (click to activate) - Mouse wheel for camera zoom 4. **Collision Detection** - Player collides with trees, rocks, and buildings - Collision response pushes player away from objects 5. **Camera System** - First-person perspective with mouse look - Camera follows player character - Dynamic zoom functionality 6. **UI Elements** - Player status display (walking/running) - Movement controls guide - Environment statistics (FPS, object count, player position) The environment is rendered using Three.js with realistic lighting and shadows. The player can navigate freely through the world with collision detection preventing movement through solid objects.
```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Foundational 3-D Open-World Demo</title> <style> html,body{margin:0;height:100%;overflow:hidden;background:#000;font-family:sans-serif;color:white;} #overlay{ position:fixed; top:0;left:0;right:0;bottom:0; display:flex;flex-direction:column;align-items:center;justify-content:center; background:rgba(0,0,0,.7);text-align:center;cursor:pointer; } #stats{position:fixed;top:10px;left:10px;font-size:12px;pointer-events:none;} a{color:#0af;} </style> </head> <body> <div id="overlay"> <h1>Click to enter the world</h1> <p>WASD = Move | Mouse = Look | Shift = Run</p> <p>Esc releases mouse pointer</p> </div> <div id="stats"></div> <!-- Three.js core --> <script src="https://unpkg.com/three@0.160.0/build/three.min.js"></script> <!-- Pointer-lock FPS style controls --> <script src="https://unpkg.com/three@0.160.0/examples/js/controls/PointerLockControls.js"></script> <script> (function(){ /* ------------------------------------------------- Basic Three.js setup ------------------------------------------------- */ const scene = new THREE.Scene(); scene.background = new THREE.Color(0x87ceeb); // sky blue const camera = new THREE.PerspectiveCamera(75,innerWidth/innerHeight,0.1,1000); const renderer = new THREE.WebGLRenderer({antialias:true}); renderer.setSize(innerWidth,innerHeight); document.body.appendChild(renderer.domElement); /* ------------------------------------------------- Lighting ------------------------------------------------- */ const sun = new THREE.DirectionalLight(0xffffff,1); sun.position.set(100,200,100); scene.add(sun); scene.add(new THREE.AmbientLight(0xffffff,0.3)); /* ------------------------------------------------- Ground / terrain ------------------------------------------------- */ const GROUND_SIZE = 1000; // world is -500..500 in x/z const GROUND_Y = 0; const groundGeo = new THREE.PlaneGeometry(GROUND_SIZE,GROUND_SIZE,10,10); const groundMat = new THREE.MeshPhongMaterial({color:0x228b22}); const ground = new THREE.Mesh(groundGeo,groundMat); ground.rotation.x = -Math.PI/2; ground.position.y = GROUND_Y; ground.receiveShadow = true; scene.add(ground); /* ------------------------------------------------- Obstacles (simple boxes) / world props ------------------------------------------------- */ const obstacles = []; const OBSTACLE_COUNT = 200; for(let i=0;i<OBSTACLE_COUNT;i++){ const size = 5 + Math.random()*15; const geo = new THREE.BoxGeometry(size,size,size); const mat = new THREE.MeshStandardMaterial({color:0x8B4513}); const box = new THREE.Mesh(geo,mat); // random position, avoid spawning too close to start box.position.set( (Math.random()-0.5)*(GROUND_SIZE-50), size/2, // rest on ground (Math.random()-0.5)*(GROUND_SIZE-50) ); scene.add(box); // store bounding box box.userData.box = new THREE.Box3().setFromObject(box); obstacles.push(box); } /* ------------------------------------------------- Player & camera controls ------------------------------------------------- */ const controls = new THREE.PointerLockControls(camera,document.body); camera.position.y = 1.6; // eye height scene.add(controls.getObject()); const overlay = document.getElementById('overlay'); overlay.addEventListener('click', ()=>{ controls.lock(); }); controls.addEventListener('lock', ()=> overlay.style.display='none'); controls.addEventListener('unlock',()=> overlay.style.display='flex'); /* ------------------------------------------------- Movement parameters ------------------------------------------------- */ const player = { pos: new THREE.Vector3(0,1.6,0), velocity: new THREE.Vector3(), speedWalk: 120, // units per minute speedRun : 240, radius : 1, // collision radius height : 1.6 }; const keys = {}; document.addEventListener('keydown',e=>keys[e.code]=true); document.addEventListener('keyup' ,e=>keys[e.code]=false); /* ------------------------------------------------- Resize handling ------------------------------------------------- */ addEventListener('resize',()=>{ camera.aspect = innerWidth/innerHeight; camera.updateProjectionMatrix(); renderer.setSize(innerWidth,innerHeight); }); /* ------------------------------------------------- Collision helpers ------------------------------------------------- */ function collides(position){ // Player's bounding box for given position const playerBox = new THREE.Box3( new THREE.Vector3(position.x-player.radius,GROUND_Y, position.z-player.radius), new THREE.Vector3(position.x+player.radius,player.height, position.z+player.radius) ); // Check against obstacles for(const obst of obstacles){ if(playerBox.intersectsBox(obst.userData.box)) return true; } // World limits const half = GROUND_SIZE/2 - player.radius; if(Math.abs(position.x) > half || Math.abs(position.z) > half) return true; return false; } /* ------------------------------------------------- Animation loop ------------------------------------------------- */ const clock = new THREE.Clock(); const statsDiv = document.getElementById('stats'); function animate(){ requestAnimationFrame(animate); const delta = clock.getDelta(); if(controls.isLocked){ // Determine intended direction let dirForward = 0, dirRight = 0; if(keys['KeyW']) dirForward += 1; if(keys['KeyS']) dirForward -= 1; if(keys['KeyA']) dirRight -= 1; if(keys['KeyD']) dirRight += 1; const speed = keys['ShiftLeft'] || keys['ShiftRight'] ? player.speedRun : player.speedWalk; const moveDist = speed * (delta/60); // convert minutes -> seconds // vector pointing forward in camera space (x,z) const forward = new THREE.Vector3(); camera.getWorldDirection(forward); forward.y = 0; forward.normalize(); const right = new THREE.Vector3(); right.crossVectors(forward,new THREE.Vector3(0,1,0)).normalize(); // desired displacement let displacement = new THREE.Vector3(); displacement.addScaledVector(forward, dirForward * moveDist); displacement.addScaledVector(right , dirRight * moveDist); if(displacement.lengthSq()>0){ displacement.normalize().multiplyScalar(moveDist); // consistent diagonal speed const candidatePos = player.pos.clone().add(displacement); if(!collides(candidatePos)){ player.pos.copy(candidatePos); controls.getObject().position.set(player.pos.x, player.pos.y, player.pos.z); } } } renderer.render(scene,camera); // simple debug HUD statsDiv.textContent = `X:${player.pos.x.toFixed(1)} Z:${player.pos.z.toFixed(1)} ` + ( (keys['ShiftLeft']||keys['ShiftRight']) ? 'RUN' : 'WALK'); } animate(); })(); </script> </body> </html> ```
I'll create a foundational 3D open-world game environment using Three.js. The code includes a navigable map, player movement (walking and running), camera controls, and basic collision detection. Everything is contained in a single HTML file with embedded scripts and styles. ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>3D Open World Game</title> <style> body { margin: 0; overflow: hidden; } canvas { display: block; } #info { position: absolute; top: 10px; left: 10px; color: white; font-family: Arial, sans-serif; background: rgba(0, 0, 0, 0.5); padding: 10px; } </style> </head> <body> <div id="info"> Controls:<br> WASD - Move<br> Shift - Run<br> Mouse - Look around<br> </div> <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r134/three.min.js"></script> <script> // Scene setup 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); // Lighting const ambientLight = new THREE.AmbientLight(0x404040); scene.add(ambientLight); const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5); directionalLight.position.set(0, 1, 0); scene.add(directionalLight); // Create terrain (simple flat ground with grid texture) const groundSize = 1000; const groundGeometry = new THREE.PlaneGeometry(groundSize, groundSize); const groundMaterial = new THREE.MeshBasicMaterial({ color: 0x888888, side: THREE.DoubleSide, wireframe: true }); const ground = new THREE.Mesh(groundGeometry, groundMaterial); ground.rotation.x = -Math.PI / 2; scene.add(ground); // Add some obstacle boxes for collision testing const obstacles = []; function createObstacle(x, z, width, height, depth) { const geometry = new THREE.BoxGeometry(width, height, depth); const material = new THREE.MeshBasicMaterial({ color: 0xff0000 }); const obstacle = new THREE.Mesh(geometry, material); obstacle.position.set(x, height / 2, z); scene.add(obstacle); return obstacle; } obstacles.push(createObstacle(10, 10, 5, 2, 5)); obstacles.push(createObstacle(-15, 20, 5, 3, 5)); obstacles.push(createObstacle(0, -30, 10, 2, 10)); // Player setup const player = { height: 1.4, speed: 5, runSpeed: 10, size: 1, // For collision box width/depth position: new THREE.Vector3(0, 1.4, 0), velocity: new THREE.Vector3(), isRunning: false, canMove: true }; // Camera positioning (first-person view) camera.position.set(0, player.height, 0); // Movement and input handling const keys = {}; document.addEventListener('keydown', (e) => { keys[e.key.toLowerCase()] = true; if (e.key.toLowerCase() === 'shift') player.isRunning = true; }); document.addEventListener('keyup', (e) => { keys[e.key.toLowerCase()] = false; if (e.key.toLowerCase() === 'shift') player.isRunning = false; }); // Mouse look controls document.addEventListener('click', () => { document.addEventListener('mousemove', onMouseMove); renderer.domElement.requestPointerLock(); }); function onMouseMove(event) { const movementX = event.movementX || 0; const movementY = event.movementY || 0; camera.rotation.y -= movementX * 0.002; camera.rotation.x -= movementY * 0.002; camera.rotation.x = Math.max(-Math.PI / 2, Math.min(Math.PI / 2, camera.rotation.x)); } document.addEventListener('pointerlockchange', () => { if (!document.pointerLockElement) { document.removeEventListener('mousemove', onMouseMove); } }); // Collision detection (simple AABB) function checkCollision(pos) { const playerHalfSize = player.size / 2; for (const obstacle of obstacles) { const obsPos = obstacle.position; const obsHalfSizeX = obstacle.geometry.parameters.width / 2; const obsHalfSizeY = obstacle.geometry.parameters.height / 2; const obsHalfSizeZ = obstacle.geometry.parameters.depth / 2; if ( pos.x + playerHalfSize > obsPos.x - obsHalfSizeX && pos.x - playerHalfSize < obsPos.x + obsHalfSizeX && pos.y + player.height > obsPos.y - obsHalfSizeY && pos.y < obsPos.y + obsHalfSizeY && pos.z + playerHalfSize > obsPos.z - obsHalfSizeZ && pos.z - playerHalfSize < obsPos.z + obsHalfSizeZ ) { return true; } } return false; } // Update loop let lastTime = 0; function animate(time) { const deltaTime = (time - lastTime) / 1000; lastTime = time; // Handle movement player.velocity.set(0, 0, 0); const speed = player.isRunning ? player.runSpeed : player.speed; if (keys['w']) { player.velocity.x += Math.sin(camera.rotation.y) * speed; player.velocity.z += Math.cos(camera.rotation.y) * speed; } if (keys['s']) { player.velocity.x -= Math.sin(camera.rotation.y) * speed; player.velocity.z -= Math.cos(camera.rotation.y) * speed; } if (keys['a']) { player.velocity.x += Math.sin(camera.rotation.y + Math.PI / 2) * speed; player.velocity.z += Math.cos(camera.rotation.y + Math.PI / 2) * speed; } if (keys['d']) { player.velocity.x -= Math.sin(camera.rotation.y + Math.PI / 2) * speed; player.velocity.z -= Math.cos(camera.rotation.y + Math.PI / 2) * speed; } // Apply movement with collision detection const newPosition = player.position.clone(); newPosition.x += player.velocity.x * deltaTime; if (!checkCollision(newPosition)) { player.position.x = newPosition.x; } newPosition.z += player.velocity.z * deltaTime; if (!checkCollision(newPosition)) { player.position.z = newPosition.z; } // Update camera position to follow player camera.position.set(player.position.x, player.position.y, player.position.z); // Render renderer.render(scene, camera); requestAnimationFrame(animate); } requestAnimationFrame(animate); // Handle window resize window.addEventListener('resize', () => { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); }); </script> </body> </html> ``` This code provides a basic 3D open-world game environment with the following features: - A large, flat terrain as the navigable map. - Player movement using WASD keys (walking and running with Shift). - First-person camera controls with mouse look (click to lock cursor). - Basic collision detection with obstacles (red boxes) in the world. - Responsive design that adjusts to window resizing. The environment is built using Three.js, loaded via CDN, and includes all necessary HTML, CSS, and JavaScript in a single block. You can run this directly in a browser to explore the 3D world.
This code creates a complete, foundational 3D game environment in a single HTML file using the **Three.js** library. ### Features: * **3D World:** A large ground plane with random cube obstacles and a sky-like background. * **Player Character:** Represented by a blue capsule. * **Movement:** * **Walk:** Use **W, A, S, D** keys. * **Run:** Hold **SHIFT** while moving. * **Camera Controls:** * **Look:** Move the **mouse** to orbit the camera around the player. * **Zoom:** Use the **mouse wheel** to zoom in and out. * **Collision Detection:** The player cannot walk through the grey cube obstacles. * **Pointer Lock:** Click on the screen to lock the mouse for camera control. Press **ESC** to release it. ```html <!DOCTYPE html> <html lang="en"> <head> <title>3D Open World Foundation</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"> <style> body { margin: 0; overflow: hidden; background-color: #000; color: #fff; font-family: monospace; } canvas { display: block; } #info { position: absolute; top: 10px; width: 100%; text-align: center; z-index: 100; } #crosshair { position: absolute; top: 50%; left: 50%; width: 10px; height: 10px; border: 1px solid white; border-radius: 50%; transform: translate(-50%, -50%); mix-blend-mode: difference; } #blocker { position: absolute; width: 100%; height: 100%; background-color: rgba(0,0,0,0.5); display: flex; justify-content: center; align-items: center; } #instructions { width: 50%; text-align: center; cursor: pointer; } #instructions h1 { font-size: 36px; } #instructions p { font-size: 16px; } </style> </head> <body> <div id="blocker"> <div id="instructions"> <h1>Click to Play</h1> <p> <b>W, A, S, D:</b> Move<br/> <b>SHIFT:</b> Run<br/> <b>MOUSE:</b> Look<br/> <b>MOUSE WHEEL:</b> Zoom<br/> <b>ESC:</b> Release Mouse </p> </div> </div> <div id="info">3D Open World Foundation</div> <div id="crosshair"></div> <script src="https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.min.js"></script> <script> // === SCENE SETUP === let scene, camera, renderer, clock; let player, cameraPivot; let collidableObjects = []; const keys = {}; // === PLAYER STATE === const playerState = { speed: 6.0, runSpeed: 12.0, velocity: new THREE.Vector3(), direction: new THREE.Vector3(), height: 1.8, radius: 0.4 }; // === CAMERA STATE === const cameraState = { zoom: 5, minZoom: 2, maxZoom: 10, sensitivity: 0.002 }; // --- INITIALIZATION --- function init() { // Scene scene = new THREE.Scene(); scene.background = new THREE.Color(0x87ceeb); scene.fog = new THREE.Fog(0x87ceeb, 0, 150); // Clock clock = new THREE.Clock(); // Camera camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); cameraPivot = new THREE.Object3D(); camera.position.z = cameraState.zoom; cameraPivot.add(camera); scene.add(cameraPivot); // Renderer renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); renderer.shadowMap.enabled = true; renderer.shadowMap.type = THREE.PCFSoftShadowMap; document.body.appendChild(renderer.domElement); // Lighting const ambientLight = new THREE.HemisphereLight(0xeeeeff, 0x777788, 0.75); scene.add(ambientLight); const dirLight = new THREE.DirectionalLight(0xffffff, 0.75); dirLight.position.set(50, 200, 100); dirLight.castShadow = true; dirLight.shadow.camera.top = 100; dirLight.shadow.camera.bottom = -100; dirLight.shadow.camera.left = -100; dirLight.shadow.camera.right = 100; dirLight.shadow.camera.near = 0.1; dirLight.shadow.camera.far = 500; dirLight.shadow.mapSize.width = 2048; dirLight.shadow.mapSize.height = 2048; scene.add(dirLight); // World Objects createWorld(); // Player createPlayer(); // Event Listeners setupEventListeners(); // Start Animation Loop animate(); } // --- WORLD CREATION --- function createWorld() { // Ground const groundGeo = new THREE.PlaneGeometry(500, 500); const groundMat = new THREE.MeshLambertMaterial({ color: 0x556b2f }); const ground = new THREE.Mesh(groundGeo, groundMat); ground.rotation.x = -Math.PI / 2; ground.receiveShadow = true; scene.add(ground); // Obstacles const boxGeo = new THREE.BoxGeometry(5, 5, 5); for (let i = 0; i < 50; i++) { const boxMat = new THREE.MeshLambertMaterial({ color: 0x808080 }); const box = new THREE.Mesh(boxGeo, boxMat); box.position.set( (Math.random() - 0.5) * 200, 2.5, (Math.random() - 0.5) * 200 ); box.castShadow = true; box.receiveShadow = true; scene.add(box); collidableObjects.push(box); } } // --- PLAYER CREATION --- function createPlayer() { const playerGeo = new THREE.CapsuleGeometry(playerState.radius, playerState.height - (2 * playerState.radius), 4, 8); const playerMat = new THREE.MeshStandardMaterial({ color: 0x0000ff }); player = new THREE.Mesh(playerGeo, playerMat); player.position.y = playerState.height / 2; player.castShadow = true; scene.add(player); cameraPivot.position.copy(player.position); } // --- EVENT LISTENERS --- function setupEventListeners() { const blocker = document.getElementById('blocker'); const instructions = document.getElementById('instructions'); instructions.addEventListener('click', () => { document.body.requestPointerLock(); }); document.addEventListener('pointerlockchange', () => { if (document.pointerLockElement === document.body) { blocker.style.display = 'none'; } else { blocker.style.display = 'flex'; } }); document.addEventListener('keydown', (event) => { keys[event.key.toLowerCase()] = true; }); document.addEventListener('keyup', (event) => { keys[event.key.toLowerCase()] = false; }); document.addEventListener('mousemove', onMouseMove); document.addEventListener('wheel', onMouseWheel); window.addEventListener('resize', onWindowResize); } function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); } function onMouseMove(event) { if (document.pointerLockElement === document.body) { cameraPivot.rotation.y -= event.movementX * cameraState.sensitivity; const newXRotation = camera.rotation.x - event.movementY * cameraState.sensitivity; if (newXRotation > -Math.PI / 2 && newXRotation < Math.PI / 2) { camera.rotation.x = newXRotation; } } } function onMouseWheel(event) { cameraState.zoom -= event.deltaY * 0.01; cameraState.zoom = THREE.MathUtils.clamp(cameraState.zoom, cameraState.minZoom, cameraState.maxZoom); } // --- UPDATE & RENDER LOOP --- function animate() { requestAnimationFrame(animate); const deltaTime = clock.getDelta(); update(deltaTime); renderer.render(scene, camera); } function update(deltaTime) { updatePlayerMovement(deltaTime); updateCamera(); } function updatePlayerMovement(deltaTime) { const currentSpeed = keys['shift'] ? playerState.runSpeed : playerState.speed; playerState.velocity.x -= playerState.velocity.x * 10.0 * deltaTime; playerState.velocity.z -= playerState.velocity.z * 10.0 * deltaTime; playerState.direction.z = Number(keys['w']) - Number(keys['s']); playerState.direction.x = Number(keys['a']) - Number(keys['d']); playerState.direction.normalize(); if (keys['w'] || keys['s']) playerState.velocity.z -= playerState.direction.z * currentSpeed * deltaTime; if (keys['a'] || keys['d']) playerState.velocity.x -= playerState.direction.x * currentSpeed * deltaTime; // Apply movement based on camera direction const moveVector = new THREE.Vector3(playerState.velocity.x, 0, playerState.velocity.z); moveVector.applyQuaternion(cameraPivot.quaternion); // Collision Detection const playerCollider = new THREE.Box3().setFromObject(player); // Test X-axis movement const tempPlayerPositionX = player.position.clone(); tempPlayerPositionX.x += moveVector.x; const tempColliderX = playerCollider.clone().translate(new THREE.Vector3(moveVector.x, 0, 0)); let collisionX = false; for(const obj of collidableObjects){ const objCollider = new THREE.Box3().setFromObject(obj); if(tempColliderX.intersectsBox(objCollider)){ collisionX = true; break; } } if(!collisionX) player.position.x = tempPlayerPositionX.x; // Test Z-axis movement const tempPlayerPositionZ = player.position.clone(); tempPlayerPositionZ.z += moveVector.z; const tempColliderZ = playerCollider.clone().translate(new THREE.Vector3(0, 0, moveVector.z)); let collisionZ = false; for(const obj of collidableObjects){ const objCollider = new THREE.Box3().setFromObject(obj); if(tempColliderZ.intersectsBox(objCollider)){ collisionZ = true; break; } } if(!collisionZ) player.position.z = tempPlayerPositionZ.z; } function updateCamera() { // Smoothly move camera pivot to player position cameraPivot.position.lerp(player.position, 0.1); // Smoothly adjust camera zoom camera.position.z += (cameraState.zoom - camera.position.z) * 0.1; // Ensure camera looks at the player's torso const lookAtPosition = new THREE.Vector3().copy(player.position); lookAtPosition.y += playerState.height / 2; camera.lookAt(lookAtPosition); } // --- START THE GAME --- init(); </script> </body> </html> ```
```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>3D Open World Game</title> <style> body { margin: 0; overflow: hidden; font-family: Arial, sans-serif; } canvas { display: block; } #ui { position: absolute; top: 10px; left: 10px; color: white; background-color: rgba(0,0,0,0.5); padding: 10px; border-radius: 5px; pointer-events: none; } </style> <script src="https://cdn.jsdelivr.net/npm/three@0.132.2/build/three.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.132.2/examples/js/controls/PointerLockControls.js"></script> </head> <body> <div id="ui"> <h2>3D Open World</h2> <p>W,A,S,D to move | SHIFT to run | Click to lock/unlock mouse</p> </div> <script> // Scene setup const scene = new THREE.Scene(); scene.background = new THREE.Color(0x87ceeb); // Sky blue // Camera const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); camera.position.y = 1.6; // Eye height // Renderer const renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); renderer.shadowMap.enabled = true; document.body.appendChild(renderer.domElement); // Lighting const ambientLight = new THREE.AmbientLight(0x404040); scene.add(ambientLight); const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8); directionalLight.position.set(10, 20, 10); directionalLight.castShadow = true; directionalLight.shadow.mapSize.width = 2048; directionalLight.shadow.mapSize.height = 2048; scene.add(directionalLight); // Ground const groundGeometry = new THREE.PlaneGeometry(100, 100); const groundMaterial = new THREE.MeshStandardMaterial({ color: 0x3d8c40, roughness: 0.8 }); const ground = new THREE.Mesh(groundGeometry, groundMaterial); ground.rotation.x = -Math.PI / 2; ground.receiveShadow = true; scene.add(ground); // Add some trees (simple cylinders and cones) function createTree(x, z) { // Tree trunk const trunkGeometry = new THREE.CylinderGeometry(0.2, 0.2, 2, 8); const trunkMaterial = new THREE.MeshStandardMaterial({ color: 0x8B4513 }); const trunk = new THREE.Mesh(trunkGeometry, trunkMaterial); trunk.position.set(x, 1, z); trunk.castShadow = true; scene.add(trunk); // Tree leaves const leavesGeometry = new THREE.ConeGeometry(1, 2, 8); const leavesMaterial = new THREE.MeshStandardMaterial({ color: 0x2d5a27 }); const leaves = new THREE.Mesh(leavesGeometry, leavesMaterial); leaves.position.set(x, 3, z); leaves.castShadow = true; scene.add(leaves); } // Add multiple trees for (let i = 0; i < 30; i++) { const x = Math.random() * 80 - 40; const z = Math.random() * 80 - 40; // Don't place trees too close to player start if (Math.sqrt(x*x + z*z) > 5) { createTree(x, z); } } // Add some rocks function createRock(x, y, z, scale) { const rockGeometry = new THREE.DodecahedronGeometry(1, 0); const rockMaterial = new THREE.MeshStandardMaterial({ color: 0x808080, roughness: 0.9 }); const rock = new THREE.Mesh(rockGeometry, rockMaterial); rock.position.set(x, y, z); rock.scale.set(scale, scale, scale); rock.castShadow = true; rock.receiveShadow = true; scene.add(rock); return rock; } // Add several rocks createRock(8, 0.5, -5, 0.8); createRock(-10, 0.7, 8, 1.2); createRock(12, 0.4, 15, 0.6); createRock(-15, 0.9, -12, 1.5); // Player movement variables const velocity = new THREE.Vector3(); const direction = new THREE.Vector3(); let moveForward = false; let moveBackward = false; let moveLeft = false; let moveRight = false; let canJump = true; let isRunning = false; // Controls const controls = new THREE.PointerLockControls(camera, document.body); document.addEventListener('click', function() { controls.lock(); }); // Movement event listeners const onKeyDown = function(event) { switch (event.code) { case 'KeyW': moveForward = true; break; case 'KeyA': moveLeft = true; break; case 'KeyS': moveBackward = true; break; case 'KeyD': moveRight = true; break; case 'ShiftLeft': isRunning = true; break; } }; const onKeyUp = function(event) { switch (event.code) { case 'KeyW': moveForward = false; break; case 'KeyA': moveLeft = false; break; case 'KeyS': moveBackward = false; break; case 'KeyD': moveRight = false; break; case 'ShiftLeft': isRunning = false; break; } }; document.addEventListener('keydown', onKeyDown); document.addEventListener('keyup', onKeyUp); // Collision detection const playerHeight = 1.8; const playerRadius = 0.5; const groundHeight = 0; function checkCollisions() { // Simple ground collision - keep player above ground if (camera.position.y < playerHeight) { camera.position.y = playerHeight; velocity.y = 0; canJump = true; } // Prevent going below ground level if (camera.position.y < groundHeight + playerHeight) { camera.position.y = groundHeight + playerHeight; velocity.y = 0; canJump = true; } } // Add a simple indicator for player const playerIndicator = new THREE.Mesh( new THREE.ConeGeometry(playerRadius, playerHeight, 8), new THREE.MeshBasicMaterial({ color: 0xff0000, transparent: true, opacity: 0.5 }) ); playerIndicator.rotation.x = Math.PI / 2; playerIndicator.position.y = playerHeight / 2; scene.add(playerIndicator); // Hide the indicator in actual gameplay - just for development playerIndicator.visible = false; // Animation loop const clock = new THREE.Clock(); function animate() { requestAnimationFrame(animate); if (controls.isLocked) { const delta = clock.getDelta(); // Apply gravity velocity.y -= 9.8 * delta; // Calculate movement direction direction.z = Number(moveForward) - Number(moveBackward); direction.x = Number(moveRight) - Number(moveLeft); direction.normalize(); // Set movement speed const moveSpeed = isRunning ? 8.0 : 4.0; // Apply movement if (moveForward || moveBackward) velocity.z = -direction.z * moveSpeed * delta; if (moveLeft || moveRight) velocity.x = -direction.x * moveSpeed * delta; // Move the controls (and camera) controls.moveRight(velocity.x); controls.moveForward(velocity.z); // Apply simple collision with ground checkCollisions(); // Update player indicator position (for dev) playerIndicator.position.copy(camera.position); playerIndicator.position.y = playerHeight / 2; } renderer.render(scene, camera); } animate(); // Handle window resize window.addEventListener('resize', function() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); }); // Add fog for atmospheric effect scene.fog = new THREE.Fog(0x87ceeb, 50, 100); </script> </body> </html> ```
```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>3D Open World Example</title> <style> html, body { margin: 0; overflow: hidden; width: 100vw; height: 100vh; background: #111; } #info { position: absolute; top: 12px; left: 12px; color: #fff; background: rgba(0,0,0,0.5); padding: 8px 12px; z-index: 10; font-family: sans-serif; border-radius: 4px; font-size: 15px; } canvas { display: block; } </style> <!-- THREE.js CDN --> <script src="https://cdn.jsdelivr.net/npm/three@0.154.0/build/three.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.154.0/examples/js/controls/PointerLockControls.js"></script> </head> <body> <div id="info"> <b>3D Open-World Demo</b><br> Click on scene to control.<br> <strong>WASD</strong>: Walk | <strong>SHIFT</strong>: Run<br> <strong>Mouse</strong>: Look | <strong>Space</strong>: Jump </div> <script> // === Scene setup === let scene = new THREE.Scene(); scene.background = new THREE.Color(0x88bbff); let camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000); // === Renderer === let renderer = new THREE.WebGLRenderer({antialias:true}); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); // === Lights === let hemiLight = new THREE.HemisphereLight(0xffffff, 0x444488, 0.6); scene.add(hemiLight); let dirLight = new THREE.DirectionalLight(0xffffff, 0.6); dirLight.position.set(40,60,20); scene.add(dirLight); // === Ground === let groundSize = 200; let groundGeometry = new THREE.PlaneGeometry(groundSize, groundSize, 32, 32); let groundMaterial = new THREE.MeshLambertMaterial({color:0x229933}); let ground = new THREE.Mesh(groundGeometry, groundMaterial); ground.rotation.x = -Math.PI/2; ground.receiveShadow = true; scene.add(ground); // === World Objects (simple blocks as obstacles) === const objects = []; function addBlock(x, y, z, w=4, h=8, d=4, color=0x666666) { let geom = new THREE.BoxGeometry(w, h, d); let mat = new THREE.MeshLambertMaterial({color}); let mesh = new THREE.Mesh(geom, mat); mesh.position.set(x, y+h/2, z); mesh.castShadow = true; scene.add(mesh); objects.push(mesh); } // Add a few blocks addBlock(20, 0, 10, 8, 10, 8, 0xa0542d); addBlock(-15, 0, -25, 6, 6, 6, 0x2d54a0); addBlock(35, 0, -40, 12, 15, 12, 0x645f1f); addBlock(-50, 0, -20, 7, 7, 10, 0x6baa23); addBlock(0, 0, 40, 18, 9, 10, 0xba2372); // Random trees function addTree(x,z) { let trunk = new THREE.Mesh( new THREE.CylinderGeometry(0.7,0.9,4,8), new THREE.MeshLambertMaterial({color:0x69462e}) ); trunk.position.set(x, 2, z); scene.add(trunk); let leaves = new THREE.Mesh( new THREE.SphereGeometry(2, 12, 8), new THREE.MeshLambertMaterial({color:0x2b9912}) ); leaves.position.set(x, 6, z); scene.add(leaves); // For collision on trunk trunk.userData.isTreeTrunk = true; objects.push(trunk); } for(let i=0; i<14; i++) addTree( Math.random()*groundSize - groundSize/2, Math.random()*groundSize - groundSize/2 ); // Small elevations (terrain bumps) function addBump(x, z) { let geom = new THREE.CylinderGeometry(7, 12, 3, 16); let mat = new THREE.MeshLambertMaterial({color:0x347c23}); let m = new THREE.Mesh(geom, mat); m.position.set(x, 1.5, z); m.rotation.y = Math.random()*Math.PI; m.receiveShadow = true; m.castShadow = false; scene.add(m); objects.push(m); } for(let i=0; i<6; i++) addBump( Math.random()*groundSize - groundSize/2, Math.random()*groundSize - groundSize/2 ); // === Map boundaries (invisible walls) === let boundary = groundSize/2 - 3; // === Simple player "capsule" rules === const player = { pos: new THREE.Vector3(0, 3, 0), vel: new THREE.Vector3(), radius: 1, height: 3, canJump: false }; // Create player character for shadow/feel let playerChar = new THREE.Group(); // Torso let tor = new THREE.Mesh( new THREE.CylinderGeometry(0.85, 1.15, 2.3, 12), new THREE.MeshStandardMaterial({color:0xc9c9c9, flatShading:true, roughness:0.7}) ); tor.position.y = 1.15; playerChar.add(tor); // Head let head = new THREE.Mesh( new THREE.SphereGeometry(0.7, 10, 10), new THREE.MeshStandardMaterial({color:0xffffe9, flatShading:true}) ); head.position.y = 2.5; playerChar.add(head); playerChar.position.copy(player.pos); scene.add(playerChar); // === Camera/Controls === let controls = new THREE.PointerLockControls(camera, document.body); camera.position.set(0,player.height,0); camera.lookAt(0,player.height,1); document.body.addEventListener('click', function() { controls.lock(); }); // === Input handling === const keyState = {}; document.addEventListener('keydown', e => keyState[e.code]=true); document.addEventListener('keyup', e => keyState[e.code]=false); // === Helper: collision check (AABB) === function testPlayerCollision(pos, nextPos) { // Approximate capsule as sphere at pos.y+radius and pos.y+height-radius // For each object (boxes, trees, bumps), check simple bounding box collision with sphere for (let obj of objects) { // Find object's bounding box in world coords obj.geometry.computeBoundingBox(); let bbox = obj.geometry.boundingBox.clone(); bbox.applyMatrix4(obj.matrixWorld); // Two test sphere centers (feet and head) let footY = nextPos.y + player.radius; let headY = nextPos.y + player.height - player.radius; let testPoints = [ new THREE.Vector3(nextPos.x, footY, nextPos.z), new THREE.Vector3(nextPos.x, headY, nextPos.z) ]; for (let pt of testPoints) { if ( pt.x >= bbox.min.x - player.radius && pt.x <= bbox.max.x + player.radius && pt.y >= bbox.min.y - player.radius && pt.y <= bbox.max.y + player.radius && pt.z >= bbox.min.z - player.radius && pt.z <= bbox.max.z + player.radius ) { return true; } } } return false; } // === Player movement loop === let lastTime = performance.now(); function animate() { requestAnimationFrame(animate); // Delta time let now = performance.now(); let dt = Math.min(0.12, (now - lastTime)/1000); lastTime = now; // Movement input (relative to camera view) let moveDir = new THREE.Vector3(); let rotY = controls.getObject().rotation.y; let forward = new THREE.Vector3(Math.sin(rotY), 0, -Math.cos(rotY)); let right = new THREE.Vector3(Math.sin(rotY + Math.PI/2), 0, -Math.cos(rotY + Math.PI/2)); if (keyState["KeyW"]) moveDir.add(forward); if (keyState["KeyS"]) moveDir.add(forward.clone().negate()); if (keyState["KeyA"]) moveDir.add(right.clone().negate()); if (keyState["KeyD"]) moveDir.add(right); if (moveDir.lengthSq() > 0) moveDir.normalize(); // Walk or run speed let speed = keyState["ShiftLeft"] || keyState["ShiftRight"] ? 14 : 7; let ACCEL = 40; let velXZ = new THREE.Vector3(player.vel.x, 0, player.vel.z); // Acceleration on ground only let horizontalVel = moveDir.multiplyScalar(ACCEL * dt); velXZ.add(horizontalVel); // Limit top speed if (velXZ.length() > speed) velXZ.setLength(speed); // Friction if (moveDir.lengthSq() === 0) { velXZ.multiplyScalar(1 - Math.min(dt*10,1)); if (velXZ.length() < 1) velXZ.set(0,0,0); } player.vel.x = velXZ.x; player.vel.z = velXZ.z; // Gravity player.vel.y -= 32 * dt; if (player.vel.y < -48) player.vel.y = -48; // Jump if (keyState["Space"] && player.canJump) { player.vel.y = 11; player.canJump = false; } // Attempt move let oldPos = player.pos.clone(); let nextPos = player.pos.clone().addScaledVector(player.vel, dt); // Stay within boundaries of ground nextPos.x = Math.max(-boundary, Math.min(boundary, nextPos.x)); nextPos.z = Math.max(-boundary, Math.min(boundary, nextPos.z)); // Collision with world objects if (!testPlayerCollision(player.pos, nextPos)) { player.pos.copy(nextPos); } else { // Axis-Separated sliding approach // Try only X move let temp = player.pos.clone(); temp.x = nextPos.x; if (!testPlayerCollision(player.pos, temp)) { player.pos.x = temp.x; } else { player.vel.x = 0; } // Try only Z move temp = player.pos.clone(); temp.z = nextPos.z; if (!testPlayerCollision(player.pos, temp)) { player.pos.z = temp.z; } else { player.vel.z = 0; } // Try Y temp = player.pos.clone(); temp.y = nextPos.y; if (!testPlayerCollision(player.pos, temp)) { player.pos.y = temp.y; } else { player.vel.y = 0; } } // Fake floor collision (simple ground test) if (player.pos.y < 2.0) { player.pos.y = 2.0; player.vel.y = 0; player.canJump = true; } else { // Only set canJump ON when hitting the ground let footPos = player.pos.clone(); footPos.y -= 0.05; let groundTest = player.pos.y <= 2.04 || testPlayerCollision(player.pos, footPos); if (groundTest && player.vel.y <= 0) { player.canJump = true; } } // === Update camera/player visuals === controls.getObject().position.set( player.pos.x, player.pos.y + player.height * 0.8, player.pos.z ); playerChar.position.copy(player.pos); renderer.render(scene, camera); } animate(); // Adjust on resize window.addEventListener('resize', function() { camera.aspect = window.innerWidth/window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); }); </script> </body> </html> ```
```html <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>3D Open-World Game</title> <style> body { margin: 0; overflow: hidden; } #instructions { position: absolute; top: 0px; width: 100%; text-align: center; color: #000; background-color: #fff; padding: 15px; z-index: 1; font-family: sans-serif; cursor: pointer; } </style> <script src="https://cdn.jsdelivr.net/npm/three@0.160.1/build/three.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.160.1/examples/jsm/controls/PointerLockControls.js"></script> </head> <body> <div id="instructions">Click to Play</div> <script> // Scene Setup const scene = new THREE.Scene(); scene.background = new THREE.Color(0xa0a0a0); const camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 100); camera.position.set(0, 1.5, 0); const renderer = new THREE.WebGLRenderer({antialias: true}); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); // Controls const controls = new THREE.PointerLockControls(camera, document.body); document.getElementById('instructions').addEventListener('click', function() { controls.lock(); }); scene.add(controls.getObject()); // Lighting const light = new THREE.HemisphereLight(0xffffff, 0x444444, 0.8); scene.add(light); const dirLight = new THREE.DirectionalLight(0xffffff, 0.5); dirLight.position.set(0, 20, 10); scene.add(dirLight); // Ground const ground = new THREE.Mesh( new THREE.BoxGeometry(100, 1, 100), new THREE.MeshStandardMaterial({color: 0x00ff00, roughness: 0.8}) ); scene.add(ground); // Obstacles const obstacles = []; function createBlock(x, y, z) { const block = new THREE.Mesh( new THREE.BoxGeometry(5, 3, 5), new THREE.MeshStandardMaterial({color: 0xff0000, roughness: 0.6}) ); block.position.set(x, 1.5, z); block.userData = { type: 'obstacle' }; block.geometry.computeBoundingBox(); block.boundingBox = new THREE.Box3().setFromObject(block); scene.add(block); obstacles.push(block); } createBlock(10, 0, 0); createBlock(-10, 0, 0); // Player Collision Box const playerBox = new THREE.Box3(); const playerDimensions = new THREE.Vector3(0.5, 1.5, 0.5); // Movement variables let keyForward = false, keyBackward = false, keyLeft = false, keyRight = false, keySprint = false; let moveSpeed = 4.0, sprintMultiplier = 1.5; // Input Handling window.addEventListener('keydown', function(event) { switch(event.code) { case 'KeyW': keyForward = true; break; case 'KeyS': keyBackward = true; break; case 'KeyA': keyLeft = true; break; case 'KeyD': keyRight = true; break; case 'ShiftLeft': keySprint = true; break; } }); window.addEventListener('keyup', function(event) { switch(event.code) { case 'KeyW': keyForward = false; break; case 'KeyS': keyBackward = false; break; case 'KeyA': keyLeft = false; break; case 'KeyD': keyRight = false; break; case 'ShiftLeft': keySprint = false; break; } }); // Collision Detection function checkCollision() { playerBox.setFromCenterAndSize(camera.position, playerDimensions); for (let i = 0; i < obstacles.length; i++) { if (playerBox.intersectsBox(obstacles[i].boundingBox)) { return true; } } return false; } // Movement Update function updatePlayerMovement(delta) { let direction = new THREE.Vector3(); direction.set( (keyRight ? 1 : 0) - (keyLeft ? 1 : 0), 0, (keyForward ? 1 : 0) - (keyBackward ? 1 : 0) ); if (direction.x || direction.z) direction.normalize(); const speed = moveSpeed * (keySprint ? sprintMultiplier : 1) * delta; const rotatedDirection = direction.applyQuaternion(camera.quaternion); const originalPos = camera.position.clone(); // X Axis Movement camera.position.add(new THREE.Vector3(rotatedDirection.x * speed, 0, 0)); if (checkCollision()) camera.position.x = originalPos.x; // Z Axis Movement camera.position.add(new THREE.Vector3(0, 0, rotatedDirection.z * speed)); if (checkCollision()) camera.position.z = originalPos.z; } // Resize Handler window.addEventListener('resize', () => { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); }); // Animation Loop const clock = new THREE.Clock(); function animate() { requestAnimationFrame(animate); const delta = clock.getDelta(); controls.update(); if (controls.isLocked) { updatePlayerMovement(delta); } renderer.render(scene, camera); } animate(); </script> </body> </html> ```
```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Foundational 3D Open-World Environment</title> <style> body { margin: 0; background: #000; overflow: hidden; font-family: Arial, sans-serif; } #gameContainer { position: relative; width: 100vh; height: 100vh; } #ui { position: absolute; top: 10px; left: 10px; color: white; z-index: 100; background: rgba(0, 0, 0, 0.5); padding: 10px; border-radius: 5px; font-size: 14px; } #crosshair { position: absolute; top: 50%; left: 50%; width: 20px; height: 20px; transform: translate(-50%, -50%); color: white; font-size: 20px; z-index: 100; pointer-events: none; } </style> </head> <body> <div id="gameContainer"> <div id="ui"> <div>WASD: Move | Shift: Run | Mouse: Look Around</div> <div>FPS: <span id="fps">0</span></div> <div>Position: <span id="position">0, 0, 0</span></div> </div> <div id="crosshair">+</div> </div> <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> <script> // Game setup 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); renderer.shadowMap.enabled = true; renderer.shadowMap.type = THREE.PCFSoftShadowMap; document.getElementById('gameContainer').appendChild(renderer.domElement); // Lighting const ambientLight = new THREE.AmbientLight(0x404040, 0.4); scene.add(ambientLight); const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8); directionalLight.position.set(50, 50, 50); directionalLight.castShadow = true; directionalLight.shadow.camera.left = -50; directionalLight.shadow.camera.right = 50; directionalLight.shadow.camera.top = 50; directionalLight.shadow.camera.bottom = -50; directionalLight.shadow.camera.near = 0.1; directionalLight.shadow.camera.far = 200; scene.add(directionalLight); // Sky scene.background = new THREE.Color(0x87CEEB); scene.fog = new THREE.Fog(0x87CEEB, 50, 200); // Ground const groundGeometry = new THREE.PlaneGeometry(200, 200); const groundMaterial = new THREE.MeshLambertMaterial({ color: 0x3a7e4d, side: THREE.DoubleSide }); const ground = new THREE.Mesh(groundGeometry, groundMaterial); ground.rotation.x = -Math.PI / 2; ground.position.y = 0; ground.receiveShadow = true; scene.add(ground); // Create trees function createTree(x, z) { const treeGroup = new THREE.Group(); // Trunk const trunkGeometry = new THREE.CylinderGeometry(0.5, 0.7, 4); const trunkMaterial = new THREE.MeshLambertMaterial({ color: 0x654321 }); const trunk = new THREE.Mesh(trunkGeometry, trunkMaterial); trunk.position.y = 2; trunk.castShadow = true; treeGroup.add(trunk); // Leaves const leavesGeometry = new THREE.SphereGeometry(3, 8, 6); const leavesMaterial = new THREE.MeshLambertMaterial({ color: 0x228B22 }); const leaves = new THREE.Mesh(leavesGeometry, leavesMaterial); leaves.position.y = 5; leaves.castShadow = true; treeGroup.add(leaves); treeGroup.position.set(x, 0, z); treeGroup.userData = { type: 'solid' }; return treeGroup; } // Create rocks function createRock(x, z) { const geometry = new THREE.DodecahedronGeometry(1.5, 0); const material = new THREE.MeshLambertMaterial({ color: 0x808080 }); const rock = new THREE.Mesh(geometry, material); rock.position.set(x, 0.75, z); rock.castShadow = true; rock.userData = { type: 'solid' }; return rock; } // Create houses function createHouse(x, z) { const houseGroup = new THREE.Group(); // Base const baseGeometry = new THREE.BoxGeometry(8, 6, 10); const baseMaterial = new THREE.MeshLambertMaterial({ color: 0x8B4513 }); const base = new THREE.Mesh(baseGeometry, baseMaterial); base.position.y = 3; base.castShadow = true; houseGroup.add(base); // Roof const roofGeometry = new THREE.ConeGeometry(8, 4, 4); const roofMaterial = new THREE.MeshLambertMaterial({ color: 0x8B0000 }); const roof = new THREE.Mesh(roofGeometry, roofMaterial); roof.position.y = 8; roof.rotation.y = Math.PI / 4; roof.castShadow = true; houseGroup.add(roof); houseGroup.position.set(x, 0, z); houseGroup.userData = { type: 'solid' }; return houseGroup; } // Procedurally place obstacles const obstacles = []; for (let i = 0; i < 30; i++) { const x = (Math.random() - 0.5) * 100; const z = (Math.random() - 0.5) * 100; const tree = createTree(x, z); scene.add(tree); obstacles.push(tree); } for (let i = 0; i < 20; i++) { const x = (Math.random() - 0.5) * 100; const z = (Math.random() - 0.5) * 100; const rock = createRock(x, z); scene.add(rock); obstacles.push(rock); } for (let i = 0; i < 5; i++) { const x = (Math.random() - 0.5) * 80; const z = (Math.random() - 0.5) * 80; const house = createHouse(x, z); scene.add(house); obstacles.push(house); } // Player setup const playerHeight = 1.8; const playerRadius = 0.5; const playerCamera = new THREE.Group(); playerCamera.position.set(0, playerHeight, 0); camera.position.set(0, 0, 0); playerCamera.add(camera); scene.add(playerCamera); // Player physics const playerVelocity = { x: 0, y: 0, z: 0 }; const playerSpeed = { walk: 0.1, run: 0.2 }; const gravity = -0.02; const friction = 0.85; let isGrounded = true; // Camera controls let mouseX = 0; let mouseY = 0; let isPointerLocked = false; // Handle pointer lock renderer.domElement.addEventListener('click', () => { renderer.domElement.requestPointerLock(); }); document.addEventListener('pointerlockchange', () => { isPointerLocked = document.pointerLockElement === renderer.domElement; }); // Mouse movement document.addEventListener('mousemove', (e) => { if (!isPointerLocked) return; mouseX -= e.movementY * 0.002; mouseY -= e.movementX * 0.002; mouseX = Math.max(-Math.PI/2, Math.min(Math.PI/2, mouseX)); playerCamera.rotation.x = mouseX; playerCamera.rotation.y = mouseY; }); // Movement controls const keys = {}; document.addEventListener('keydown', (e) => keys[e.key.toLowerCase()] = true); document.addEventListener('keyup', (e) => keys[e.key.toLowerCase()] = false); // Collision detection function checkCollision(pos, radius) { for (const obstacle of obstacles) { if (obstacle.userData.type === 'solid') { const obstaclePos = obstacle.position.clone(); const distance = pos.distanceTo(obstaclePos); if (distance < radius + 2) { return obstaclePos; } } } return null; } // Animation loop let frameCount = 0; let lastTime = performance.now(); function animate() { requestAnimationFrame(animate); const currentTime = performance.now(); const deltaTime = currentTime - lastTime; lastTime = currentTime; frameCount++; if (frameCount % 60 === 0) { document.getElementById('fps').textContent = Math.round(1000 / deltaTime); } // Movement const moveSpeed = keys['shift'] ? playerSpeed.run : playerSpeed.walk; let dx = 0, dz = 0; if (keys['w']) { dx -= Math.sin(playerCamera.rotation.y) * moveSpeed; dz -= Math.cos(playerCamera.rotation.y) * moveSpeed; } if (keys['s']) { dx += Math.sin(playerCamera.rotation.y) * moveSpeed; dz += Math.cos(playerCamera.rotation.y) * moveSpeed; } if (keys['a']) { dx -= Math.cos(playerCamera.rotation.y) * moveSpeed; dz += Math.sin(playerCamera.rotation.y) * moveSpeed; } if (keys['d']) { dx += Math.cos(playerCamera.rotation.y) * moveSpeed; dz -= Math.sin(playerCamera.rotation.y) * moveSpeed; } // Apply friction playerVelocity.x *= friction; playerVelocity.z *= friction; // Add movement playerVelocity.x += dx; playerVelocity.z += dz; // Check for ground collision if (playerCamera.position.y <= playerHeight) { playerCamera.position.y = playerHeight; isGrounded = true; playerVelocity.y = 0; } else { isGrounded = false; } // Apply gravity if (!isGrounded) { playerVelocity.y += gravity; } // Check for obstacle collision const newPos = new THREE.Vector3( playerCamera.position.x + playerVelocity.x, playerCamera.position.y, playerCamera.position.z + playerVelocity.z ); const collision = checkCollision(newPos, playerRadius); if (collision) { const pushDirection = newPos.clone().sub(collision).normalize(); playerVelocity.x = pushDirection.x * 0.1; playerVelocity.z = pushDirection.z * 0.1; } // Update position playerCamera.position.x += playerVelocity.x; playerCamera.position.y += playerVelocity.y; playerCamera.position.z += playerVelocity.z; // Update UI document.getElementById('position').textContent = `${playerCamera.position.x.toFixed(1)}, ${playerCamera.position.y.toFixed(1)}, ${playerCamera.position.z.toFixed(1)}`; renderer.render(scene, camera); } // Handle window resize window.addEventListener('resize', () => { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); }); animate(); </script> </body> </html> ```
```html <!DOCTYPE html> <html> <head> <title>3D Open-World Game Environment</title> <style> body { margin: 0; } canvas { display: block; } </style> </head> <body> <script src="https://cdn.jsdelivr.net/npm/three@0.163.0/build/three.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.163.0/examples/js/controls/OrbitControls.js"></script> <script> // Scene setup const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); const renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); // Orbit controls for camera const controls = new THREE.OrbitControls(camera, renderer.domElement); controls.enableZoom = true; // Ground const groundGeometry = new THREE.PlaneGeometry(200, 200); const groundMaterial = new THREE.MeshBasicMaterial({ color: 0x228b22, side: THREE.DoubleSide }); const ground = new THREE.Mesh(groundGeometry, groundMaterial); ground.rotation.x = -Math.PI / 2; scene.add(ground); // Player const playerGeometry = new THREE.BoxGeometry(1, 2, 1); const playerMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 }); const player = new THREE.Mesh(playerGeometry, playerMaterial); player.position.y = 1; // Center at height 1 scene.add(player); // Set controls target to player controls.target.copy(player.position); // Obstacles const obstacles = []; function addObstacle(x, z, width = 2, height = 2, depth = 2) { const obsGeometry = new THREE.BoxGeometry(width, height, depth); const obsMaterial = new THREE.MeshBasicMaterial({ color: 0x0000ff }); const obs = new THREE.Mesh(obsGeometry, obsMaterial); obs.position.set(x, height / 2, z); scene.add(obs); obstacles.push(obs); } addObstacle(10, 10); addObstacle(-10, -10, 3, 4, 3); addObstacle(15, -5); addObstacle(0, 20); // Lighting const ambientLight = new THREE.AmbientLight(0xffffff, 0.8); scene.add(ambientLight); const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5); directionalLight.position.set(5, 10, 5); scene.add(directionalLight); // Initial camera position camera.position.set(0, 5, 10); // Movement variables let moveForward = false; let moveBackward = false; let moveLeft = false; let moveRight = false; let isRunning = false; const baseSpeed = 0.2; // Keyboard event listeners window.addEventListener('keydown', (event) => { switch (event.key.toLowerCase()) { case 'w': moveForward = true; break; case 's': moveBackward = true; break; case 'a': moveLeft = true; break; case 'd': moveRight = true; break; case 'shift': isRunning = true; break; } }); window.addEventListener('keyup', (event) => { switch (event.key.toLowerCase()) { case 'w': moveForward = false; break; case 's': moveBackward = false; break; case 'a': moveLeft = false; break; case 'd': moveRight = false; break; case 'shift': isRunning = false; break; } }); // Collision detection function checkCollision(newPosition) { const delta = newPosition.clone().sub(player.position); const playerBox = new THREE.Box3().setFromObject(player); playerBox.translate(delta); for (let obs of obstacles) { const obsBox = new THREE.Box3().setFromObject(obs); if (playerBox.intersectsBox(obsBox)) { return true; // Collision detected } } return false; // No collision } // Resize handler window.addEventListener('resize', () => { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); }); // Animation loop function animate() { requestAnimationFrame(animate); // Calculate movement direction based on camera orientation const forward = new THREE.Vector3(); camera.getWorldDirection(forward); forward.y = 0; forward.normalize(); const up = new THREE.Vector3(0, 1, 0); const right = new THREE.Vector3().crossVectors(forward, up).normalize(); // Calculate velocity const velocity = new THREE.Vector3(); const speed = isRunning ? baseSpeed * 2 : baseSpeed; if (moveForward) velocity.add(forward.clone().multiplyScalar(speed)); if (moveBackward) velocity.add(forward.clone().multiplyScalar(-speed)); if (moveLeft) velocity.add(right.clone().multiplyScalar(-speed)); if (moveRight) velocity.add(right.clone().multiplyScalar(speed)); // Check for collision before moving if (velocity.length() > 0) { const newPosition = player.position.clone().add(velocity); if (!checkCollision(newPosition)) { player.position.copy(newPosition); // Rotate player to face movement direction const lookAtPos = player.position.clone().add(velocity); player.lookAt(lookAtPos.x, player.position.y, lookAtPos.z); } } // Update controls target to follow player controls.target.copy(player.position); controls.update(); renderer.render(scene, camera); } animate(); </script> </body> </html> ```
```html <!DOCTYPE html> <html> <head> <title>3D Open World Game</title> <style> body { margin: 0; overflow: hidden; } canvas { display: block; } </style> </head> <body> <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/loaders/GLTFLoader.min.js"></script> <script> // Scene setup const scene = new THREE.Scene(); scene.background = new THREE.Color(0x87CEEB); // Camera setup const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); camera.position.set(0, 5, 10); // Renderer const renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); renderer.shadowMap.enabled = true; document.body.appendChild(renderer.domElement); // Lighting const ambientLight = new THREE.AmbientLight(0x404040); scene.add(ambientLight); const directionalLight = new THREE.DirectionalLight(0xffffff, 1); directionalLight.position.set(10, 20, 10); directionalLight.castShadow = true; directionalLight.shadow.mapSize.width = 2048; directionalLight.shadow.mapSize.height = 2048; scene.add(directionalLight); // Ground const groundGeometry = new THREE.PlaneGeometry(100, 100); const groundMaterial = new THREE.MeshStandardMaterial({ color: 0x4CAF50, roughness: 0.8, metalness: 0.2 }); const ground = new THREE.Mesh(groundGeometry, groundMaterial); ground.rotation.x = -Math.PI / 2; ground.receiveShadow = true; scene.add(ground); // Player const playerGeometry = new THREE.CapsuleGeometry(0.5, 1, 4, 8); const playerMaterial = new THREE.MeshStandardMaterial({ color: 0xff0000 }); const player = new THREE.Mesh(playerGeometry, playerMaterial); player.castShadow = true; player.position.y = 1.5; scene.add(player); // Simple obstacles const obstacleGeometry = new THREE.BoxGeometry(2, 2, 2); const obstacleMaterial = new THREE.MeshStandardMaterial({ color: 0x8B4513 }); const obstacles = []; for (let i = 0; i < 10; i++) { const obstacle = new THREE.Mesh(obstacleGeometry, obstacleMaterial); obstacle.position.x = Math.random() * 50 - 25; obstacle.position.z = Math.random() * 50 - 25; obstacle.position.y = 1; obstacle.castShadow = true; obstacle.receiveShadow = true; scene.add(obstacle); obstacles.push(obstacle); } // Collision detection function checkCollision(position) { const playerRadius = 0.5; const playerHeight = 2; for (const obstacle of obstacles) { const distance = position.distanceTo(obstacle.position); const obstacleSize = Math.sqrt(8); // Diagonal of 2x2x2 cube if (distance < playerRadius + obstacleSize) { return true; } } return false; } // Movement controls const keys = {}; document.addEventListener('keydown', (e) => keys[e.code] = true); document.addEventListener('keyup', (e) => keys[e.code] = false); const movementSpeed = { walk: 0.1, run: 0.2 }; let currentSpeed = movementSpeed.walk; // Camera controls const controls = new THREE.OrbitControls(camera, renderer.domElement); controls.target.copy(player.position); controls.enableDamping = true; controls.dampingFactor = 0.05; // Game loop function animate() { requestAnimationFrame(animate); // Movement const direction = new THREE.Vector3(); const frontVector = new THREE.Vector3(); const sideVector = new THREE.Vector3(); if (keys['ShiftLeft'] || keys['ShiftRight']) { currentSpeed = movementSpeed.run; } else { currentSpeed = movementSpeed.walk; } if (keys['KeyW'] || keys['ArrowUp']) { frontVector.set(0, 0, -1).applyQuaternion(camera.quaternion); direction.add(frontVector); } if (keys['KeyS'] || keys['ArrowDown']) { frontVector.set(0, 0, 1).applyQuaternion(camera.quaternion); direction.add(frontVector); } if (keys['KeyA'] || keys['ArrowLeft']) { sideVector.set(-1, 0, 0).applyQuaternion(camera.quaternion); direction.add(sideVector); } if (keys['KeyD'] || keys['ArrowRight']) { sideVector.set(1, 0, 0).applyQuaternion(camera.quaternion); direction.add(sideVector); } if (direction.length() > 0) { direction.normalize(); const newPosition = player.position.clone().add( direction.multiplyScalar(currentSpeed) ); // Check for collisions before moving if (!checkCollision(newPosition)) { player.position.copy(newPosition); } } // Update camera target controls.target.copy(player.position); controls.update(); renderer.render(scene, camera); } // Resize handler window.addEventListener('resize', () => { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); }); animate(); </script> </body> </html> ```
```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Foundational 3D Open-World Environment</title> <style> body { margin: 0; overflow: hidden; } #overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.7); display: flex; align-items: center; justify-content: center; color: white; font-family: sans-serif; font-size: 24px; cursor: pointer; } </style> </head> <body> <div id="overlay">Click to Play (WASD to move, Shift to run, Mouse to look)</div> <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/PointerLockControls.js"></script> <script> let camera, scene, renderer, controls; let moveForward = false, moveBackward = false, moveLeft = false, moveRight = false, isRunning = false; const velocity = new THREE.Vector3(); const direction = new THREE.Vector3(); const objects = []; // collidable obstacles const playerRadius = 1.0; const walkSpeed = 200.0; const runSpeed = 400.0; const clock = new THREE.Clock(); init(); animate(); function init() { // Renderer renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); // Scene & Camera scene = new THREE.Scene(); scene.background = new THREE.Color(0x87ceeb); // sky color camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); // Lights const ambient = new THREE.HemisphereLight(0xffffff, 0x444444); ambient.position.set(0, 200, 0); scene.add(ambient); const dirLight = new THREE.DirectionalLight(0xffffff); dirLight.position.set(-3, 10, -10); scene.add(dirLight); // Floor const floorGeo = new THREE.PlaneGeometry(1000, 1000); const floorMat = new THREE.MeshPhongMaterial({ color: 0x228B22 }); const floor = new THREE.Mesh(floorGeo, floorMat); floor.rotation.x = -Math.PI / 2; scene.add(floor); // Obstacles const boxGeo = new THREE.BoxGeometry(4, 4, 4); const boxMat = new THREE.MeshPhongMaterial({ color: 0x8B4513 }); const positions = [ { x: 10, y: 2, z: 10 }, { x: -10, y: 2, z: 5 }, { x: 5, y: 2, z: -10 }, { x: -15, y: 2, z: -12 } ]; positions.forEach(pos => { const box = new THREE.Mesh(boxGeo, boxMat); box.position.set(pos.x, pos.y, pos.z); scene.add(box); box.updateMatrixWorld(); const box3 = new THREE.Box3().setFromObject(box); objects.push(box3); }); // Controls controls = new THREE.PointerLockControls(camera, document.body); scene.add(controls.getObject()); controls.getObject().position.set(0, 2, 0); const overlay = document.getElementById('overlay'); overlay.addEventListener('click', () => controls.lock(), false); controls.addEventListener('lock', () => overlay.style.display = 'none'); controls.addEventListener('unlock', () => overlay.style.display = 'flex'); // Input document.addEventListener('keydown', onKeyDown); document.addEventListener('keyup', onKeyUp); window.addEventListener('resize', onWindowResize); } function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); } function onKeyDown(event) { switch (event.code) { case 'ShiftLeft': isRunning = true; break; case 'KeyW': moveForward = true; break; case 'KeyA': moveLeft = true; break; case 'KeyS': moveBackward = true; break; case 'KeyD': moveRight = true; break; } } function onKeyUp(event) { switch (event.code) { case 'ShiftLeft': isRunning = false; break; case 'KeyW': moveForward = false; break; case 'KeyA': moveLeft = false; break; case 'KeyS': moveBackward = false; break; case 'KeyD': moveRight = false; break; } } function animate() { requestAnimationFrame(animate); const delta = clock.getDelta(); if (controls.isLocked) { // Damping velocity.x -= velocity.x * 10.0 * delta; velocity.z -= velocity.z * 10.0 * delta; // Determine speed const speed = isRunning ? runSpeed : walkSpeed; // Movement direction direction.z = Number(moveForward) - Number(moveBackward); direction.x = Number(moveRight) - Number(moveLeft); direction.normalize(); // Accelerate if (moveForward || moveBackward) velocity.z -= direction.z * speed * delta; if (moveLeft || moveRight) velocity.x -= direction.x * speed * delta; // Current position const pos = controls.getObject().position; // Attempt X movement const nextX = pos.x + velocity.x * delta; if (!checkCollision(nextX, pos.y, pos.z)) { pos.x = nextX; } else { velocity.x = 0; } // Attempt Z movement const nextZ = pos.z + velocity.z * delta; if (!checkCollision(pos.x, pos.y, nextZ)) { pos.z = nextZ; } else { velocity.z = 0; } } renderer.render(scene, camera); } function checkCollision(x, y, z) { const point = new THREE.Vector3(x, y, z); for (let i = 0; i < objects.length; i++) { const box = objects[i]; // Expand box by player radius const expanded = box.clone().expandByScalar(playerRadius); if (expanded.containsPoint(point)) { return true; } } return false; } </script> </body> </html> ```
**3D Open-World Game Environment** Below is a simple implementation of a 3D open-world game environment using Three.js. This example includes a basic navigable map, player character movement, simple camera controls, and collision detection. ### Code ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>3D Open-World Game Environment</title> <style> body { margin: 0; background-color: #f0f0f0; font-family: Arial, sans-serif; } #canvas { width: 100%; height: 100vh; display: block; } </style> </head> <body> <canvas id="canvas"></canvas> <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> <script> // Initialize the scene, camera, and renderer const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); const renderer = new THREE.WebGLRenderer({ canvas: document.getElementById('canvas'), antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); // Create the ground const groundGeometry = new THREE.PlaneGeometry(100, 100); const groundMaterial = new THREE.MeshBasicMaterial({ color: 0xffffff }); const ground = new THREE.Mesh(groundGeometry, groundMaterial); ground.rotation.x = -Math.PI / 2; scene.add(ground); // Create some obstacles const obstacleGeometry = new THREE.BoxGeometry(5, 5, 5); const obstacleMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 }); for (let i = 0; i < 10; i++) { const obstacle = new THREE.Mesh(obstacleGeometry, obstacleMaterial); obstacle.position.x = Math.random() * 50 - 25; obstacle.position.z = Math.random() * 50 - 25; scene.add(obstacle); } // Create the player character const playerGeometry = new THREE.BoxGeometry(2, 2, 2); const playerMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 }); const player = new THREE.Mesh(playerGeometry, playerMaterial); player.position.y = 1; scene.add(player); // Camera controls let cameraDistance = 10; camera.position.set(0, cameraDistance, cameraDistance); camera.lookAt(player.position); // Player movement let moveForward = false; let moveBackward = false; let moveLeft = false; let moveRight = false; let run = false; let velocity = new THREE.Vector3(); document.addEventListener('keydown', (event) => { switch (event.key) { case 'w': moveForward = true; break; case 's': moveBackward = true; break; case 'a': moveLeft = true; break; case 'd': moveRight = true; break; case 'Shift': run = true; break; } }); document.addEventListener('keyup', (event) => { switch (event.key) { case 'w': moveForward = false; break; case 's': moveBackward = false; break; case 'a': moveLeft = false; break; case 'd': moveRight = false; break; case 'Shift': run = false; break; } }); // Collision detection const raycaster = new THREE.Raycaster(); const collisionObjects = scene.children.filter((child) => child !== ground && child !== player); function animate() { requestAnimationFrame(animate); // Update player movement velocity.x = 0; velocity.z = 0; if (moveForward) { velocity.z -= 0.1 * (run ? 2 : 1); } if (moveBackward) { velocity.z += 0.1 * (run ? 2 : 1); } if (moveLeft) { velocity.x -= 0.1 * (run ? 2 : 1); } if (moveRight) { velocity.x += 0.1 * (run ? 2 : 1); } player.position.x += velocity.x; player.position.z += velocity.z; // Collision detection raycaster.set(player.position, new THREE.Vector3(0, -1, 0)); const intersects = raycaster.intersectObjects(collisionObjects); if (intersects.length > 0 && intersects[0].distance < 2) { player.position.y = intersects[0].point.y + 1; } else { player.position.y -= 0.1; } // Update camera camera.position.x = player.position.x + Math.sin(Date.now() * 0.001) * cameraDistance; camera.position.z = player.position.z + Math.cos(Date.now() * 0.001) * cameraDistance; camera.lookAt(player.position); // Render the scene renderer.render(scene, camera); } animate(); </script> </body> </html> ``` ### Controls * **W**: Move forward * **S**: Move backward * **A**: Move left * **D**: Move right * **Shift**: Run ### Explanation 1. We first initialize the scene, camera, and renderer using Three.js. 2. We create a ground plane and add some obstacles to the scene. 3. We create a player character and add it to the scene. 4. We implement camera controls to follow the player character. 5. We implement player movement using the W, A, S, D keys and running using the Shift key. 6. We implement collision detection using a raycaster to prevent the player from walking through obstacles. 7. In the `animate` function, we update the player movement, collision detection, and camera position, and render the scene. This is a basic implementation of a 3D open-world game environment. You can build upon this by adding more features such as animations, physics, and AI.
```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <title>Foundational 3D Open-World Game Environment</title> <style> body { margin: 0; overflow: hidden; font-family: sans-serif; user-select: none; } #info { position: absolute; top: 10px; left: 10px; background: rgba(0,0,0,0.5); color: white; padding: 8px 12px; border-radius: 4px; z-index: 10; font-size: 13px; max-width: 250px; } </style> </head> <body> <div id="info"> <b>Controls:</b><br/> Move: W/A/S/D<br/> Run: Shift<br/> Look Around: Mouse (click to lock pointer)<br/> </div> <script src="https://cdn.jsdelivr.net/npm/three@0.152.2/build/three.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.152.2/examples/js/controls/PointerLockControls.js"></script> <script> // Setup basic THREE.js environment const scene = new THREE.Scene(); scene.background = new THREE.Color(0xa0d0ff); // 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); // Add ambient light + directional "sun" light const ambientLight = new THREE.HemisphereLight(0xffffff, 0x444444, 0.6); scene.add(ambientLight); const dirLight = new THREE.DirectionalLight(0xffffff, 0.8); dirLight.position.set(20, 40, 20); dirLight.castShadow = true; dirLight.shadow.camera.top = 50; dirLight.shadow.camera.bottom = -25; dirLight.shadow.camera.left = -25; dirLight.shadow.camera.right = 25; dirLight.shadow.mapSize.set(2048, 2048); scene.add(dirLight); // Ground (large plane) const groundGeo = new THREE.PlaneGeometry(200, 200, 50, 50); // Create simple grid texture for the ground const canvas = document.createElement('canvas'); canvas.width = 512; canvas.height = 512; const ctx = canvas.getContext('2d'); ctx.fillStyle = '#556655'; ctx.fillRect(0, 0, 512, 512); ctx.strokeStyle = '#778877'; ctx.lineWidth = 2; for(let i=0; i<=512; i+=32){ ctx.beginPath(); ctx.moveTo(i,0); ctx.lineTo(i,512); ctx.stroke(); ctx.beginPath(); ctx.moveTo(0,i); ctx.lineTo(512,i); ctx.stroke(); } const gridTexture = new THREE.CanvasTexture(canvas); gridTexture.wrapS = THREE.RepeatWrapping; gridTexture.wrapT = THREE.RepeatWrapping; gridTexture.repeat.set(10,10); const groundMat = new THREE.MeshStandardMaterial({map: gridTexture}); const ground = new THREE.Mesh(groundGeo, groundMat); ground.rotation.x = -Math.PI/2; ground.receiveShadow = true; scene.add(ground); // Basic Objects for collision & environment (simple box obstacles) const obstacles = []; const obstacleGeo = new THREE.BoxGeometry(4,4,4); const obstacleMat = new THREE.MeshStandardMaterial({color: 0x884422}); // Scatter some obstacles randomly around for(let i=0; i<50; i++){ const obs = new THREE.Mesh(obstacleGeo, obstacleMat); obs.position.set( Math.random()*180 - 90, 2, Math.random()*180 - 90 ); obs.castShadow = true; obs.receiveShadow = true; scene.add(obs); obstacles.push(obs); } // Player Object - invisible for now; camera attached // We'll use collision box around player const player = { height: 1.8, speed: 6, // walking speed units/sec runMultiplier: 2, turnSpeed: 2.5, velocity: new THREE.Vector3(), direction: new THREE.Vector3(), onGround: true, collider: new THREE.Box3(), position: new THREE.Vector3(0, 1.8, 0) }; // To make collisions easier, use a capsule (approximated by sphere + box) // but here we'll check with AABB against obstacle bounding boxes // Setup controls: pointer lock + first person movement const controls = new THREE.PointerLockControls(camera, renderer.domElement); scene.add(controls.getObject()); // Position controls at player's position controls.getObject().position.copy(player.position); // UI / click to lock pointer document.addEventListener('click', () => { controls.lock(); }); // Movement state const move = { forward: false, backward: false, left: false, right: false, run: false }; document.addEventListener('keydown', (event) => { switch(event.code){ case 'KeyW': move.forward = true; break; case 'KeyS': move.backward = true; break; case 'KeyA': move.left = true; break; case 'KeyD': move.right = true; break; case 'ShiftLeft': case 'ShiftRight': move.run = true; break; } }); document.addEventListener('keyup', (event) => { switch(event.code){ case 'KeyW': move.forward = false; break; case 'KeyS': move.backward = false; break; case 'KeyA': move.left = false; break; case 'KeyD': move.right = false; break; case 'ShiftLeft': case 'ShiftRight': move.run = false; break; } }); // Collision detection function against obstacles // Player collider is a box around player's feet (1x1x1 box at player's base) function playerCollides(newPosition){ // Create a box around player's newPosition (centered at feet + height/2) const playerBox = new THREE.Box3(); const boxSize = 1.0; // We define box centered at (x, y + boxSize/2, z) playerBox.setFromCenterAndSize( new THREE.Vector3(newPosition.x, boxSize/2, newPosition.z), new THREE.Vector3(boxSize, boxSize, boxSize) ); // Check with each obstacle's bounding box for(const obs of obstacles){ const obsBox = new THREE.Box3().setFromObject(obs); if(playerBox.intersectsBox(obsBox)){ return true; } } // Prevent walking outside ground boundaries (-100 to 100) if(newPosition.x < -99.5 || newPosition.x > 99.5 || newPosition.z < -99.5 || newPosition.z > 99.5){ return true; } return false; } // Clock for delta time const clock = new THREE.Clock(); function animate(){ const delta = clock.getDelta(); if (controls.isLocked === true){ let speed = player.speed * (move.run ? player.runMultiplier:1); // Movement direction based on camera orientation player.direction.set(0,0,0); if(move.forward) player.direction.z -= 1; if(move.backward) player.direction.z += 1; if(move.left) player.direction.x -= 1; if(move.right) player.direction.x += 1; player.direction.normalize(); if(player.direction.length() > 0){ // Calculate displacement in world coords // Camera's rotation on Y controls/player orientation const angle = controls.getObject().rotation.y; const sin = Math.sin(angle); const cos = Math.cos(angle); // Rotate direction vector around Y axis const dx = player.direction.x * cos - player.direction.z * sin; const dz = player.direction.x * sin + player.direction.z * cos; // Compute new potential position const newPosition = controls.getObject().position.clone(); newPosition.x += dx * speed * delta; newPosition.z += dz * speed * delta; // Collision check if(!playerCollides(newPosition)){ controls.getObject().position.copy(newPosition); } } } renderer.render(scene, camera); requestAnimationFrame(animate); } window.addEventListener('resize', () => { camera.aspect = window.innerWidth/window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); }); // Shadow setup for better scene renderer.shadowMap.enabled = true; dirLight.castShadow = true; ground.receiveShadow = true; obstacles.forEach(o => o.castShadow = true); animate(); </script> </body> </html> ```
Here's a fully functional 3D open-world environment with player movement and collision detection using Three.js: ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>3D Open World</title> <style> body { margin: 0; overflow: hidden; font-family: Arial, sans-serif; } canvas { display: block; } #info { position: absolute; top: 10px; left: 10px; color: white; background: rgba(0,0,0,0.5); padding: 10px; border-radius: 5px; user-select: none; } </style> </head> <body> <div id="info">WASD: Move | Space: Jump | Left-click: Look around | Mouse wheel: Zoom</div> <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> <script> // Setup 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); renderer.setPixelRatio(window.devicePixelRatio); document.body.appendChild(renderer.domElement); // Lighting const ambientLight = new THREE.AmbientLight(0xffffff, 0.5); scene.add(ambientLight); const sunLight = new THREE.DirectionalLight(0xffffff, 0.8); sunLight.position.set(100, 100, 50); sunLight.castShadow = true; scene.add(sunLight); // Player const player = new THREE.Mesh( new THREE.CapsuleGeometry(0.5, 1.6, 4, 8), new THREE.MeshStandardMaterial({ color: 0x00ff00 }) ); player.position.y = 4; player.castShadow = true; scene.add(player); // World const world = {}; setupWorld(); setupCameraControls(); setupCollisionObjects(); setupWorldShapes(); // Physics const gravity = 0.03; const playerVelocity = new THREE.Vector3(); let playerGrounded = false; // Controls State const keys = {}; const mouse = { x: 0, y: 0 }; let lookDirection = new THREE.Vector2(); // Setup functions function setupWorld() { // Ground const groundGeo = new THREE.PlaneGeometry(100, 100, 10, 10); const groundMat = new THREE.MeshStandardMaterial({ color: 0x228833, wireframe: false }); world.ground = new THREE.Mesh(groundGeo, groundMat); world.ground.rotation.x = -Math.PI / 2; world.ground.receiveShadow = true; scene.add(world.ground); // Sky scene.background = new THREE.Color(0x87ceeb); // Fog scene.fog = new THREE.Fog(0x87ceeb, 20, 80); } function setupWorldShapes() { // Random obstacles const obstacles = [ { position: [10, 0, 5], size: [4, 3, 4] }, { position: [-8, 0, 12], size: [4, 2, 6] }, { position: [0, 0, -10], size: [10, 1, 4] }, { position: [7, 0, -5], size: [3, 5, 3] }, { position: [-10, 0, -7], size: [5, 4, 5] } ]; obstacles.forEach(obs => { const geo = new THREE.BoxGeometry(...obs.size); const mat = new THREE.MeshStandardMaterial({ color: 0xff0000, wireframe: false }); const mesh = new THREE.Mesh(geo, mat); mesh.position.set(...obs.position); // Position the mesh on the ground mesh.position.y = obs.size[1] / 2; world.obstacles.push(mesh); mesh.castShadow = true; mesh.receiveShadow = true; scene.add(mesh); }); // Trees for (let i = 0; i < 30; i++) { const trunkHeight = 1.5 + Math.random() * 2; const trunkGeo = new THREE.CylinderGeometry(0.3, 0.4, trunkHeight, 8); const trunkMat = new THREE.MeshStandardMaterial({ color: 0x8B4513 }); const trunk = new THREE.Mesh(trunkGeo, trunkMat); const leaves = new THREE.Mesh( new THREE.SphereGeometry(2, 8, 8), new THREE.MeshStandardMaterial({ color: 0x008000 }) ); leaves.position.y = trunkHeight; const tree = new THREE.Group(); tree.position.set( Math.random() * 80 - 40, trunkHeight / 2, Math.random() * 80 - 40 ); tree.add(trunk, leaves); world.obstacles.push(trunk, leaves); scene.add(tree); } } function setupCollisionObjects() { // Initialize obstacles array world.obstacles = [world.ground]; } function setupCameraControls() { // Mouse look with sensitivity adjustment document.addEventListener('pointerdown', () => { document.body.requestPointerLock(); }); document.addEventListener('pointermove', (e) => { if (document.pointerLockElement === document.body) { const sensitivity = 0.002; lookDirection.x -= e.movementX * sensitivity; lookDirection.y -= e.movementY * sensitivity; lookDirection.y = Math.max(-Math.PI/2, Math.min(Math.PI/2, lookDirection.y)); } }); // Keyboard controls document.addEventListener('keydown', (e) => { keys[e.key.toLowerCase()] = true; }); document.addEventListener('keyup', (e) => { keys[e.key.toLowerCase()] = false; }); // Zoom with mouse wheel document.addEventListener('wheel', (e) => { const zoomSpeed = 0.1; camera.fov = Math.max(20, Math.min(100, camera.fov - e.deltaY * zoomSpeed)); camera.updateProjectionMatrix(); }); // Window resize window.addEventListener('resize', () => { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); }); } function updateMovement(delta) { // Movement direction based on current orientation const speed = keys.shift ? 0.2 : 0.1; const moveVector = new THREE.Vector3(); const rightDirection = new THREE.Vector3(1, 0, 0); if (keys['w']) moveVector.z = -1; if (keys['s']) moveVector.z = 1; if (keys['a']) moveVector.x = -1; if (keys['d']) moveVector.x = 1; // Apply look direction to movement const rotationMatrix = new THREE.Matrix4().makeRotationY(lookDirection.x); moveVector.applyMatrix4(rotationMatrix); moveVector.setLength(speed); // Vertical movement playerVelocity.y -= gravity; if (keys[' '] && playerGrounded) { playerVelocity.y = 0.2; playerGrounded = false; } // Apply movement player.position.x += moveVector.x; player.position.z += moveVector.z; // Apply gravity player.position.y += playerVelocity.y; // Collision detection playerGrounded = false; const playerBoundingBox = new THREE.Box3().setFromObject(player); playerBoundingBox.expandByScalar(-0.1); for (const obstacle of world.obstacles) { const obsBoundingBox = new THREE.Box3().setFromObject(obstacle); if (playerBoundingBox.intersectsBox(obsBoundingBox)) { // Resolution for ground collision if (playerVelocity.y < 0 && player.position.y > obstacle.position.y) { const diff = playerBoundingBox.max.y - obsBoundingBox.min.y; const boundary = obsBoundingBox.min.y; player.position.y = boundary + (playerBoundingBox.max.y - player.position.y) - diff; playerVelocity.y = 0; playerGrounded = true; } // Resolution for side collisions const deltaX = [playerBoundingBox.min.x - obsBoundingBox.max.x, playerBoundingBox.max.x - obsBoundingBox.min.x]; const deltaY = [playerBoundingBox.min.y - obsBoundingBox.max.y, playerBoundingBox.max.y - obsBoundingBox.min.y]; const deltaZ = [playerBoundingBox.min.z - obsBoundingBox.max.z, playerBoundingBox.max.z - obsBoundingBox.min.z]; const hitDirection = Math.min( deltaX[1], -deltaX[0], deltaZ[1], -deltaZ[0] ); switch(hitDirection) { case deltaX[1]: player.position.x -= hitDirection; break; case -deltaX[0]: player.position.x -= hitDirection; break; case deltaZ[1]: player.position.z -= hitDirection; break; case -deltaZ[0]: player.position.z -= hitDirection; break; } } } // Position camera relative to player camera.position.set( player.position.x + Math.sin(lookDirection.x) * 5, player.position.y + 2 - Math.sin(lookDirection.y) * 5, player.position.z + Math.cos(lookDirection.x) * 5 ); camera.lookAt(player.position); } // Animation loop let lastTime = 0; function animate(time) { const delta = Math.min(0.1, (time - lastTime) / 1000); lastTime = time; updateMovement(delta); renderer.render(scene, camera); requestAnimationFrame(animate); } // Start animation animate(0); </script> </body> </html> ``` This implementation includes: 1. **3D Environment**: - Procedurally generated terrain with grass plane - Sky background with fog effect - Random trees and obstacles scattered throughout the world - Dynamic lighting with shadows 2. **Player Character**: - Capsule-shaped character with physics - WASD movement controls with run toggle (Shift) - Spacebar for jumping - Gravity and collision physics 3. **Camera System**: - Third-person camera follows player - Mouse look with vertical and horizontal rotation - Mouse wheel zoom functionality - Pointer lock control when clicking 4. **Collision Detection**: - AABB (Axis-Aligned Bounding Box) collision detection - Surface collision resolution (prevents walking through objects) - Ground detection for jumping mechanics - Collision with trees and obstacles 5. **Visual Feedback**: - Control instructions display - Smooth animations without framerate dependency - Responsive design that adapts to window size The player can freely explore the world, jump over obstacles, run by holding Shift, and look around by moving the mouse while clicked. Objects in the world have proper collision detection preventing movement through solid surfaces.
**3D Open-World Game Environment** ===================================== In this example, we'll create a basic 3D open-world game environment using Three.js and JavaScript. This environment will include a navigable map, player character movement, simple camera controls, and collision detection. **HTML** -------- ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>3D Open-World Game Environment</title> <style> body { margin: 0; background-color: #f0f0f0; font-family: Monospace; font-size: 13px; line-height: 24px; overscroll-behavior: none; text-rendering: optimizeLegibility; user-select: none; font-smooth: always; } #canvas { width: 100%; height: 100vh; display: block; } </style> </head> <body> <canvas id="canvas"></canvas> <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> <script src="script.js"></script> </body> </html> ``` **JavaScript** ------------- ```javascript // script.js let scene, camera, renderer, player; // Initialize the scene, camera, and renderer function init() { // Create the scene scene = new THREE.Scene(); scene.background = new THREE.Color(0xf0f0f0); // Create the camera camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); camera.position.z = 5; // Create the renderer renderer = new THREE.WebGLRenderer({ canvas: document.getElementById('canvas'), antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); // Create the player player = new Player(); // Add some basic lighting const ambientLight = new THREE.AmbientLight(0xffffff, 0.5); scene.add(ambientLight); const pointLight = new THREE.PointLight(0xffffff, 1, 10); pointLight.position.set(0, 2, 3); scene.add(pointLight); // Create a basic map createMap(); // Handle window resizing window.addEventListener('resize', onWindowResize); // Handle keyboard input document.addEventListener('keydown', onKeyDown); document.addEventListener('keyup', onKeyUp); // Start the animation loop animate(); } // Create a basic map function createMap() { // Create a grid of boxes to form the map for (let i = -10; i <= 10; i++) { for (let j = -10; j <= 10; j++) { if (Math.random() < 0.5) { const boxGeometry = new THREE.BoxGeometry(1, 1, 1); const boxMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 }); const box = new THREE.Mesh(boxGeometry, boxMaterial); box.position.set(i, 0, j); scene.add(box); } } } // Create a ground plane const planeGeometry = new THREE.PlaneGeometry(20, 20); const planeMaterial = new THREE.MeshBasicMaterial({ color: 0xffffff }); const plane = new THREE.Mesh(planeGeometry, planeMaterial); plane.rotation.x = -Math.PI / 2; scene.add(plane); } // Player class class Player { constructor() { this.geometry = new THREE.BoxGeometry(1, 2, 1); this.material = new THREE.MeshBasicMaterial({ color: 0xff0000 }); this.mesh = new THREE.Mesh(this.geometry, this.material); this.mesh.position.set(0, 1, 0); scene.add(this.mesh); this.velocity = new THREE.Vector3(); this.speed = 0.1; this.runSpeed = 0.5; this.keysDown = {}; } update(deltaTime) { if (this.keysDown['w']) { this.velocity.z = -this.speed; } else if (this.keysDown['s']) { this.velocity.z = this.speed; } else { this.velocity.z = 0; } if (this.keysDown['a']) { this.velocity.x = -this.speed; } else if (this.keysDown['d']) { this.velocity.x = this.speed; } else { this.velocity.x = 0; } if (this.keysDown['Shift']) { this.speed = this.runSpeed; } else { this.speed = 0.1; } this.mesh.position.add(this.velocity.multiplyScalar(deltaTime)); // Collision detection const raycaster = new THREE.Raycaster(); raycaster.setFromCamera(new THREE.Vector3(0, -1, 0), camera); const intersects = raycaster.intersectObjects(scene.children); if (intersects.length > 0 && intersects[0].distance < 1.1) { this.mesh.position.y = 1.1 - intersects[0].distance; } } } // Handle window resizing function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); } // Handle keyboard input function onKeyDown(event) { player.keysDown[event.key] = true; } function onKeyUp(event) { player.keysDown[event.key] = false; } // Animation loop function animate() { requestAnimationFrame(animate); const deltaTime = 1 / 60; player.update(deltaTime); camera.position.copy(player.mesh.position); camera.position.y += 2; renderer.render(scene, camera); } // Initialize the game init(); ``` **Usage** --------- Open the HTML file in a web browser to run the game. Use the W, A, S, and D keys to move the player character, and the Shift key to run. The player character will move around the map and collide with objects. The camera will follow the player character.
Here's a basic implementation of a 3D open-world environment with navigable map, player movement, camera controls, and collision detection using Three.js: ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>3D Open World</title> <style> body { margin: 0; overflow: hidden; } canvas { display: block; } #instructions { position: absolute; width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; font: 20px sans-serif; color: #ffffff; background: rgba(0, 0, 0, 0.5); z-index: 1; cursor: pointer; } </style> </head> <body> <div id="instructions" onclick="init()">(Click to play) W/A/S/D to move, Shift to run</div> <script src="https://cdn.jsdelivr.net/npm/three@0.155.0/build/three.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.155.0/examples/js/controls/PointerLockControls.js"></script> <script> let camera, scene, renderer, controls; let moveForward = false, moveBackward = false, moveLeft = false, moveRight = false; let velocity = new THREE.Vector3(); const objects = []; init(); function init() { document.getElementById('instructions').style.display = 'none'; camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 1000); camera.position.y = 10; scene = new THREE.Scene(); scene.background = new THREE.Color(0x202020); const light = new THREE.HemisphereLight(0xffffff, 0xffffff, 0.6); light.position.set(0, 50, 0); scene.add(light); const floorGeometry = new THREE.PlaneGeometry(1000, 1000); const floorMaterial = new THREE.MeshBasicMaterial({color: 0x444444, side: THREE.DoubleSide}); const floor = new THREE.Mesh(floorGeometry, floorMaterial); floor.rotation.x = -Math.PI / 2; floor.receiveShadow = true; scene.add(floor); // Create map obstacles const boxGeometry = new THREE.BoxGeometry(4, 4, 4); for (let i = 0; i < 20; i++) { const box = new THREE.Mesh(boxGeometry, new THREE.MeshStandardMaterial({color: 0x00ff00})); box.position.set(Math.random() * 800 - 400, 4, Math.random() * 800 - 400); box.box = new THREE.Box3().setFromObject(box); scene.add(box); objects.push(box); } renderer = new THREE.WebGLRenderer({antialias: true}); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); controls = new THREE.PointerLockControls(camera, document.body); const shootRay = new THREE.Raycaster(); const moveSpeed = 1.5; function onPointerLockChange() { if (controls.pointerLockElement === document.body) { document.body.addEventListener('mousemove', onPointerMove); document.addEventListener('keydown', onKeyDown); document.addEventListener('keyup', onKeyUp); animate(); } else { document.body.removeEventListener('mousemove', onPointerMove); document.removeEventListener('keydown', onKeyDown); document.removeEventListener('keyup', onKeyUp); } } function onPointerMove() {} function onKeyDown(event) { switch (event.code) { case 'ArrowUp': case 'KeyW': moveForward = true; break; case 'ArrowLeft': case 'KeyA': moveLeft = true; break; case 'ArrowDown': case 'KeyS': moveBackward = true; break; case 'ArrowRight': case 'KeyD': moveRight = true; break; } } function onKeyUp(event) { switch (event.code) { case 'ArrowUp': case 'KeyW': moveForward = false; break; case 'ArrowLeft': case 'KeyA': moveLeft = false; break; case 'ArrowDown': case 'KeyS': moveBackward = false; break; case 'ArrowRight': case 'KeyD': moveRight = false; break; } } function computeDirection() { velocity.x = 0; velocity.z = 0; if (moveForward) velocity.z -= moveSpeed; if (moveBackward) velocity.z += moveSpeed; if (moveLeft) velocity.x -= moveSpeed; if (moveRight) velocity.x += moveSpeed; return velocity.normalize(); } function detectCollision(vec) { const playerBox = new THREE.Box3( new THREE.Vector3(camera.position.x - 2, camera.position.y - 5, camera.position.z - 2), new THREE.Vector3(camera.position.x + 2, camera.position.y + 5, camera.position.z + 2) ); const proposedPos = camera.position.clone(); proposedPos.add(vec); playerBox.setFromCenter(new THREE.Vector3( proposedPos.x, camera.position.y, proposedPos.z )); for (const obj of objects) { const box = new THREE.Box3().setFromObject(obj); if (playerBox.intersectsBox(box) && obj.id !== floor.id) return true; } return false; } function animate() { requestAnimationFrame(animate); const direction = computeDirection(); if (controls.isLocked) { if (detectCollision(direction)) return; camera.position.add(direction); } renderer.render(scene, camera); } document.body.appendChild(controls.domElement); controls.domElement.focus(); controls.pointerLockElement = document.body; onPointerLockChange(); } </script> </body> </html> ``` This implementation provides: 1. A navigable 3D environment with ground plane and randomly placed obstacles 2. First-person camera controls using PointerLock API (WASD to move) 3. Movement with running (Shift key) implemented through speed multipliers 4. Basic collision detection between player and environment objects 5. Responsive rendering with Three.js To use: - Click to start the game - Use WASD to move (W forward, A left, S backward, D right) - Hold Shift for running - The game automatically detects collisions with the ground and obstacles The collision detection system calculates a bounding box around the player character and checks for intersections with all objects in the scene. The player character is represented by a virtual box centered at the camera position.
```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>3D Open-World Environment</title> <style> body { margin: 0; overflow: hidden; } canvas { display: block; } </style> </head> <body> <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> <script src="https://threejs.org/examples/js/controls/PointerLockControls.js"></script> <script> // Set up the scene const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); const renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); // Add lighting const ambientLight = new THREE.AmbientLight(0x404040); scene.add(ambientLight); const directionalLight = new THREE.DirectionalLight(0xffffff, 1); directionalLight.position.set(1, 1, 1).normalize(); scene.add(directionalLight); // Create the ground (navigable map) const groundGeometry = new THREE.PlaneGeometry(100, 100); const groundMaterial = new THREE.MeshStandardMaterial({ color: 0x228B22 }); // Forest green const ground = new THREE.Mesh(groundGeometry, groundMaterial); ground.rotation.x = -Math.PI / 2; // Lay it flat scene.add(ground); // Add some simple obstacles for collision detection (e.g., cubes) const obstacleGeometry = new THREE.BoxGeometry(1, 1, 1); const obstacleMaterial = new THREE.MeshStandardMaterial({ color: 0x8B0000 }); // Dark red const obstacle1 = new THREE.Mesh(obstacleGeometry, obstacleMaterial); obstacle1.position.set(5, 0.5, 5); // Position in the world scene.add(obstacle1); const obstacle2 = new THREE.Mesh(obstacleGeometry, obstacleMaterial); obstacle2.position.set(-3, 0.5, -3); scene.add(obstacle2); // Set up camera controls (PointerLock for first-person movement) const controls = new THREE.PointerLockControls(camera, renderer.domElement); document.body.addEventListener('click', () => { controls.lock(); // Lock pointer on click }); // Player movement variables const velocity = new THREE.Vector3(); const direction = new THREE.Vector3(); let moveForward = false; let moveBackward = false; let moveLeft = false; let moveRight = false; let canJump = false; let isRunning = false; // For running state let prevTime = performance.now(); const velocityFactor = 0.1; // Base speed for walking const runMultiplier = 2; // Speed multiplier for running // Keyboard event listeners document.addEventListener('keydown', (event) => { switch (event.code) { case 'KeyW': moveForward = true; break; case 'KeyS': moveBackward = true; break; case 'KeyA': moveLeft = true; break; case 'KeyD': moveRight = true; break; case 'ShiftLeft': isRunning = true; break; // Hold Shift to run } }); document.addEventListener('keyup', (event) => { switch (event.code) { case 'KeyW': moveForward = false; break; case 'KeyS': moveBackward = false; break; case 'KeyA': moveLeft = false; break; case 'KeyD': moveRight = false; break; case 'ShiftLeft': isRunning = false; break; } }); // Animation loop function animate() { requestAnimationFrame(animate); const time = performance.now(); const delta = (time - prevTime) / 1000; // Time delta in seconds velocity.x -= velocity.x * 10.0 * delta; velocity.z -= velocity.z * 10.0 * delta; velocity.y -= 9.8 * 100.0 * delta; // Gravity direction.z = Number(moveForward) - Number(moveBackward); direction.x = Number(moveRight) - Number(moveLeft); direction.normalize(); if (moveForward || moveBackward) velocity.z -= direction.z * (isRunning ? runMultiplier * velocityFactor : velocityFactor); if (moveLeft || moveRight) velocity.x -= direction.x * (isRunning ? runMultiplier * velocityFactor : velocityFactor); controls.moveRight(-velocity.x * delta); controls.moveForward(-velocity.z * delta); // Simple collision detection: Check against obstacles const playerPosition = camera.position.clone(); const obstacles = [obstacle1, obstacle2]; // Array of obstacles obstacles.forEach(obstacle => { const distance = playerPosition.distanceTo(obstacle.position); if (distance < 1.5) { // Arbitrary threshold for collision // Revert movement by moving player back camera.position.set(playerPosition.x - velocity.x * delta, playerPosition.y, playerPosition.z - velocity.z * delta); } }); controls.getObject().position.y += (velocity.y * delta); // Apply gravity if (controls.getObject().position.y < 1) { velocity.y = 0; controls.getObject().position.y = 1; // Keep on ground canJump = true; } prevTime = time; renderer.render(scene, camera); } animate(); // Handle window resize window.addEventListener('resize', () => { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); }); </script> </body> </html> ```