Un jeu HTML5 : dimension et plein écran

Quand on fait un jeu en HTML5 on espère qu’il fonctionnera sur tous les supports.  Mais il y a quand même une différence entre l’écran géant d’un PC ou d’un Mac et celui d’un mobile, d’autant que ce dernier a une grande facilité à passer de portrait à paysage. Dans ce chapitre on va voir les implications de ce problème et les solutions possibles.

La taille du canvas

On a vu dans tous les exemples qu’on peut définir la taille du canvas. Prenons un exemple simple pour illustrer cela :

<!DOCTYPE HTML>
<html>
<head>
  <title>Cours pixi</title>
  <meta charset="UTF-8">
  <script src="../js/pixi.js"></script>
  <style>
    canvas {
      position: absolute;
      margin: auto;
      top: 0;
      right: 0;
      bottom: 0;
      left: 0;
    }
  </style>
</head>
<body>
  <script>
    var renderer = PIXI.autoDetectRenderer(400, 300);
    document.body.appendChild(renderer.view);
    var stage = new PIXI.Container();

    PIXI.loader
      .add('img/image.png')
      .once('complete', setup)
      .load();

    function setup() {
        var sprite1 = new PIXI.Sprite(PIXI.loader.resources['img/image.png'].texture);
        stage.addChild(sprite1);

        var sprite2 = new PIXI.Sprite(PIXI.loader.resources['img/image.png'].texture);
        sprite2.position.set(renderer.width - sprite2.width, renderer.height - sprite2.height);
        stage.addChild(sprite2);  

        gameLoop();
    }

    function gameLoop(){
      requestAnimationFrame(gameLoop);
      renderer.render(stage);
    }
  </script>
</body>
</html>

 Ici je dis que mon canvas fera 400 px sur 300 px. On a vu également que les valeurs par défaut sont 600 x 400 si on ne précise rien. J’ai prévu un sprite en haut à gauche et un autre en bas à droite pour bien repérer les limites :

img01

Le centrage sur la page est effectué avec des règles css classiques sur le canvas. Tout semble bien se passer…

Imaginons que je crée mon jeu avec toute ses textures pour un canvas de 400 x 300 comme vu ci-dessus. Que va-t-il se passer sur un support qui n’atteint pas cette dimension ? On ne va pas avoir toute la scène et c’est la catastrophe pour le jeu ! Je vais même voir apparaître des ascenseurs :

img02

Pour les ascenseurs on peut facilement régler ça avec du css :

body { overflow: hidden; }

 Mais ça n’arrange rien parce qu’il nous manque un morceau du canvas :

img03

Il est possible de tester les possibilités d’affichage et donc de réduire le canvas au besoin :

var width = window.innerWidth;
var height = window.innerHeight;
if(width > 400) width = 400;
if(height > 300) height = 300;
var renderer = PIXI.autoDetectRenderer(width, height);

 Si largeur ou la hauteur disponible est inférieur à mes valeurs de référence je la réduis en conséquence :

img04

Maintenant mon canvas est complet mais j’ai encore 3 soucis :

  • je peux totalement perdre mes proportions de départ :

img05
  • pour un canvas vraiment plus petit mes sprites seront bien trop gros,

  • si je redimensionne la page le canvas va rester ce qu’il était lors du chargement :

img06

Pour ne pas perdre les proportions on peut se baser sur la plus petite dimension :

var widthBase = 400;
var heightBase = 300;
var width = window.innerWidth;
var height = window.innerHeight; 
if(width > widthBase) width = widthBase;
if(height > heightBase) height = heightBase;
if(width / widthBase < height / heightBase) {
  height = width * heightBase / widthBase;
} else {
  width = height * widthBase / heightBase;
}
var renderer = PIXI.autoDetectRenderer(width, height);

img07

Maintenant que le souci de proportionnalité est réglé comment régler le problème de la taille des sprites ? On va apporter juste un petit changement :

var renderer = PIXI.autoDetectRenderer(widthBase, heightBase);
renderer.view.style.width = width + "px";
renderer.view.style.height = height + "px";

 On commence par utiliser les dimensions de base puis on ajuste avec les dimensions corrigées :

img08

Il ne nous reste plus que l’histoire du changement de dimension de la fenêtre. On va en profiter pour regrouper tout ce qu’on a dit ci-dessus avec un exemple complet :

<!DOCTYPE HTML>
<html>
<head>
  <title>Cours pixi</title>
  <meta charset="UTF-8">
  <script src="../js/pixi.js"></script>
  <style>
    canvas {
      position: absolute;
      margin: auto;
      top: 0;
      right: 0;
      bottom: 0;
      left: 0;
    }
  </style>
</head>
<body>
  <script>
    var widthBase = 400;
    var heightBase = 300;
    var renderer = PIXI.autoDetectRenderer(widthBase, heightBase);
    setDimensions();
    document.body.appendChild(renderer.view);
    var stage = new PIXI.Container();

    PIXI.loader
      .add('img/image.png')
      .once('complete', setup)
      .load();

    function setup() {
        var sprite1 = new PIXI.Sprite(PIXI.loader.resources['img/image.png'].texture);
        stage.addChild(sprite1);

        var sprite2 = new PIXI.Sprite(PIXI.loader.resources['img/image.png'].texture);
        sprite2.position.set(renderer.width - sprite2.width, renderer.height - sprite2.height);
        stage.addChild(sprite2);  

        gameLoop();
    }

    function setDimensions() {
      var width = window.innerWidth;
      var height = window.innerHeight; 
      if(width > widthBase) width = widthBase;
      if(height > heightBase) height = heightBase;
      if(width / widthBase < height / heightBase) {
        height = width * heightBase / widthBase;
      } else {
        width = height * widthBase / heightBase;
      }
      renderer.view.style.width = width + "px";
      renderer.view.style.height = height + "px";
    }

    window.onresize = function (){
      setDimensions();
    }

    function gameLoop(){
      requestAnimationFrame(gameLoop);
      renderer.render(stage);
    }
  </script>
