Meta Description" name="description" />
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>3D War Advanced</title>
<style>
body { margin:0; overflow:hidden; background:black; touch-action:none; }
canvas { display:block; }
#attackBtn, #axeBtn{
position:absolute;
width:75px;
height:75px;
border-radius:50%;
text-align:center;
line-height:75px;
font-size:22px;
color:white;
opacity:0.9;
}
#attackBtn{ right:30px; bottom:60px; background:#8b0000; }
#axeBtn{ right:120px; bottom:130px; background:#555; }
#joystickBase{
position:absolute;
left:40px;
bottom:80px;
width:130px;
height:130px;
border-radius:50%;
background:rgba(255,255,255,0.08);
}
#joystickStick{
position:absolute;
width:60px;
height:60px;
border-radius:50%;
background:rgba(255,255,255,0.35);
left:75px;
bottom:115px;
}
</style>
</head>
<body>
<div id="attackBtn">⚔</div>
<div id="axeBtn">🪓</div>
<div id="joystickBase"></div>
<div id="joystickStick"></div>
<script src="https://cdn.jsdelivr.net/npm/three@0.152.2/build/three.min.js"></script>
<script>
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x0d0d12);
scene.fog = new THREE.Fog(0x000000, 20, 80);
const camera = new THREE.PerspectiveCamera(70, window.innerWidth/window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({antialias:true});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
document.body.appendChild(renderer.domElement);
// Luces
scene.add(new THREE.HemisphereLight(0xffffff,0x222233,0.6));
const dirLight = new THREE.DirectionalLight(0xffffff,1);
dirLight.position.set(10,15,10);
dirLight.castShadow = true;
scene.add(dirLight);
// Suelo
const floor = new THREE.Mesh(
new THREE.PlaneGeometry(300,300),
new THREE.MeshStandardMaterial({color:0x1a1a1f, roughness:0.9})
);
floor.rotation.x = -Math.PI/2;
floor.receiveShadow = true;
scene.add(floor);
// ===== PERSONAJE =====
function createCharacter(color){
const g = new THREE.Group();
const mat = new THREE.MeshStandardMaterial({color, roughness:0.7});
const skin = new THREE.MeshStandardMaterial({color:0xffd2a6});
const torso = new THREE.Mesh(new THREE.CylinderGeometry(0.7,0.9,2.2,32), mat);
torso.position.y = 2;
g.add(torso);
const head = new THREE.Mesh(new THREE.SphereGeometry(0.6,32,32), skin);
head.position.y = 3.6;
g.add(head);
const limbGeo = new THREE.CylinderGeometry(0.3,0.3,2,16);
const armL = new THREE.Mesh(limbGeo, mat);
armL.position.set(-1.1,2.2,0);
armL.rotation.z = Math.PI/8;
g.add(armL);
const armR = armL.clone();
armR.position.x = 1.1;
armR.rotation.z = -Math.PI/8;
g.add(armR);
const legL = new THREE.Mesh(limbGeo, mat);
legL.scale.y = 1.1;
legL.position.set(-0.4,0.9,0);
g.add(legL);
const legR = legL.clone();
legR.position.x = 0.4;
g.add(legR);
return g;
}
const player = createCharacter(0xaa0000);
scene.add(player);
// ===== HACHA =====
const axe = new THREE.Group();
const handle = new THREE.Mesh(
new THREE.CylinderGeometry(0.1,0.1,2,16),
new THREE.MeshStandardMaterial({color:0x8b4513})
);
handle.rotation.z = Math.PI/2;
axe.add(handle);
const blade = new THREE.Mesh(
new THREE.BoxGeometry(0.2,1.2,1),
new THREE.MeshStandardMaterial({color:0xcccccc, metalness:0.8})
);
blade.position.x = 1;
axe.add(blade);
axe.visible=false;
scene.add(axe);
let axeState="idle";
let axeVelocity=new THREE.Vector3();
// ===== ENEMIGOS =====
let enemies=[];
function spawnEnemy(){
const e = createCharacter(0x006600);
e.position.set((Math.random()-0.5)*40,0,(Math.random()-0.5)*40);
scene.add(e);
enemies.push(e);
}
setInterval(spawnEnemy,5000);
// ===== JOYSTICK =====
const base=document.getElementById("joystickBase");
const stick=document.getElementById("joystickStick");
let joystick={active:false,dx:0,dy:0,touchId:null};
renderer.domElement.addEventListener("touchstart", e=>{
for(let t of e.changedTouches){
if(t.clientX<window.innerWidth/2 && !joystick.active){
joystick.active=true;
joystick.touchId=t.identifier;
moveStick(t.clientX,t.clientY);
}
}
});
renderer.domElement.addEventListener("touchmove", e=>{
for(let t of e.changedTouches){
if(t.identifier===joystick.touchId){
moveStick(t.clientX,t.clientY);
}
}
// Cámara (lado derecho)
const t=e.touches[0];
if(t.clientX>window.innerWidth/2){
camAngle += e.movementX*0.002;
}
});
renderer.domElement.addEventListener("touchend", e=>{
joystick.dx=0; joystick.dy=0;
joystick.active=false;
stick.style.left="75px";
stick.style.bottom="115px";
});
function moveStick(x,y){
const r=base.getBoundingClientRect();
const cx=r.left+r.width/2;
const cy=r.top+r.height/2;
let dx=x-cx;
let dy=y-cy;
const max=45;
const dist=Math.sqrt(dx*dx+dy*dy);
if(dist>max){
dx=dx/dist*max;
dy=dy/dist*max;
}
joystick.dx=dx/max;
joystick.dy=dy/max;
stick.style.left=(cx-30+dx)+"px";
stick.style.top=(cy-30+dy)+"px";
}
// ===== CONTROLES =====
document.getElementById("axeBtn").addEventListener("touchstart", ()=>{
if(axeState==="idle"){
axe.visible=true;
axe.position.copy(player.position);
const dir=new THREE.Vector3(0,0,-1).applyQuaternion(player.quaternion);
axeVelocity.copy(dir).multiplyScalar(0.8);
axeState="forward";
}
});
// ===== CÁMARA LIBRE =====
let camAngle=0;
const camDistance=15;
const camHeight=8;
// ===== LOOP =====
function animate(){
requestAnimationFrame(animate);
// Movimiento jugador relativo a cámara
const forward=new THREE.Vector3(Math.sin(camAngle),0,Math.cos(camAngle));
const right=new THREE.Vector3(forward.z,0,-forward.x);
player.position.add(forward.clone().multiplyScalar(-joystick.dy*0.4));
player.position.add(right.clone().multiplyScalar(joystick.dx*0.4));
if(joystick.dx||joystick.dy){
player.rotation.y=Math.atan2(-joystick.dx,-joystick.dy)+camAngle;
}
// Enemigos siguen
enemies.forEach(e=>{
e.position.lerp(player.position,0.002);
e.lookAt(player.position);
});
// Hacha
if(axeState==="forward"){
axe.position.add(axeVelocity);
if(axe.position.distanceTo(player.position)>15){
axeState="return";
}
} else if(axeState==="return"){
const dir=new THREE.Vector3().subVectors(player.position,axe.position).normalize();
axe.position.add(dir.multiplyScalar(1));
if(axe.position.distanceTo(player.position)<1){
axe.visible=false;
axeState="idle";
}
}
enemies.forEach((e,i)=>{
if(axe.visible && axe.position.distanceTo(e.position)<2){
scene.remove(e);
enemies.splice(i,1);
}
});
// Cámara orbitable
camera.position.x = player.position.x + Math.sin(camAngle)*camDistance;
camera.position.z = player.position.z + Math.cos(camAngle)*camDistance;
camera.position.y = camHeight;
camera.lookAt(player.position);
renderer.render(scene,camera);
}
animate();
</script>
</body>
</html>2
2
162KB
626KB
2,612.0ms
136.0ms
2,612.0ms