Caso de éxito: Ajuste automático del tamaño de los juegos HTML5

Derek Detweiler
Derek Detweiler

Introducción

En el verano boreal de 2010, creamos Sand Trap, un juego al que participamos en una competencia de juegos HTML5 para teléfonos celulares. Sin embargo, la mayoría de los teléfonos celulares mostraban solo una parte del juego o lo hacían demasiado pequeño, por lo que no se podía jugar por completo. Por eso, nos encargamos de hacer que el juego se ajuste de forma fluida para que coincidiera con cualquier resolución. Después de volver a programar y usar las ideas que se describen en este artículo, creamos un juego que se expandió a cualquier navegador moderno, ya sea que se ejecutara en una computadora de escritorio o un dispositivo móvil.

Captura de pantalla de thwack (pantalla completa)
captura de pantalla de Thwack más pequeño en la ventana del navegador

Este enfoque funcionó bien para Sand Trap, así que usamos el mismo método en nuestro juego más reciente, Thwack!. El juego ajusta automáticamente las resoluciones de pantalla para adaptarse a las ventanas de pantalla completa y tamaño personalizado, como se muestra en las siguientes capturas de pantalla.

Para implementar esto, era necesario aprovechar CSS y JavaScript. Usar CSS para llenar toda la pantalla es trivial, pero CSS no te permite mantener la misma proporción de ancho y alto para evitar que se estiren el lienzo y el área de juego. Ahí es donde entra en juego JavaScript. Puedes cambiar la escala de los elementos de los documentos con JavaScript y activar el cambio de tamaño en los eventos de la ventana.

Cómo preparar la página

El primer paso es designar el área de la página donde se llevará a cabo el juego. Si incluyes esto como un bloque div, puedes colocar otras etiquetas o un elemento canvas dentro de él. Al configurarlo correctamente, estos elementos secundarios heredarán el escalamiento del bloque div principal.

Si en tu área de juego hay dos partes, un área de juego y un área de registro de puntuaciones, podría verse de la siguiente manera:

<div id="gameArea">
  <canvas id="gameCanvas"></canvas>
  <div id="statsPanel"></div>
</div>

Una vez que tengas una estructura básica de documento, puedes asignar algunas propiedades de CSS a estos elementos para prepararlos para cambiar su tamaño. Muchas de las propiedades de CSS para “gameArea” son manipuladas directamente por JavaScript, pero para que funcionen, configura algunas otras propiedades de CSS que comienzan con el bloque div gameArea superior:

#gameArea {
  position: absolute;
  left:     50%;
  top:      50%;
}

Esto colocará la esquina superior izquierda del lienzo en el centro de la pantalla. La función de cambio de tamaño automático de JavaScript que se describe en la siguiente sección manipula propiedades de CSS adicionales para cambiar el tamaño del área de juego y centrarla en la ventana.

Dado que el tamaño del área de juego se cambia automáticamente según las dimensiones de la ventana, no te recomendamos que uses las dimensiones en píxeles para los elementos secundarios del bloque de div del área de juego. En cambio, usa porcentajes. Los valores de píxel no permiten que los elementos internos escalen con el div superior a medida que cambia. Sin embargo, puede ser útil comenzar con píxeles y luego convertirlos en porcentajes cuando tengas un diseño que te guste.

Para este ejemplo, comienza con un área de juego de 300 píxeles de alto y 400 píxeles de ancho. El lienzo cubre toda el área de juego, y un panel de estadísticas semitransparente se extiende a lo largo de la parte inferior con 24 píxeles de alto, como se muestra en la Figura 1.

Dimensiones de los elementos secundarios gameArea en píxeles
Figura 1: Dimensiones de los elementos secundarios de gameArea en píxeles

La traducción de estos valores a porcentajes hace que el lienzo sea 100% de ancho y 100% de alto (de gameArea, no la ventana). Dividir 24 por 300 da como resultado la altura del panel de estadísticas en un 8% y, como cubrirá la parte inferior del área de juego, el ancho también será del 100%, como se muestra en la Figura 2.

Dimensiones de los elementos secundarios gameArea en porcentajes
Figura 2: Dimensiones de los elementos secundarios gameArea en porcentajes

Ahora que determinaste las dimensiones del área de juego y sus elementos secundarios, puedes combinar las propiedades de CSS para los dos elementos internos de la siguiente manera:

#gameCanvas {
  width: 100%;
  height: 100%;
}
#statsPanel {
  position: absolute;
  width: 100%;
  height: 8%;
  bottom: 0;
  opacity: 0.8;
}

Cambio del tamaño del juego

Ya tienes todo listo para crear una función que maneje el cambio de tamaño de la ventana. Primero, toma una referencia al elemento principal del documento gameArea.

var gameArea = document.getElementById('gameArea');

Como no te preocupa el ancho o la altura exactos, la siguiente información que debes establecer es la proporción entre el ancho y la altura. Con tu referencia anterior de un área de juego de 400 píxeles de ancho y 300 píxeles de alto, sabes que deseas establecer la relación de aspecto en 4 unidades de ancho y 3 unidades de alto.

var widthToHeight = 4 / 3;

Dado que se llama a esta función cada vez que se cambia el tamaño de la ventana, también querrás tomar las nuevas dimensiones de la ventana para que puedas ajustar las dimensiones del juego para que coincidan. Encuentra esto usando las propiedades internalWidth y internalHeight de la ventana.

