ACCUEIL BLOG FORUM OUTILS DOCS CONNEXION
Contact - Change Log
---

Comprendre le positionnement et la mise à l’échelle de Solar2D

Un programme Solar2D pouvant s’executer sur plusieurs périphériques disposant d’une résolution, d’un ratio largeur / hauteur et d’une densité de pixels différents, il fallait trouver une solution pour qu’un même programme puisse fonctionner de façon optimal sur tous ces périphériques.
Solar2D propose donc différents modes pour gérer le redimentionnement. Si ces modes vont au final vous simplifier la vie, au début, il peut être compliqué d’appréhender leur fonctionnement et le positionnement des objets sur l’écran.
Je vous explique tout ça ici.

Hello World !

Lorsque l’on teste pour la premiere fois un langage il est d’usage d’essayer d’écrire “Hello World” à l’écran. C’est un rituel indiquant qu’on a réussi à écrire un premier programme simple qui fonctionne. Avec Solar2D on peut faire cela à l’aide de la commande newText (avec Solar2D une fonction système s’appelle commande) qui va faire le job :

local hello = display.newText( "Hello World", 0, 0, native.systemFont, 16 )

HelloWorld #1

Note : On notera (et c’est une bonne nouvelle) que l’écriture de ce premier programme ne nécessite qu’une seule ligne de code ! Il est important de le noter car essayer de faire la meme chose en Flutter ou ReactNative et vous n’allez pas être décu du voyage !

Comme on à spécifier une position (0, 0), on s’attend à ce que le texte soit en haut a gauche de l’écran comme c’est généralement le cas avec la plupart des langages de programmation.
Hors il n’en n’est rien, il manque la moitié de la phrase et il y a une bande noire en haut de l’ecran. Comprendre ce qui se passe n’est pas simple car plusieurs phénomènes entre en jeu pour expliquer le positionnement plus qu’étrange de notre texte !
Heureusement, je vais tout vous expliquer car après avoir un peu galéré, j’y fini par comprendre moi aussi !

Positionnement des objets.

La première chose à comprendre avec Solar2D est que les objets sont positionnés par rapport à leur centre !
C’est à dire que les coordonnées fournies dans notre commande newText ne repésente pas le point haut gauche du texte, mais son centre.
Un objet graphique est toujours englobé dans un rectangle invisible. Lorsqu’on positionne un objet graphique à l’écran, c’est en fait ce rectangle que l’on place en le manipulant par son ancre.

Objet graphique

Dans la plupart des environnements de développement, l’ancre va se trouver en haut à gauche (le point jaune sur le schema ci-dessus), mais avec Solar2D, l’ancre est au centre (point rouge).
On comprend donc pourquoi avoir donné une coordonnée x à 0 à notre objet newText a placé la moitié de l’objet hors de l’écran à gauche puisque c’est le point rouge qui est aligné sur le bord.

Il n’est pas possible de contourner simplement ce problème car la taille du texte peut varier selon que le texte est plus ou moins long, et donc son débordement aussi.
Il nous faudrait connaitre la dimension de l’objet graphique avant de demander son tracé afin d’ajouter à la position demandée la moitié de sa largeur et la moitié de sa hauteur, mais ces informations ne seront disponibles qu’après l’appel de la commande newText.

Il va donc être necessaire de corriger la position après l’appel en utilisant les propriétés width et height de l’object hello

local hello = display.newText("Hello Wolrd", 0, 0, native.systemFont, 16)
hello.x = hello.x + hello.width / 2
hello.y = hello.y + hello.height / 2

HelloWorld #2

Cette fois-ci, miracle, notre texte est bien aligné à gauche, au bord de l’écran mais toujours pas en haut. La correction de la valeur de la propriété y à meme empiré le problème puisqu’on à augmenté sa valeur et donc fait descendre l’objet. La bande noire supérieure est encore plus large.

Note : La performance n’est pas impactée par ce code. Le tracé de l’objet text ne sera pas réalisé 2 fois. Il faut comprendre que Solar2D gère pour vous le moment où il faut tracer les objects à l’écran. La commande NewText n’execute aucun tracé, elle ajoute juste un objet à la liste des objets qu’il va falloir tracer. De fait, modifier à postériori hello.x et hello.y à un impact sur la performance très limité!

Le problème ne vient plus à présent du positionnement de l’objet Text mais du mode de mise à l’échelle de Solar2D… Explications…

Mise a l’échelle de Solar2D

Solar2D est un environnement de développement multi-platforme. Cela implique qu’une application Solar2D doit pouvoir fonctionner sur plusieurs devices ayant éventuellement chacun un écran de résolution différente.
Dès lors il devient plus compliqué de produire une application compatible pour tous ces devices lorsque l’un d’entre eux est plus grand que l’autre, plus allongé ou bien a l’inverse propose un écran presque carré!
C’est là que Solar2D intervient pour essayer de nous simplifier le travail en proposant différentes options pour solutionner ce problème.

