function initThreeJS() {
// 1. Scene
scene = new THREE.Scene();
scene.background = new THREE.Color(0x87ceeb); // Sky blue background
// 2. Camera
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 10, 20); // Initial camera position
// 3. Renderer
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio); // Handle retina displays
renderer.domElement.id = 'threejs-canvas'; // Add an ID for CSS styling
document.body.appendChild(renderer.domElement);
// 4. OrbitControls (for debugging camera, remove or comment out for game-like experience)
// controls = new THREE.OrbitControls(camera, renderer.domElement);
// controls.enableDamping = true; // Smooth camera movement
// controls.dampingFactor = 0.05;
// controls.screenSpacePanning = false;
// controls.maxPolarAngle = Math.PI / 2; // Prevent camera from going below ground
// 5. Lighting
const ambientLight = new THREE.AmbientLight(0x404040); // Soft white light
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 1); // White directional light
directionalLight.position.set(5, 10, 7);
directionalLight.castShadow = true; // Enable shadows
directionalLight.shadow.mapSize.width = 1024; // Default 512
directionalLight.shadow.mapSize.height = 1024; // Default 512
directionalLight.shadow.camera.near = 0.5; // Default 0.5
directionalLight.shadow.camera.far = chunkSize * visibleRadius * 2; // Adjust far plane for visible chunks
directionalLight.shadow.camera.left = -chunkSize * visibleRadius; // Adjust frustum for visible chunks
directionalLight.shadow.camera.right = chunkSize * visibleRadius;
directionalLight.shadow.camera.top = chunkSize * visibleRadius;
directionalLight.shadow.camera.bottom = -chunkSize * visibleRadius;
scene.add(directionalLight);
// --- Create Reusable Geometries and Materials Here (ONE TIME!) ---
groundGeometry = new THREE.PlaneGeometry(chunkSize, chunkSize);
roadGeometry = new THREE.PlaneGeometry(10, chunkSize);
sirenLightGeometry = new THREE.CylinderGeometry(0.2, 0.2, 0.5, 8);
groundMaterial = new THREE.MeshLambertMaterial({ color: 0x33aa33 });
roadMaterial = new THREE.MeshLambertMaterial({ color: 0x555555 });
buildingMaterial = new THREE.MeshLambertMaterial({ color: 0xaaaaaa });
npcMaterial = new THREE.MeshLambertMaterial({ color: 0x00ff00 });
redSirenMaterial = new THREE.MeshLambertMaterial({ color: 0xff0000 });
blueSirenMaterial = new THREE.MeshLambertMaterial({ color: 0x0000ff });
// Car parts
carBodyGeometry = new THREE.BoxGeometry(3, 1, 1.5);
carBodyMaterial = new THREE.MeshLambertMaterial({ color: 0xff0000 });
carCabinGeometry = new THREE.BoxGeometry(2, 0.8, 1.2);
carCabinMaterial = new THREE.MeshLambertMaterial({ color: 0x3333ff });
carWheelGeometry = new THREE.CylinderGeometry(0.5, 0.5, 0.4, 16);
carWheelMaterial = new THREE.MeshLambertMaterial({ color: 0x000000 });
// Cop Car parts (reusing some car geometries/materials)
copCarBodyGeometry = new THREE.BoxGeometry(3, 1, 1.5); // Same as car body
copCarBodyMaterial = new THREE.MeshLambertMaterial({ color: 0x0000ff }); // Blue body
copCarCabinGeometry = new THREE.BoxGeometry(2, 0.8, 1.2); // Same as car cabin
copCarCabinMaterial = new THREE.MeshLambertMaterial({ color: 0x3333ff }); // Blue cabin
copCarWheelGeometry = new THREE.CylinderGeometry(0.5, 0.5, 0.4, 16); // Same as car wheel
copCarWheelMaterial = new THREE.MeshLambertMaterial({ color: 0x000000 }); // Black wheels
// Player parts
playerBodyGeometry = new THREE.CylinderGeometry(0.5, 0.5, 1.8, 16);
playerBodyMaterial = new THREE.MeshLambertMaterial({ color: 0x00ff00 });
// Gun parts
gunGeometry = new THREE.CylinderGeometry(0.1, 0.2, 0.8, 8);
gunMaterial = new THREE.MeshLambertMaterial({ color: 0x333333 });
// Tank parts
tankBodyGeometry = new THREE.BoxGeometry(4, 2, 2.5);
tankBodyMaterial = new THREE.MeshLambertMaterial({ color: 0x444444 }); // Dark grey/green for tank
turretGeometry = new THREE.CylinderGeometry(0.8, 1.2, 1, 16);
turretMaterial = new THREE.MeshLambertMaterial({ color: 0x666666 });
cannonGeometry = new THREE.CylinderGeometry(0.3, 0.4, 2, 8);
cannonMaterial = new THREE.MeshLambertMaterial({ color: 0x333333 });
tankWheelGeometry = new THREE.BoxGeometry(0.8, 1.5, 0.5); // Tank tracks
// 6. Create Car (using reusable parts)
createCar();
// 7. Create On-Foot Player and Gun (using reusable parts)
createPlayer();
createGun(); // Create the gun and attach it to the player's view
// 8. Position player at car's initial position
player.position.copy(car.position);
player.visible = false; // Player is initially driving
// 9. Initial chunk generation
updateChunks();
// 10. Add Keyboard and Mouse Event Listeners for Car & Player Control
document.addEventListener('keydown', onKeyDown);
document.addEventListener('keyup', onKeyUp);
document.addEventListener('mousedown', onMouseDown); // For shooting
}