Spaces:
Running
Running
| <html lang="es"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <title>Worms 3D</title> | |
| <style> | |
| body { | |
| margin: 0; | |
| overflow: hidden; | |
| background: #222; | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| justify-content: flex-start; | |
| } | |
| /* CONTENEDOR GENERAL */ | |
| #game-container { | |
| width: 90vw; | |
| height: 70vh; | |
| background: #000; | |
| border: 3px solid #00ff88; | |
| margin-top: 10px; | |
| position: relative; | |
| } | |
| /* BOTONES */ | |
| #ui-bar { | |
| margin-top: 12px; | |
| display: flex; | |
| gap: 12px; | |
| } | |
| button { | |
| padding: 14px 26px; | |
| font-size: 18px; | |
| font-weight: bold; | |
| border-radius: 8px; | |
| border: none; | |
| background: #00ff99; | |
| color: #000; | |
| cursor: pointer; | |
| } | |
| button:hover { | |
| background: #00d97e; | |
| } | |
| </style> | |
| <!-- THREE.JS + LIBRERÍAS --> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/examples/js/controls/OrbitControls.js"></script> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/examples/js/loaders/OBJLoader.js"></script> | |
| </head> | |
| <body> | |
| <!-- BOTONES VISIBLES --> | |
| <div id="ui-bar"> | |
| <button onclick="cameraFollow = true">Seguir Gusano</button> | |
| <button onclick="cameraFollow = false">Control Libre</button> | |
| </div> | |
| <!-- CONTENEDOR DEL JUEGO (AGRANDADO) --> | |
| <div id="game-container"></div> | |
| <script> | |
| // ======================================================= | |
| // === Three.js 3D Initialization === | |
| // ======================================================= | |
| let scene, camera, renderer, terrain, controls; | |
| let wormMesh; | |
| const keys = {}; | |
| let cameraFollow = true; // Activar o desactivar seguimiento automático | |
| const container = document.getElementById("game-container"); | |
| const textureLoader = new THREE.TextureLoader(); | |
| const movementSpeed = 0.5; | |
| function init() { | |
| scene = new THREE.Scene(); | |
| scene.background = new THREE.Color(0x87ceeb); | |
| camera = new THREE.PerspectiveCamera( | |
| 75, | |
| container.clientWidth / container.clientHeight, | |
| 0.1, | |
| 1000 | |
| ); | |
| camera.position.set(0, 50, 70); | |
| renderer = new THREE.WebGLRenderer({ antialias: true }); | |
| renderer.setSize(container.clientWidth, container.clientHeight); | |
| container.appendChild(renderer.domElement); | |
| const ambientLight = new THREE.AmbientLight(0x404040, 3); | |
| scene.add(ambientLight); | |
| const directionalLight = new THREE.DirectionalLight(0xffffff, 2); | |
| directionalLight.position.set(100, 100, 50); | |
| scene.add(directionalLight); | |
| createTerrain(); | |
| controls = new THREE.OrbitControls(camera, renderer.domElement); | |
| controls.enableDamping = true; | |
| controls.dampingFactor = 0.05; | |
| controls.minDistance = 20; | |
| controls.maxDistance = 150; | |
| loadWormModel(); | |
| window.addEventListener("keydown", (e) => keys[e.key.toLowerCase()] = true); | |
| window.addEventListener("keyup", (e) => keys[e.key.toLowerCase()] = false); | |
| window.addEventListener("resize", onWindowResize); | |
| } | |
| // ======================================================= | |
| // === Terreno Procedural === | |
| // ======================================================= | |
| function createTerrain() { | |
| const texture = textureLoader.load("grass_texture.jpg"); | |
| const size = 150, segments = 64; | |
| const geometry = new THREE.PlaneGeometry(size, size, segments, segments); | |
| geometry.rotateX(-Math.PI / 2); | |
| const material = new THREE.MeshPhongMaterial({ | |
| map: texture, | |
| side: THREE.DoubleSide | |
| }); | |
| terrain = new THREE.Mesh(geometry, material); | |
| const verts = geometry.attributes.position.array; | |
| const maxH = 15; | |
| for (let i = 0; i < verts.length; i += 3) { | |
| const x = verts[i] / size; | |
| const y = verts[i + 2] / size; | |
| const height = | |
| Math.sin(x * 10) * 0.5 + | |
| Math.cos(y * 7) * 0.8 + | |
| Math.sin((x + y) * 5) * 0.3; | |
| verts[i + 1] = height * maxH; | |
| } | |
| geometry.computeVertexNormals(); | |
| scene.add(terrain); | |
| } | |
| // ======================================================= | |
| // === Modelo OBJ del gusano === | |
| // ======================================================= | |
| function loadWormModel() { | |
| const objLoader = new THREE.OBJLoader(); | |
| objLoader.load("cascabel123.obj", (object) => { | |
| const material = new THREE.MeshPhongMaterial({ | |
| color: 0x68e079, | |
| shininess: 30 | |
| }); | |
| object.traverse((child) => { | |
| if (child.isMesh) child.material = material; | |
| }); | |
| wormMesh = object; | |
| wormMesh.scale.set(10, 10, 10); | |
| wormMesh.position.set(0, 5, 0); | |
| scene.add(wormMesh); | |
| }); | |
| } | |
| // ======================================================= | |
| // === Movimiento === | |
| // ======================================================= | |
| function moveWorm() { | |
| if (!wormMesh) return; | |
| let moved = false; | |
| const prev = wormMesh.position.clone(); | |
| if (keys["w"]) { wormMesh.position.z -= movementSpeed; wormMesh.rotation.y = 0; moved = true; } | |
| if (keys["s"]) { wormMesh.position.z += movementSpeed; wormMesh.rotation.y = Math.PI; moved = true; } | |
| if (keys["a"]) { wormMesh.position.x -= movementSpeed; wormMesh.rotation.y = Math.PI/2; moved = true; } | |
| if (keys["d"]) { wormMesh.position.x += movementSpeed; wormMesh.rotation.y = -Math.PI/2; moved = true; } | |
| if (cameraFollow && moved) { | |
| const delta = wormMesh.position.clone().sub(prev); | |
| camera.position.add(delta); | |
| } | |
| } | |
| // ======================================================= | |
| // === Render Loop === | |
| // ======================================================= | |
| function animate() { | |
| requestAnimationFrame(animate); | |
| moveWorm(); | |
| if (controls) { | |
| if (cameraFollow && wormMesh) { | |
| controls.target.copy(wormMesh.position); | |
| } | |
| controls.update(); | |
| } | |
| renderer.render(scene, camera); | |
| } | |
| // ======================================================= | |
| // === Resize === | |
| // ======================================================= | |
| function onWindowResize() { | |
| camera.aspect = container.clientWidth / container.clientHeight; | |
| camera.updateProjectionMatrix(); | |
| renderer.setSize(container.clientWidth, container.clientHeight); | |
| } | |
| init(); | |
| animate(); | |
| </script> | |
| </body> | |
| </html> |