L’ecran virtuel

Pour commencer, Solar2D va vous proposer de travailler dans un ecran virtuel affublé d’une resolution particulière que vous allez vous même définir.
Le réglage des propriétés de cet écran se trouve dans le fichier de configuration config.lua qui se trouve dans chacun des projets Solar2D.

Vous trouverez entre autres les 4 paramètres suivants:

        width = 320,
        height = 480, 
        scale = "letterbox",
        fps = 60,

width et height définissent la taille de l’écran virtuel de Solar2D.
Par défaut l’écran virtuel est de résolution 320 * 480 (au format 3/2 donc !). Cela veut plus ou moins dire que le point (0, 0) est le pointle plus en haut a gauche de l’ecran alors que le point (319, 479) sera le point le plus en bas à droite.
Sauf que cela n’est pas tout à fait ausi simple !
Car en plus de la taille de l’écran virtuel vous avez la propriété scale qui va définir le type de déformation que Solar2D va appliquer pour faire entrer l’ecran virtuel dans l’écran physique du device !

Pour bien comprendre le fonctionnement de la propriété scale du fichierconfig.lua, imaginons que Nintendo veuille porter Super Mario Switch sur mobile et utilise pour cela Solar2D (mouais…)
L’ecran de la switch n’etant pas au même proportions (et surtout ratio) que notre telephone portable, Nintendo défini un écran virtuel au format de la switch. il défini donc width à 1280 et la valeiur de height à 720 puis essaye les différentes valeur possble de scale pour voir ce que ça donne !

zoomStretch

zoomStretch est la valeur de scale la plus simple pour Nintendo, il prend l’écran virtuel et le déforme pour le faire entrer parfaitement dans l’écran.
Tout est visible, et il ne manque rien mais l’image est sensiblement écrasée !
Pas sur que Nintendo ce satisfasse de ce petit détail !

zoomStretch

Note : Dans ce mode le point (0, 0) de l’écran virtuel correspond bien au coin gauche / gauche de l’écran. Notre “Hello Wolrd” s’affichera dons bien dans le coin de l’écran mais le texte sera déformé. Il sera un peut plus haut que large (en mode portrait) !

letterbox

Le mode par defaut lorsque l’on crée un nouveau projet Solar2D est le format letterbox. Ce mode garanti que l’image n’est pas déformée (le ratio hauteur / largeur reste constant) et que l’image sera intégralement visible sur l’écran. Si le terminal n’a pas exactement le même ratio que l’image que l’on veut afficher alors des parties ne seront pas recouvertes par des pixel de l’image.
Car ce n’est pas parceque les bandes sont actuellement noires qu’il n’est pas possible d’aller tracer dedans. Pour cela il faudrait tracer à des coordonnées négatives afin de sortir de l’écran virtuel ! Ce n’est pas infaisable, masi ce n’est pas hyper pratique !
On remarque en tout cas que le coin surpérieur gauche de l’écran virtuel ne se trouve donc plus sur le coin supérieur gauche de l’écran !

letterbox

Note : C’est très exactement ce qui de passe dans notre exemple “Hello World”. La zone de travail étant défini avec un ratio 2/3 (320 * 480) et notre smartphone ayant un ratio 10/16, le coin superieur gauche de coordonnées (0, 0) ne coïncide pas avec le coin haut / gauche de l’écran.

zoomEven

le mode zoomEven va grossir l’écran virtuel jusqu’à ce qu’il remplisse le coté le plus grand L’autre axe va lui déborder.
Une partie importante de notre Super Mario n’est plus affiché comme les scores !
En fait ce mode serait pas mal pour notre “Super Mario Bros” si on descendait un peu les scores (ce qu’il est tout à fait possible de faire).
le mode zoomEven est surtout utilisé si l’utilisateur peut librement zommer et dézoomer l’écran avec ses doigts. Par exemple Clash of Clan exploiterai propablement ce mode !

zoomEven

Note : en mode portrait, notre “Hello World” s’afficherai bien en haut de l’écran mais sortirai à gauche puisque là aussi le le point (0, 0) de l’écran virtuel ne correspond pas au coin gauch / gauche de l’écran

adaptative

Ce mode particulier, ne se prète pas à notre exemple.
Le mode adaptative est assez compliqué à manipuler. Il va essayer de conserver une dimension physique identique quelques soit le device.
Imaginons que Nintendo veuille que Mario mesure exactement 2cm de haut qu’il soit affiché sur un smartphone, une tablette au projeté dans une salle de cinéma 🙄.
Dans ce cas, c’est le mode adaptative qu’il faudrait utiliser !

