top of page
Buscar

BATALLA ELECTRICA 3D


<!DOCTYPE html>

<html lang="es">

<head>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<title>Batalla de Humanoides 3D - Laberinto Eléctrico</title>

<style>

body {

margin: 0;

padding: 0;

background: radial-gradient(circle, #0a0a2e 0%, #16213e 50%, #0f3460 100%);

overflow: hidden;

cursor: crosshair;

font-family: 'Arial', sans-serif;

}


position: absolute;

top: 20px;

left: 20px;

color: white;

background: rgba(0,0,0,0.8);

padding: 20px;

border-radius: 15px;

font-size: 16px;

z-index: 100;

backdrop-filter: blur(10px);

border: 2px solid #4fc3f7;

}


position: absolute;

top: 20px;

right: 20px;

color: white;

background: rgba(0,0,0,0.8);

padding: 15px;

border-radius: 10px;

font-size: 12px;

z-index: 100;

backdrop-filter: blur(10px);

border: 2px solid #ff6b35;

}


position: absolute;

top: 0;

left: 0;

width: 100%;

height: 100%;

background: rgba(0,0,0,0.9);

display: none;

align-items: center;

justify-content: center;

flex-direction: column;

color: white;

font-size: 24px;

text-align: center;

z-index: 200;

}


background: linear-gradient(45deg, #4fc3f7, #29b6f6);

color: white;

border: none;

padding: 20px 40px;

font-size: 18px;

border-radius: 15px;

cursor: pointer;

margin-top: 30px;

transition: all 0.3s ease;

text-transform: uppercase;

font-weight: bold;

}


#restartBtn:hover {

transform: scale(1.1);

box-shadow: 0 0 30px rgba(79, 195, 247, 0.8);

}


.stat {

display: flex;

justify-content: space-between;

margin: 8px 0;

padding: 5px 0;

border-bottom: 1px solid rgba(255,255,255,0.2);

}


.stat-value {

font-weight: bold;

color: #4fc3f7;

}


position: absolute;

top: 50%;

left: 50%;

transform: translate(-50%, -50%);

width: 30px;

height: 30px;

border: 2px solid #4fc3f7;

border-radius: 50%;

pointer-events: none;

z-index: 150;

box-shadow: 0 0 20px rgba(79, 195, 247, 0.6);

}


#crosshair::before, #crosshair::after {

content: '';

position: absolute;

background: #4fc3f7;

box-shadow: 0 0 10px rgba(79, 195, 247, 0.8);

}


#crosshair::before {

width: 2px;

height: 10px;

top: 50%;

left: 50%;

transform: translate(-50%, -50%);

}


#crosshair::after {

width: 10px;

height: 2px;

top: 50%;

left: 50%;

transform: translate(-50%, -50%);

}

</style>

</head>

<body>

<div id="crosshair"></div>

<div id="gameUI">

<h3>⚡ BATALLA ELÉCTRICA 3D ⚡</h3>

<div class="stat">

<span>Puntuación:</span>

<span class="stat-value" id="score">0</span>

</div>

<div class="stat">

<span>Rayos Recibidos:</span>

<span class="stat-value" id="hitsReceived">0 / 10</span>

</div>

<div class="stat">

<span>NPCs Activos:</span>

<span class="stat-value" id="npcCount">0</span>

</div>

<div class="stat">

<span>Nivel Laberinto:</span>

<span class="stat-value" id="mazeLevel">1</span>

</div>

</div>


<div id="controls">

<strong>🎮 CONTROLES:</strong><br>

🖱️ Mover Mouse: Controlar humanoide<br>

🖱️ Click Izquierdo: Disparar rayo<br>

⚡ Objetivo: Evita 10 impactos enemigos<br>

🎯 Destruye NPCs para ganar puntos

</div>


<div id="gameOverScreen">

<h1 id="gameOverTitle">¡JUEGO TERMINADO!</h1>

<h2 id="finalScore">Puntuación Final: 0</h2>

