Can you finish the project to make the whole thing give me a let me be able to walk around and feel what it's like
80c0783 verified | ```javascript | |
| // SquirrelVision 3D Playground - Main Game Script | |
| class SquirrelVisionPlayground { | |
| constructor() { | |
| this.scene = null; | |
| this.camera = null; | |
| this.renderer = null; | |
| this.controls = null; | |
| this.objects = []; | |
| this.isPlaying = false; | |
| this.moveForward = false; | |
| this.moveBackward = false; | |
| this.moveLeft = false; | |
| this.moveRight = false; | |
| this.canJump = false; | |
| this.prevTime = performance.now(); | |
| this.velocity = new THREE.Vector3(); | |
| this.direction = new THREE.Vector3(); | |
| this.interactiveObjects = []; | |
| this.init(); | |
| } | |
| async init() { | |
| // Update loading progress | |
| this.updateLoadingProgress(10, 'Creating 3D scene...'); | |
| // Create scene | |
| this.scene = new THREE.Scene(); | |
| this.scene.background = new THREE.Color(0x87CEEB); // Sky blue | |
| // Create camera | |
| this.camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); | |
| this.camera.position.set(0, 1.6, 0); | |
| // Create renderer | |
| this.renderer = new THREE.WebGLRenderer({ antialias: true }); | |
| this.renderer.setSize(window.innerWidth, window.innerHeight); | |
| this.renderer.shadowMap.enabled = true; | |
| this.renderer.shadowMap.type = THREE.PCFSoftShadowMap; | |
| // Add renderer to DOM | |
| document.getElementById('gameContainer').appendChild(this.renderer.domElement); | |
| this.updateLoadingProgress(30, 'Setting up lighting...'); | |
| await this.setupLighting(); | |
| this.updateLoadingProgress(50, 'Creating world geometry...'); | |
| await this.createWorld(); | |
| this.updateLoadingProgress(70, 'Adding interactive objects...'); | |
| await this.addInteractiveObjects(); | |
| this.updateLoadingProgress(90, 'Setting up controls...'); | |
| this.setupControls(); | |
| this.updateLoadingProgress(100, 'Ready!'); | |
| // Show start screen after a brief delay | |
| setTimeout(() => { | |
| document.getElementById('loadingScreen').style.display = 'none'; | |
| document.getElementById('startScreen').style.display = 'flex'; | |
| }, 500); | |
| // Start animation loop | |
| this.animate(); | |
| // Handle window resize | |
| window.addEventListener('resize', () => this.onWindowResize()); | |
| } | |
| updateLoadingProgress(percent, text) { | |
| document.getElementById('progressBar').style.width = percent + '%'; | |
| document.getElementById('loadingText').textContent = text; | |
| } | |
| async setupLighting() { | |
| // 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, 50, 50); | |
| directionalLight.castShadow = true; | |
| directionalLight.shadow.mapSize.width = 2048; | |
| directionalLight.shadow.mapSize.height = 2048; | |
| directionalLight.shadow.camera.near = 0.5; | |
| directionalLight.shadow.camera.far = 500; | |
| directionalLight.shadow.camera.left = -100; | |
| directionalLight.shadow.camera.right = 100; | |
| directionalLight.shadow.camera.top = 100; | |
| directionalLight.shadow.camera.bottom = -100; | |
| this.scene.add(directionalLight); | |
| // Hemisphere light for more natural outdoor lighting | |
| const hemisphereLight = new THREE.HemisphereLight(0x87CEEB, 0x8B4513, 0.4); | |
| this.scene.add(hemisphereLight); | |
| } | |
| async createWorld() { | |
| // Create ground | |
| const groundGeometry = new THREE.PlaneGeometry(100, 100); | |
| const groundMaterial = new THREE.MeshLambertMaterial({ | |
| color: 0x7CFC00, // Lawn green | |
| side: THREE.DoubleSide | |
| }); | |
| const ground = new THREE.Mesh(groundGeometry, groundMaterial); | |
| ground.rotation.x = -Math.PI / 2; | |
| ground.receiveShadow = true; | |
| this.scene.add(ground); | |
| // Add some grass texture variation | |
| const grassTexture = new THREE.MeshLambertMaterial({ | |
| color: 0x32CD32, // Lime green | |
| side: THREE.DoubleSide | |
| }); | |
| for (let i = 0; i < 200; i++) { | |
| const grassBlade = new THREE.Mesh( | |
| new THREE.PlaneGeometry(0.1, 0.5), | |
| grassTexture | |
| ); | |
| grassBlade.position.set( | |
| Math.random() * 80 - 40, | |
| 0.25, | |
| Math.random() * 80 - 40 | |
| ); | |
| grassBlade.rotation.x = -Math.PI / 2; | |
| grassBlade.rotation.z = Math.random() * Math.PI; | |
| this.scene.add(grassBlade); | |
| } | |
| // Create trees | |
| for (let i = 0; i < 15; i++) { | |
| this.createTree( | |
| Math.random() * 70 - 35, | |
| Math.random() * 70 - 35 | |
| ); | |
| } | |
| // Create buildings | |
| this.createBuilding(-15, 0, -15, 8, 12, 8, 0x888888); | |
| this.createBuilding(15, 0, 15, 6, 8, 6, 0x666666); | |
| this.createBuilding(-15, 0, 15, 5, 6, 5, 0x777777); | |
| // Create a central fountain | |
| this.createFountain(0, 0, 0); | |
| // Create some rocks | |
| for (let i = 0; i < 10; i++) { | |
| this.createRock( | |
| Math.random() * 60 - 30, | |
| Math.random() * 60 - 30 | |
| ); | |
| } | |
| } | |
| createTree(x, z) { | |
| // Tree trunk | |
| const trunkGeometry = new THREE.CylinderGeometry(0.3, 0.4, 4); | |
| const trunkMaterial = new THREE.MeshLambertMaterial({ color: 0x8B4513 }); | |
| const trunk = new THREE.Mesh(trunkGeometry, trunkMaterial); | |
| trunk.position.set(x, 2, z); | |
| trunk.castShadow = true; | |
| this.scene.add(trunk); | |
| // Tree leaves | |
| const leavesGeometry = new THREE.SphereGeometry(2); | |
| const leavesMaterial = new THREE.MeshLambertMaterial({ color: 0x228B22 }); | |
| const leaves = new THREE.Mesh(leavesGeometry, leavesMaterial); | |
| leaves.position.set(x, 5, z); | |
| leaves.castShadow = true; | |
| this.scene.add(leaves); | |
| } | |
| createBuilding(x, y, 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; | |
| this.scene.add(building); | |
| // Add windows | |
| const windowMaterial = new THREE.MeshLambertMaterial({ color: 0x87CEEB }); | |
| for (let i = 0; i < 3; i++) { | |
| for (let j = 0; j < 2; j++) { | |
| const window = new THREE.Mesh( | |
| new THREE.PlaneGeometry(0.8, 0.8), | |
| windowMaterial | |
| ); | |
| window.position.set( | |
| x + (j - 0.5) * (width - 1), | |
| y + 2 + i * 2.5, | |
| z + depth / 2 + 0.1 | |
| ); | |
| window.rotation.y = Math.PI; | |
| this.scene.add(window); | |
| } | |
| } | |
| } | |
| createFountain(x, y, z) { | |
| // Fountain base | |
| const baseGeometry = new THREE.CylinderGeometry(3, 3, 0.5, 32); | |
| const baseMaterial = new THREE.MeshLambertMaterial({ color: 0xCCCCCC }); | |
| const base = new THREE.Mesh(baseGeometry, baseMaterial); | |
| base.position.set(x, 0.25, z); | |
| base.receiveShadow = true; | |
| this.scene.add(base); | |
| // Fountain center | |
| const centerGeometry = new THREE.CylinderGeometry(1, 1.2, 2, 32); | |
| const centerMaterial = new THREE.MeshLambertMaterial({ color: 0xDDDDDD }); | |
| const center = new THREE.Mesh(centerGeometry, centerMaterial); | |
| center.position.set(x, 1.5, z); | |
| center.castShadow = true; | |
| this.scene.add(center); | |
| // Water (animated) | |
| const waterGeometry = new THREE.CylinderGeometry(1.2, 1.2, 0.2, 32); | |
| const waterMaterial = new THREE.MeshLambertMaterial({ | |
| color: 0x4F94CD, | |
| transparent: true, | |
| opacity: 0.7 | |
| }); | |
| const water = new THREE.Mesh(waterGeometry, waterMaterial); | |
| water.position.set(x, 1.1, z); | |
| water.userData = { type: 'fountain', animate: true }; | |
| this.scene.add(water); | |
| this.interactiveObjects.push(water); | |
| } | |
| createRock(x, z) { | |
| const geometry = new THREE.SphereGeometry(0.5 + Math.random() * 0.5, 6, 6); | |
| const material = new THREE.MeshLambertMaterial({ color: 0x696969 }); | |
| const rock = new THREE.Mesh(geometry, material); | |
| rock.position.set(x, 0.5, z); | |
| rock.rotation.set( | |
| Math.random() * Math.PI, | |
| Math.random() * Math.PI, | |
| Math.random() * Math.PI | |
| ); | |
| rock.castShadow = true; | |
| this.scene.add(rock); | |
| } | |
| async addInteractiveObjects() { | |
| // Create interactive squirrel NPC | |
| this.createSquirrel(10, 0, 0); | |
| // Create treasure chest | |
| this.createTreasureChest(-10, 0, 10); | |
| // Create signpost | |
| this.createSignpost(15, 0, -15); | |
| } | |
| createSquirrel(x, y, z) { | |
| // Squirrel body | |
| const bodyGeometry = new THREE.SphereGeometry(0.4, 16, 16); | |
| const bodyMaterial = new THREE.MeshLambertMaterial({ color: 0xFF8C00 }); | |
| const body = new THREE.Mesh(bodyGeometry, bodyMaterial); | |
| body.position.set(x, y + 0.4, z); | |
| // Squirrel head | |
| const headGeometry = new THREE.SphereGeometry(0.3, 16, 16); | |
| const head = new THREE.Mesh(headGeometry, bodyMaterial); | |
| head.position.set(x, y + 0.8, z + 0.3); | |
| // Squirrel tail | |
| const tailGeometry = new THREE.SphereGeometry(0.25, 16, 16); | |
| const tail = new THREE.Mesh(tailGeometry, bodyMaterial); | |
| tail.position.set(x, y + 0.4, z - 0.5); | |
| tail.scale.set(1.5, 0.8, 2); | |
| const squirrel = new THREE.Group(); | |
| squirrel.add(body); | |
| squirrel.add(head); | |
| squirrel.add(tail); | |
| squirrel.userData = { | |
| type: 'squirrel', | |
| interactive: true, | |
| message: "Hello! I'm your friendly squirrel guide. Explore the playground and find hidden treasures!" | |
| }; | |
| squirrel.castShadow = true; | |
| this.scene.add(squirrel); | |
| this.interactiveObjects.push(squirrel); | |
| } | |
| createTreasureChest(x, y, z) { | |
| const chestGeometry = new THREE.BoxGeometry(1.5, 0.8, 1); | |
| const chestMaterial = new THREE.MeshLambertMaterial({ color: 0xDAA520 }); |