Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>3D RPG Adventure</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.min.js"></script> | |
| <style> | |
| body { | |
| margin: 0; | |
| overflow: hidden; | |
| font-family: 'Arial', sans-serif; | |
| } | |
| #game-container { | |
| position: relative; | |
| width: 100vw; | |
| height: 100vh; | |
| } | |
| #ui { | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| padding: 20px; | |
| color: white; | |
| text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.7); | |
| pointer-events: none; | |
| z-index: 10; | |
| } | |
| #controls { | |
| position: absolute; | |
| bottom: 20px; | |
| right: 20px; | |
| z-index: 10; | |
| background: rgba(0, 0, 0, 0.5); | |
| padding: 10px; | |
| border-radius: 10px; | |
| color: white; | |
| } | |
| #start-screen { | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| background: rgba(0, 0, 0, 0.8); | |
| display: flex; | |
| flex-direction: column; | |
| justify-content: center; | |
| align-items: center; | |
| color: white; | |
| z-index: 20; | |
| } | |
| .btn { | |
| background: linear-gradient(135deg, #6e8efb, #a777e3); | |
| border: none; | |
| color: white; | |
| padding: 12px 24px; | |
| margin: 10px; | |
| border-radius: 30px; | |
| cursor: pointer; | |
| font-size: 18px; | |
| font-weight: bold; | |
| box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2); | |
| transition: all 0.3s ease; | |
| pointer-events: auto; | |
| } | |
| .btn:hover { | |
| transform: translateY(-3px); | |
| box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3); | |
| } | |
| .btn:active { | |
| transform: translateY(1px); | |
| } | |
| #health-bar { | |
| width: 200px; | |
| height: 20px; | |
| background: rgba(255, 0, 0, 0.3); | |
| border-radius: 10px; | |
| overflow: hidden; | |
| margin-bottom: 10px; | |
| } | |
| #health-progress { | |
| height: 100%; | |
| width: 100%; | |
| background: linear-gradient(90deg, #ff0000, #ff5e00); | |
| transition: width 0.3s; | |
| } | |
| #score { | |
| font-size: 24px; | |
| margin-bottom: 10px; | |
| } | |
| #game-title { | |
| font-size: 48px; | |
| margin-bottom: 30px; | |
| text-shadow: 0 0 10px #6e8efb, 0 0 20px #a777e3; | |
| animation: glow 2s infinite alternate; | |
| } | |
| @keyframes glow { | |
| from { | |
| text-shadow: 0 0 10px #6e8efb, 0 0 20px #a777e3; | |
| } | |
| to { | |
| text-shadow: 0 0 15px #6e8efb, 0 0 30px #a777e3, 0 0 40px #a777e3; | |
| } | |
| } | |
| #game-over-screen, #victory-screen { | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| background: rgba(0, 0, 0, 0.8); | |
| display: none; | |
| flex-direction: column; | |
| justify-content: center; | |
| align-items: center; | |
| color: white; | |
| z-index: 20; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div id="game-container"> | |
| <div id="ui"> | |
| <div id="health-bar"> | |
| <div id="health-progress"></div> | |
| </div> | |
| <div id="score">Score: 0</div> | |
| <div id="items">Items: 0/5</div> | |
| </div> | |
| <div id="controls"> | |
| <div>WASD: Move</div> | |
| <div>Space: Jump</div> | |
| <div>Mouse: Look around</div> | |
| </div> | |
| <div id="start-screen"> | |
| <h1 id="game-title">3D RPG Adventure</h1> | |
| <p class="mb-8">Explore the world, collect items, and avoid obstacles!</p> | |
| <button id="start-btn" class="btn">Start Game</button> | |
| </div> | |
| <div id="game-over-screen"> | |
| <h1 class="text-4xl font-bold mb-4">Game Over!</h1> | |
| <p id="final-score" class="text-2xl mb-8">Your score: 0</p> | |
| <button id="restart-btn" class="btn">Play Again</button> | |
| </div> | |
| <div id="victory-screen"> | |
| <h1 class="text-4xl font-bold mb-4">Victory!</h1> | |
| <p id="victory-score" class="text-2xl mb-8">Your score: 0</p> | |
| <button id="victory-restart-btn" class="btn">Play Again</button> | |
| </div> | |
| </div> | |
| <script> | |
| // Game variables | |
| let scene, camera, renderer, player, controls; | |
| let health = 100; | |
| let score = 0; | |
| let itemsCollected = 0; | |
| let itemsTotal = 5; | |
| let gameStarted = false; | |
| let obstacles = []; | |
| let collectibles = []; | |
| let keys = {}; | |
| let playerVelocity = new THREE.Vector3(); | |
| let playerDirection = new THREE.Vector3(); | |
| let clock = new THREE.Clock(); | |
| // Initialize the game | |
| function init() { | |
| // Create scene | |
| scene = new THREE.Scene(); | |
| scene.background = new THREE.Color(0x87CEEB); // Sky blue | |
| // Create camera | |
| camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); | |
| camera.position.set(0, 10, 20); | |
| // 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, 1); | |
| directionalLight.position.set(10, 20, 10); | |
| directionalLight.castShadow = true; | |
| directionalLight.shadow.mapSize.width = 2048; | |
| directionalLight.shadow.mapSize.height = 2048; | |
| scene.add(directionalLight); | |
| // Create ground | |
| const groundGeometry = new THREE.PlaneGeometry(100, 100); | |
| const groundMaterial = new THREE.MeshStandardMaterial({ | |
| color: 0x3a5f0b, | |
| roughness: 0.8 | |
| }); | |
| const ground = new THREE.Mesh(groundGeometry, groundMaterial); | |
| ground.rotation.x = -Math.PI / 2; | |
| ground.receiveShadow = true; | |
| scene.add(ground); | |
| // Create player | |
| const playerGeometry = new THREE.CapsuleGeometry(0.5, 1, 8, 16); | |
| const playerMaterial = new THREE.MeshStandardMaterial({ | |
| color: 0x4169E1, | |
| roughness: 0.3, | |
| metalness: 0.5 | |
| }); | |
| player = new THREE.Mesh(playerGeometry, playerMaterial); | |
| player.castShadow = true; | |
| player.position.y = 1; | |
| scene.add(player); | |
| // Add camera controls | |
| controls = new THREE.OrbitControls(camera, renderer.domElement); | |
| controls.target = player.position; | |
| controls.enableDamping = true; | |
| controls.dampingFactor = 0.05; | |
| // Create obstacles | |
| createObstacles(); | |
| // Create collectibles | |
| createCollectibles(); | |
| // Event listeners | |
| window.addEventListener('resize', onWindowResize); | |
| document.addEventListener('keydown', onKeyDown); | |
| document.addEventListener('keyup', onKeyUp); | |
| document.getElementById('start-btn').addEventListener('click', startGame); | |
| document.getElementById('restart-btn').addEventListener('click', resetGame); | |
| document.getElementById('victory-restart-btn').addEventListener('click', resetGame); | |
| // Start game loop | |
| animate(); | |
| } | |
| function createObstacles() { | |
| const obstacleGeometry = new THREE.BoxGeometry(3, 2, 3); | |
| const obstacleMaterial = new THREE.MeshStandardMaterial({ | |
| color: 0x8B4513, | |
| roughness: 0.7 | |
| }); | |
| for (let i = 0; i < 10; i++) { | |
| const obstacle = new THREE.Mesh(obstacleGeometry, obstacleMaterial); | |
| obstacle.castShadow = true; | |
| obstacle.receiveShadow = true; | |
| // Random position | |
| obstacle.position.x = (Math.random() - 0.5) * 80; | |
| obstacle.position.z = (Math.random() - 0.5) * 80; | |
| obstacle.position.y = 1; | |
| scene.add(obstacle); | |
| obstacles.push(obstacle); | |
| } | |
| } | |
| function createCollectibles() { | |
| const collectibleGeometry = new THREE.SphereGeometry(0.5, 16, 16); | |
| const collectibleMaterial = new THREE.MeshStandardMaterial({ | |
| color: 0xFFD700, | |
| roughness: 0.2, | |
| metalness: 0.8, | |
| emissive: 0xFFD700, | |
| emissiveIntensity: 0.5 | |
| }); | |
| for (let i = 0; i < itemsTotal; i++) { | |
| const collectible = new THREE.Mesh(collectibleGeometry, collectibleMaterial); | |
| collectible.castShadow = true; | |
| // Random position | |
| collectible.position.x = (Math.random() - 0.5) * 80; | |
| collectible.position.z = (Math.random() - 0.5) * 80; | |
| collectible.position.y = 0.5; | |
| scene.add(collectible); | |
| collectibles.push(collectible); | |
| } | |
| } | |
| function startGame() { | |
| document.getElementById('start-screen').style.display = 'none'; | |
| gameStarted = true; | |
| resetGame(); | |
| } | |
| function resetGame() { | |
| // Hide game over/victory screens | |
| document.getElementById('game-over-screen').style.display = 'none'; | |
| document.getElementById('victory-screen').style.display = 'none'; | |
| // Reset game state | |
| health = 100; | |
| score = 0; | |
| itemsCollected = 0; | |
| updateUI(); | |
| // Reset player position | |
| player.position.set(0, 1, 0); | |
| playerVelocity.set(0, 0, 0); | |
| // Reset collectibles | |
| collectibles.forEach(collectible => { | |
| collectible.visible = true; | |
| }); | |
| // Reset camera | |
| camera.position.set(0, 10, 20); | |
| controls.target.copy(player.position); | |
| } | |
| function updateUI() { | |
| document.getElementById('health-progress').style.width = `${health}%`; | |
| document.getElementById('score').textContent = `Score: ${score}`; | |
| document.getElementById('items').textContent = `Items: ${itemsCollected}/${itemsTotal}`; | |
| } | |
| function onWindowResize() { | |
| camera.aspect = window.innerWidth / window.innerHeight; | |
| camera.updateProjectionMatrix(); | |
| renderer.setSize(window.innerWidth, window.innerHeight); | |
| } | |
| function onKeyDown(event) { | |
| keys[event.code] = true; | |
| // Space to jump | |
| if (event.code === 'Space' && player.position.y <= 1.1) { | |
| playerVelocity.y = 5; | |
| } | |
| } | |
| function onKeyUp(event) { | |
| keys[event.code] = false; | |
| } | |
| function handlePlayerMovement(delta) { | |
| playerDirection.set(0, 0, 0); | |
| if (keys['KeyW']) playerDirection.z = -1; | |
| if (keys['KeyS']) playerDirection.z = 1; | |
| if (keys['KeyA']) playerDirection.x = -1; | |
| if (keys['KeyD']) playerDirection.x = 1; | |
| playerDirection.normalize(); | |
| // Apply movement relative to camera | |
| const angle = Math.atan2( | |
| camera.position.x - player.position.x, | |
| camera.position.z - player.position.z | |
| ); | |
| const moveX = playerDirection.x * Math.cos(angle) - playerDirection.z * Math.sin(angle); | |
| const moveZ = playerDirection.z * Math.cos(angle) + playerDirection.x * Math.sin(angle); | |
| playerVelocity.x = moveX * 10; | |
| playerVelocity.z = moveZ * 10; | |
| // Apply gravity | |
| playerVelocity.y -= 20 * delta; | |
| // Update position | |
| player.position.x += playerVelocity.x * delta; | |
| player.position.y += playerVelocity.y * delta; | |
| player.position.z += playerVelocity.z * delta; | |
| // Keep player on ground | |
| if (player.position.y < 1) { | |
| player.position.y = 1; | |
| playerVelocity.y = 0; | |
| } | |
| // Check collisions with obstacles | |
| obstacles.forEach(obstacle => { | |
| const distance = player.position.distanceTo(obstacle.position); | |
| if (distance < 2) { | |
| // Push player away from obstacle | |
| const direction = new THREE.Vector3().subVectors(player.position, obstacle.position).normalize(); | |
| player.position.add(direction.multiplyScalar(0.1)); | |
| // Reduce health | |
| health -= 0.5; | |
| updateUI(); | |
| if (health <= 0) { | |
| gameOver(); | |
| } | |
| } | |
| }); | |
| // Check collection of items | |
| collectibles.forEach((collectible, index) => { | |
| if (collectible.visible) { | |
| const distance = player.position.distanceTo(collectible.position); | |
| if (distance < 1.5) { | |
| collectible.visible = false; | |
| itemsCollected++; | |
| score += 100; | |
| updateUI(); | |
| if (itemsCollected >= itemsTotal) { | |
| victory(); | |
| } | |
| } | |
| } | |
| }); | |
| } | |
| function gameOver() { | |
| document.getElementById('final-score').textContent = `Your score: ${score}`; | |
| document.getElementById('game-over-screen').style.display = 'flex'; | |
| gameStarted = false; | |
| } | |
| function victory() { | |
| score += 1000; | |
| updateUI(); | |
| document.getElementById('victory-score').textContent = `Your score: ${score}`; | |
| document.getElementById('victory-screen').style.display = 'flex'; | |
| gameStarted = false; | |
| } | |
| function animate() { | |
| requestAnimationFrame(animate); | |
| const delta = clock.getDelta(); | |
| if (gameStarted) { | |
| handlePlayerMovement(delta); | |
| // Update camera to follow player | |
| controls.target.copy(player.position); | |
| camera.position.x = player.position.x; | |
| camera.position.z = player.position.z + 10; | |
| camera.position.y = player.position.y + 5; | |
| controls.update(); | |
| } | |
| // Rotate collectibles | |
| collectibles.forEach(collectible => { | |
| if (collectible.visible) { | |
| collectible.rotation.y += delta; | |
| } | |
| }); | |
| 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=CaffieneRats/rpg" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |