Block World 3D
by MagicKnight13519 lines21.3 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>Block World 3D</title>
<style>
*{box-sizing:border-box}
body{margin:0;background:#263b2b;color:white;font-family:Arial,sans-serif}
button,input{font:inherit}
button{background:#4b913b;color:white;border:2px solid #b5e58c;padding:10px;margin:3px;font-weight:bold;touch-action:manipulation}
button:active{filter:brightness(1.35)}
input{padding:12px;width:100%;margin:8px 0}
header{text-align:center;background:#263e2a;padding:12px}
h1{margin:0;color:#b8f18d}
main{max-width:850px;margin:auto;padding:8px}
.panel{background:#293d32;border:2px solid #678b62;padding:12px;margin:8px 0}
.hidden{display:none!important}
.world{display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;background:#3b5143;padding:8px;margin:6px 0}
.modes{display:grid;grid-template-columns:1fr 1fr;gap:6px}
.mode{background:#3a5142;padding:8px;border:2px solid #71956b}
.mode button{width:100%}
#game{height:75vh;height:75dvh;min-height:350px;position:relative;overflow:hidden;background:#80c9f2;touch-action:none}
#game canvas{display:block}
#hud{position:absolute;inset:0;pointer-events:none;text-shadow:1px 1px #222}
#top{position:absolute;top:5px;left:5px;right:5px;display:flex;justify-content:space-between;align-items:start}
#top button{font-size:10px;padding:7px 4px}
#crosshair{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);font-size:24px}
#toast{position:absolute;top:50px;left:5px;background:#17251ddd;padding:6px;font-size:12px}
#bottom{position:absolute;bottom:5px;left:5px;right:5px;display:flex;justify-content:space-between;align-items:end;gap:2px}
#move{display:grid;grid-template-columns:36px 36px 36px;gap:2px;pointer-events:auto}
#move button{margin:0;padding:8px 0;background:#263b49df}
.up{grid-column:2}.left{grid-column:1;grid-row:2}.down{grid-column:2;grid-row:2}.right{grid-column:3;grid-row:2}
#actions{display:flex;flex-direction:column;pointer-events:auto}
#actions button{margin:2px;padding:7px 3px;font-size:10px;background:#263b49ed}
#hotbar{display:flex;justify-content:center;flex-wrap:wrap;max-width:160px;pointer-events:auto}
.slot{width:35px;height:40px;padding:2px;margin:1px;font-size:9px;background:#343434dd}
.slot.selected{border:3px solid yellow}
#inventory{display:none;position:absolute;bottom:75px;left:3%;right:3%;background:#1c2a27f5;padding:8px;text-align:center;pointer-events:auto}
#inventory button{font-size:11px}
#loading{display:none;position:fixed;inset:0;background:#17251cf5;z-index:10;align-items:center;justify-content:center;text-align:center}
#bar{height:18px;background:#111;border:2px solid #b8ef90;width:80vw;max-width:330px;margin:18px auto}
#progress{height:100%;width:0;background:#71ce4c}
@media(max-width:430px){#hotbar{max-width:110px}.slot{width:31px;font-size:8px}main{padding:4px}}
</style>
</head>
<body>
<header><h1>⛏ BLOCK WORLD 3D</h1><small>Explora, construye y crea</small></header>
<main id="menu">
<section id="home" class="panel">
<h2>Menú principal</h2>
<button style="width:100%" onclick="showScreen('worlds')">▶ JUGAR</button>
<button style="width:100%" onclick="showScreen('market')">🛒 MARKETPLACE</button>
<button style="width:100%" onclick="showScreen('settings')">⚙ AJUSTES</button>
</section>
<section id="worlds" class="panel hidden">
<h2>Mis mundos</h2><div id="worldList"></div>
<button onclick="showScreen('create')">+ CREAR MUNDO</button>
<button onclick="showScreen('home')">VOLVER</button>
</section>
<section id="create" class="panel hidden">
<h2>Crear mundo</h2>
<input id="worldName" maxlength="40" placeholder="Nombre del mundo">
<button style="width:100%" onclick="createWorld()">CREAR Y CONTINUAR</button>
<button onclick="showScreen('worlds')">CANCELAR</button>
</section>
<section id="chooseMode" class="panel hidden">
<h2>Elegir modo</h2><p id="selectedWorldName"></p>
<div class="modes">
<div class="mode"><h3>🌲 Supervivencia</h3><p>Recoge y construye.</p><button onclick="startSelectedWorld('survival')">ELEGIR</button></div>
<div class="mode"><h3>🧱 Creativo</h3><p>Bloques ilimitados.</p><button onclick="startSelectedWorld('creative')">ELEGIR</button></div>
<div class="mode"><h3>🗺 Aventura</h3><p>Explora el mundo.</p><button onclick="startSelectedWorld('adventure')">ELEGIR</button></div>
<div class="mode"><h3>💀 Hardcore</h3><p>Modo difícil de prueba.</p><button onclick="startSelectedWorld('hardcore')">ELEGIR</button></div>
</div>
<button onclick="showScreen('worlds')">VOLVER</button>
</section>
<section id="market" class="panel hidden">
<h2>Marketplace</h2><p>Complementos de demostración.</p><div id="addonList"></div>
<button onclick="showScreen('home')">VOLVER</button>
</section>
<section id="settings" class="panel hidden">
<h2>Ajustes</h2>
<label><input id="touchLook" type="checkbox" checked style="width:auto"> Girar cámara al tocar y arrastrar</label>
<button onclick="showScreen('home')">VOLVER</button>
</section>
</main>
<section id="gameScreen" class="hidden">
<div id="game"></div>
<div id="hud">
<div id="top"><span id="modeStatus" class="panel">Mi mundo</span>
<div><button id="cameraBtn">TERCERA PERSONA</button><button id="pauseBtn">PAUSA</button></div></div>
<div id="crosshair">+</div><div id="toast">¡Bienvenido!</div>
<div id="bottom">
<div id="move">
<button class="up" data-move="forward">▲</button>
<button class="left" data-move="left">◀</button>
<button class="down" data-move="back">▼</button>
<button class="right" data-move="right">▶</button>
</div>
<div id="hotbar"></div>
<div id="actions">
<button id="jumpBtn">SALTAR</button>
<button id="breakBtn">ROMPER</button>
<button id="placeBtn">COLOCAR</button>
<button id="invBtn">MOCHILA</button>
<button id="exitBtn">SALIR</button>
</div></div>
<div id="inventory" class="panel"><h3>Inventario</h3><div id="inventoryItems"></div><button id="closeInv">CERRAR</button></div>
</div>
</section>
<div id="loading"><div><h2 id="loadingTitle">Generando mundo...</h2><p id="loadingText">Preparando terreno</p><div id="bar"><div id="progress"></div></div></div></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script>
(()=>{
"use strict";
const $=id=>document.getElementById(id);
const screens=["home","worlds","create","chooseMode","market","settings"];
let worlds=[];
let addons={};
try{worlds=JSON.parse(localStorage.getItem("bw_worlds")||"[]")}catch(e){}
try{addons=JSON.parse(localStorage.getItem("bw_addons")||"{}")}catch(e){}
let pendingWorld=null,currentWorld=null,mode="creative";
let scene,camera,renderer,geo,materials={};
let blocks=new Map(),meshes=[];
let playing=false,paused=false,cameraMode="first";
let player={x:0,y:5,z:0,vy:0,grounded:false};
let yaw=0,pitch=0,lastTime=0,dragging=false,lastX=0,lastY=0;
let selected="grass";
const keys={},mobile={};
const definitions=[
["grass","Hierba"],["dirt","Tierra"],["stone","Piedra"],
["cobble","Roca"],["sand","Arena"],["wood","Madera"],
["planks","Tablones"],["leaves","Hojas"],["brick","Ladrillos"],
["glass","Vidrio"],["snow","Nieve"],["water","Agua"],
["gold","Oro"],["diamond","Diamante"],["ruby","Rubí"],
["pillow_red","Cojín rojo"],["pillow_blue","Cojín azul"],
["pillow_green","Cojín verde"],["diagonal","Bloque diagonal"]
];
const names=Object.fromEntries(definitions);
const inventory={};
definitions.forEach(d=>inventory[d[0]]=32);
function save(){
try{
localStorage.setItem("bw_worlds",JSON.stringify(worlds));
localStorage.setItem("bw_addons",JSON.stringify(addons));
}catch(e){}
}
window.showScreen=id=>{
screens.forEach(s=>$(s).classList.toggle("hidden",s!==id));
$("menu").classList.remove("hidden");
$("gameScreen").classList.add("hidden");
if(id==="worlds")renderWorlds();
if(id==="market")renderMarket();
};
function renderWorlds(){
const list=$("worldList");list.innerHTML="";
if(!worlds.length)list.innerHTML="<p>No hay mundos todavía. Crea uno.</p>";
worlds.forEach(w=>{
const row=document.createElement("div");row.className="world";
const label=document.createElement("span");label.textContent="🌎 "+w.name;
const controls=document.createElement("div");
const play=document.createElement("button");play.textContent="JUGAR";
play.onclick=()=>{pendingWorld=w;showModes();};
const del=document.createElement("button");del.textContent="ELIMINAR";
del.onclick=()=>{
if(confirm("¿Eliminar "+w.name+"?")){
worlds=worlds.filter(v=>v.id!==w.id);save();renderWorlds();
}};
controls.append(play,del);row.append(label,controls);list.append(row);
});
}
window.createWorld=()=>{
const name=$("worldName").value.trim()||"Mundo nuevo";
pendingWorld={id:Date.now(),name,mode:"creative"};
worlds.push(pendingWorld);save();$("worldName").value="";showModes();
};
function showModes(){
screens.forEach(s=>$(s).classList.toggle("hidden",s!=="chooseMode"));
$("selectedWorldName").textContent="Mundo: "+pendingWorld.name;
}
window.startSelectedWorld=chosen=>{
mode=chosen;currentWorld=pendingWorld;
const w=worlds.find(v=>v.id===currentWorld.id);if(w)w.mode=chosen;
save();loadWorld();
};
function loadWorld(){
$("menu").classList.add("hidden");
$("gameScreen").classList.add("hidden");
$("loading").style.display="flex";$("progress").style.width="0%";
$("loadingTitle").textContent="Generando "+currentWorld.name;
let n=0;
const timer=setInterval(()=>{
n++;$("progress").style.width=(n*25)+"%";
$("loadingText").textContent=["Preparando terreno...","Creando árboles...","Cargando texturas...","¡Listo!"][n-1];
if(n>=4){clearInterval(timer);$("loading").style.display="none";enterGame();}
},180);
}
/* Texturas pixeladas originales, generadas localmente */
function makeTexture(type){
const c=document.createElement("canvas");c.width=c.height=64;
const ctx=c.getContext("2d");
const colors={
grass:["#579b37","#66ad40","#45852f","#78b94a"],
dirt:["#80512f","#95603a","#6e4328","#a16b42"],
stone:["#858585","#969696","#707070","#a5a5a5"],
cobble:["#686868","#858585","#525252","#9a9a9a"],
sand:["#d6c27a","#e7d68e","#c4af69","#f0dfa0"],
wood:["#81552d","#a8743e","#684421","#bd8b50"],
planks:["#a77743","#c28c4f","#8b5d31","#d09a5c"],
leaves:["#347b34","#469642","#28632b","#5aa447"],
brick:["#9d5140","#b96751","#854032","#c67b60"],
glass:["#b4e6ed","#ddf7fa","#87cdd8","#ffffff"],
snow:["#e7f1f7","#ffffff","#d5e5ef","#f7fbff"],
water:["#327fc5","#4797d8","#2268aa","#69b4ed"],
gold:["#d9ae24","#f4d64a","#b78c12","#ffe779"],
diamond:["#32c9c7","#70f4e8","#169d9f","#a4fff4"],
ruby:["#a51536","#dc2850","#780c29","#f34a65"],
pillow_red:["#a72c35","#d94c55","#7f2028","#ed7277"],
pillow_blue:["#245da4","#377ed0","#194477","#76b5f5"],
pillow_green:["#39834b","#58a966","#246537","#83d18d"],
diagonal:["#80512f","#95603a","#6e4328","#a16b42"]
};
const palette=colors[type]||colors.stone;
ctx.fillStyle=palette[0];ctx.fillRect(0,0,64,64);
let seed=type.length*153+7;
function rand(){seed=(seed*9301+49297)%233280;return seed/233280}
for(let i=0;i<300;i++){
ctx.fillStyle=palette[Math.floor(rand()*palette.length)];
const x=Math.floor(rand()*16)*4,y=Math.floor(rand()*16)*4;
ctx.fillRect(x,y,2+Math.floor(rand()*3),2+Math.floor(rand()*3));
}
if(type==="grass"){
ctx.fillStyle="#80512f";ctx.fillRect(0,8,64,56);
for(let i=0;i<120;i++){ctx.fillStyle=palette[Math.floor(rand()*palette.length)];ctx.fillRect(rand()*64,rand()*10,3,3);}
}
if(type==="wood"){
ctx.fillStyle="#54371f";for(let x=8;x<64;x+=15)ctx.fillRect(x,0,2,64);
}
if(type==="planks"){
ctx.strokeStyle="#70451f";ctx.lineWidth=2;
for(let y=0;y<64;y+=16){ctx.beginPath();ctx.moveTo(0,y);ctx.lineTo(64,y);ctx.stroke();}
ctx.fillStyle="#70451f";ctx.fillRect(19,2,2,3);ctx.fillRect(47,19,2,3);ctx.fillRect(9,35,2,3);ctx.fillRect(38,51,2,3);
}
if(type==="cobble"){
ctx.strokeStyle="#505050";ctx.lineWidth=3;
for(let y=0;y<64;y+=16){ctx.beginPath();ctx.moveTo(0,y);ctx.lineTo(64,y);ctx.stroke();}
ctx.beginPath();ctx.moveTo(22,0);ctx.lineTo(22,16);ctx.moveTo(48,16);ctx.lineTo(48,32);ctx.moveTo(12,32);ctx.lineTo(12,48);ctx.moveTo(40,48);ctx.lineTo(40,64);ctx.stroke();
}
if(type==="glass"){
ctx.clearRect(0,0,64,64);ctx.strokeStyle="#eaffff";ctx.lineWidth=3;ctx.strokeRect(2,2,60,60);
ctx.beginPath();ctx.moveTo(7,7);ctx.lineTo(27,7);ctx.moveTo(7,7);ctx.lineTo(7,27);ctx.stroke();
}
if(type.startsWith("pillow")){
ctx.fillStyle=palette[0];ctx.fillRect(5,5,54,54);
ctx.strokeStyle=palette[2];ctx.lineWidth=4;ctx.strokeRect(6,6,52,52);
ctx.strokeStyle=palette[3];ctx.lineWidth=2;ctx.strokeRect(11,11,42,42);
}
const texture=new THREE.CanvasTexture(c);
texture.magFilter=THREE.NearestFilter;
texture.minFilter=THREE.NearestFilter;
texture.colorSpace=THREE.SRGBColorSpace||THREE.NoColorSpace;
return texture;
}
function makeMaterials(){
definitions.forEach(([type])=>{
materials[type]=new THREE.MeshLambertMaterial({
map:makeTexture(type),
transparent:type==="glass"||type==="water",
opacity:type==="glass"?.48:type==="water"?.7:1
});
});
}
function setup3D(){
scene=new THREE.Scene();
scene.background=new THREE.Color(0x87c9f4);
scene.fog=new THREE.Fog(0x87c9f4,30,85);
camera=new THREE.PerspectiveCamera(75,1,.1,1000);
renderer=new THREE.WebGLRenderer({antialias:false});
renderer.setPixelRatio(Math.min(devicePixelRatio,1.5));
$("game").innerHTML="";$("game").appendChild(renderer.domElement);
scene.add(new THREE.HemisphereLight(0xffffff,0x63764d,2));
const sun=new THREE.DirectionalLight(0xffffff,1.3);
sun.position.set(15,30,10);scene.add(sun);
geo=new THREE.BoxGeometry(1,1,1);
makeMaterials();
renderer.domElement.addEventListener("pointerdown",onPointerDown);
renderer.domElement.addEventListener("pointermove",onPointerMove);
renderer.domElement.addEventListener("pointerup",()=>dragging=false);
renderer.domElement.addEventListener("pointercancel",()=>dragging=false);
renderer.domElement.addEventListener("contextmenu",e=>e.preventDefault());
resize3D();
}
function resize3D(){
if(!renderer||!camera)return;
const w=$("game").clientWidth,h=$("game").clientHeight;
if(w&&h){renderer.setSize(w,h);camera.aspect=w/h;camera.updateProjectionMatrix();}
}
function addBlock(x,y,z,type){
x=Math.round(x);y=Math.round(y);z=Math.round(z);
const key=x+","+y+","+z;if(blocks.has(key))return;
let mesh;
if(type.startsWith("pillow")){
mesh=new THREE.Mesh(new THREE.BoxGeometry(.94,.28,.94),materials[type]);
mesh.position.set(x,y-.34,z);
}else if(type==="diagonal"){
const shape=new THREE.Shape();
shape.moveTo(-.5,-.5);shape.lineTo(.5,-.5);shape.lineTo(.5,.5);shape.closePath();
const geom=new THREE.ExtrudeGeometry(shape,{depth:1,bevelEnabled:false});
geom.translate(0,0,-.5);
mesh=new THREE.Mesh(geom,materials[type]);
mesh.rotation.y=Math.PI/2;
mesh.position.set(x,y,z);
}else{
mesh=new THREE.Mesh(geo,materials[type]||materials.stone);
mesh.position.set(x,y,z);
}
mesh.userData={x,y,z,type};
scene.add(mesh);blocks.set(key,mesh);meshes.push(mesh);
}
function removeBlock(mesh){
if(!mesh)return;
scene.remove(mesh);const d=mesh.userData;
blocks.delete(d.x+","+d.y+","+d.z);
const i=meshes.indexOf(mesh);if(i>=0)meshes.splice(i,1);
}
function generateWorld(){
for(let x=-12;x<=12;x++)for(let z=-12;z<=12;z++){
const h=Math.max(1,Math.floor(2+Math.sin(x*.45)*Math.cos(z*.37)*1.5));
for(let y=0;y<h;y++)addBlock(x,y,z,y===h-1?"grass":"dirt");
if(Math.random()<.035){
for(let y=h;y<h+3;y++)addBlock(x,y,z,"wood");
for(let dx=-1;dx<=1;dx++)for(let dz=-1;dz<=1;dz++)
for(let dy=2;dy<=4;dy++)
if(Math.abs(dx)+Math.abs(dz)+(dy===4?1:0)<4)addBlock(x+dx,h+dy,z+dz,"leaves");
}
}
}
function clearWorld(){meshes.forEach(m=>scene.remove(m));meshes=[];blocks.clear();}
function enterGame(){
if(!window.THREE){alert("Se necesita conexión a internet para cargar Three.js.");showScreen("home");return;}
if(!renderer)setup3D();
clearWorld();generateWorld();
player={x:0,y:5,z:0,vy:0,grounded:false};
yaw=0;pitch=0;paused=false;playing=true;
$("menu").classList.add("hidden");$("gameScreen").classList.remove("hidden");
$("modeStatus").textContent=currentWorld.name+" · "+mode;
$("toast").textContent="Texturas pixeladas cargadas";
$("inventory").style.display="none";
renderHotbar();renderInventory();resize3D();updateCamera();
}
function targetBlock(){
raycaster.setFromCamera(new THREE.Vector2(0,0),camera);
return raycaster.intersectObjects(meshes,false).find(h=>h.distance<7);
}
function breakBlock(){
if(!playing||paused)return;
if(mode==="adventure"){toast("No puedes romper bloques en Aventura.");return;}
const hit=targetBlock();if(!hit)return;
const type=hit.object.userData.type;removeBlock(hit.object);
if(mode!=="creative")inventory[type]=(inventory[type]||0)+1;
renderHotbar();renderInventory();
}
function placeBlock(){
if(!playing||paused)return;
if(mode==="adventure"){toast("No puedes colocar bloques en Aventura.");return;}
if(mode!=="creative"&&(inventory[selected]||0)<=0){toast("No tienes ese bloque.");return;}
const hit=targetBlock();if(!hit)return;
const p=hit.object.position.clone().add(hit.face.normal);
const x=Math.round(p.x),y=Math.round(p.y),z=Math.round(p.z);
if(Math.abs(x-player.x)<1&&Math.abs(z-player.z)<1&&y>=player.y-1&&y<=player.y+2)return;
addBlock(x,y,z,selected);
if(mode!=="creative")inventory[selected]--;
renderHotbar();renderInventory();
}
function renderHotbar(){
const bar=$("hotbar");bar.innerHTML="";
const chosen=definitions.slice(0,5);
chosen.forEach(([type,label])=>{
const b=document.createElement("button");b.className="slot"+(selected===type?" selected":"");
b.textContent=label+(mode==="creative"?"":" "+(inventory[type]||0));
b.onclick=()=>{selected=type;renderHotbar();};
bar.append(b);
});
}
function renderInventory(){
const el=$("inventoryItems");el.innerHTML="";
definitions.forEach(([type,label])=>{
const b=document.createElement("button");
b.textContent=label+" "+(mode==="creative"?"∞":inventory[type]||0);
b.onclick=()=>{selected=type;renderHotbar();toast("Seleccionado: "+label);};
el.append(b);
});
}
function toast(t){$("toast").textContent=t;}
function jump(){if(playing&&!paused&&player.grounded){player.vy=7;player.grounded=false;}}
$("breakBtn").onclick=breakBlock;
$("placeBtn").onclick=placeBlock;
$("jumpBtn").onclick=jump;
$("invBtn").onclick=()=>{
$("inventory").style.display=$("inventory").style.display==="block"?"none":"block";
renderInventory();
};
$("closeInv").onclick=()=>$("inventory").style.display="none";
$("cameraBtn").onclick=()=>{
cameraMode=cameraMode==="first"?"third":"first";
$("cameraBtn").textContent=cameraMode==="first"?"TERCERA PERSONA":"PRIMERA PERSONA";
updateCamera();
};
$("pauseBtn").onclick=()=>{
paused=!paused;
$("pauseBtn").textContent=paused?"CONTINUAR":"PAUSA";
toast(paused?"Partida pausada":"Partida reanudada");
};
$("exitBtn").onclick=()=>{
playing=false;paused=false;
$("gameScreen").classList.add("hidden");showScreen("worlds");
};
document.addEventListener("keydown",e=>{
keys[e.key.toLowerCase()]=true;
if([" ","arrowup","arrowdown"].includes(e.key.toLowerCase()))e.preventDefault();
if(e.key>="1"&&e.key<="5"){selected=definitions[Number(e.key)-1][0];renderHotbar();}
});
document.addEventListener("keyup",e=>keys[e.key.toLowerCase()]=false);
document.querySelectorAll("[data-move]").forEach(b=>{
const d=b.dataset.move;
const down=e=>{e.preventDefault();mobile[d]=true;};
const up=e=>{e.preventDefault();mobile[d]=false;};
b.addEventListener("pointerdown",down);b.addEventListener("pointerup",up);
b.addEventListener("pointercancel",up);b.addEventListener("pointerleave",up);
});
function onPointerDown(e){
if(!playing||paused)return;
if(e.pointerType==="mouse"){if(e.button===0)breakBlock();if(e.button===2)placeBlock();}
if(e.pointerType==="touch"&&$("touchLook").checked){
dragging=true;lastX=e.clientX;lastY=e.clientY;
}
}
function onPointerMove(e){
if(!dragging||!playing||paused||!$("touchLook").checked)return;
yaw-=(e.clientX-lastX)*.006;pitch-=(e.clientY-lastY)*.006;
pitch=Math.max(-1.2,Math.min(1.2,pitch));
lastX=e.clientX;lastY=e.clientY;
}
function updateCamera(){
if(!camera)return;
if(cameraMode==="first"){
camera.position.set(player.x,player.y+1.6,player.z);
camera.rotation.set(pitch,yaw,0,"YXZ");
}else{
camera.position.set(player.x+Math.sin(yaw)*5,player.y+2.5,player.z+Math.cos(yaw)*5);
camera.lookAt(player.x,player.y+1,player.z);
}
}
function update(now){
requestAnimationFrame(update);
const dt=Math.min((now-lastTime)/1000,.04)||0;lastTime=now;
if(renderer&&scene&&playing&&!paused){
let f=0,s=0;
if(keys.w||keys.arrowup||mobile.forward)f++;
if(keys.s||keys.arrowdown||mobile.back)f--;
if(keys.a||mobile.left)s--;
if(keys.d||mobile.right)s++;
const len=Math.hypot(f,s)||1;f/=len;s/=len;
const speed=mode==="creative"?7:4.5;
player.x+=(-Math.sin(yaw)*f+Math.cos(yaw)*s)*speed*dt;
player.z+=(-Math.cos(yaw)*f-Math.sin(yaw)*s)*speed*dt;
if(keys[" "]&&player.grounded)jump();
player.vy-=18*dt;player.y+=player.vy*dt;
if(player.y<3){player.y=3;player.vy=0;player.grounded=true;}
updateCamera();
}
if(renderer&&scene&&camera)renderer.render(scene,camera);
}
const addonData=[
["verity","Verity","Complemento de demostración."],
["huggy","Huggy & sus amigos","Criaturas de demostración."],
["actions","Actions & Stuff","Vista previa visual."],
["ruby","Ruby Update","Bloques de rubí de demostración."]
];
function renderMarket(){
const list=$("addonList");list.innerHTML="";
addonData.forEach(([id,name,desc])=>{
const card=document.createElement("div");card.className="panel";
const h=document.createElement("h3");h.textContent=name;
const p=document.createElement("p");p.textContent=desc;
const b=document.createElement("button");
b.textContent=addons[id]?"DESACTIVAR":"ACTIVAR";
b.onclick=()=>{addons[id]=!addons[id];save();renderMarket();};
card.append(h,p,b);list.append(card);
});
}
addEventListener("resize",resize3D);
renderWorlds();showScreen("home");requestAnimationFrame(update);
})();
</script>
</body>
</html>Game Source: Block World 3D
Creator: MagicKnight13
Libraries: three
Complexity: complex (519 lines, 21.3 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: block-world-3d-magicknight13-mvoflrlz" to link back to the original. Then publish at arcadelab.ai/publish.