</body>
</html>

Maintenant on a bien obtenu ce qu’on voulait !

Le mode plein écran

Mais la plupart du temps on préfère que le jeu occupe toute la fenêtre et passer en plein écran même si on a des déformations et d’ajuster à chaque redimensionnement. Dans ce cas on part d’une dimension de référence avec des sprites proportionnés et on laisse se réduire ou grossir selon les possibilités d’affichage. Voici l’exemple modifié en conséquence :

<!DOCTYPE HTML>
<html>
<head>
  <title>Cours pixi</title>
  <meta charset="UTF-8">
  <script src="../js/pixi.js"></script>
  <style>
    canvas {
      position: absolute;
      margin: auto;
      top: 0;
      right: 0;
      bottom: 0;
      left: 0;
    }
  </style>
</head>
<body>
  <script>
    var widthBase = 400;
    var heightBase = 300;
    var renderer = PIXI.autoDetectRenderer(widthBase, heightBase);
    setDimensions();
    document.body.appendChild(renderer.view);
    var stage = new PIXI.Container();

    PIXI.loader
      .add('img/image.png')
      .once('complete', setup)
      .load();

    function setup() {
        var sprite1 = new PIXI.Sprite(PIXI.loader.resources['img/image.png'].texture);
        stage.addChild(sprite1);

        var sprite2 = new PIXI.Sprite(PIXI.loader.resources['img/image.png'].texture);
        sprite2.position.set(renderer.width - sprite2.width, renderer.height - sprite2.height);
        stage.addChild(sprite2);  

        gameLoop();
    }

    function setDimensions() {
      renderer.view.style.width = window.innerWidth + "px";
      renderer.view.style.height = window.innerHeight + "px";
    }

    window.onresize = function (){
      setDimensions();
    }

    function gameLoop(){
      requestAnimationFrame(gameLoop);
      renderer.render(stage);
    }
  </script>
</body>
</html>

img09

Dans ce cas là il est judicieux de détecter si on est en portrait et de proposer de passer en mode paysage. Il suffit de comparer la largeur et la hauteur.

Le mode plein écran vu ci-dessus est tout de même un peu gâché par la barre de navigation, il serait bien de la faire disparaître. Pour le faire on dispose de l’API « plein écran ». On va poursuivre l’exemple avec un seul sprite central qu’on va équiper d’une détection d’un événement de souris ou tactile et passer en mode plein écran ou en sortir selon l’état :

<!DOCTYPE HTML>
<html>
<head>
  <title>Cours pixi</title>
  <meta charset="UTF-8">
  <script src="../js/pixi.js"></script>
  <style>
    body {
      margin: 0px;
      overflow: hidden;
    }
  </style>
</head>
<body>
  <script>
    var widthBase = 400;
    var heightBase = 300;
    var renderer = PIXI.autoDetectRenderer(widthBase, heightBase);
    setDimensions();
    document.body.appendChild(renderer.view);
    var stage = new PIXI.Container();

    PIXI.loader
      .add('img/image.png')
      .once('complete', setup)
      .load();

    function setup() {
        var sprite = new PIXI.Sprite(PIXI.loader.resources['img/image.png'].texture);
        sprite.anchor.set(0.5);
        sprite.position.set(renderer.width / 2, renderer.height / 2);
        stage.addChild(sprite);

        sprite.interactive = true;
        sprite.touchstart = toggleFullScreen;  
        sprite.mousedown = toggleFullScreen; 

        gameLoop();
    }

    function setDimensions() {
      var width = window.innerWidth;
      var height = window.innerHeight; 
      renderer.view.style.width = width + "px";
      renderer.view.style.height = height + "px";
    }

    window.onresize = function (){
      setDimensions();
    }

    document.addEventListener("keydown", function(e) {
      if (e.keyCode == 13) {
        toggleFullScreen();
      }
    }, false)

    function toggleFullScreen() {
      if (!document.fullscreenElement && 
          !document.mozFullScreenElement && !document.webkitFullscreenElement) {  
        if (document.documentElement.requestFullscreen) {
          document.documentElement.requestFullscreen();
        } else if (document.documentElement.mozRequestFullScreen) {
          document.documentElement.mozRequestFullScreen();
        } else if (document.documentElement.webkitRequestFullscreen) {
          document.documentElement.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
        }
      } else {
        if (document.cancelFullScreen) {
          document.cancelFullScreen();
        } else if (document.mozCancelFullScreen) {
          document.mozCancelFullScreen();
        } else if (document.webkitCancelFullScreen) {
          document.webkitCancelFullScreen();
        }
      }
    }

    function gameLoop(){
      requestAnimationFrame(gameLoop);
      renderer.render(stage);
    }
  </script>
</body>
</html>

 Au chargement on a la barre de navigation et le sprite au milieu :

img11

Et lorsqu’on clique (ou avec un événement tactile) sur le sprite on passe en « vrai » plein écran :

img10

Il suffit d’effectuer la même action pour revenir en arrière.

On a maintenant tout ce qu’il nous faut pour réaliser un jeu.

En résumé

Dans ce chapitre on a vu :

  • comment ajuster la taille du canvas selon le support,

  • comment passer en mode plein écran.

Laisser un commentaire