Created
January 16, 2025 02:54
-
-
Save shakaran/2a9613a0770c6db4874fb7e7070f9ae1 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="es"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Juego Isométrico con Habilidades</title> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> | |
</head> | |
<body> | |
<div id="ui"></div> | |
<script> | |
let scene, camera, renderer; | |
let selectedTile = null; | |
let tileSize = 2; // Tamaño de cada celda del tablero | |
let rows = 8; // Filas del tablero | |
let cols = 8; // Columnas del tablero | |
let units = []; // Arreglo para las unidades | |
let currentPlayer = 1; // Turno actual (1 o 2) | |
let messages = []; // Mensajes de la interfaz | |
let gameOver = false; | |
// Definimos los tipos de unidades | |
const UNIT_TYPES = { | |
WARRIOR: 'warrior', | |
ARCHER: 'archer', | |
MAGE: 'mage' | |
}; | |
// Inicialización de la escena | |
function init() { | |
scene = new THREE.Scene(); | |
// Configuración de la cámara (vista isométrica) | |
const aspectRatio = window.innerWidth / window.innerHeight; | |
camera = new THREE.OrthographicCamera(-10 * aspectRatio, 10 * aspectRatio, 10, -10, 1, 1000); | |
camera.position.set(30, 30, 30); | |
camera.lookAt(0, 0, 0); | |
// Configuración del renderizador | |
renderer = new THREE.WebGLRenderer(); | |
renderer.setSize(window.innerWidth, window.innerHeight); | |
document.body.appendChild(renderer.domElement); | |
createBoard(); // Crear el tablero | |
createUnit(0, 0, 1, UNIT_TYPES.WARRIOR); // Crear una unidad para el jugador 1 (Guerrero) | |
createUnit(6, 6, 2, UNIT_TYPES.ARCHER); // Crear una unidad para el jugador 2 (Arquero) | |
// Evento de clic | |
window.addEventListener('click', onClick); | |
// Mostrar mensaje de turno | |
updateUI(); | |
animate(); // Iniciar la animación | |
} | |
// Crear el tablero isométrico | |
function createBoard() { | |
for (let row = 0; row < rows; row++) { | |
for (let col = 0; col < cols; col++) { | |
let geometry = new THREE.BoxGeometry(tileSize, 0.2, tileSize); // Celda de 3D (caja) | |
let material = new THREE.MeshBasicMaterial({ color: 0x00ff00, wireframe: true }); | |
let cube = new THREE.Mesh(geometry, material); | |
// Transformar para crear la perspectiva isométrica | |
cube.position.set(col * tileSize - (cols * tileSize) / 2, 0, row * tileSize - (rows * tileSize) / 2); | |
cube.rotation.x = Math.PI / 3; // Rotar en el eje X para efecto isométrico | |
cube.rotation.z = Math.PI / 4; // Rotar en el eje Z para vista isométrica | |
scene.add(cube); | |
} | |
} | |
} | |
// Crear una unidad en el tablero | |
function createUnit(x, z, player, type) { | |
let geometry = new THREE.SphereGeometry(0.5, 16, 16); | |
let color = (player === 1) ? 0xff0000 : 0x0000ff; // Jugador 1 rojo, Jugador 2 azul | |
let material = new THREE.MeshBasicMaterial({ color: color }); | |
let unit = new THREE.Mesh(geometry, material); | |
unit.position.set(x * tileSize - (cols * tileSize) / 2, 0.5, z * tileSize - (rows * tileSize) / 2); | |
unit.player = player; | |
unit.type = type; // Tipo de unidad (Guerrero, Arquero, Mago) | |
unit.health = 100; // Salud inicial de cada unidad | |
unit.actionPoints = 3; // Puntos de acción iniciales | |
unit.range = (type === UNIT_TYPES.ARCHER) ? 3 : 1; // El arquero tiene un rango de ataque mayor | |
unit.damage = (type === UNIT_TYPES.WARRIOR) ? 20 : (type === UNIT_TYPES.ARCHER) ? 15 : 10; // Daño por tipo de unidad | |
unit.specialAbility = (type === UNIT_TYPES.MAGE) ? "Area Attack" : null; // Habilidad especial para mago (ataque de área) | |
scene.add(unit); | |
units.push(unit); | |
} | |
// Mover una unidad | |
function moveUnit(unit, targetX, targetZ) { | |
if (unit.actionPoints > 0) { | |
unit.position.set(targetX * tileSize - (cols * tileSize) / 2, 0.5, targetZ * tileSize - (rows * tileSize) / 2); | |
unit.actionPoints--; // Reducir puntos de acción al mover | |
updateUI(); | |
} | |
} | |
// Detectar clic en el tablero | |
function onClick(event) { | |
if (gameOver) return; // Si el juego ha terminado, no hacer nada | |
const rect = renderer.domElement.getBoundingClientRect(); | |
const mouseX = ((event.clientX - rect.left) / rect.width) * 2 - 1; | |
const mouseY = -((event.clientY - rect.top) / rect.height) * 2 + 1; | |
const raycaster = new THREE.Raycaster(); | |
const mouse = new THREE.Vector2(mouseX, mouseY); | |
raycaster.setFromCamera(mouse, camera); | |
const intersects = raycaster.intersectObjects(scene.children); | |
if (intersects.length > 0) { | |
const targetTile = intersects[0].object; | |
if (selectedTile && selectedTile !== targetTile) { | |
let unit = units.find(u => u.position.x === selectedTile.position.x && u.position.z === selectedTile.position.z); | |
if (unit && unit.player === currentPlayer) { | |
moveUnit(unit, targetTile.position.x / tileSize + (cols * tileSize) / 2, targetTile.position.z / tileSize + (rows * tileSize) / 2); | |
switchTurn(); | |
} | |
} | |
selectedTile = targetTile; | |
} | |
} | |
// Alternar entre turnos | |
function switchTurn() { | |
currentPlayer = currentPlayer === 1 ? 2 : 1; | |
resetActionPoints(); | |
updateUI(); | |
} | |
// Reiniciar los puntos de acción al final del turno | |
function resetActionPoints() { | |
units.forEach(unit => { | |
if (unit.player === currentPlayer) { | |
unit.actionPoints = 3; // Resetear puntos de acción | |
} | |
}); | |
} | |
// Mostrar mensajes en la interfaz | |
function updateUI() { | |
messages = [`Turno del Jugador ${currentPlayer}`]; | |
messages.push(`Puntos de acción restantes: ${units.filter(u => u.player === currentPlayer)[0].actionPoints}`); | |
units.forEach(unit => { | |
if (unit.player === currentPlayer) { | |
messages.push(`${unit.type} tiene habilidad especial: ${unit.specialAbility || "Ninguna"}`); | |
} | |
}); | |
if (gameOver) { | |
messages.push("¡El juego ha terminado!"); | |
} | |
document.getElementById("ui").innerHTML = messages.join("<br/>"); | |
} | |
// Ataque mágico de área (solo para magos) | |
function areaAttack(unit) { | |
if (unit.type === UNIT_TYPES.MAGE && unit.actionPoints >= 1) { | |
unit.actionPoints--; | |
units.forEach(target => { | |
if (target !== unit && Math.abs(target.position.x - unit.position.x) <= unit.range * tileSize && | |
Math.abs(target.position.z - unit.position.z) <= unit.range * tileSize) { | |
// Daño de área a todas las unidades enemigas dentro del rango | |
target.health -= 20; | |
if (target.health <= 0) { | |
scene.remove(target); | |
units = units.filter(u => u !== target); | |
} | |
} | |
}); | |
updateUI(); | |
} | |
} | |
// Función para animar las batallas (simular un ataque) | |
function animateAttack(attacker, defender) { | |
// Hacer que las unidades se muevan hacia el objetivo para "simular" un ataque | |
let moveTween = new THREE.Vector3(defender.position.x - attacker.position.x, 0, defender.position.z - attacker.position.z); | |
moveTween.multiplyScalar(0.1); // Controlar la velocidad de movimiento | |
attacker.position.add(moveTween); | |
if (Math.abs(attacker.position.x - defender.position.x) < 0.5 && Math.abs(attacker.position.z - defender.position.z) < 0.5) { | |
// Cuando el atacante llega al defensor | |
defender.health -= attacker.damage; | |
if (defender.health <= 0) { | |
scene.remove(defender); | |
units = units.filter(u => u !== defender); | |
} | |
} | |
} | |
// Función para renderizar continuamente | |
function animate() { | |
requestAnimationFrame(animate); | |
renderer.render(scene, camera); | |
} | |
window.onload = init; | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment