Juego Esquiva los Asteroides (HTML+CSS+JS)

Se trata de un juego de «esquiva los asteroides». Controlas una nave espacial en la parte inferior de la pantalla y debes evitar que los asteroides que caen desde arriba te golpeen. Tu puntuación aumenta cada segundo que sobrevives.

Características del Juego:

  • Gráficos simples: Utiliza el elemento <canvas> de HTML5 para dibujar los elementos del juego.
  • Controles: Usa las flechas izquierda y derecha del teclado para mover la nave.
  • Puntuación: Ganas puntos por cada segundo que permaneces en el juego.
  • Game Over: El juego termina cuando un asteroide choca contra tu nave.
  • Reinicio: Puedes reiniciar el juego con un botón después de perder.

Código Completo

Solo tienes que copiar todo este código, pegarlo en un archivo de texto, guardarlo con la extensión .html (por ejemplo, juego.html) y abrirlo en tu navegador web.

<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Esquiva los Asteroides</title>
<style>
        /* Estilos básicos para que se vea bien */
        body {
            background-color: #000;
            color: #fff;
            font-family: 'Courier New', Courier, monospace;
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            height: 100vh;
            margin: 0;
            overflow: hidden; /* Evita barras de desplazamiento */
        }

        h1 {
            margin-bottom: 20px;
            color: #00ff00;
            text-shadow: 0 0 10px #00ff00;
        }

        #game-container {
            position: relative;
            border: 2px solid #fff;
            box-shadow: 0 0 20px #00ff00;
        }

        #gameCanvas {
            display: block;
            background-color: #111;
        }

        #score {
            position: absolute;
            top: 10px;
            left: 10px;
            font-size: 24px;
            color: #fff;
        }

        #game-over {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            text-align: center;
            display: none; /* Oculto por defecto */
        }

        #game-over h2 {
            font-size: 48px;
            color: #ff0000;
            margin-bottom: 20px;
        }

        #restart-button {
            padding: 10px 20px;
            font-size: 20px;
            cursor: pointer;
            background-color: #00ff00;
            color: #000;
            border: none;
            border-radius: 5px;
            transition: background-color 0.3s;
        }

        #restart-button:hover {
            background-color: #fff;
        }
    </style>
</head>
<body>

    <h1>Esquiva los Asteroides</h1>

    <div id="game-container">
        <canvas id="gameCanvas" width="600" height="400"></canvas>
        <div id="score">Puntuación: 0</div>
        <div id="game-over">
            <h2>GAME OVER</h2>
            <button id="restart-button">Reiniciar</button>
        </div>
    </div>