var newWidth = window.innerWidth;
var newHeight = window.innerHeight;

Así como determinaste la proporción entre el ancho y la altura, ahora puedes determinar la proporción actual de ancho y alto de la ventana:

var newWidthToHeight = newWidth / newHeight;

De esta manera, podrás decidir si harás que el juego ocupe toda la pantalla de forma horizontal o vertical, como se muestra en la Figura 3.

Ajusta el elemento gameArea a la ventana y, al mismo tiempo, mantiene la relación de aspecto
Figura 3: Ajusta el elemento gameArea a la ventana y, al mismo tiempo, mantener la relación de aspecto

Si la forma del área de juego deseada es más ancha que la forma de la ventana (y la altura es más corta), debes llenar la ventana de forma horizontal y dejar márgenes en la parte inferior y superior. Del mismo modo, si la forma del área de juego deseada es mayor que la forma de la ventana (y el ancho es más estrecho), deberás llenar la ventana de forma vertical y dejar márgenes a la izquierda y la derecha.

Para ello, prueba la proporción deseada de ancho y alto con la relación actual de ancho a altura y realiza los ajustes correspondientes de la siguiente manera:

if (newWidthToHeight > widthToHeight) {
  // window width is too wide relative to desired game width
  newWidth = newHeight * widthToHeight;
  gameArea.style.height = newHeight + 'px';
  gameArea.style.width = newWidth + 'px';
} else { // window height is too high relative to desired game height
  newHeight = newWidth / widthToHeight;
  gameArea.style.width = newWidth + 'px';
  gameArea.style.height = newHeight + 'px';
}

Ahora que ajustaste el ancho y el alto del área de juego, debes centrar las cosas hacia arriba colocando un margen negativo en la parte superior, que es la mitad de la altura, y, en el lado izquierdo, la mitad del ancho. Recuerda que CSS ya coloca la esquina superior izquierda del elemento div de gameArea en el centro exacto de la ventana, por lo que se centra el área de juego en la ventana:

gameArea.style.marginTop = (-newHeight / 2) + 'px';
gameArea.style.marginLeft = (-newWidth / 2) + 'px';

También querrás ajustar automáticamente el tamaño de la fuente. Si todos los elementos secundarios los usan, simplemente puedes establecer la propiedad fontSize de CSS del bloque div gameArea en un valor determinado por su tamaño.

gameArea.style.fontSize = (newWidth / 400) + 'em';

Por último, haz que las dimensiones del dibujo del lienzo coincidan con su ancho y alto nuevos. Ten en cuenta que el resto del código del juego debe mantener las dimensiones del motor de juego separadas de las dimensiones del dibujo del lienzo para adaptarse a una resolución de lienzo dinámica.

var gameCanvas = document.getElementById('gameCanvas');
gameCanvas.width = newWidth;
gameCanvas.height = newHeight;

Por lo tanto, la función de cambio de tamaño completada podría verse de la siguiente manera:

function resizeGame() {
    var gameArea = document.getElementById('gameArea');
    var widthToHeight = 4 / 3;
    var newWidth = window.innerWidth;
    var newHeight = window.innerHeight;
    var newWidthToHeight = newWidth / newHeight;
    
    if (newWidthToHeight > widthToHeight) {
        newWidth = newHeight * widthToHeight;
        gameArea.style.height = newHeight + 'px';
        gameArea.style.width = newWidth + 'px';
    } else {
        newHeight = newWidth / widthToHeight;
        gameArea.style.width = newWidth + 'px';
        gameArea.style.height = newHeight + 'px';
    }
    
    gameArea.style.marginTop = (-newHeight / 2) + 'px';
    gameArea.style.marginLeft = (-newWidth / 2) + 'px';
    
    var gameCanvas = document.getElementById('gameCanvas');
    gameCanvas.width = newWidth;
    gameCanvas.height = newHeight;
}

Ahora, querrás que estos ajustes se realicen automáticamente cada vez que se cambie el tamaño de la ventana o, en el caso de los dispositivos móviles, cuando cambie la orientación de la pantalla. Para controlar estos eventos, haz que llamen a la función renameGame() de la siguiente manera:

window.addEventListener('resize', resizeGame, false);
window.addEventListener('orientationchange', resizeGame, false);

Si el tamaño de la ventana es demasiado alto o la orientación de la pantalla es vertical, haces que el ancho sea el 100% de la ventana, y si el tamaño de la ventana es demasiado ancho o la orientación de la pantalla es horizontal, el ancho es el 100% de la ventana. La dimensión restante se dimensiona según la relación de aspecto de ancho a altura predeterminada.

Resumen

Gopherwood Studios ha utilizado versiones de esta estructura para todos nuestros juegos HTML5 y ha demostrado ser muy útil para adaptarse a múltiples resoluciones de pantalla y distintos dispositivos móviles. Además, con la ayuda de un navegador en pantalla completa, esto brinda a nuestros juegos web una experiencia inmersiva similar a los juegos tradicionales para computadoras de escritorio que a muchos juegos basados en navegador. Esperamos con ansias más innovaciones en los juegos web a medida que las tecnologías web y HTML5 sigan evolucionando.