Bucle de animación de videojuegos

En este artículo vamos a explicar cómo crear un bucle de animación de videojuegos de forma fluida y creando experiencias de juego agradables. En el mundo de los videojuegos, una animación fluida es esencial para crear buenas experiencias de juego en los jugadores. Para lograr esto, se utiliza la técnica llamada «bucle de animación o game loop» lo que da pie a las famosas siglas fps, Fotogramas Por Segundo o Frames Per Second en Inglés.

Estas siglas (fps) son la cantidad de veces que se actualiza el videojuego por segundo, o lo que es lo mismo, la cantidad de veces que se ejecuta el bucle de animación. Actualmente existen muchos juegos basados en navegadores web y se ha creado un nuevo método de implementar este bucle de animación en HTML. En este artículo me centraré en explicar este nuevo método y no mencionaré las técnicas antiguas realizadas con setInterval().

Game loop en HTML5 Canvas

En HTML5 se ha desarrollado un método que permite crear un bucle a 60 fps estables, componente fundamental en el desarrollo de videojuegos. Dentro de este ciclo o bucle continuo es donde ejecutaremos nuestro videojuego que mostraremos en el lienzo <canvas> a 60 fotogramas por segundo.

En esencia, el bucle de animación o game loop es el motor de los videojuegos, encargado de mantener todo en movimiento y sincronizado, desde la lógica del juego hasta la representación visual en pantalla. Básicamente se trata de borrar el lienzo, dibujar todos los graficos y preparar el juego para el siguiente fotograma.

Apunte importante sobre los FPS y el monitor: Aunque tradicionalmente se habla de conseguir unos 60 fps estables, la API requestAnimationFrame() es inteligente y se adapta automáticamente a la tasa de refresco (hercios) de la pantalla del jugador. Esto significa que si alguien ejecuta este código en un monitor moderno de 120Hz o más, el bucle se ejecutará a esa misma velocidad, aumentando la velocidad de movimiento y pudiendo desvirtuar la experiencia de juego. En proyectos más avanzados o videojuegos complejos, se suele restringir los fps a una la tasa fija o calcular el tiempo transcurrido entre fotogramas (leer artículo Delta Time) para asegurar que los elementos se muevan a la misma velocidad sin importar los hercios de la pantalla.

Estructura HTML y Estilo CSS

Ahora, vamos ver cómo se implementa este bucle universal de animación en un contexto de desarrollo web utilizando HTML5, Canvas y JavaScript. La primera parte es crear una página web sencilla añadiendo nuestra etiqueta <canvas>, puedes saber más sobre esta etiqueta en el artículo sobre el tag canvas.

<!DOCTYPE html>
<html lang="es">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Bucle universal de animación</title>
  <style>
    body {
      text-align: center;
    }
    canvas {
      width: 60%;
      margin: auto;
      display: block;
      border: 1px solid #000;
    }
  </style>
</head>
<body>
  <h1>Bucle universal de animación</h1>
  <canvas id="miCanvas" width="800" height="600">Canvas no soportado</canvas>
</body>
</html>

En esta sección, creamos la estructura básica del documento HTML y aplicamos algunos estilos CSS para centrar el contenido y darle un aspecto visual agradable. El elemento <canvas> se utiliza para dibujar gráficos en la página.

Hasta aquí simplemente se ha habilitado una «ventana gráfica» de 800×600 píxeles que hemos rodeado con un borde negro para visualizarla. Ahora vamos a ver cómo dibujar en ella con JavaScript.

JavaScript y el bucle de animación de videojuegos

Justo debajo de nuestra etiqueta <canvas> debemos añadir el siguiente código JavaScript que provocará la animación de 10 círculos rellenos rebotando por el Canvas.

  <script>
    // Obtenemos datos del Canvas
    let canvas = document.getElementById("miCanvas");
    let ctx = canvas.getContext("2d");  

    // CREAMOS UN LISTADO DE OBJETOS TIPO CÍRCULO
    let items = [];
    for (let i=0; i<10; i++) {
      let radius = 25 + Math.random() * 25;
      items.push({
        x: canvas.width/2,
        y: canvas.height/2,        
        radius: radius,
        width: radius * 2,
        height: radius * 2,
        color: "#" + Math.random().toString(16).substr(-6),
        speedY: 5 - Math.random() * 10,
        speedX: 5 - Math.random() * 10
      });   
    } 

    // FUNCIÓN DE RENDERIZADO
    function drawCircle(data) {
      ctx.fillStyle = data.color;
      ctx.beginPath();
      ctx.arc(data.x + data.radius, data.y + data.radius, data.radius, 0, 2 * Math.PI);
      ctx.fill();
    }  

    // FUNCIÓN DE ACTUALIZACIÓN (LÓGICA Y FÍSICA)
    function updateCircle(data) {
      // Desplazamiento
      data.x += data.speedX;
      data.y += data.speedY;

      // Rebotes en los bordes
      if (data.x < 0 || (data.x + data.width) > canvas.width) data.speedX = -data.speedX;
      if (data.y < 0 || (data.y + data.height) > canvas.height) data.speedY = -data.speedY;
    }

    // EL BUCLE PRINCIPAL (GAME LOOP)
    function animateScene() {
      // Primero: Borramos el lienzo anterior
      ctx.clearRect(0, 0, canvas.width, canvas.height);

      // Segundo: Dibujamos todos los objetos círculo en su nueva posición
      for (let i = 0; i < items.length; i++) {
        drawCircle(items[i]);
      } 

      // Tercero: Actualizamos la posición de todos los objetos círculo
      for (let i = 0; i < items.length; i++) {
        updateCircle(items[i]);
      }
      
      // Solicitamos el siguiente fotograma
      requestAnimationFrame(animateScene);
    }
    // Activamos la animación
    animateScene();	
  </script>

En este bloque de código JavaScript es donde se implementan los gráficos y su animación mediante el bucle universal de animación o game loop. Su funcionamiento es el siguiente:

  • canvas, ctx: En estas variables obtenemos el contexto del tag canvas para poder dibujar en él.
  • items Creamos un listado de datos aleatorios que representan círculos en el Canvas. Cada círculo tiene una posición, tamaño, color y velocidad aleatorios que utilizaremos para animarlos.
  • drawCircle(data): Es la función de renderizado de cada círculo. Recibe un objeto data con la información necesaria para dibujar un círculo en el Canvas. Lee el artículo Círculos en HTML5 Canvas para saber más sobre cómo dibujar círculos.
  • updateCircle(data): Es la función de actualización de cada círculo. Recibe un objeto data que contiene la información necesaria para ejecutar su movimiento y física.
  • animateScene(): Esta función que será nuestro bucle de animación o game loop, al que llamaremos repetidamente utilizando requestAnimationFrame(). Esta es la forma en que se pueden ejecutar animaciones en HTML5 y Canvas.
  • Orden de animación: Dentro de nuestro bucle de animación (game loop), primero borramos el Canvas, luego dibujamos todos los círculos y finalmente actualizamos sus posiciones para el siguiente fotograma. Es importante mantener siempre este orden y que el tiempo entre «borrado» y «dibujado» sea el menor posible.
  • Activación: Para activar la animación llamamos una primera vez a la función animateScene() y luego se ejecuta indefinidamente.

El resultado del código de nuestro bucle universal de animación o game loop es el siguiente:

Resumen del bucle de animación de videojuegos

En este artículo hemos explicado qué es el bucle universal de animación de videojuegos o game loop, y cómo se implementa en un contexto de desarrollo web utilizando HTML5, Canvas y JavaScript. Con un ejemplo de código hemos visto paso a paso la creación de una escena animada con círculos en movimiento.

Si estás interesado en profundizar más sobre la animación o creación de videojuegos en un entorno web con JavaScript, puedes consultar más artículos de esta sección o mirar la página Videojuegos JavaScript donde muestro algunos de los juegos que se pueden hacer con las técnicas descritas en los libros que tengo publicados sobre Programación de Videojuegos.

¡ Espero que este artículo sea de vuestro interés !

Logotipo de Safe CreativeLogotipo de Creative Commons Attribution 4.0

Deja un comentario