270 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
		
		
			
		
	
	
			270 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
| 
								 | 
							
								<!DOCTYPE html>
							 | 
						||
| 
								 | 
							
								<html lang="en">
							 | 
						||
| 
								 | 
							
								<head>
							 | 
						||
| 
								 | 
							
								    <meta charset="UTF-8">
							 | 
						||
| 
								 | 
							
								    <meta name="viewport" content="width=device-width, initial-scale=1.0">
							 | 
						||
| 
								 | 
							
								    <title>Three.js City Walker</title>
							 | 
						||
| 
								 | 
							
								    <style>
							 | 
						||
| 
								 | 
							
								        body {
							 | 
						||
| 
								 | 
							
								            margin: 0;
							 | 
						||
| 
								 | 
							
								            overflow: hidden;
							 | 
						||
| 
								 | 
							
								            font-family: Arial, sans-serif;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        #info {
							 | 
						||
| 
								 | 
							
								            position: absolute;
							 | 
						||
| 
								 | 
							
								            top: 10px;
							 | 
						||
| 
								 | 
							
								            left: 10px;
							 | 
						||
| 
								 | 
							
								            color: white;
							 | 
						||
| 
								 | 
							
								            background: rgba(0,0,0,0.7);
							 | 
						||
| 
								 | 
							
								            padding: 10px;
							 | 
						||
| 
								 | 
							
								            border-radius: 5px;
							 | 
						||
| 
								 | 
							
								            font-size: 14px;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    </style>
							 | 
						||
| 
								 | 
							
								</head>
							 | 
						||
| 
								 | 
							
								<body>
							 | 
						||
| 
								 | 
							
								    <div id="info">
							 | 
						||
| 
								 | 
							
								        Use WASD or Arrow Keys to move<br>
							 | 
						||
| 
								 | 
							
								        Mouse to look around<br>
							 | 
						||
| 
								 | 
							
								        Click to lock pointer
							 | 
						||
| 
								 | 
							
								    </div>
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
							 | 
						||
| 
								 | 
							
								    <script>
							 | 
						||
| 
								 | 
							
								        // Scene setup
							 | 
						||
| 
								 | 
							
								        const scene = new THREE.Scene();
							 | 
						||
| 
								 | 
							
								        scene.background = new THREE.Color(0x87CEEB); // Sky blue
							 | 
						||
| 
								 | 
							
								        scene.fog = new THREE.Fog(0x87CEEB, 10, 500);
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        // Camera
							 | 
						||
| 
								 | 
							
								        const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
							 | 
						||
| 
								 | 
							
								        camera.position.set(0, 1.6, 10); // Eye height
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        // Renderer
							 | 
						||
| 
								 | 
							
								        const 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.AmbientLight(0xffffff, 0.6);
							 | 
						||
| 
								 | 
							
								        scene.add(ambientLight);
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
							 | 
						||
| 
								 | 
							
								        directionalLight.position.set(50, 100, 50);
							 | 
						||
| 
								 | 
							
								        directionalLight.castShadow = true;
							 | 
						||
| 
								 | 
							
								        directionalLight.shadow.camera.left = -100;
							 | 
						||
| 
								 | 
							
								        directionalLight.shadow.camera.right = 100;
							 | 
						||
| 
								 | 
							
								        directionalLight.shadow.camera.top = 100;
							 | 
						||
| 
								 | 
							
								        directionalLight.shadow.camera.bottom = -100;
							 | 
						||
| 
								 | 
							
								        directionalLight.shadow.camera.near = 0.1;
							 | 
						||
| 
								 | 
							
								        directionalLight.shadow.camera.far = 200;
							 | 
						||
| 
								 | 
							
								        directionalLight.shadow.mapSize.width = 2048;
							 | 
						||
| 
								 | 
							
								        directionalLight.shadow.mapSize.height = 2048;
							 | 
						||
| 
								 | 
							
								        scene.add(directionalLight);
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        // Ground
							 | 
						||
| 
								 | 
							
								        const groundGeometry = new THREE.PlaneGeometry(200, 200);
							 | 
						||
| 
								 | 
							
								        const groundMaterial = new THREE.MeshLambertMaterial({ color: 0x3a3a3a });
							 | 
						||
| 
								 | 
							
								        const ground = new THREE.Mesh(groundGeometry, groundMaterial);
							 | 
						||
| 
								 | 
							
								        ground.rotation.x = -Math.PI / 2;
							 | 
						||
| 
								 | 
							
								        ground.receiveShadow = true;
							 | 
						||
| 
								 | 
							
								        scene.add(ground);
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        // Create buildings
							 | 
						||
| 
								 | 
							
								        const buildings = [];
							 | 
						||
| 
								 | 
							
								        const buildingColors = [0x8B4513, 0x696969, 0x778899, 0x2F4F4F, 0x483D8B];
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        function createBuilding(x, z, width, height, depth, color) {
							 | 
						||
| 
								 | 
							
								            const geometry = new THREE.BoxGeometry(width, height, depth);
							 | 
						||
| 
								 | 
							
								            const material = new THREE.MeshLambertMaterial({ color: color });
							 | 
						||
| 
								 | 
							
								            const building = new THREE.Mesh(geometry, material);
							 | 
						||
| 
								 | 
							
								            building.position.set(x, height / 2, z);
							 | 
						||
| 
								 | 
							
								            building.castShadow = true;
							 | 
						||
| 
								 | 
							
								            building.receiveShadow = true;
							 | 
						||
| 
								 | 
							
								            scene.add(building);
							 | 
						||
| 
								 | 
							
								            buildings.push(building);
							 | 
						||
| 
								 | 
							
								            
							 | 
						||
| 
								 | 
							
								            // Add windows (simple emissive rectangles)
							 | 
						||
| 
								 | 
							
								            const windowMaterial = new THREE.MeshBasicMaterial({ color: 0xffff99, emissive: 0xffff99 });
							 | 
						||
| 
								 | 
							
								            const windowSize = 0.8;
							 | 
						||
| 
								 | 
							
								            const windowSpacing = 2;
							 | 
						||
| 
								 | 
							
								            
							 | 
						||
| 
								 | 
							
								            for (let floor = 1; floor < height / 3; floor++) {
							 | 
						||
| 
								 | 
							
								                for (let i = 0; i < width / windowSpacing - 1; i++) {
							 | 
						||
| 
								 | 
							
								                    const windowGeometry = new THREE.PlaneGeometry(windowSize, windowSize);
							 | 
						||
| 
								 | 
							
								                    const window1 = new THREE.Mesh(windowGeometry, windowMaterial);
							 | 
						||
| 
								 | 
							
								                    window1.position.set(x - width/2 + (i + 1) * windowSpacing, floor * 3, z + depth/2 + 0.01);
							 | 
						||
| 
								 | 
							
								                    scene.add(window1);
							 | 
						||
| 
								 | 
							
								                    
							 | 
						||
| 
								 | 
							
								                    const window2 = new THREE.Mesh(windowGeometry, windowMaterial);
							 | 
						||
| 
								 | 
							
								                    window2.position.set(x - width/2 + (i + 1) * windowSpacing, floor * 3, z - depth/2 - 0.01);
							 | 
						||
| 
								 | 
							
								                    window2.rotation.y = Math.PI;
							 | 
						||
| 
								 | 
							
								                    scene.add(window2);
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        // Create city layout
							 | 
						||
| 
								 | 
							
								        createBuilding(-20, -20, 10, 25, 10, buildingColors[0]);
							 | 
						||
| 
								 | 
							
								        createBuilding(20, -20, 8, 30, 8, buildingColors[1]);
							 | 
						||
| 
								 | 
							
								        createBuilding(-20, 20, 12, 20, 12, buildingColors[2]);
							 | 
						||
| 
								 | 
							
								        createBuilding(20, 20, 15, 35, 15, buildingColors[3]);
							 | 
						||
| 
								 | 
							
								        createBuilding(0, 0, 10, 40, 10, buildingColors[4]);
							 | 
						||
| 
								 | 
							
								        createBuilding(-40, 0, 8, 15, 8, buildingColors[0]);
							 | 
						||
| 
								 | 
							
								        createBuilding(40, 0, 10, 25, 10, buildingColors[1]);
							 | 
						||
| 
								 | 
							
								        createBuilding(0, -40, 12, 18, 12, buildingColors[2]);
							 | 
						||
| 
								 | 
							
								        createBuilding(0, 40, 8, 22, 8, buildingColors[3]);
							 | 
						||
| 
								 | 
							
								        createBuilding(-40, -40, 10, 28, 10, buildingColors[4]);
							 | 
						||
| 
								 | 
							
								        createBuilding(40, -40, 8, 20, 8, buildingColors[0]);
							 | 
						||
| 
								 | 
							
								        createBuilding(-40, 40, 15, 32, 15, buildingColors[1]);
							 | 
						||
| 
								 | 
							
								        createBuilding(40, 40, 10, 25, 10, buildingColors[2]);
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        // Add some street lights
							 | 
						||
| 
								 | 
							
								        function createStreetLight(x, z) {
							 | 
						||
| 
								 | 
							
								            const poleGeometry = new THREE.CylinderGeometry(0.1, 0.1, 4);
							 | 
						||
| 
								 | 
							
								            const poleMaterial = new THREE.MeshLambertMaterial({ color: 0x333333 });
							 | 
						||
| 
								 | 
							
								            const pole = new THREE.Mesh(poleGeometry, poleMaterial);
							 | 
						||
| 
								 | 
							
								            pole.position.set(x, 2, z);
							 | 
						||
| 
								 | 
							
								            pole.castShadow = true;
							 | 
						||
| 
								 | 
							
								            scene.add(pole);
							 | 
						||
| 
								 | 
							
								            
							 | 
						||
| 
								 | 
							
								            const lightGeometry = new THREE.SphereGeometry(0.3);
							 | 
						||
| 
								 | 
							
								            const lightMaterial = new THREE.MeshBasicMaterial({ color: 0xffffaa, emissive: 0xffffaa });
							 | 
						||
| 
								 | 
							
								            const lightBulb = new THREE.Mesh(lightGeometry, lightMaterial);
							 | 
						||
| 
								 | 
							
								            lightBulb.position.set(x, 4, z);
							 | 
						||
| 
								 | 
							
								            scene.add(lightBulb);
							 | 
						||
| 
								 | 
							
								            
							 | 
						||
| 
								 | 
							
								            const pointLight = new THREE.PointLight(0xffffaa, 0.5, 10);
							 | 
						||
| 
								 | 
							
								            pointLight.position.set(x, 4, z);
							 | 
						||
| 
								 | 
							
								            scene.add(pointLight);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        createStreetLight(-30, 0);
							 | 
						||
| 
								 | 
							
								        createStreetLight(30, 0);
							 | 
						||
| 
								 | 
							
								        createStreetLight(0, -30);
							 | 
						||
| 
								 | 
							
								        createStreetLight(0, 30);
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        // Movement controls
							 | 
						||
| 
								 | 
							
								        const moveSpeed = 0.1;
							 | 
						||
| 
								 | 
							
								        const lookSpeed = 0.002;
							 | 
						||
| 
								 | 
							
								        let moveForward = false;
							 | 
						||
| 
								 | 
							
								        let moveBackward = false;
							 | 
						||
| 
								 | 
							
								        let moveLeft = false;
							 | 
						||
| 
								 | 
							
								        let moveRight = false;
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        const velocity = new THREE.Vector3();
							 | 
						||
| 
								 | 
							
								        const direction = new THREE.Vector3();
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        // Pointer lock controls
							 | 
						||
| 
								 | 
							
								        let isPointerLocked = false;
							 | 
						||
| 
								 | 
							
								        const euler = new THREE.Euler(0, 0, 0, 'YXZ');
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        renderer.domElement.addEventListener('click', () => {
							 | 
						||
| 
								 | 
							
								            renderer.domElement.requestPointerLock();
							 | 
						||
| 
								 | 
							
								        });
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        document.addEventListener('pointerlockchange', () => {
							 | 
						||
| 
								 | 
							
								            isPointerLocked = document.pointerLockElement === renderer.domElement;
							 | 
						||
| 
								 | 
							
								        });
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        document.addEventListener('mousemove', (event) => {
							 | 
						||
| 
								 | 
							
								            if (!isPointerLocked) return;
							 | 
						||
| 
								 | 
							
								            
							 | 
						||
| 
								 | 
							
								            euler.setFromQuaternion(camera.quaternion);
							 | 
						||
| 
								 | 
							
								            euler.y -= event.movementX * lookSpeed;
							 | 
						||
| 
								 | 
							
								            euler.x -= event.movementY * lookSpeed;
							 | 
						||
| 
								 | 
							
								            euler.x = Math.max(-Math.PI / 2, Math.min(Math.PI / 2, euler.x));
							 | 
						||
| 
								 | 
							
								            camera.quaternion.setFromEuler(euler);
							 | 
						||
| 
								 | 
							
								        });
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        // Keyboard controls
							 | 
						||
| 
								 | 
							
								        document.addEventListener('keydown', (event) => {
							 | 
						||
| 
								 | 
							
								            switch (event.code) {
							 | 
						||
| 
								 | 
							
								                case 'KeyW':
							 | 
						||
| 
								 | 
							
								                case 'ArrowUp':
							 | 
						||
| 
								 | 
							
								                    moveForward = true;
							 | 
						||
| 
								 | 
							
								                    break;
							 | 
						||
| 
								 | 
							
								                case 'KeyS':
							 | 
						||
| 
								 | 
							
								                case 'ArrowDown':
							 | 
						||
| 
								 | 
							
								                    moveBackward = true;
							 | 
						||
| 
								 | 
							
								                    break;
							 | 
						||
| 
								 | 
							
								                case 'KeyA':
							 | 
						||
| 
								 | 
							
								                case 'ArrowLeft':
							 | 
						||
| 
								 | 
							
								                    moveLeft = true;
							 | 
						||
| 
								 | 
							
								                    break;
							 | 
						||
| 
								 | 
							
								                case 'KeyD':
							 | 
						||
| 
								 | 
							
								                case 'ArrowRight':
							 | 
						||
| 
								 | 
							
								                    moveRight = true;
							 | 
						||
| 
								 | 
							
								                    break;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        });
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        document.addEventListener('keyup', (event) => {
							 | 
						||
| 
								 | 
							
								            switch (event.code) {
							 | 
						||
| 
								 | 
							
								                case 'KeyW':
							 | 
						||
| 
								 | 
							
								                case 'ArrowUp':
							 | 
						||
| 
								 | 
							
								                    moveForward = false;
							 | 
						||
| 
								 | 
							
								                    break;
							 | 
						||
| 
								 | 
							
								                case 'KeyS':
							 | 
						||
| 
								 | 
							
								                case 'ArrowDown':
							 | 
						||
| 
								 | 
							
								                    moveBackward = false;
							 | 
						||
| 
								 | 
							
								                    break;
							 | 
						||
| 
								 | 
							
								                case 'KeyA':
							 | 
						||
| 
								 | 
							
								                case 'ArrowLeft':
							 | 
						||
| 
								 | 
							
								                    moveLeft = false;
							 | 
						||
| 
								 | 
							
								                    break;
							 | 
						||
| 
								 | 
							
								                case 'KeyD':
							 | 
						||
| 
								 | 
							
								                case 'ArrowRight':
							 | 
						||
| 
								 | 
							
								                    moveRight = false;
							 | 
						||
| 
								 | 
							
								                    break;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        });
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        // Window 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);
							 | 
						||
| 
								 | 
							
								            
							 | 
						||
| 
								 | 
							
								            // Update movement
							 | 
						||
| 
								 | 
							
								            direction.z = Number(moveForward) - Number(moveBackward);
							 | 
						||
| 
								 | 
							
								            direction.x = Number(moveRight) - Number(moveLeft);
							 | 
						||
| 
								 | 
							
								            direction.normalize();
							 | 
						||
| 
								 | 
							
								            
							 | 
						||
| 
								 | 
							
								            if (moveForward || moveBackward) {
							 | 
						||
| 
								 | 
							
								                velocity.z -= direction.z * moveSpeed;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            if (moveLeft || moveRight) {
							 | 
						||
| 
								 | 
							
								                velocity.x -= direction.x * moveSpeed;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            
							 | 
						||
| 
								 | 
							
								            // Apply movement with friction
							 | 
						||
| 
								 | 
							
								            velocity.x *= 0.9;
							 | 
						||
| 
								 | 
							
								            velocity.z *= 0.9;
							 | 
						||
| 
								 | 
							
								            
							 | 
						||
| 
								 | 
							
								            // Move camera
							 | 
						||
| 
								 | 
							
								            const moveVector = new THREE.Vector3();
							 | 
						||
| 
								 | 
							
								            moveVector.x = -velocity.x;
							 | 
						||
| 
								 | 
							
								            moveVector.z = -velocity.z;
							 | 
						||
| 
								 | 
							
								            moveVector.applyQuaternion(camera.quaternion);
							 | 
						||
| 
								 | 
							
								            
							 | 
						||
| 
								 | 
							
								            camera.position.add(moveVector);
							 | 
						||
| 
								 | 
							
								            
							 | 
						||
| 
								 | 
							
								            // Keep camera at eye height
							 | 
						||
| 
								 | 
							
								            camera.position.y = 1.6;
							 | 
						||
| 
								 | 
							
								            
							 | 
						||
| 
								 | 
							
								            renderer.render(scene, camera);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        animate();
							 | 
						||
| 
								 | 
							
								    </script>
							 | 
						||
| 
								 | 
							
								</body>
							 | 
						||
| 
								 | 
							
								</html>
							 |