<p id="gameOverMessage">Has sido alcanzado por 10 rayos enemigos</p>

<button id="restartBtn">🔄 JUGAR DE NUEVO</button>

</div>


<script>

class ElectricMazeGame {

constructor() {

this.scene = new THREE.Scene();

this.camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);

this.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });

// Game state

this.player = null;

this.npcs = [];

this.playerRays = [];

this.enemyRays = [];

this.maze = [];

this.mazeColors = [0x4fc3f7, 0xff6b35, 0x4caf50, 0xff4757, 0x9c88ff, 0xffa726];

this.currentMazeColor = 0;

// Game stats

this.score = 0;

this.hitsReceived = 0;

this.maxHits = 10;

this.gameRunning = true;

this.mazeLevel = 1;

// Mouse control

this.mouse = new THREE.Vector2();

this.raycaster = new THREE.Raycaster();

this.init();

}


init() {

// Configurar renderer

this.renderer.setSize(window.innerWidth, window.innerHeight);

this.renderer.shadowMap.enabled = true;

this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;

this.renderer.setClearColor(0x000011, 1);

document.body.appendChild(this.renderer.domElement);


// Configurar cámara (vista desde arriba)

this.camera.position.set(0, 25, 8);

this.camera.lookAt(0, 0, 0);


// Iluminación

this.setupLighting();

// Crear laberinto

this.createMaze();

// Crear jugador

this.createPlayer();

// Crear NPCs

this.createNPCs();

// Event listeners

this.setupEventListeners();

// Iniciar bucle de juego

this.gameLoop();

}


setupLighting() {

// Luz ambiental

const ambientLight = new THREE.AmbientLight(0x404040, 0.4);

this.scene.add(ambientLight);


// Luz direccional

const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);

directionalLight.position.set(0, 20, 0);

directionalLight.castShadow = true;

directionalLight.shadow.mapSize.width = 2048;

directionalLight.shadow.mapSize.height = 2048;

directionalLight.shadow.camera.left = -20;

directionalLight.shadow.camera.right = 20;

directionalLight.shadow.camera.top = 20;

directionalLight.shadow.camera.bottom = -20;

this.scene.add(directionalLight);


// Luces de colores dinámicas

for (let i = 0; i < 4; i++) {

const light = new THREE.PointLight(0x4fc3f7, 0.5, 15);

light.position.set(

(i % 2 === 0 ? -1 : 1) * 12,

5,

(i < 2 ? -1 : 1) * 12

);

this.scene.add(light);

}

}


createMaze() {

// Limpiar laberinto anterior

this.maze.forEach(wall => this.scene.remove(wall));

this.maze = [];


const wallColor = this.mazeColors[this.currentMazeColor];

const mazeSize = 8 + this.mazeLevel;

// Paredes exteriores

this.createWall(-mazeSize, 0, 0, mazeSize * 2, 1, wallColor);

this.createWall(mazeSize, 0, 0, mazeSize * 2, 1, wallColor);

this.createWall(0, 0, -mazeSize, 1, mazeSize * 2, wallColor);

this.createWall(0, 0, mazeSize, 1, mazeSize * 2, wallColor);


// Paredes internas aleatorias

const numWalls = 6 + this.mazeLevel * 2;

for (let i = 0; i < numWalls; i++) {

const x = (Math.random() - 0.5) * (mazeSize * 1.5);

const z = (Math.random() - 0.5) * (mazeSize * 1.5);

const isVertical = Math.random() > 0.5;

if (isVertical) {

this.createWall(x, 0, z, 1, 3 + Math.random() * 3, wallColor);

} else {

this.createWall(x, 0, z, 3 + Math.random() * 3, 1, wallColor);

}

}


// Suelo

const floorGeometry = new THREE.PlaneGeometry(mazeSize * 3, mazeSize * 3);

const floorMaterial = new THREE.MeshLambertMaterial({

color: 0x1a1a2e,

transparent: true,

opacity: 0.8

});

const floor = new THREE.Mesh(floorGeometry, floorMaterial);

floor.rotation.x = -Math.PI / 2;

floor.receiveShadow = true;

this.scene.add(floor);

}


createWall(x, y, z, width, depth, color) {

const wallGeometry = new THREE.BoxGeometry(width, 4, depth);

const wallMaterial = new THREE.MeshPhongMaterial({

color: color,

transparent: true,

opacity: 0.8,

emissive: color,

emissiveIntensity: 0.1

});

const wall = new THREE.Mesh(wallGeometry, wallMaterial);

wall.position.set(x, y + 2, z);

wall.castShadow = true;

wall.receiveShadow = true;

this.scene.add(wall);

this.maze.push(wall);

}


createHumanoid(x, y, z, color, isPlayer = false) {

const group = new THREE.Group();

// Torso

const torsoGeometry = new THREE.BoxGeometry(0.8, 1.5, 0.5);

const torsoMaterial = new THREE.MeshPhongMaterial({ color: color });

const torso = new THREE.Mesh(torsoGeometry, torsoMaterial);

torso.position.y = 1;

torso.castShadow = true;

group.add(torso);


// Cabeza

const headGeometry = new THREE.SphereGeometry(0.4);

const headMaterial = new THREE.MeshPhongMaterial({ color: 0xffdbac });

const head = new THREE.Mesh(headGeometry, headMaterial);

head.position.y = 2.2;

head.castShadow = true;

group.add(head);


// Brazos

const armGeometry = new THREE.CylinderGeometry(0.15, 0.2, 1.2);

const armMaterial = new THREE.MeshPhongMaterial({ color: 0xffdbac });

const leftArm = new THREE.Mesh(armGeometry, armMaterial);

leftArm.position.set(-0.7, 1, 0);

leftArm.castShadow = true;

group.add(leftArm);

const rightArm = new THREE.Mesh(armGeometry, armMaterial);

rightArm.position.set(0.7, 1, 0);

rightArm.castShadow = true;

group.add(rightArm);


// Piernas

const legGeometry = new THREE.CylinderGeometry(0.2, 0.25, 1.5);

const legMaterial = new THREE.MeshPhongMaterial({ color: color });

const leftLeg = new THREE.Mesh(legGeometry, legMaterial);

leftLeg.position.set(-0.3, -0.2, 0);

leftLeg.castShadow = true;

group.add(leftLeg);

const rightLeg = new THREE.Mesh(legGeometry, legMaterial);

rightLeg.position.set(0.3, -0.2, 0);

rightLeg.castShadow = true;

group.add(rightLeg);


// Aura especial para el jugador

if (isPlayer) {

const auraGeometry = new THREE.SphereGeometry(2);

const auraMaterial = new THREE.MeshBasicMaterial({

color: 0x4fc3f7,

transparent: true,

opacity: 0.15,

side: THREE.DoubleSide

});

const aura = new THREE.Mesh(auraGeometry, auraMaterial);

group.add(aura);

}


group.position.set(x, y, z);

return {

group: group,

color: color,

health: isPlayer ? 100 : 50,

lastShot: 0,

shootCooldown: isPlayer ? 200 : 1000 + Math.random() * 2000,

moveSpeed: isPlayer ? 0 : 0.02,

direction: new THREE.Vector3(Math.random() - 0.5, 0, Math.random() - 0.5).normalize()

};

}


createPlayer() {

this.player = this.createHumanoid(0, 0, 0, 0x4fc3f7, true);

this.scene.add(this.player.group);

}


createNPCs() {

const npcCount = 3 + this.mazeLevel;

const positions = [

{x: -8, z: -8}, {x: 8, z: 8}, {x: -8, z: 8},

{x: 8, z: -8}, {x: 0, z: -10}, {x: -10, z: 0},

{x: 10, z: 0}, {x: 0, z: 10}

];


for (let i = 0; i < npcCount && i < positions.length; i++) {

const npc = this.createHumanoid(positions[i].x, 0, positions[i].z, 0xff4757);

this.npcs.push(npc);

this.scene.add(npc.group);

}

this.updateUI();

}


createRay(startPos, direction, isPlayer = true) {

const rayGroup = new THREE.Group();

// Núcleo del rayo

const coreGeometry = new THREE.SphereGeometry(0.2);

const coreMaterial = new THREE.MeshBasicMaterial({

color: isPlayer ? 0x4fc3f7 : 0xff4757,

emissive: isPlayer ? 0x2288cc : 0xcc2222

});

const core = new THREE.Mesh(coreGeometry, coreMaterial);

rayGroup.add(core);


// Efectos de rayo

for (let i = 0; i < 8; i++) {

const sparkGeometry = new THREE.SphereGeometry(0.05);

const sparkMaterial = new THREE.MeshBasicMaterial({

color: isPlayer ? 0x88ccff : 0xff8888,

emissive: isPlayer ? 0x4488cc : 0xcc4444

});

const spark = new THREE.Mesh(sparkGeometry, sparkMaterial);

const angle = (i / 8) * Math.PI * 2;

spark.position.set(

Math.cos(angle) * 0.4,

Math.sin(angle * 2) * 0.2,

Math.sin(angle) * 0.4

);

rayGroup.add(spark);

}


rayGroup.position.copy(startPos);

rayGroup.position.y += 1.5;

this.scene.add(rayGroup);

return {

group: rayGroup,

velocity: direction.clone().multiplyScalar(0.5),

life: 300,

isPlayer: isPlayer

};

}


setupEventListeners() {

// Control del jugador con mouse

document.addEventListener('mousemove', (event) => {

if (!this.gameRunning) return;

this.mouse.x = (event.clientX / window.innerWidth) * 2 - 1;

this.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;

// Convertir posición del mouse a coordenadas del mundo

this.raycaster.setFromCamera(this.mouse, this.camera);

const intersects = this.raycaster.intersectObjects(this.scene.children, true);

if (intersects.length > 0) {

const point = intersects[0].point;

this.player.group.position.x = THREE.MathUtils.lerp(this.player.group.position.x, point.x, 0.1);

this.player.group.position.z = THREE.MathUtils.lerp(this.player.group.position.z, point.z, 0.1);

}

});


// Disparar con click

document.addEventListener('click', (event) => {

if (!this.gameRunning) return;

this.playerShoot();

});


// Botón de reinicio

document.getElementById('restartBtn').addEventListener('click', () => {

this.restart();

});


// Resize

window.addEventListener('resize', () => {

this.camera.aspect = window.innerWidth / window.innerHeight;

this.camera.updateProjectionMatrix();

this.renderer.setSize(window.innerWidth, window.innerHeight);

});

}


playerShoot() {

const now = Date.now();

if (now - this.player.lastShot < this.player.shootCooldown) return;

// Dirección hacia el centro de NPCs más cercano

let targetDirection = new THREE.Vector3(0, 0, -1);

if (this.npcs.length > 0) {

const closestNPC = this.npcs.reduce((closest, npc) => {

const dist1 = this.player.group.position.distanceTo(npc.group.position);

const dist2 = this.player.group.position.distanceTo(closest.group.position);

return dist1 < dist2 ? npc : closest;

});

targetDirection = closestNPC.group.position.clone()

.sub(this.player.group.position)

.normalize();

}

const ray = this.createRay(this.player.group.position, targetDirection, true);

this.playerRays.push(ray);

this.player.lastShot = now;

}


updateNPCs() {

this.npcs.forEach((npc, index) => {

const now = Date.now();

// Movimiento aleatorio

npc.group.position.add(npc.direction.clone().multiplyScalar(npc.moveSpeed));

// Cambiar dirección ocasionalmente

if (Math.random() < 0.02) {

npc.direction = new THREE.Vector3(Math.random() - 0.5, 0, Math.random() - 0.5).normalize();

}

// Mantener en límites

const limit = 12;

if (Math.abs(npc.group.position.x) > limit || Math.abs(npc.group.position.z) > limit) {

npc.direction.multiplyScalar(-1);

}

// Disparar al jugador

if (now - npc.lastShot > npc.shootCooldown) {

const direction = this.player.group.position.clone()

.sub(npc.group.position)

.normalize();

const ray = this.createRay(npc.group.position, direction, false);

this.enemyRays.push(ray);

npc.lastShot = now;

npc.shootCooldown = 1000 + Math.random() * 2000;

}

});

}


updateRays() {

// Rayos del jugador

this.playerRays.forEach((ray, index) => {

ray.group.position.add(ray.velocity);

ray.group.rotation.x += 0.1;

ray.group.rotation.y += 0.15;

ray.life--;

// Colisión con NPCs

this.npcs.forEach((npc, npcIndex) => {

if (ray.group.position.distanceTo(npc.group.position) < 1.5) {

this.scene.remove(ray.group);

this.scene.remove(npc.group);

this.playerRays.splice(index, 1);

this.npcs.splice(npcIndex, 1);

this.score += 100;

this.updateUI();

}

});

if (ray.life <= 0) {

this.scene.remove(ray.group);

this.playerRays.splice(index, 1);

}

});


// Rayos enemigos

this.enemyRays.forEach((ray, index) => {

ray.group.position.add(ray.velocity);

ray.group.rotation.x += 0.1;

ray.group.rotation.y += 0.15;

ray.life--;

// Colisión con jugador

if (ray.group.position.distanceTo(this.player.group.position) < 1.5) {

this.scene.remove(ray.group);

this.enemyRays.splice(index, 1);

this.hitsReceived++;

this.updateUI();

if (this.hitsReceived >= this.maxHits) {

this.gameOver();

}

}

if (ray.life <= 0) {

this.scene.remove(ray.group);

this.enemyRays.splice(index, 1);

}

});

}


checkLevelProgression() {

if (this.npcs.length === 0) {

this.mazeLevel++;

this.currentMazeColor = (this.currentMazeColor + 1) % this.mazeColors.length;

this.createMaze();

this.createNPCs();

this.score += this.mazeLevel * 50;

this.updateUI();

}

}


updateUI() {

document.getElementById('score').textContent = this.score;

document.getElementById('hitsReceived').textContent = `${this.hitsReceived} / ${this.maxHits}`;

document.getElementById('npcCount').textContent = this.npcs.length;

document.getElementById('mazeLevel').textContent = this.mazeLevel;

}


gameOver() {

this.gameRunning = false;

document.getElementById('finalScore').textContent = `Puntuación Final: ${this.score}`;

document.getElementById('gameOverScreen').style.display = 'flex';

}


restart() {

// Limpiar escena

this.npcs.forEach(npc => this.scene.remove(npc.group));

this.playerRays.forEach(ray => this.scene.remove(ray.group));

this.enemyRays.forEach(ray => this.scene.remove(ray.group));

this.maze.forEach(wall => this.scene.remove(wall));

this.scene.remove(this.player.group);

// Resetear estado

this.npcs = [];

this.playerRays = [];

this.enemyRays = [];

this.maze = [];

this.score = 0;

this.hitsReceived = 0;

this.mazeLevel = 1;

this.currentMazeColor = 0;

this.gameRunning = true;

// Recrear juego

this.createMaze();

this.createPlayer();

this.createNPCs();

this.updateUI();

document.getElementById('gameOverScreen').style.display = 'none';

}


gameLoop() {

requestAnimationFrame(() => this.gameLoop());

if (this.gameRunning) {

this.updateNPCs();

this.updateRays();

this.checkLevelProgression();

}

this.renderer.render(this.scene, this.camera);

}

}


// Inicializar el juego

const game = new ElectricMazeGame();

</script>

</body>

</html>


ree

 
 
 

Comentarios


bottom of page