Meta Description" name="description" />
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Ciudad Zombie - CELULAR</title>
<style>
*{margin:0;padding:0;touch-action:none;user-select:none}
body{overflow:hidden;background:#000}
#hud{position:absolute;top:0;left:0;right:0;background:rgba(0,0,0,0.7);color:white;font-family:Arial;padding:8px;display:flex;justify-content:space-between;z-index:10;font-size:14px}
#inv{position:absolute;top:40px;left:10px;background:rgba(0,0,0,0.6);color:white;font-family:Arial;padding:6px 10px;border-radius:8px;z-index:10;font-size:12px}
#joyleft{position:absolute;bottom:20px;left:20px;width:120px;height:120px;background:rgba(255,255,255,0.15);border-radius:50%;z-index:10}
#stickleft{position:absolute;width:50px;height:50px;background:rgba(255,255,255,0.5);border-radius:50%;left:35px;top:35px}
#joyright{position:absolute;bottom:20px;right:20px;width:120px;height:120px;background:rgba(255,255,255,0.1);border-radius:50%;z-index:10}
#btns{position:absolute;bottom:160px;right:20px;display:flex;gap:10px;z-index:10}
.btn{width:70px;height:70px;background:rgba(255,255,255,0.25);border:2px solid white;border-radius:50%;color:white;font-weight:bold;display:flex;align-items:center;justify-content:center;font-size:12px}
#msg{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);background:rgba(0,0,0,0.9);color:white;padding:25px;border-radius:15px;display:none;z-index:20;font-family:Arial;text-align:center}
</style>
<script type="importmap">{"imports":{"three":"https://unpkg.com/three@0.160.0/build/three.module.js"}}</script>
</head>
<body>
<div id="hud"><div>β€οΈ<span id="hp">100</span> | π<span id="hunger">100</span> | π§<span id="kills">0</span></div><div id="tip">BUSCA CAJAS AMARILLAS</div></div>
<div id="inv">π <span id="invtxt">Machete x1 | Lata x1</span></div>
<div id="joyleft"><div id="stickleft"></div></div>
<div id="joyright"></div>
<div id="btns">
<div class="btn" id="bLoot">LOOT</div>
<div class="btn" id="bEat">COMER</div>
<div class="btn" id="bAtk" style="background:rgba(255,0,0,0.4)">MACHETE</div>
</div>
<div id="msg"></div>
<script type="module">
import * as THREE from 'three';
let scene=new THREE.Scene(); scene.background=new THREE.Color(0x202030); scene.fog=new THREE.Fog(0x202030,15,80);
let camera=new THREE.PerspectiveCamera(75,innerWidth/innerHeight,0.1,1000);
let renderer=new THREE.WebGLRenderer({antialias:true}); renderer.setSize(innerWidth,innerHeight); document.body.appendChild(renderer.domElement);
scene.add(new THREE.AmbientLight(0xffffff,0.6)); let dl=new THREE.DirectionalLight(0xffffff,0.9); dl.position.set(20,30,10); scene.add(dl);
let floor=new THREE.Mesh(new THREE.PlaneGeometry(400,400), new THREE.MeshLambertMaterial({color:0x333333})); floor.rotation.x=-Math.PI/2; scene.add(floor);
let buildings=[]; let lootBoxes=[];
for(let i=0;i<35;i++){
let w=4+Math.random()*6,h=5+Math.random()*10,d=4+Math.random()*6;
let x=(Math.random()-0.5)*160,z=(Math.random()-0.5)*160;
if(Math.sqrt(x*x+z*z)<12) continue;
let b=new THREE.Mesh(new THREE.BoxGeometry(w,h,d), new THREE.MeshLambertMaterial({color:0x555555}));
b.position.set(x,h/2,z); scene.add(b); buildings.push(b);
if(Math.random()<0.75){
let loot=new THREE.Mesh(new THREE.BoxGeometry(0.9,0.9,0.9), new THREE.MeshLambertMaterial({color:0xffff00}));
loot.position.set(x+(Math.random()-0.5)*2,0.45,z+(Math.random()-0.5)*2); scene.add(loot);
lootBoxes.push({mesh:loot,type:Math.random()<0.6?'comida':'arma',taken:false});
}
}
let player={hp:100,hunger:100,kills:0,inv:{lata:1,pistola:0,balas:0},pos:new THREE.Vector3(0,1.6,0),yaw:0,pitch:0,moveX:0,moveY:0};
let zombies=[];
function spawnZ(){
let a=Math.random()*Math.PI*2,r=25+Math.random()*45;
let mesh=new THREE.Mesh(new THREE.CapsuleGeometry(0.4,1.2), new THREE.MeshLambertMaterial({color:0x00aa00}));
mesh.position.set(Math.cos(a)*r,1,Math.sin(a)*r); scene.add(mesh);
zombies.push({mesh,hp:100});
}
for(let i=0;i<12;i++) spawnZ();
setInterval(()=>{if(zombies.length<18) spawnZ();},2500);
let joyL=document.getElementById('joyleft'), stickL=document.getElementById('stickleft'), touchIdL=null;
joyL.addEventListener('touchstart',e=>{touchIdL=e.changedTouches[0].identifier;});
joyL.addEventListener('touchmove',e=>{
for(let t of e.changedTouches){ if(t.identifier===touchIdL){
let rect=joyL.getBoundingClientRect();
let x=t.clientX-rect.left-60, y=t.clientY-rect.top-60;
let d=Math.sqrt(x*x+y*y); if(d>45){x=x/d*45; y=y/d*45;}
stickL.style.left=35+x+'px'; stickL.style.top=35+y+'px';
player.moveX=x/45; player.moveY=y/45;
}}
});
function resetL(){stickL.style.left='35px'; stickL.style.top='35px'; player.moveX=0; player.moveY=0; touchIdL=null;}
joyL.addEventListener('touchend',resetL); joyL.addEventListener('touchcancel',resetL);
let joyR=document.getElementById('joyright'), touchIdR=null, lastX=0,lastY=0;
joyR.addEventListener('touchstart',e=>{touchIdR=e.changedTouches[0].identifier; lastX=e.changedTouches[0].clientX; lastY=e.changedTouches[0].clientY;});
joyR.addEventListener('touchmove',e=>{
for(let t of e.changedTouches){ if(t.identifier===touchIdR){
let dx=t.clientX-lastX, dy=t.clientY-lastY;
player.yaw-=dx*0.008; player.pitch-=dy*0.008; player.pitch=Math.max(-1.3,Math.min(1.3,player.pitch));
lastX=t.clientX; lastY=t.clientY;
}}
});
joyR.addEventListener('touchend',()=>touchIdR=null);
function updateInv(){
let t=`Machete | Latas x${player.inv.lata}`; if(player.inv.pistola) t+=` | Pistola ${player.inv.balas}`;
document.getElementById('invtxt').innerText=t;
document.getElementById('hp').innerText=Math.floor(player.hp);
document.getElementById('hunger').innerText=Math.floor(player.hunger);
document.getElementById('kills').innerText=player.kills;
}
document.getElementById('bLoot').addEventListener('touchstart',e=>{e.preventDefault(); tryLoot();});
document.getElementById('bEat').addEventListener('touchstart',e=>{e.preventDefault(); if(player.inv.lata>0){player.inv.lata--; player.hunger=Math.min(100,player.hunger+35); player.hp=Math.min(100,player.hp+10); updateInv();}});
document.getElementById('bAtk').addEventListener('touchstart',e=>{e.preventDefault(); attack();});
function tryLoot(){
let near=null,d=3;
lootBoxes.forEach(l=>{ if(!l.taken && l.mesh.position.distanceTo(player.pos)<d){near=l; d=l.mesh.position.distanceTo(player.pos);} });
if(near){near.taken=true; scene.remove(near.mesh); if(near.type=='comida') player.inv.lata++; else{player.inv.pistola=1; player.inv.balas+=12;} updateInv();}
}
function attack(){
zombies.forEach((z,i)=>{
if(z.mesh.position.distanceTo(player.pos)<3){
z.hp-=50; z.mesh.material.color.set(0xff0000); setTimeout(()=>{if(z.mesh.material) z.mesh.material.color.set(0x00aa00)},150);
if(z.hp<=0){scene.remove(z.mesh); zombies.splice(i,1); player.kills++; updateInv();}
}
});
}
let hungerT=0;
function update(){
let fwd=new THREE.Vector3(Math.sin(player.yaw),0,Math.cos(player.yaw)).multiplyScalar(-1);
let right=new THREE.Vector3().crossVectors(fwd,new THREE.Vector3(0,1,0)).normalize();
let move=new THREE.Vector3();
move.addScaledVector(fwd,-player.moveY); move.addScaledVector(right,player.moveX);
move.normalize().multiplyScalar(0.15);
let np=player.pos.clone().add(move);
let block=buildings.some(b=>Math.abs(np.x-b.position.x)<b.geometry.parameters.width/2+0.6 && Math.abs(np.z-b.position.z)<b.geometry.parameters.depth/2+0.6);
if(!block) player.pos.copy(np);
camera.position.copy(player.pos); camera.rotation.order='YXZ'; camera.rotation.y=player.yaw; camera.rotation.x=player.pitch;
zombies.forEach(z=>{
let dir=player.pos.clone().sub(z.mesh.position); dir.y=0; dir.normalize().multiplyScalar(0.04);
let nz=z.mesh.position.clone().add(dir);
let bBlock=buildings.some(b=>Math.abs(nz.x-b.position.x)<b.geometry.parameters.width/2+0.6 && Math.abs(nz.z-b.position.z)<b.geometry.parameters.depth/2+0.6);
if(!bBlock) z.mesh.position.copy(nz);
z.mesh.lookAt(player.pos);
if(z.mesh.position.distanceTo(player.pos)<1.9){player.hp-=0.25; updateInv(); if(player.hp<=0) end();}
});
hungerT+=0.016; if(hungerT>2.5){hungerT=0; player.hunger-=1; if(player.hunger<=0) player.hp-=1.2; updateInv(); if(player.hp<=0) end();}
}
function end(){document.getElementById('msg').innerHTML=`<h2>MORISTE</h2><p>Matastes: ${player.kills}</p><button onclick="location.reload()" style="padding:10px 20px;background:#27ae60;border:none;border-radius:8px;color:white;font-weight:bold;margin-top:10px">REINTENTAR</button>`; document.getElementById('msg').style.display='block';}
function animate(){requestAnimationFrame(animate); update(); renderer.render(scene,camera);}
animate();
addEventListener('resize',()=>{camera.aspect=innerWidth/innerHeight; camera.updateProjectionMatrix(); renderer.setSize(innerWidth,innerHeight);});
updateInv();
</script>
</body>
</html>2
2
260KB
1252KB
558.0ms
368.0ms
559.0ms