Aprende a crear juegos en HTML5 Canvas

lunes, 10 de septiembre de 2012

Estirar el canvas y pantalla completa

Los que han creado proyectos antes en Flash, recordarán una de sus características singulares, que a veces podía ser funcional y otras veces molesto, dependiendo el efecto que se deseaba hacer. Hablo de la peculiar propiedad que, si ponías el contenedor de diferente tamaño al proyecto original, este se escalaba para ajustarse al nuevo tamaño.

Los HTML5 Canvas aparentan en un comienzo no tener esta propiedad, pues si cambiamos el ancho o alto de uno, solo nos da un escenario más grande o chico, pero este no se escala. Lo que ocurre, es que aprovecha una segunda propiedad para escalar el canvas, de forma aparte a poner su tamaño inicial. Lo hace mediante la etiqueta Style.

Para comprobar esto, tomaremos el juego de la serpiente, y buscaremos al comienzo donde creamos la etiqueta canvas que contiene nuestro juego. Lo modificaremos agregando en el atributo style, las propiedades width y height:
<canvas id="canvas" width="300" height="150" style="background:#999; width:600px; height:300px;">
    [Canvas not supported by your browser]
</canvas>
Podemos comprobar que al comienzo, tenemos los atributos width y height:
width="300" height="150"
Pero posteriormente, los tenemos como propiedad dentro del style, con valores al doble que en su atributo:
width:600px; height:300px;
Al abrir el archivo, notaremos que nuestro juego está ahora escalado al doble. De esta forma, es como podemos escalar un juego a diferentes tamaños.

Pantalla completa.


Supondré que ahora que has visto como escalar un juego, has pensado en llenar la pantalla de tu navegador con él. Esto es ciertamente posible, pero se trata de una técnica un tanto compleja. Descuida, te enseñaré a aplicarla.

Lo primero que viene a nuestra mente, es darle ancho y alto del 100%. Pero para realmente llenar la pantalla, tenemos que convertir su posición a absoluta, y posicionarla justo hasta la esquina superior izquierda:
<canvas id="canvas" width="300" height="150" style="background:#999; width:100%; height:100%; position:fixed; top:0; left:0">
    [Canvas not supported by your browser]
</canvas>
El problema que descubrimos con esto, es que la imagen se estira de forma no proporcionada. Y tomemos en cuenta que no todas las pantallas tienen la misma proporción, por lo que en pantallas más cuadradas se verá la imagen aun más achatada que en pantallas widescreen.

Para hacer que llene lo mayor posible la pantalla de forma proporcional tendremos que crear una función en Javascript, a la que llamaremos setFullscreen. Lo primero que hará esta función, es encontrar la proporción de nuestra pantalla, y determinar la escala a la cual debe ser estirado, dependiendo del ancho y alto de la pantalla:
            var w = window.innerWidth / canvas.width;
            var h = window.innerHeight / canvas.height;
            var scale = Math.min(h, w);
Una vez obteniendo este valor, asignamos el ancho y alto de nuestro canvas de acuerdo a la escala resultante:
            canvas.style.width = (canvas.width * scale) + 'px';
            canvas.style.height = (canvas.height * scale) + 'px';
Por último, queda centrar el canvas, pues si lo asignamos a los valores top:0 left:0, no se verá tan bien como que esté centrado. Como le asignaremos una posición absoluta/fija, no podremos centrarlo por la propiedad text-align:center, por lo que deberemos hacer uso de una técnica más avanzada, conocida como <a href="http://ayotli.com/web-tips/div-fixed-center.html">divisor siempre fijo en el centro</a>.

