Slendytubbies 1 - Recreación 3D
by MagicKnight13373 lines10.1 KB🛠️ Three.js (3D graphics)
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no">
<title>Slendytubbies 1 - Recreación 3D</title>
<style>
*{box-sizing:border-box}
body{margin:0;overflow:hidden;background:#111;color:white;font-family:Arial}
#hud{position:fixed;top:10px;left:10px;z-index:3;padding:12px;background:#000a;border-radius:10px;font-size:14px;line-height:1.7}
#message{position:fixed;top:44%;left:0;width:100%;text-align:center;font-size:23px;font-weight:bold;text-shadow:2px 2px 5px #000;pointer-events:none}
#menu{position:fixed;inset:0;z-index:10;background:linear-gradient(#101c20ee,#050705f5);display:flex;align-items:center;justify-content:center;flex-direction:column;text-align:center;padding:22px}
#menu h1{font-size:34px;color:#b8e1a0;text-shadow:0 0 15px #6bff4b}
#menu p{max-width:500px;line-height:1.6}
button{border:0;border-radius:9px;background:#57218b;color:white;padding:13px;margin:4px;font-size:16px}
#mobile{position:fixed;bottom:12px;left:10px;z-index:5;display:grid;grid-template-columns:55px 55px 55px;gap:4px}
#mobile button{padding:12px 0;margin:0;background:#171b18c9;font-size:21px;touch-action:none}
#actions{position:fixed;right:10px;bottom:20px;z-index:5;display:flex;flex-direction:column}
#actions button{font-size:13px;background:#352347dd}
#crosshair{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);pointer-events:none;color:white;font-size:20px}
select{padding:10px;border-radius:8px;margin:8px;font-size:16px}
</style>
</head>
<body>
<div id="hud">
<b>SLENDYTUBBIES 1</b><br>
Ambiente: <span id="time">Día</span><br>
Papillas: <span id="count">0</span>/10<br>
Linterna: <span id="battery">100</span>%<br>
Criatura: <span id="danger">Lejos</span><br>
Lugar: <span id="place">Bosque</span>
</div>
<div id="message">Encuentra las 10 papillas moradas.</div>
<div id="crosshair">+</div>
<div id="menu">
<h1>SLENDYTUBBIES 1</h1>
<h2>El mapa perdido</h2>
<p>Explora el bosque, visita la casa, la cueva, la playa y el árbol. Encuentra los diez recipientes de papilla y evita a Tinky Winky.</p>
<label for="environment">Selecciona el ambiente:</label>
<select id="environment">
<option value="day">Día</option>
<option value="sunset">Atardecer</option>
<option value="night">Noche</option>
</select>
<button id="start">INICIAR PARTIDA</button>
<p>En móvil, usa los botones para moverte y desliza el dedo por la pantalla para mirar.</p>
</div>
<div id="mobile">
<span></span><button data-key="KeyW">▲</button><span></span>
<button data-key="KeyA">◀</button><button data-key="KeyS">▼</button><button data-key="KeyD">▶</button>
</div>
<div id="actions">
<button id="flash">🔦 Linterna</button>
<button id="sprint">🏃 Correr</button>
<button id="pause">Ⅱ Pausa</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.min.js"></script>
<script>
(() => {
'use strict';
if(!window.THREE){
document.getElementById('message').textContent=
'No se pudo cargar el motor 3D. Comprueba tu conexión.';
return;
}
const scene=new THREE.Scene();
scene.background=new THREE.Color(0x91b4c9);
scene.fog=new THREE.Fog(0x91b4c9,35,150);
const camera=new THREE.PerspectiveCamera(75,innerWidth/innerHeight,.1,400);
camera.position.set(0,1.7,0);
const renderer=new THREE.WebGLRenderer({antialias:true});
renderer.setSize(innerWidth,innerHeight);
renderer.setPixelRatio(Math.min(devicePixelRatio,1.6));
renderer.shadowMap.enabled=true;
document.body.appendChild(renderer.domElement);
const hemi=new THREE.HemisphereLight(0xb8d9ed,0x303a22,1.4);
scene.add(hemi);
const sun=new THREE.DirectionalLight(0xffe3b2,1.8);
sun.position.set(30,55,15);
sun.castShadow=true;
scene.add(sun);
const flashlight=new THREE.SpotLight(0xffffff,0,45,Math.PI/7,.55,1);
flashlight.position.set(0,1.4,0);
flashlight.target.position.set(0,1,-1);
camera.add(flashlight);
camera.add(flashlight.target);
scene.add(camera);
function material(color,roughness=1){
return new THREE.MeshStandardMaterial({color,roughness});
}
const grass=material(0x506d37);
const dirt=material(0x514431);
const wood=material(0x55412c);
const leaves=material(0x34512b);
const rockmat=material(0x77796d);
const concrete=material(0x77766c);
const dark=material(0x20221f);
const purple= new THREE.MeshStandardMaterial({
color:0xa527ff,emissive:0x6b00bb,emissiveIntensity:1.5,roughness:.3
});
const floor=new THREE.Mesh(new THREE.PlaneGeometry(300,300),grass);
floor.rotation.x=-Math.PI/2;
floor.receiveShadow=true;
floor.position.y=-.1;
scene.add(floor);
const water=new THREE.Mesh(
new THREE.PlaneGeometry(100,70),
new THREE.MeshStandardMaterial({color:0x315e78,roughness:.3})
);
water.rotation.x=-Math.PI/2;
water.position.set(0,-.04,-95);
scene.add(water);
// Caminos que ayudan a orientarse por el mapa.
function path(x,z,w,d){
const p=new THREE.Mesh(new THREE.PlaneGeometry(w,d),dirt);
p.rotation.x=-Math.PI/2;
p.position.set(x,-.045,z);
scene.add(p);
}
path(0,-10,5,75);
path(-15,-30,38,4);
path(17,-53,40,4);
path(-25,4,4,50);
// Árboles.
function tree(x,z,s=1){
const g=new THREE.Group();
const trunk=new THREE.Mesh(
new THREE.CylinderGeometry(.25*s,.42*s,3*s,7),wood
);
trunk.position.y=1.5*s;
trunk.castShadow=true;
g.add(trunk);
for(let i=0;i<3;i++){
const crown=new THREE.Mesh(
new THREE.ConeGeometry((1.7-i*.25)*s,2.5*s,7),leaves
);
crown.position.y=(3+i*.7)*s;
crown.castShadow=true;
g.add(crown);
}
g.position.set(x,0,z);
scene.add(g);
}
for(let i=0;i<250;i++){
const x=(Math.random()-.5)*250;
const z=(Math.random()-.5)*220;
// Dejar espacio para los lugares principales.
if(Math.hypot(x,z)<12)continue;
if(z < -65 && Math.abs(x)<48)continue;
tree(x,z,.65+Math.random()*.8);
}
// Rocas.
for(let i=0;i<90;i++){
const r=new THREE.Mesh(
new THREE.DodecahedronGeometry(.4+Math.random()*1.1,0),rockmat
);
r.position.set((Math.random()-.5)*230,.35,(Math.random()-.5)*210);
r.rotation.set(Math.random(),Math.random(),Math.random());
r.castShadow=true;
scene.add(r);
}
// Edificios explorables visualmente.
function building(x,z,w,h,d){
const g=new THREE.Group();
const body=new THREE.Mesh(
new THREE.BoxGeometry(w,h,d),concrete
);
body.position.y=h/2;
body.castShadow=true;
body.receiveShadow=true;
g.add(body);
const roof=new THREE.Mesh(
new THREE.ConeGeometry(w*.78,1.6,4),dark
);
roof.position.y=h+.8;
roof.rotation.y=Math.PI/4;
g.add(roof);
const door=new THREE.Mesh(
new THREE.BoxGeometry(1,1.8,.12),wood
);
door.position.set(0,.9,d/2+.07);
g.add(door);
const windowmat=material(0x252d31);
for(const xw of [-w*.28,w*.28]){
const win=new THREE.Mesh(
new THREE.BoxGeometry(.65,.65,.1),windowmat
);
win.position.set(xw,h*.65,d/2+.08);
g.add(win);
}
g.position.set(x,0,z);
scene.add(g);
return g;
}
building(-18,-35,10,5,9); // Casa
building(30,-53,12,6,11); // Edificio apartado
// Cueva de entrada oscura.
const cave=new THREE.Group();
const cavearch=new THREE.Mesh(
new THREE.TorusGeometry(4,1.2,8,12,Math.PI),
rockmat
);
cavearch.rotation.z=Math.PI;
cavearch.position.y=2;
cave.add(cavearch);
const caveinside=new THREE.Mesh(
new THREE.CircleGeometry(3.5,12),
dark
);
caveinside.position.set(0,1.5,-.2);
cave.add(caveinside);
cave.position.set(-35,0,-45);
scene.add(cave);
// Árbol especial de Po, hecho con geometría sencilla.
tree(37,-7,2.1);
for(let i=0;i<4;i++){
const branch=new THREE.Mesh(
new THREE.CylinderGeometry(.12,.25,3,6),wood
);
branch.position.set(37+i*1.2,2+i*.35,-7);
branch.rotation.z=.7;
scene.add(branch);
}
// Cadáveres estilizados de los personajes.
function corpse(x,z,skinColor,rotation=0){
const g=new THREE.Group();
const skin=material(skinColor);
const body=new THREE.Mesh(
new THREE.SphereGeometry(.48,12,9),skin
);
body.scale.set(1.6,.55,.8);
body.position.y=.35;
g.add(body);
const head=new THREE.Mesh(
new THREE.SphereGeometry(.32,12,9),skin
);
head.position.set(-.7,.36,0);
g.add(head);
const eyeMat=material(0x181818);
for(const side of [-1,1]){
const limb=new THREE.Mesh(
new THREE.CylinderGeometry(.08,.12,.75,6),skin
);
limb.position.set(.3,.23,side*.4);
limb.rotation.z=side*.9;
g.add(limb);
const eye=new THREE.Mesh(
new THREE.SphereGeometry(.045,7,6),eyeMat
);
eye.position.set(-.83,.43,side*.19);
g.add(eye);
}
g.position.set(x,0,z);
g.rotation.y=rotation;
scene.add(g);
return g;
}
corpse(-8,-24,0x4cba42,.4);
corpse(24,-25,0xd9b83c,1.8);
corpse(-29,-8,0xe3a2b4,2.3);
// Personaje tipo Teletubby.
function tubby(color,antenna,scary=false){
const g=new THREE.Group();
const skin=material(color);
const body=new THREE.Mesh(
new THREE.SphereGeometry(.65,16,12),skin
);
body.scale.set(.9,1.2,.72);
body.position.y=1.08;
body.castShadow=true;
g.add(body);
const head=new THREE.Mesh(
new THREE.SphereGeometry(.45,16,12),skin
);
head.position.y=2;
g.add(head);
const face=new THREE.Mesh(
new THREE.SphereGeometry(.3,12,9),
material(scary?0x665b52:0xcbb99c)
);
face.scale.set(1,.72,.4);
face.position.set(0,1.98,.36);
g.add(face);
for(const x of [-.13,.13]){
const eye=new THREE.Mesh(
new THREE.SphereGeometry(.05,8,8),material(0x151515)
);
eye.position.set(x,2.04,.48);
g.add(eye);
}
const mouth=new THREE.Mesh(
new THREE.BoxGeometry(.15,.035,.025),material(0x30211b)
);
mouth.position.set(0,1.87,.49);
g.add(mouth);
if(antenna===0){
const a=new THREE.Mesh(new THREE.TorusGeometry(.16,.05,7,12),skin);
a.position.y=2.58;g.add(a);
}else if(antenna===1){
const a=new THREE.Mesh(new THREE.ConeGeometry(.12,.5,8),skin);
a.position.y=2.67;g.add(a);
}else if(antenna===2){
const a=new THREE.Mesh(new THREE.BoxGeometry(.12,.48,.12),skin);
a.position.y=2.62;g.add(a);
}else{
const a=new THREE.Mesh(new THREE.SphereGeometry(.13,8,8),skin);
a.position.y=2.6;g.add(a);
}
for(const x of [-.38,.38]){
const arm=new THREE.Mesh(
new THREE.CapsuleGeometry(.12,.48,3,6),skin
);
arm.position.set(x,1.15,0);
g.add(arm);
}
for(const x of [-.23,.23]){
const leg=new THREE.Mesh(
new THREE.CapsuleGeometry(.13,.42,3,6),skin
);
leg.position.set(x,.32,0);
g.add(leg);
}
return g;
}
// Personajes que deambulan por el mapa.
const friends=[
{obj:tubby(0x42a5e8,0),x:-24,z:8,speed:.6},
{obj:tubby(0x4fbb42,2),x:27,z:22,speed:.5},
{obj:tubby(0xd9bd3b,1),x:43Game Source: Slendytubbies 1 - Recreación 3D
Creator: MagicKnight13
Libraries: three
Complexity: complex (373 lines, 10.1 KB)
The full source code is displayed above on this page.
Remix Instructions
To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: slendytubbies-1-recreaci-n-3d-magicknight13" to link back to the original. Then publish at arcadelab.ai/publish.