Download index.html from DevForML/3d-game: direct link, hf CLI and curl.
- Browser
- Download file 30.1 kB
-
https://huggingface.co/spaces/DevForML/3d-game/resolve/main/index.html
- Command line
-
hf download hf://spaces/DevForML/3d-game/index.html
-
curl -L -o index.html https://huggingface.co/spaces/DevForML/3d-game/resolve/main/index.html
30.1 kB
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Pixel Quest 3D: Find Your Friend</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> | |
| <style> | |
| body { | |
| margin: 0; | |
| overflow: hidden; | |
| font-family: 'Press Start 2P', cursive; | |
| image-rendering: pixelated; | |
| } | |
| canvas { | |
| display: block; | |
| image-rendering: pixelated; | |
| } | |
| @font-face { | |
| font-family: 'Press Start 2P'; | |
| src: url('https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap'); | |
| } | |
| .pixel-border { | |
| border: 4px solid #fff; | |
| box-shadow: 0 0 0 4px #000, inset 0 0 0 4px #000; | |
| } | |
| .quiz-option { | |
| transition: all 0.1s; | |
| border: 3px solid #fff; | |
| box-shadow: 0 0 0 3px #000; | |
| } | |
| .quiz-option:hover { | |
| background-color: #4a5568; | |
| transform: translateY(-2px); | |
| } | |
| .quiz-option.correct { | |
| background-color: #48bb78; | |
| } | |
| .quiz-option.wrong { | |
| background-color: #f56565; | |
| } | |
| .pixel-text { | |
| text-shadow: 3px 3px 0 #000, -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000; | |
| } | |
| .health-bar { | |
| height: 10px; | |
| background-color: #4a5568; | |
| border: 2px solid #000; | |
| } | |
| .health-fill { | |
| height: 100%; | |
| background-color: #48bb78; | |
| transition: width 0.3s; | |
| } | |
| /* New styles for better camera controls */ | |
| .controls-info { | |
| position: absolute; | |
| bottom: 20px; | |
| left: 50%; | |
| transform: translateX(-50%); | |
| background-color: rgba(0, 0, 0, 0.7); | |
| padding: 10px; | |
| border-radius: 5px; | |
| color: white; | |
| text-align: center; | |
| font-size: 12px; | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-900"> | |
| <div id="game-container" class="relative w-full h-screen"> | |
| <!-- Start Screen --> | |
| <div id="start-screen" class="absolute inset-0 flex flex-col items-center justify-center bg-gray-900 z-10"> | |
| <h1 class="text-4xl md:text-6xl text-green-400 mb-8 pixel-text">PIXEL QUEST 3D</h1> | |
| <p class="text-xl text-white mb-12 pixel-text">Find Your Lost Friend!</p> | |
| <div class="flex mb-8"> | |
| <div class="w-16 h-16 bg-red-500 pixel-border mr-4"></div> | |
| <div class="w-16 h-16 bg-blue-500 pixel-border"></div> | |
| </div> | |
| <button id="start-btn" class="px-8 py-4 bg-green-500 hover:bg-green-600 text-white font-bold pixel-border transition-all transform hover:scale-105"> | |
| START ADVENTURE | |
| </button> | |
| <div class="mt-8 text-white text-sm pixel-text"> | |
| Controls: WASD to move, Space to jump, F to attack | |
| </div> | |
| </div> | |
| <!-- Quiz Modal --> | |
| <div id="quiz-modal" class="hidden absolute inset-0 flex items-center justify-center z-20 bg-black bg-opacity-70"> | |
| <div class="bg-gray-800 p-6 rounded-lg w-11/12 max-w-md pixel-border"> | |
| <h2 id="quiz-question" class="text-xl text-white mb-6 pixel-text"></h2> | |
| <div id="quiz-options" class="space-y-3"></div> | |
| <div id="quiz-feedback" class="mt-4 text-yellow-300 hidden pixel-text"></div> | |
| </div> | |
| </div> | |
| <!-- End Screen --> | |
| <div id="end-screen" class="hidden absolute inset-0 flex flex-col items-center justify-center bg-gray-900 z-10"> | |
| <h1 id="end-title" class="text-4xl md:text-6xl text-green-400 mb-8 pixel-text">FRIEND FOUND!</h1> | |
| <div class="flex items-center mb-8"> | |
| <div class="w-32 h-32 bg-red-500 pixel-border mr-4"></div> | |
| <div class="w-32 h-32 bg-blue-500 pixel-border"></div> | |
| </div> | |
| <p id="final-score" class="text-xl text-white mb-12 pixel-text"></p> | |
| <button id="restart-btn" class="px-8 py-4 bg-green-500 hover:bg-green-600 text-white font-bold pixel-border transition-all transform hover:scale-105"> | |
| PLAY AGAIN | |
| </button> | |
| </div> | |
| <!-- Game Over Screen --> | |
| <div id="game-over-screen" class="hidden absolute inset-0 flex flex-col items-center justify-center bg-gray-900 z-10"> | |
| <h1 class="text-4xl md:text-6xl text-red-500 mb-8 pixel-text">GAME OVER</h1> | |
| <p id="game-over-score" class="text-xl text-white mb-12 pixel-text"></p> | |
| <button id="game-over-restart-btn" class="px-8 py-4 bg-green-500 hover:bg-green-600 text-white font-bold pixel-border transition-all transform hover:scale-105"> | |
| TRY AGAIN | |
| </button> | |
| </div> | |
| <!-- UI Elements --> | |
| <div id="ui-container" class="absolute top-4 left-4 z-10 hidden"> | |
| <div class="bg-gray-800 bg-opacity-70 p-3 rounded pixel-border"> | |
| <p class="text-white pixel-text">Score: <span id="score-display">0</span></p> | |
| <p class="text-white pixel-text">Quizzes: <span id="quiz-count">0</span>/4</p> | |
| <div class="mt-2"> | |
| <p class="text-white pixel-text mb-1">Health:</p> | |
| <div class="health-bar"> | |
| <div id="health-fill" class="health-fill" style="width: 100%"></div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Controls Info --> | |
| <div id="controls-info" class="controls-info hidden text-white pixel-text"> | |
| WASD: Move | SPACE: Jump | F: Attack Obstacles | |
| </div> | |
| </div> | |
| <script> | |
| // Game variables | |
| let scene, camera, renderer; | |
| let player, platforms = [], checkpoints = [], coins = [], obstacles = []; | |
| let playerVelocity = { x: 0, y: 0, z: 0 }; | |
| let playerSpeed = 0.2; | |
| let jumpForce = 0.4; | |
| let gravity = 0.02; | |
| let isJumping = false; | |
| let gamePaused = false; | |
| let score = 0; | |
| let quizzesCompleted = 0; | |
| let currentCheckpoint = null; | |
| let friendFound = false; | |
| let keys = {}; | |
| let worldWidth = 100; | |
| let worldDepth = 20; | |
| let friend; | |
| let playerHealth = 100; | |
| let lastObstacleHitTime = 0; | |
| let obstacleHitCooldown = 1000; // 1 second cooldown | |
| let playerDirection = new THREE.Vector3(); | |
| let cameraOffset = new THREE.Vector3(0, 3, -5); | |
| // Quiz questions | |
| const quizQuestions = [ | |
| { | |
| question: "What is your friend's favorite color?", | |
| options: ["Blue", "Green", "Red", "Yellow"], | |
| correct: 0 | |
| }, | |
| { | |
| question: "Where did you first meet your friend?", | |
| options: ["At school", "At the park", "At a party", "Online"], | |
| correct: 1 | |
| }, | |
| { | |
| question: "What's your friend's favorite food?", | |
| options: ["Pizza", "Sushi", "Burgers", "Tacos"], | |
| correct: 2 | |
| }, | |
| { | |
| question: "What was your last adventure together?", | |
| options: ["Hiking", "Movie marathon", "Road trip", "Camping"], | |
| correct: 3 | |
| } | |
| ]; | |
| // Initialize game | |
| function init() { | |
| // Scene setup | |
| scene = new THREE.Scene(); | |
| scene.background = new THREE.Color(0x87CEEB); // Sky blue | |
| // Camera (perspective for better 3D feel) | |
| const aspect = window.innerWidth / window.innerHeight; | |
| camera = new THREE.PerspectiveCamera(75, aspect, 0.1, 1000); | |
| camera.position.set(0, 5, 15); | |
| camera.lookAt(0, 0, 0); | |
| // Renderer | |
| renderer = new THREE.WebGLRenderer({ antialias: false }); | |
| renderer.setPixelRatio(window.devicePixelRatio > 1 ? 2 : 1); | |
| renderer.setSize(window.innerWidth, window.innerHeight); | |
| renderer.shadowMap.enabled = true; | |
| document.getElementById('game-container').appendChild(renderer.domElement); | |
| // Lighting | |
| const ambientLight = new THREE.AmbientLight(0xffffff, 0.5); | |
| scene.add(ambientLight); | |
| const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8); | |
| directionalLight.position.set(10, 20, 10); | |
| directionalLight.castShadow = true; | |
| directionalLight.shadow.mapSize.width = 1024; | |
| directionalLight.shadow.mapSize.height = 1024; | |
| scene.add(directionalLight); | |
| // Create world | |
| createWorld(); | |
| // Create player | |
| createPlayer(); | |
| // Create checkpoints | |
| createCheckpoints(); | |
| // Create obstacles | |
| createObstacles(); | |
| // Create friend | |
| createFriend(); | |
| // Event listeners | |
| window.addEventListener('resize', onWindowResize); | |
| document.addEventListener('keydown', onKeyDown); | |
| document.addEventListener('keyup', onKeyUp); | |
| // Start button | |
| document.getElementById('start-btn').addEventListener('click', startGame); | |
| document.getElementById('restart-btn').addEventListener('click', restartGame); | |
| document.getElementById('game-over-restart-btn').addEventListener('click', restartGame); | |
| } | |
| function createWorld() { | |
| // Ground | |
| const groundGeometry = new THREE.BoxGeometry(worldWidth, 1, worldDepth); | |
| const groundMaterial = new THREE.MeshStandardMaterial({ | |
| color: 0x2e8b57, | |
| roughness: 1.0 | |
| }); | |
| const ground = new THREE.Mesh(groundGeometry, groundMaterial); | |
| ground.position.y = -0.5; | |
| ground.receiveShadow = true; | |
| scene.add(ground); | |
| // Platforms | |
| const platformPositions = [ | |
| { x: 10, y: 2, z: 0, width: 8, depth: 8 }, | |
| { x: 25, y: 3, z: 5, width: 6, depth: 6 }, | |
| { x: 40, y: 4, z: -3, width: 10, depth: 10 }, | |
| { x: 60, y: 2, z: 2, width: 12, depth: 8 }, | |
| { x: 80, y: 5, z: 0, width: 8, depth: 8 } | |
| ]; | |
| platformPositions.forEach(pos => { | |
| const platformGeometry = new THREE.BoxGeometry(pos.width, 1, pos.depth); | |
| const platformMaterial = new THREE.MeshStandardMaterial({ | |
| color: 0x8b4513, | |
| roughness: 1.0 | |
| }); | |
| const platform = new THREE.Mesh(platformGeometry, platformMaterial); | |
| platform.position.set(pos.x, pos.y, pos.z); | |
| platform.receiveShadow = true; | |
| scene.add(platform); | |
| platforms.push(platform); | |
| }); | |
| // Coins | |
| for (let i = 0; i < 20; i++) { | |
| const coinGeometry = new THREE.CylinderGeometry(0.5, 0.5, 0.2, 16); | |
| const coinMaterial = new THREE.MeshStandardMaterial({ color: 0xffd700 }); | |
| const coin = new THREE.Mesh(coinGeometry, coinMaterial); | |
| // Position coins randomly above platforms or ground | |
| let x, y, z; | |
| if (Math.random() > 0.5) { | |
| // On platforms | |
| const platform = platforms[Math.floor(Math.random() * platforms.length)]; | |
| x = platform.position.x + (Math.random() - 0.5) * (platform.geometry.parameters.width - 2); | |
| y = platform.position.y + 1; | |
| z = platform.position.z + (Math.random() - 0.5) * (platform.geometry.parameters.depth - 2); | |
| } else { | |
| // On ground | |
| x = Math.random() * worldWidth; | |
| y = 0.5; | |
| z = (Math.random() - 0.5) * (worldDepth - 4); | |
| } | |
| coin.position.set(x, y, z); | |
| coin.rotation.x = Math.PI / 2; | |
| coin.userData.isCoin = true; | |
| scene.add(coin); | |
| coins.push(coin); | |
| } | |
| } | |
| function createPlayer() { | |
| // Simple pixel-style character | |
| const playerGeometry = new THREE.BoxGeometry(1, 2, 1); | |
| const playerMaterial = new THREE.MeshStandardMaterial({ color: 0xff0000 }); | |
| player = new THREE.Mesh(playerGeometry, playerMaterial); | |
| player.position.set(0, 1, 0); | |
| player.castShadow = true; | |
| scene.add(player); | |
| } | |
| function createCheckpoints() { | |
| // Create 4 checkpoints along the path | |
| const checkpointPositions = [ | |
| { x: 15, y: 0, z: 0 }, | |
| { x: 30, y: 0, z: 0 }, | |
| { x: 50, y: 0, z: 0 }, | |
| { x: 70, y: 0, z: 0 } | |
| ]; | |
| checkpointPositions.forEach((pos, index) => { | |
| const checkpointGeometry = new THREE.BoxGeometry(2, 2, 2); | |
| const checkpointMaterial = new THREE.MeshStandardMaterial({ | |
| color: 0xffff00, | |
| transparent: true, | |
| opacity: 0.7 | |
| }); | |
| const checkpoint = new THREE.Mesh(checkpointGeometry, checkpointMaterial); | |
| checkpoint.position.set(pos.x, pos.y + 1, pos.z); | |
| checkpoint.userData.isCheckpoint = true; | |
| checkpoint.userData.quizIndex = index; | |
| scene.add(checkpoint); | |
| checkpoints.push(checkpoint); | |
| }); | |
| } | |
| function createObstacles() { | |
| // Create destroyable obstacles | |
| const obstaclePositions = [ | |
| { x: 5, y: 1, z: 3, width: 1, height: 2, depth: 1 }, | |
| { x: 20, y: 1, z: -2, width: 1, height: 2, depth: 1 }, | |
| { x: 35, y: 1, z: 4, width: 1, height: 2, depth: 1 }, | |
| { x: 45, y: 1, z: -3, width: 1, height: 2, depth: 1 }, | |
| { x: 65, y: 1, z: 2, width: 1, height: 2, depth: 1 } | |
| ]; | |
| obstaclePositions.forEach(pos => { | |
| const obstacleGeometry = new THREE.BoxGeometry(pos.width, pos.height, pos.depth); | |
| const obstacleMaterial = new THREE.MeshStandardMaterial({ | |
| color: 0x8b0000, | |
| roughness: 1.0 | |
| }); | |
| const obstacle = new THREE.Mesh(obstacleGeometry, obstacleMaterial); | |
| obstacle.position.set(pos.x, pos.y + pos.height/2, pos.z); | |
| obstacle.castShadow = true; | |
| obstacle.receiveShadow = true; | |
| obstacle.userData.isObstacle = true; | |
| obstacle.userData.health = 30; // Obstacle health | |
| scene.add(obstacle); | |
| obstacles.push(obstacle); | |
| }); | |
| } | |
| function createFriend() { | |
| // Create the friend character at the end | |
| const friendGeometry = new THREE.BoxGeometry(1, 2, 1); | |
| const friendMaterial = new THREE.MeshStandardMaterial({ color: 0x0000ff }); | |
| friend = new THREE.Mesh(friendGeometry, friendMaterial); | |
| friend.position.set(worldWidth - 5, 1, 0); | |
| friend.castShadow = true; | |
| friend.userData.isFriend = true; | |
| scene.add(friend); | |
| } | |
| function startGame() { | |
| document.getElementById('start-screen').classList.add('hidden'); | |
| document.getElementById('ui-container').classList.remove('hidden'); | |
| document.getElementById('controls-info').classList.remove('hidden'); | |
| animate(); | |
| } | |
| function restartGame() { | |
| // Reset game state | |
| score = 0; | |
| quizzesCompleted = 0; | |
| friendFound = false; | |
| gamePaused = false; | |
| playerHealth = 100; | |
| // Reset player position | |
| player.position.set(0, 1, 0); | |
| playerVelocity = { x: 0, y: 0, z: 0 }; | |
| // Reset UI | |
| document.getElementById('score-display').textContent = '0'; | |
| document.getElementById('quiz-count').textContent = '0/4'; | |
| document.getElementById('health-fill').style.width = '100%'; | |
| document.getElementById('end-screen').classList.add('hidden'); | |
| document.getElementById('game-over-screen').classList.add('hidden'); | |
| document.getElementById('ui-container').classList.remove('hidden'); | |
| document.getElementById('controls-info').classList.remove('hidden'); | |
| // Clear and recreate all game objects | |
| clearScene(); | |
| createWorld(); | |
| createPlayer(); | |
| createCheckpoints(); | |
| createObstacles(); | |
| createFriend(); | |
| // Restart animation | |
| animate(); | |
| } | |
| function clearScene() { | |
| // Remove all objects from scene except lights and camera | |
| while(scene.children.length > 0) { | |
| const obj = scene.children[0]; | |
| if (!(obj instanceof THREE.Light) && !(obj instanceof THREE.Camera)) { | |
| scene.remove(obj); | |
| } | |
| } | |
| // Clear arrays | |
| platforms = []; | |
| checkpoints = []; | |
| coins = []; | |
| obstacles = []; | |
| } | |
| function showQuiz(quizIndex) { | |
| gamePaused = true; | |
| currentCheckpoint = quizIndex; | |
| const quiz = quizQuestions[quizIndex]; | |
| document.getElementById('quiz-question').textContent = quiz.question; | |
| const optionsContainer = document.getElementById('quiz-options'); | |
| optionsContainer.innerHTML = ''; | |
| quiz.options.forEach((option, i) => { | |
| const optionBtn = document.createElement('button'); | |
| optionBtn.className = 'w-full p-3 bg-gray-700 text-white quiz-option pixel-text'; | |
| optionBtn.textContent = option; | |
| optionBtn.addEventListener('click', () => checkAnswer(i)); | |
| optionsContainer.appendChild(optionBtn); | |
| }); | |
| document.getElementById('quiz-feedback').classList.add('hidden'); | |
| document.getElementById('quiz-modal').classList.remove('hidden'); | |
| } | |
| function checkAnswer(answerIndex) { | |
| const quiz = quizQuestions[currentCheckpoint]; | |
| const options = document.querySelectorAll('.quiz-option'); | |
| if (answerIndex === quiz.correct) { | |
| // Correct answer | |
| options[answerIndex].classList.add('correct'); | |
| document.getElementById('quiz-feedback').textContent = 'Correct!'; | |
| document.getElementById('quiz-feedback').classList.remove('hidden'); | |
| setTimeout(() => { | |
| quizzesCompleted++; | |
| document.getElementById('quiz-count').textContent = `${quizzesCompleted}/4`; | |
| document.getElementById('quiz-modal').classList.add('hidden'); | |
| gamePaused = false; | |
| // Remove the checkpoint | |
| const checkpoint = checkpoints[currentCheckpoint]; | |
| scene.remove(checkpoint); | |
| checkpoints[currentCheckpoint] = null; | |
| // Add score | |
| score += 100; | |
| document.getElementById('score-display').textContent = score; | |
| // Reset player velocity to prevent getting stuck | |
| playerVelocity.x = 0; | |
| playerVelocity.z = 0; | |
| }, 1000); | |
| } else { | |
| // Wrong answer | |
| options[answerIndex].classList.add('wrong'); | |
| options[quiz.correct].classList.add('correct'); | |
| document.getElementById('quiz-feedback').textContent = 'Try again!'; | |
| document.getElementById('quiz-feedback').classList.remove('hidden'); | |
| } | |
| } | |
| function onWindowResize() { | |
| const aspect = window.innerWidth / window.innerHeight; | |
| camera.aspect = aspect; | |
| camera.updateProjectionMatrix(); | |
| renderer.setSize(window.innerWidth, window.innerHeight); | |
| } | |
| function onKeyDown(event) { | |
| keys[event.code] = true; | |
| // Jump | |
| if (event.code === 'Space' && !isJumping && !gamePaused) { | |
| playerVelocity.y = jumpForce; | |
| isJumping = true; | |
| } | |
| // Attack (destroy obstacles) | |
| if (event.code === 'KeyF' && !gamePaused) { | |
| attackObstacle(); | |
| } | |
| } | |
| function onKeyUp(event) { | |
| keys[event.code] = false; | |
| } | |
| function attackObstacle() { | |
| const playerBox = new THREE.Box3().setFromObject(player); | |
| obstacles.forEach((obstacle, index) => { | |
| if (obstacle && new THREE.Box3().setFromObject(player).intersectsBox(new THREE.Box3().setFromObject(obstacle))) { | |
| // Reduce obstacle health | |
| obstacle.userData.health -= 10; | |
| // If obstacle is destroyed | |
| if (obstacle.userData.health <= 0) { | |
| scene.remove(obstacle); | |
| obstacles[index] = null; | |
| score += 50; | |
| document.getElementById('score-display').textContent = score; | |
| } | |
| } | |
| }); | |
| } | |
| function checkCollisions() { | |
| // Check platform collisions | |
| let onGround = false; | |
| const playerBox = new THREE.Box3().setFromObject(player); | |
| platforms.forEach(platform => { | |
| const platformBox = new THREE.Box3().setFromObject(platform); | |
| if (playerBox.intersectsBox(platformBox)) { | |
| // Check if player is on top of platform | |
| if (player.position.y > platform.position.y + 0.9) { | |
| player.position.y = platform.position.y + 1 + player.geometry.parameters.height / 2; | |
| playerVelocity.y = 0; | |
| isJumping = false; | |
| onGround = true; | |
| } | |
| } | |
| }); | |
| // Ground collision | |
| if (player.position.y <= 0) { | |
| player.position.y = 0; | |
| playerVelocity.y = 0; | |
| isJumping = false; | |
| onGround = true; | |
| } | |
| // Check coin collisions | |
| coins.forEach((coin, index) => { | |
| if (coin && new THREE.Box3().setFromObject(player).intersectsBox(new THREE.Box3().setFromObject(coin))) { | |
| scene.remove(coin); | |
| coins[index] = null; | |
| score += 10; | |
| document.getElementById('score-display').textContent = score; | |
| } | |
| }); | |
| // Check checkpoint collisions | |
| checkpoints.forEach((checkpoint, index) => { | |
| if (checkpoint && new THREE.Box3().setFromObject(player).intersectsBox(new THREE.Box3().setFromObject(checkpoint))) { | |
| showQuiz(index); | |
| } | |
| }); | |
| // Check obstacle collisions (damage player) | |
| const currentTime = Date.now(); | |
| obstacles.forEach(obstacle => { | |
| if (obstacle && new THREE.Box3().setFromObject(player).intersectsBox(new THREE.Box3().setFromObject(obstacle))) { | |
| if (currentTime - lastObstacleHitTime > obstacleHitCooldown) { | |
| playerHealth -= 10; | |
| lastObstacleHitTime = currentTime; | |
| document.getElementById('health-fill').style.width = `${playerHealth}%`; | |
| // Flash player red when hit | |
| player.material.color.setHex(0xff0000); | |
| setTimeout(() => { | |
| player.material.color.setHex(0xff0000); | |
| }, 200); | |
| // Check if player died | |
| if (playerHealth <= 0) { | |
| gameOver(); | |
| } | |
| } | |
| } | |
| }); | |
| // Check friend collision | |
| if (!friendFound && new THREE.Box3().setFromObject(player).intersectsBox(new THREE.Box3().setFromObject(friend)) && quizzesCompleted === 4) { | |
| friendFound = true; | |
| gamePaused = true; | |
| // Show end screen | |
| document.getElementById('final-score').textContent = `Final Score: ${score}`; | |
| document.getElementById('end-screen').classList.remove('hidden'); | |
| document.getElementById('ui-container').classList.add('hidden'); | |
| document.getElementById('controls-info').classList.add('hidden'); | |
| } | |
| return onGround; | |
| } | |
| function gameOver() { | |
| gamePaused = true; | |
| document.getElementById('game-over-score').textContent = `Score: ${score}`; | |
| document.getElementById('game-over-screen').classList.remove('hidden'); | |
| document.getElementById('ui-container').classList.add('hidden'); | |
| document.getElementById('controls-info').classList.add('hidden'); | |
| } | |
| function updateCamera() { | |
| // Calculate camera position based on player position and direction | |
| const targetPosition = new THREE.Vector3(); | |
| targetPosition.copy(player.position); | |
| // Apply offset relative to player's direction | |
| const offset = cameraOffset.clone(); | |
| // Apply the offset to the target position | |
| targetPosition.add(offset); | |
| // Smooth camera movement | |
| camera.position.lerp(targetPosition, 0.1); | |
| // Make camera look at player | |
| camera.lookAt(player.position); | |
| } | |
| function animate() { | |
| if (gamePaused) return; | |
| requestAnimationFrame(animate); | |
| // Player movement - now using WASD for full 3D movement | |
| const moveSpeed = playerSpeed; | |
| const moveVector = new THREE.Vector3(0, 0, 0); | |
| if (keys['KeyW'] || keys['ArrowUp']) { | |
| moveVector.z -= moveSpeed; | |
| } | |
| if (keys['KeyS'] || keys['ArrowDown']) { | |
| moveVector.z += moveSpeed; | |
| } | |
| if (keys['KeyA'] || keys['ArrowLeft']) { | |
| moveVector.x -= moveSpeed; | |
| } | |
| if (keys['KeyD'] || keys['ArrowRight']) { | |
| moveVector.x += moveSpeed; | |
| } | |
| // Normalize the vector to prevent faster diagonal movement | |
| if (moveVector.length() > 0) { | |
| moveVector.normalize().multiplyScalar(moveSpeed); | |
| // Rotate player to face movement direction | |
| if (moveVector.x !== 0 || moveVector.z !== 0) { | |
| player.rotation.y = Math.atan2(moveVector.x, moveVector.z); | |
| } | |
| } | |
| // Apply movement to velocity | |
| playerVelocity.x = moveVector.x; | |
| playerVelocity.z = moveVector.z; | |
| // Apply gravity | |
| playerVelocity.y -= gravity; | |
| // Update player position | |
| player.position.x += playerVelocity.x; | |
| player.position.y += playerVelocity.y; | |
| player.position.z += playerVelocity.z; | |
| // Check collisions | |
| checkCollisions(); | |
| // Update camera | |
| updateCamera(); | |
| renderer.render(scene, camera); | |
| } | |
| // Start the game | |
| init(); | |
| </script> | |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=DevForML/3d-game" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |