// Main game script document.addEventListener('DOMContentLoaded', () => { // Game variables let scene, camera, renderer, controls; let player, enemies = [], mazeWalls = [], exit; let moveDirection = { forward: 0, backward: 0, left: 0, right: 0 }; let isGameOver = false; let gameStarted = false; let playerSpeed = 0.1; let enemySpeed = 0.08; let mazeSize = 15; let cellSize = 3; let wallHeight = 2; // Initialize game init(); createMaze(); createPlayer(); createEnemies(3); createExit(); setupControls(); animate(); // Event listeners document.getElementById('restart-btn').addEventListener('click', restartGame); document.getElementById('next-level-btn').addEventListener('click', nextLevel); function init() { // Create scene scene = new THREE.Scene(); scene.background = new THREE.Color(0x111111); // Create camera camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); camera.position.set(0, 10, 15); camera.lookAt(0, 0, 0); // Create renderer renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); renderer.shadowMap.enabled = true; document.getElementById('game-container').appendChild(renderer.domElement); // Add lights const ambientLight = new THREE.AmbientLight(0x404040); scene.add(ambientLight); const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8); directionalLight.position.set(1, 1, 1); directionalLight.castShadow = true; scene.add(directionalLight); // Add fog scene.fog = new THREE.FogExp2(0x111111, 0.05); // Handle window resize window.addEventListener('resize', () => { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); }); } function createMaze() { // Simple maze generation - in a real game you'd implement a proper maze algorithm const wallGeometry = new THREE.BoxGeometry(cellSize, wallHeight, 0.2); const wallMaterial = new THREE.MeshStandardMaterial({ color: 0x880000 }); // Create outer walls const outerWalls = []; const outerSize = mazeSize * cellSize; // Floor const floorGeometry = new THREE.PlaneGeometry(outerSize, outerSize); const floorMaterial = new THREE.MeshStandardMaterial({ color: 0x333333 }); const floor = new THREE.Mesh(floorGeometry, floorMaterial); floor.rotation.x = -Math.PI / 2; floor.receiveShadow = true; scene.add(floor); // Ceiling const ceiling = new THREE.Mesh(floorGeometry, floorMaterial); ceiling.rotation.x = Math.PI / 2; ceiling.position.y = wallHeight; scene.add(ceiling); // Inner maze walls (simplified) for (let i = 0; i < mazeSize - 1; i++) { for (let j = 0; j < mazeSize - 1; j++) { if (Math.random() > 0.7) { const wall = new THREE.Mesh(wallGeometry, wallMaterial); wall.position.set( (i - mazeSize/2) * cellSize + cellSize/2, wallHeight/2, (j - mazeSize/2) * cellSize + cellSize/2 ); wall.castShadow = true; wall.receiveShadow = true; scene.add(wall); mazeWalls.push(wall); // Randomly rotate some walls if (Math.random() > 0.5) { wall.rotation.y = Math.PI / 2; } } } } } function createPlayer() { const geometry = new THREE.CylinderGeometry(0.5, 0.5, 1.8, 16); const material = new THREE.MeshStandardMaterial({ color: 0x00ff00 }); player = new THREE.Mesh(geometry, material); player.position.set(0, 1, 0); player.castShadow = true; player.receiveShadow = true; scene.add(player); // Add a small directional light to the player to make them stand out const playerLight = new THREE.PointLight(0x00ff00, 1, 5); player.add(playerLight); } function createEnemies(count) { const geometry = new THREE.CylinderGeometry(0.5, 0.5, 1.8, 16); const material = new THREE.MeshStandardMaterial({ color: 0xff0000 }); for (let i = 0; i < count; i++) { const enemy = new THREE.Mesh(geometry, material); // Position enemies at random locations in the maze enemy.position.set( (Math.random() - 0.5) * mazeSize * cellSize * 0.8, 1, (Math.random() - 0.5) * mazeSize * cellSize * 0.8 ); enemy.castShadow = true; enemy.receiveShadow = true; // Add red glow const enemyLight = new THREE.PointLight(0xff0000, 1, 5); enemy.add(enemyLight); scene.add(enemy); enemies.push(enemy); } } function createExit() { const exitGeometry = new THREE.BoxGeometry(cellSize, 0.1, cellSize); const exitMaterial = new THREE.MeshStandardMaterial({ color: 0xffff00 }); exit = new THREE.Mesh(exitGeometry, exitMaterial); // Position exit at a random edge of the maze const side = Math.floor(Math.random() * 4); let x, z; switch(side) { case 0: // top x = (Math.random() - 0.5) * mazeSize * cellSize * 0.8; z = -mazeSize * cellSize / 2; break; case 1: // right x = mazeSize * cellSize / 2; z = (Math.random() - 0.5) * mazeSize * cellSize * 0.8; break; case 2: // bottom x = (Math.random() - 0.5) * mazeSize * cellSize * 0.8; z = mazeSize * cellSize / 2; break; case 3: // left x = -mazeSize * cellSize / 2; z = (Math.random() - 0.5) * mazeSize * cellSize * 0.8; break; } exit.position.set(x, 0.05, z); scene.add(exit); } function setupControls() { const forwardBtn = document.getElementById('forward-btn'); const backwardBtn = document.getElementById('backward-btn'); const leftBtn = document.getElementById('left-btn'); const rightBtn = document.getElementById('right-btn'); // Touch start events forwardBtn.addEventListener('touchstart', (e) => { e.preventDefault(); moveDirection.forward = 1; gameStarted = true; }); backwardBtn.addEventListener('touchstart', (e) => { e.preventDefault(); moveDirection.backward = 1; gameStarted = true; }); leftBtn.addEventListener('touchstart', (e) => { e.preventDefault(); moveDirection.left = 1; gameStarted = true; }); rightBtn.addEventListener('touchstart', (e) => { e.preventDefault(); moveDirection.right = 1; gameStarted = true; }); // Touch end events forwardBtn.addEventListener('touchend', (e) => { e.preventDefault(); moveDirection.forward = 0; }); backwardBtn.addEventListener('touchend', (e) => { e.preventDefault(); moveDirection.backward = 0; }); leftBtn.addEventListener('touchend', (e) => { e.preventDefault(); moveDirection.left = 0; }); rightBtn.addEventListener('touchend', (e) => { e.preventDefault(); moveDirection.right = 0; }); } function update() { if (!gameStarted || isGameOver) return; // Player movement const moveForward = moveDirection.forward - moveDirection.backward; const moveSideways = moveDirection.right - moveDirection.left; // Apply movement const speed = playerSpeed; player.position.x -= Math.sin(player.rotation.y) * moveForward * speed; player.position.z -= Math.cos(player.rotation.y) * moveForward * speed; // Apply rotation player.rotation.y -= moveSideways * speed * 0.5; // Simple collision detection with walls const playerRadius = 0.5; for (const wall of mazeWalls) { const wallBox = new THREE.Box3().setFromObject(wall); const playerBox = new THREE.Box3( new THREE.Vector3(player.position.x - playerRadius, 0, player.position.z - playerRadius), new THREE.Vector3(player.position.x + playerRadius, 2, player.position.z + playerRadius) ); if (wallBox.intersectsBox(playerBox)) { // Push player back player.position.x -= moveDirection.x * speed * 2; player.position.z -= moveDirection.z * speed * 2; break; } } // Enemy movement for (const enemy of enemies) { // Simple AI: move toward player const direction = new THREE.Vector3( player.position.x - enemy.position.x, 0, player.position.z - enemy.position.z ).normalize(); enemy.position.x += direction.x * enemySpeed; enemy.position.z += direction.z * enemySpeed; // Simple collision detection between enemies and walls const enemyRadius = 0.5; for (const wall of mazeWalls) { const wallBox = new THREE.Box3().setFromObject(wall); const enemyBox = new THREE.Box3( new THREE.Vector3(enemy.position.x - enemyRadius, 0, enemy.position.z - enemyRadius), new THREE.Vector3(enemy.position.x + enemyRadius, 2, enemy.position.z + enemyRadius) ); if (wallBox.intersectsBox(enemyBox)) { // Push enemy back enemy.position.x -= direction.x * enemySpeed * 2; enemy.position.z -= direction.z * enemySpeed * 2; break; } } // Check for collision with player const distanceToPlayer = Math.sqrt( Math.pow(player.position.x - enemy.position.x, 2) + Math.pow(player.position.z - enemy.position.z, 2) ); if (distanceToPlayer < 1) { gameOver(); } } // Check if player reached exit const exitBox = new THREE.Box3().setFromObject(exit); const playerBox = new THREE.Box3( new THREE.Vector3(player.position.x - 0.5, 0, player.position.z - 0.5), new THREE.Vector3(player.position.x + 0.5, 2, player.position.z + 0.5) ); if (exitBox.intersectsBox(playerBox)) { winGame(); } // Update camera position to follow player camera.position.x = player.position.x; camera.position.z = player.position.z + 5; camera.lookAt(player.position.x, player.position.y, player.position.z); } function gameOver() { isGameOver = true; document.getElementById('game-over').classList.remove('hidden'); } function winGame() { isGameOver = true; document.getElementById('win-screen').classList.remove('hidden'); } function restartGame() { // Reset player and enemies player.position.set(0, 1, 0); enemies.forEach(enemy => { enemy.position.set( (Math.random() - 0.5) * mazeSize * cellSize * 0.8, 1, (Math.random() - 0.5) * mazeSize * cellSize * 0.8 ); }); // Reset UI document.getElementById('game-over').classList.add('hidden'); document.getElementById('win-screen').classList.add('hidden'); isGameOver = false; gameStarted = false; } function nextLevel() { // Clear current maze and enemies mazeWalls.forEach(wall => scene.remove(wall)); mazeWalls = []; enemies.forEach(enemy => scene.remove(enemy)); enemies = []; scene.remove(exit); // Increase difficulty mazeSize += 2; enemySpeed += 0.01; // Create new level createMaze(); createEnemies(3 + Math.floor(mazeSize / 5)); createExit(); // Reset player player.position.set(0, 1, 0); // Reset UI document.getElementById('win-screen').classList.add('hidden'); isGameOver = false; gameStarted = false; } function animate() { requestAnimationFrame(animate); update(); renderer.render(scene, camera); } });