En nuestro caso no lo aplicaremos a un div, si no a nuestro canvas, pero el principio es el mismo. Damos al canvas la propiedad position:fixed, lo ubicamos a top:50% left:50%, y finalmente le restamos a marginTop y marginLeft la mitad del ancho y alto de nuestro canvas respectivamente. El código queda así:
            canvas.style.position = 'fixed';
            canvas.style.left = '50%';
            canvas.style.top = '50%';
            canvas.style.marginLeft = -(canvas.width * scale) / 2 + 'px';
            canvas.style.marginTop = -(canvas.height * scale) / 2 + 'px';
Finalmente, tendremos de esta forma armada nuestra función setFullscreen:
        function setFullscreen(){
            var w = window.innerWidth / canvas.width;
            var h = window.innerHeight / canvas.height;
            var scale = Math.min(h, w);

            canvas.style.width = (canvas.width * scale) + 'px';
            canvas.style.height = (canvas.height * scale) + 'px';
            canvas.style.position = 'fixed';
            canvas.style.left = '50%';
            canvas.style.top = '50%';
            canvas.style.marginLeft = -(canvas.width * scale) / 2 + 'px';
            canvas.style.marginTop = -(canvas.height * scale) / 2 + 'px';
        }
Ahora tan solo debemos llamar a setFullscreen desde nuestro init, y tendremos nuestro juego en pantalla completa. Para complementarlo mejor, recomiendo que pongas el color de fondo de la página en negro:
<body style="background:black">
Si en algún momento de nuestro juego queremos revertir esta función, solo debemos convertir las propiedades del estilo a vacío, lo cual podemos hacer mediante esta función unsetFullscreen:
        function unsetFullscreen(){
            canvas.style.width = '';
            canvas.style.height = '';
            canvas.style.position = '';
            canvas.style.left = '';
            canvas.style.top = '';
            canvas.style.marginLeft = '';
            canvas.style.marginTop = '';
        }
Con esto, concluimos esta lección. Si blogspot es tan amable con nosotros, y nos permite tan malvadas e intrusivas instrucciones, al presionar la tecla F de tu teclado, podrás jugar este juego en pantalla completa. Desactívalo presionando F de nuevo.

[Canvas not supported by your browser]
Felices códigos. ¡Hasta la siguiente semana!