<script>
        // --- CONFIGURACIÓN INICIAL ---
        const canvas = document.getElementById('gameCanvas');
        const ctx = canvas.getContext('2d');
        const scoreElement = document.getElementById('score');
        const gameOverElement = document.getElementById('game-over');
        const restartButton = document.getElementById('restart-button');

        // --- VARIABLES DEL JUEGO ---
        let score = 0;
        let gameIsOver = false;
        let asteroidSpawnTimer = 0;
        let asteroidSpawnInterval = 60; // Spawn cada 60 frames (aprox. 1 segundo)

        // --- OBJETO JUGADOR ---
        const player = {
            x: canvas.width / 2 - 25,
            y: canvas.height - 60,
            width: 50,
            height: 50,
            speed: 7,
            color: '#00ff00',
            draw: function() {
                ctx.fillStyle = this.color;
                // Dibujamos una forma simple de nave
                ctx.beginPath();
                ctx.moveTo(this.x + this.width / 2, this.y);
                ctx.lineTo(this.x, this.y + this.height);
                ctx.lineTo(this.x + this.width, this.y + this.height);
                ctx.closePath();
                ctx.fill();
            }
        };

        // --- ARRAY DE ASTEROIDES ---
        let asteroids = [];

        // --- CLASE ASTEROIDE ---
        class Asteroid {
            constructor() {
                this.width = 30 + Math.random() * 40; // Tamaño aleatorio
                this.height = this.width;
                this.x = Math.random() * (canvas.width - this.width);
                this.y = -this.height; // Empieza fuera de la pantalla
                this.speed = 2 + Math.random() * 3; // Velocidad aleatoria
                this.color = `hsl(${Math.random() * 60 + 15}, 100%, 50%)`; // Color entre naranja y amarillo
            }

            draw() {
                ctx.fillStyle = this.color;
                ctx.fillRect(this.x, this.y, this.width, this.height);
            }

            update() {
                this.y += this.speed;
            }
        }

        // --- CONTROL DE TECLADO ---
        const keys = {};
        document.addEventListener('keydown', (e) => {
            keys[e.key] = true;
        });
        document.addEventListener('keyup', (e) => {
            keys[e.key] = false;
        });

        // --- LÓGICA DEL JUEGO ---
        function handlePlayerInput() {
            if (keys['ArrowLeft'] && player.x > 0) {
                player.x -= player.speed;
            }
            if (keys['ArrowRight'] && player.x < canvas.width - player.width) {
                player.x += player.speed;
            }
        }

        function spawnAsteroid() {
            asteroidSpawnTimer++;
            if (asteroidSpawnTimer >= asteroidSpawnInterval) {
                asteroids.push(new Asteroid());
                asteroidSpawnTimer = 0;
                // Aumentar dificultad gradualmente
                if (asteroidSpawnInterval > 20) {
                    asteroidSpawnInterval -= 0.5;
                }
            }
        }

        function checkCollisions() {
            for (let asteroid of asteroids) {
                if (
                    player.x < asteroid.x + asteroid.width &&
                    player.x + player.width > asteroid.x &&
                    player.y < asteroid.y + asteroid.height &&
                    player.y + player.height > asteroid.y
                ) {
                    endGame();
                }
            }
        }

        function updateScore() {
            // La puntuación se actualiza en un setInterval separado para mayor precisión
        }

        function endGame() {
            gameIsOver = true;
            gameOverElement.style.display = 'block';
        }

        function resetGame() {
            score = 0;
            scoreElement.textContent = `Puntuación: ${score}`;
            gameIsOver = false;
            gameOverElement.style.display = 'none';
            player.x = canvas.width / 2 - 25;
            asteroids = [];
            asteroidSpawnTimer = 0;
            asteroidSpawnInterval = 60;
            gameLoop(); // Reiniciar el bucle del juego
        }

        // --- BUCLE PRINCIPAL DEL JUEGO ---
        function gameLoop() {
            if (gameIsOver) {
                return; // Detener el bucle si el juego ha terminado
            }

            // 1. LIMPIAR CANVAS
            ctx.clearRect(0, 0, canvas.width, canvas.height);

            // 2. ACTUALIZAR ELEMENTOS
            handlePlayerInput();
            spawnAsteroid();

            // Actualizar y filtrar asteroides que salen de la pantalla
            asteroids = asteroids.filter(asteroid => {
                asteroid.update();
                return asteroid.y < canvas.height; // Mantener solo los que están visibles
            });

            checkCollisions();

            // 3. DIBUJAR ELEMENTOS
            player.draw();
            asteroids.forEach(asteroid => asteroid.draw());

            // 4. VOLVER A LLAMAR AL BUCLE
            requestAnimationFrame(gameLoop);
        }

        // --- INICIALIZACIÓN ---
        restartButton.addEventListener('click', resetGame);

        // Iniciar el bucle del juego
        gameLoop();

        // Intervalo para aumentar la puntuación
        setInterval(() => {
            if (!gameIsOver) {
                score++;
                scoreElement.textContent = `Puntuación: ${score}`;
            }
        }, 1000); // Cada segundo

    </script>
</body>
</html>

Cómo Funciona

  1. HTML (<body>): Define la estructura visible de la página.
    • Un <h1> para el título.
    • Un <div> (#game-container) que envuelve el área de juego.
    • Un <canvas> que es el lienzo donde se dibujará el juego.
    • Un <div> para mostrar la puntuación (#score).
    • Un <div> (#game-over) que contiene el mensaje de fin de juego y el botón de reinicio. Está oculto por defecto.
  2. CSS (<style>): Se encarga de la apariencia.
    • Centra el contenido en la página.
    • Le da un fondo negro y un estilo «futurista» con colores verdes y sombras.
    • Posiciona la puntuación y la pantalla de «Game Over» sobre el canvas.
  3. JavaScript (<script>): Es el cerebro del juego.
    • Configuración: Obtiene las referencias a los elementos del HTML y define variables iniciales.
    • Objeto player: Define las propiedades de la nave (posición, tamaño, color) y un método draw() para dibujarla.
    • Clase Asteroid: Es un molde para crear asteroides. Cada asteroide tendrá un tamaño, posición y velocidad aleatorios.
    • Control de Teclado: Escucha los eventos keydown y keyup para saber cuándo el jugador presiona las flechas.
    • gameLoop(): Es la función más importante. Se ejecuta constantemente usando requestAnimationFrame para crear la ilusión de movimiento. En cada «fotograma»:
      1. Limpia el canvas.
      2. Actualiza la posición del jugador y los asteroides.
      3. Comprueba si hay colisiones.
      4. Dibuja todos los elementos en sus nuevas posiciones.
      5. Se llama a sí misma para el siguiente fotograma.
    • resetGame(): Restablece todas las variables a su estado inicial para poder empezar de nuevo.

Posibles Mejoras (¡para que practiques!)

  • Vidas: En lugar de que el juego termine en el primer golpe, dale al jugador 3 vidas.
  • Disparar: Permite que la nave dispare láseres para destruir asteroides.
  • Sonidos: Añade efectos de sonido para los disparos, explosiones y la música de fondo.
  • Pantalla de Inicio: Crea una pantalla de bienvenida antes de que comience el juego.
  • Mejores Gráficos: Dibuja sprites más detallados en lugar de simples formas geométricas.