Skip to content

Instantly share code, notes, and snippets.

@shakaran
Created January 16, 2025 02:54
Show Gist options
  • Save shakaran/2a9613a0770c6db4874fb7e7070f9ae1 to your computer and use it in GitHub Desktop.
Save shakaran/2a9613a0770c6db4874fb7e7070f9ae1 to your computer and use it in GitHub Desktop.
<!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