18 comentarios:

  1. Muchas gracias, me han ayudado bastante!

    Y enhorabuena por la web!

    Saludos desde Aguascalientes, México.

    ResponderEliminar
  2. Este comentario ha sido eliminado por el autor.

    ResponderEliminar
  3. Es una pasada jugar a pantalla completa, es una idea muy original modificar directamente el css desde el juego.

    Yo tenía echa una cosa parecida para poner la pantalla completa, no es con css.

    Ejemplo Pantalla Completa
    ---------------------------
    https://developer.mozilla.org/samples/domref/fullscreen.html

    Cuando pulsas intro en el video se pone a pantalla completa. Hay que hacer lo mismo pero en este caso indicandole el elemento div, en este caso el canvas.

    Tutorial
    ------------
    https://developer.mozilla.org/en-US/docs/Web/Guide/DOM/Using_full_screen_mode?redirectlocale=en-US&redirectslug=DOM%2FUsing_fullscreen_mode

    Por si a alguien le interesa este otro modo ahí tenéis el enlace. Saludos!

    ResponderEliminar
    Respuestas
    1. ¡Ah si! El Fullscreen API. Pero hay grandes diferencias entre ambas implementaciones. Para empezar, Fullscreen API aun es experimental y solo disponible en Firefox y Chrome, por ello no he tocado aun el tema de forma oficial.

      Por otro lado, el propósito es muy diferente. Mientras Fullscreen API tiene como meta entrar en Modo Pantalla Completa de verdad, el método aquí presentado sirve para tener un juego que ocupe la mayor cantidad disponible de la pantalla del navegador, creando una experiencia inmersiva no-intrusiva del juego.

      Dos soluciones para diferentes metas, aunque compartiendo nombres similares. Ya los desarrolladores elegirán el método que consideren mejor, dependiendo del objetivo en mente.

      Gracias por tu comentario y enlaces. ¡Éxito en tus desarrollos!

      Eliminar
    2. Vaya, pues no sabía que aún estuviese en desarrollo, además cuando encontré pensaba que era lo mismo por eso de tener nombres parecidos. Esperemos que con el tiempo evolucione para mejor y se estandarice en todos los navegadores!

      Me lo has aclarado totalmente compañero, como se notan tus años programando videojuegos, la experiencia dice mucho de una persona.

      Saludos!

      Eliminar
  4. Amigo quería hacerte una pregunta, existe alguna forma de que el canvas se adapte al tamaño de la ventana? Porque de la forma que tú lo haces se estira pero si redimensiono la ventana queda del mismo tamaño y tengo que quitar la pantalla completa y volverla a poner.

    ResponderEliminar
    Respuestas
    1. ¡Buen detalle que has notado! Para que el canvas se adapte automáticamente al cambiar la pagina de tamaño, debes agregar un escucha:

      window.addEventListener('resize', setFullscreen, false);

      Eso deberá solucionar el problema que indicas ;)

      Eliminar
  5. Hola fenómeno,

    En primer lugar enhorabuena por este tutorial, de los mejores que he visto en internet por calidad de los contenido y explicaciones. Quería comentarte que he probado a poner a pantalla completa el juego de disparos con el ratón, pero la posición del puntero del ratón no coincide con la de la mirilla y es bastante incómodo, ¿hay alguna manera sencilla de solucionarlo?

    ¡¡Muchas gracias y muchos ánimos para continuar!!

    ResponderEliminar
    Respuestas
    1. ¡Ah sí! Cambiar el tamaño del canvas juega con la mirilla virtual...
      Ajustarle no es un problema demasiado grande; tan solo divide mousex y mousey entre sale dentro del mismo escucha dr mousemove, eso deberá resolver el problema ;)

      Eliminar
  6. Perfecto Karl, la he dividido entre "scale" (como me indicaste a pesar del error ortográfico XD) y ya se ha ajustado. Aclarar a posibles lectores que para este caso la variable scale habría que declararla de manera global.

    Muchas gracias.

    ResponderEliminar
    Respuestas
    1. Mas que un error ortográfico, fue del autocorrector móvil... Pero me aseguraré no se pase de nuevo por lo mismo. ¡Y gracias por la aclaración con respecto a la variable global!

      Eliminar
  7. Por cierto, estoy mirando librerías para canvas HTML5, como fabric.js, ¿crees que merece la pena para realizar un juego de tablero (por ejemplo el ajedrez)?

    ResponderEliminar
    Respuestas
    1. Seguramente una librería te ayudaría muchísimo a desarrollar un juego de forma más rápida, pero fabric.js es mas una librería de manipulación de objetos, que una librería de juegos, por lo que no creo que esa específicamente te ayude con el objetivo que deseas.

      Eliminar
  8. solo por correción...el codigo < body style="color:black" > no es correcto no? sera asi < body style="background-color:black" >
    Un saludo

    ResponderEliminar
    Respuestas
    1. Efectivamente tenía un error de elemento, el cual ya ha sido corregido. Muchas gracias por tu aviso.

      Eliminar
  9. Y cómo se podría hacer para que al reescalar no pierda el efecto pixelado? Es que para juegos estilo retro esto no viene muy bien...
    Muchas gracias, muy últiles tus tutoriales por cierto!
    Un saludo!

    ResponderEliminar
    Respuestas
    1. Precisamente ese tema fue visto en la entrada del fin de semana pasado. Puedes verlo en este enlace: http://juegos.canvas.ninja/2014/10/doble-bufer-y-escalado-pixeleado.html

      Eliminar
    2. Vaya, no lo había visto! Eres tan rápido que incluso te adelantas a nuestras necesidades jeje.
      En cuanto saque un rato le hecho un ojo. Muchísimas gracias! :D

      Eliminar