Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Blank Wall 3D Game</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/PointerLockControls.js"></script> | |
| <style> | |
| body { | |
| margin: 0; | |
| overflow: hidden; | |
| font-family: 'Arial', sans-serif; | |
| } | |
| #container { | |
| position: relative; | |
| width: 100%; | |
| height: 100vh; | |
| } | |
| #ui { | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| padding: 1rem; | |
| color: white; | |
| text-align: center; | |
| pointer-events: none; | |
| z-index: 10; | |
| } | |
| #start-screen { | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| background-color: rgba(0, 0, 0, 0.8); | |
| display: flex; | |
| flex-direction: column; | |
| justify-content: center; | |
| align-items: center; | |
| z-index: 20; | |
| color: white; | |
| } | |
| #crosshair { | |
| position: absolute; | |
| top: 50%; | |
| left: 50%; | |
| width: 20px; | |
| height: 20px; | |
| transform: translate(-50%, -50%); | |
| z-index: 10; | |
| pointer-events: none; | |
| } | |
| #crosshair:before, #crosshair:after { | |
| content: ''; | |
| position: absolute; | |
| background-color: white; | |
| } | |
| #crosshair:before { | |
| width: 2px; | |
| height: 20px; | |
| left: 9px; | |
| top: 0; | |
| } | |
| #crosshair:after { | |
| width: 20px; | |
| height: 2px; | |
| left: 0; | |
| top: 9px; | |
| } | |
| .btn { | |
| pointer-events: auto; | |
| background-color: #4f46e5; | |
| color: white; | |
| padding: 0.75rem 1.5rem; | |
| border-radius: 0.375rem; | |
| font-weight: 600; | |
| cursor: pointer; | |
| transition: all 0.2s; | |
| margin-top: 1rem; | |
| border: none; | |
| } | |
| .btn:hover { | |
| background-color: #4338ca; | |
| } | |
| #mobile-controls { | |
| position: absolute; | |
| bottom: 20px; | |
| width: 100%; | |
| display: flex; | |
| justify-content: center; | |
| gap: 20px; | |
| z-index: 30; | |
| } | |
| .mobile-btn { | |
| width: 60px; | |
| height: 60px; | |
| background-color: rgba(255, 255, 255, 0.3); | |
| border-radius: 50%; | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| font-size: 24px; | |
| color: white; | |
| user-select: none; | |
| -webkit-user-select: none; | |
| touch-action: manipulation; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div id="container"> | |
| <div id="ui"> | |
| <h1 class="text-2xl font-bold mb-2">Blank Wall 3D</h1> | |
| <p>Arrow keys to move | Space to jump | Mouse to look around</p> | |
| <div id="debug" class="mt-4"></div> | |
| </div> | |
| <div id="crosshair"></div> | |
| <div id="start-screen"> | |
| <h1 class="text-4xl font-bold mb-4">Blank Wall 3D</h1> | |
| <p class="mb-8 text-lg">Explore an infinite world of blank walls</p> | |
| <button id="start-btn" class="btn">Start Game</button> | |
| </div> | |
| <div id="mobile-controls"> | |
| <div class="mobile-btn" id="up-btn">↑</div> | |
| <div class="mobile-btn" id="left-btn">←</div> | |
| <div class="mobile-btn" id="right-btn">→</div> | |
| <div class="mobile-btn" id="jump-btn">↥</div> | |
| </div> | |
| </div> | |
| <script> | |
| // Game variables | |
| let scene, camera, renderer, controls; | |
| let walls = []; | |
| let floor; | |
| let isJumping = false; | |
| let velocityY = 0; | |
| const gravity = -0.002; | |
| const jumpForce = 0.15; | |
| const movementSpeed = 0.1; | |
| const worldSize = 50; | |
| const wallSize = 5; | |
| const wallHeight = 3; | |
| // 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.y = 1.6; // Eye level | |
| // Create renderer | |
| renderer = new THREE.WebGLRenderer({ antialias: true }); | |
| renderer.setSize(window.innerWidth, window.innerHeight); | |
| document.getElementById('container').appendChild(renderer.domElement); | |
| // Add lights | |
| const ambientLight = new THREE.AmbientLight(0xffffff, 0.6); | |
| scene.add(ambientLight); | |
| const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8); | |
| directionalLight.position.set(100, 100, 50); | |
| scene.add(directionalLight); | |
| // Create floor | |
| const floorGeometry = new THREE.PlaneGeometry(worldSize * 2, worldSize * 2); | |
| const floorMaterial = new THREE.MeshStandardMaterial({ | |
| color: 0xcccccc, | |
| roughness: 0.8, | |
| metalness: 0.2 | |
| }); | |
| floor = new THREE.Mesh(floorGeometry, floorMaterial); | |
| floor.rotation.x = -Math.PI / 2; | |
| floor.receiveShadow = true; | |
| scene.add(floor); | |
| // Generate walls | |
| generateWalls(); | |
| // Set up controls | |
| controls = new THREE.PointerLockControls(camera, document.body); | |
| // Event listeners | |
| document.getElementById('start-btn').addEventListener('click', () => { | |
| document.getElementById('start-screen').style.display = 'none'; | |
| controls.lock(); | |
| }); | |
| // Mobile controls | |
| const upBtn = document.getElementById('up-btn'); | |
| const leftBtn = document.getElementById('left-btn'); | |
| const rightBtn = document.getElementById('right-btn'); | |
| const jumpBtn = document.getElementById('jump-btn'); | |
| upBtn.addEventListener('touchstart', () => keys.ArrowUp = true); | |
| upBtn.addEventListener('touchend', () => keys.ArrowUp = false); | |
| leftBtn.addEventListener('touchstart', () => keys.ArrowLeft = true); | |
| leftBtn.addEventListener('touchend', () => keys.ArrowLeft = false); | |
| rightBtn.addEventListener('touchstart', () => keys.ArrowRight = true); | |
| rightBtn.addEventListener('touchend', () => keys.ArrowRight = false); | |
| jumpBtn.addEventListener('touchstart', () => { | |
| if (!isJumping) { | |
| velocityY = jumpForce; | |
| isJumping = true; | |
| } | |
| }); | |
| document.addEventListener('keydown', onKeyDown); | |
| document.addEventListener('keyup', onKeyUp); | |
| window.addEventListener('resize', onWindowResize); | |
| // Start animation loop | |
| animate(); | |
| } | |
| // Generate random walls in the world | |
| function generateWalls() { | |
| // Clear existing walls | |
| walls.forEach(wall => scene.remove(wall)); | |
| walls = []; | |
| // Create walls in a grid pattern | |
| for (let x = -worldSize; x <= worldSize; x += wallSize) { | |
| for (let z = -worldSize; z <= worldSize; z += wallSize) { | |
| // Skip some spaces to create pathways | |
| if (Math.random() > 0.3) { | |
| const wallGeometry = new THREE.BoxGeometry(wallSize, wallHeight, 0.2); | |
| const wallMaterial = new THREE.MeshStandardMaterial({ | |
| color: 0xffffff, | |
| roughness: 0.7 | |
| }); | |
| const wall = new THREE.Mesh(wallGeometry, wallMaterial); | |
| wall.position.set(x, wallHeight/2, z); | |
| // Randomly rotate some walls | |
| if (Math.random() > 0.7) { | |
| wall.rotation.y = Math.PI / 2; | |
| } | |
| walls.push(wall); | |
| scene.add(wall); | |
| } | |
| } | |
| } | |
| } | |
| // Handle keyboard input | |
| const keys = { | |
| ArrowUp: false, | |
| ArrowLeft: false, | |
| ArrowRight: false, | |
| Space: false | |
| }; | |
| function onKeyDown(event) { | |
| switch (event.code) { | |
| case 'ArrowUp': keys.ArrowUp = true; break; | |
| case 'ArrowLeft': keys.ArrowLeft = true; break; | |
| case 'ArrowRight': keys.ArrowRight = true; break; | |
| case 'Space': | |
| if (!isJumping) { | |
| velocityY = jumpForce; | |
| isJumping = true; | |
| } | |
| break; | |
| case 'KeyR': generateWalls(); break; // Regenerate walls | |
| } | |
| } | |
| function onKeyUp(event) { | |
| switch (event.code) { | |
| case 'ArrowUp': keys.ArrowUp = false; break; | |
| case 'ArrowLeft': keys.ArrowLeft = false; break; | |
| case 'ArrowRight': keys.ArrowRight = false; break; | |
| } | |
| } | |
| // Handle window resize | |
| function onWindowResize() { | |
| camera.aspect = window.innerWidth / window.innerHeight; | |
| camera.updateProjectionMatrix(); | |
| renderer.setSize(window.innerWidth, window.innerHeight); | |
| } | |
| // Animation loop | |
| function animate() { | |
| requestAnimationFrame(animate); | |
| // Movement | |
| if (controls.isLocked) { | |
| const direction = new THREE.Vector3(); | |
| const frontVector = new THREE.Vector3(0, 0, -1); | |
| const sideVector = new THREE.Vector3(-1, 0, 0); | |
| frontVector.applyQuaternion(camera.quaternion); | |
| sideVector.applyQuaternion(camera.quaternion); | |
| if (keys.ArrowUp) direction.add(frontVector); | |
| if (keys.ArrowLeft) direction.add(sideVector); | |
| if (keys.ArrowRight) direction.sub(sideVector); | |
| direction.y = 0; | |
| direction.normalize(); | |
| // Apply movement speed | |
| direction.multiplyScalar(movementSpeed); | |
| // Check for collisions before moving | |
| const newPosition = new THREE.Vector3().copy(controls.getObject().position).add(direction); | |
| // Simple collision detection with walls | |
| let canMove = true; | |
| walls.forEach(wall => { | |
| const wallBox = new THREE.Box3().setFromObject(wall); | |
| const playerBox = new THREE.Box3( | |
| new THREE.Vector3(newPosition.x - 0.3, newPosition.y - 1.6, newPosition.z - 0.3), | |
| new THREE.Vector3(newPosition.x + 0.3, newPosition.y + 0.2, newPosition.z + 0.3) | |
| ); | |
| if (wallBox.intersectsBox(playerBox)) { | |
| canMove = false; | |
| } | |
| }); | |
| if (canMove) { | |
| controls.moveRight(-direction.x); | |
| controls.moveForward(direction.z); | |
| } | |
| // Jumping physics | |
| if (isJumping) { | |
| controls.getObject().position.y += velocityY; | |
| velocityY += gravity; | |
| // Check if landed | |
| if (controls.getObject().position.y <= 1.6) { | |
| controls.getObject().position.y = 1.6; | |
| isJumping = false; | |
| velocityY = 0; | |
| } | |
| } | |
| } | |
| // Update debug info | |
| if (controls.isLocked) { | |
| const pos = controls.getObject().position; | |
| document.getElementById('debug').innerHTML = ` | |
| Position: X: ${pos.x.toFixed(1)}, Y: ${pos.y.toFixed(1)}, Z: ${pos.z.toFixed(1)} | |
| `; | |
| } | |
| 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=justelludwig/cuatro" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |