BATALLA ELECTRICA 3D
- samuel gaitan
- 5 jun
- 7 Min. de lectura
<!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;
overflow: hidden;
cursor: crosshair;
font-family: 'Arial', sans-serif;
}
#gameUI {
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;
}
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>




Comentarios