non définie

Enfin il reste un mode peu documenté, il s’agit du mode non défini.
Si vous supprimez la propriété scale du fichier config.lua, Solar2D ne fera aucune transformation.
Les valeur width et height de l’écran virtuel ne seront pas pris en compte. L’écran virtuel fera exactement la résolution de votre device. Solar2D ne cherchera à faire aucune transformation lors du tracé.

Non défini

Finalement, il semble bien que ce mode (ou absence de mode) soit exatement ce qu’on espèrait avoir au depart.
Un mode où le point (0, 0) se trouve bien en haut à gauche de l’écran et permettant d’afficher un texte aucunement déformé !
Le seul problème c’est qu’il est alors à la charge de l’utilisateur de gérer les différentes résolutions des multiples devices sur lesquels l’application pourrait tourner !
Le texte s’affichera dans une taille différente en fonction de la résolution du device. Sur un smartphone récent le text sera ecrit en tout petit à cause du grande nombre de pixel alors que sur un smarphone plus ancien le texte sera nettement plus gros !

Maintenant qu’on a vu que Solar2D pouvait aider à gérer le problème des taille d’écran différent, je doute que vous ayez envie de gérer ça manuellement, sans parler du fait que vous aller finir par faire exactement ce que l’un des modes zoomStretch, letterbox ou zoomEven fait à votre place !

Ecrire en haut à gauche quelquesoit le mode choisi.

Nous y sommes presque !
Vous êtes mainteant persuadé (enfin j’espère) qu’il faut utiliser l’un des modes proposé par Solar2D.
Mais quelques soit le mode choisi, vous voudriez quand même écrire en haut à gauche de l’écran car un jour, ce n’est plus “Hello World” que vous allez vouloir afficher mais le score de votre joueur, et c’est exactement là que vous voudrez le mettre !

Et bien… C’est possible !

Solar2D vous donne accès plusieurs valeurs précalculées

le valeurs retournées par display.contentWidth et display.contentHeight sont les dimension de l’écran virtuel. Il s’agit des valeurs width et height du fichier config.lua, alors que display.actualContentWidth et display.actualContentHeight sont les valeurs la dimension de l’écran physique placé dans le repère de l’écran virtuel !

En clair,

  • Si display.actualContentWidth est supérieur à display.contentWidth alors l’écran virtuel déborde de l’écran physique sur l’axe des x
  • Si display.actualContentWidth est égal à display.contentWidth alors l’écran virtuel correspond parfaitement à l’écran physique
  • Si display.actualContentWidth est inférieur à display.contentWidth alors l’écran virtuel est plus petit et des bandes noires sont présente sur l’axe des des x

Evidement, nous avons les mêmes propriété sur l’axe des y.

Il devient donc possible de calculer l’offset (le nombre de pixels) permettant de passer du point (0, 0) de l’écran virtuel au point (0, 0) de l’écran physique

local OffsetX = (display.contentWidth - display.actualContentWidth) / 2;
local OffsetY = (display.contentHeight - display.actualContentHeight) / 2;

Les variables OffsetX et OffsetY valent très exactement le décalage qu’il existe en les deux écrans !

Notre programme “Hello World” peut donc être corrigé pour afficher notre texte dans le coin haut gauche de l’écran et ce, quelquesoit le mode choisi !

local OffsetX = (display.contentWidth - display.actualContentWidth) / 2;
local OffsetY = (display.contentHeight - display.actualContentHeight) / 2;

local hello = display.newText(Hello World" , 0, 0, native.systemFont, 16)
hello.x = 0 + OffsetX + hello.width / 2
hello.y = 0 + OffsetY + hello.height / 2

Hello World #3

Youpi !

Remarque : Vous remarquerez que j’ai placé des 0 totalement inutiles dans le positionnement de notre text. cela est fait pour que vous puissiez le placer en (10, 10) si cela vous amuse de tester !
Remarque ; OffsetX et OffsetY sont des valeurs flottantes. Solar2D a déjà calculé pour vous ces valeurs et les à arrondi pour en faire des entiers. Vous pouvez obtenir ces valeurs via display.screenOriginX et display.screenOriginY. Utiliser des entiers plutôt que des nombres à virgule est significativement plus rapide mais également moins précis !

Conclusion

Au début, vous vous êtes peut etre arraché les cheveux pour comprendre le système de positionnement de Solar2D, mais au final, une fois que tout cela sera devenu limpide pour vous, vous vous rendrez compte que les choix faits par Solar2D sont plutôt judicieux !
Peut-etre en êtes vous déjà persuadé ! Dans ce cas, il n’y a plus qu’à avancer dans votre projet !