566 lines
22 KiB
Text
566 lines
22 KiB
Text
[#_lesson_19_text_that_looks_good]
|
||
== Leçon 19 : Du texte qui a de l'allure
|
||
|
||
image::learn/19-text.png[La leçon terminée, 480]
|
||
|
||
Chaque mot que vous avez mis à l'écran depuis la leçon un est sorti de
|
||
`overlayPrint`, et `overlayPrint` a exactement une police : un bloc de pixels à
|
||
chasse fixe intégré au moteur, toujours de la même taille, dans la couleur qu'il
|
||
se trouve avoir. C'est parfait pour un compteur d'images. C'est mauvais pour un
|
||
titre, un score, un menu, ou tout ce qu'un joueur est censé regarder plutôt que
|
||
survoler.
|
||
|
||
Cette leçon charge une vraie police depuis le disque, affiche du texte avec, le
|
||
colore, le mesure pour que vous puissiez centrer les choses, et vous montre ce
|
||
que coûte vraiment l'affichage de texte. À la fin, vous aurez un écran titre
|
||
qui a l'air voulu plutôt qu'accidentel.
|
||
|
||
[#_set_the_overlay_to_a_size_you_can_think_in]
|
||
=== Donnez à l'overlay une taille qui vous parle
|
||
|
||
Commencez un nouveau fichier appelé `text.singe` avec deux lignes :
|
||
|
||
[source,lua]
|
||
----
|
||
dofile("Singe/Framework.singe")
|
||
|
||
overlaySetResolution(discGetWidth(), discGetHeight())
|
||
----
|
||
|
||
Vous avez écrit la première ligne de nombreuses fois ; elle charge le
|
||
framework, qui définit `DIR`, les noms `SWITCH_*`, et les noms `FONT_QUALITY_*`
|
||
dont vous allez vous servir.
|
||
|
||
La seconde compte plus qu'elle n'en a l'air. L'overlay -- la surface sur
|
||
laquelle atterrit tout ce que vous dessinez -- commence à la *moitié* de la
|
||
taille du monde. Un jeu sans vidéo a un canevas de 720 sur 480, donc l'overlay
|
||
commence à 360 sur 240, et une police de 36 points sur un overlay de 360 pixels
|
||
de large est énorme. `overlaySetResolution` remplace l'overlay par un overlay de
|
||
la taille que vous demandez, et à partir de là, chaque coordonnée que vous
|
||
donnez, et tout ce que vous disent `overlayGetWidth` et `overlayGetHeight`, est
|
||
dans cet espace.
|
||
|
||
`discGetWidth()` et `discGetHeight()` donnent la taille de la vidéo quand il y
|
||
en a une et la taille du canevas quand il n'y en a pas, donc cette seule ligne
|
||
vous donne un overlay accordé au monde, quel que soit le genre de jeu.
|
||
Appelez-la une fois, au début, avant de dessiner quoi que ce soit.
|
||
|
||
[#_load_a_font]
|
||
=== Chargez une police
|
||
|
||
En dessous, ajoutez :
|
||
|
||
[source,lua]
|
||
----
|
||
fontQuality(FONT_QUALITY_BLENDED)
|
||
|
||
local titleFont = fontLoad("Singe/FreeSansBold.ttf", 48)
|
||
----
|
||
|
||
`fontLoad` ouvre un fichier de police TrueType et vous renvoie un nombre -- un
|
||
*handle* -- dont vous vous servez chaque fois que vous voulez cette police. Le
|
||
second argument est la taille en points, et elle est fixée pour toute la vie du
|
||
handle. Il n'y a aucun moyen de changer ensuite la taille d'une police chargée.
|
||
Si vous voulez la même police en trois tailles, vous appelez `fontLoad` trois
|
||
fois et vous gardez trois handles.
|
||
|
||
`Singe/FreeSansBold.ttf` est la police que le moteur a déballée dans le dossier
|
||
`Singe` de votre dossier de travail la première fois que vous avez lancé quoi
|
||
que ce soit. Chaque copie de Singe l'a, donc vous pouvez compter dessus.
|
||
N'importe quel autre fichier `.ttf` marche de la même façon ; mettez-le à côté
|
||
de votre script et chargez-le avec `DIR .. "myfont.ttf"`.
|
||
|
||
Maintenant, affichez du texte avec :
|
||
|
||
[source,lua]
|
||
----
|
||
function onOverlayUpdate()
|
||
overlayClear()
|
||
fontPrint(24, 60, "ASTEROID PATROL")
|
||
return OVERLAY_UPDATED
|
||
end
|
||
----
|
||
|
||
Lancez-le. De grandes lettres blanches près du coin supérieur gauche.
|
||
|
||
Remarquez ce que vous n'avez *pas* fait : vous n'avez jamais dit à `fontPrint`
|
||
quelle police utiliser. `fontLoad` fait de la police qu'il vient de charger la
|
||
police sélectionnée, et `fontPrint` dessine toujours avec celle qui est
|
||
sélectionnée. Avec plus d'une police chargée, vous choisissez entre elles avec
|
||
`fontSelect`, ce que vous allez faire dans un instant.
|
||
|
||
Remarquez aussi que `24` et `60` sont des pixels, pas des cellules de caractère.
|
||
C'est la différence entre `fontPrint` et `overlayPrint` qui fait trébucher les
|
||
gens : `overlayPrint(2, 2, ...)` veut dire deux lettres vers la droite et deux
|
||
lignes vers le bas, alors que `fontPrint(2, 2, ...)` veut dire deux pixels vers
|
||
la droite et deux pixels vers le bas, c'est-à-dire coincé dans le coin. Le `y`
|
||
que vous donnez est le *haut* du texte, pas la ligne de base sur laquelle il
|
||
repose.
|
||
|
||
[#_color]
|
||
=== La couleur
|
||
|
||
`fontPrint` prend trois arguments et aucun n'est une couleur. La couleur vient
|
||
d'ailleurs :
|
||
|
||
[source,lua]
|
||
----
|
||
colorForeground(255, 211, 90)
|
||
fontPrint(24, 60, "ASTEROID PATROL")
|
||
----
|
||
|
||
`colorForeground` règle le rouge, le vert et le bleu de `0` à `255`, avec un
|
||
quatrième nombre facultatif pour l'opacité. Ce n'est l'argument de rien du
|
||
tout ; c'est un réglage dont le moteur se souvient. Tout ce qui est dessiné
|
||
après sort dans cette couleur jusqu'à ce que vous la changiez de nouveau,
|
||
exactement comme ça marche déjà pour `overlayBox` et `overlayLine`. Le schéma
|
||
est donc toujours le même : réglez la couleur, puis dessinez.
|
||
|
||
C'est la première chose que les gens font de travers avec les polices, et la
|
||
deuxième arrive dans quelques pages.
|
||
|
||
[#_quality]
|
||
=== La qualité
|
||
|
||
`fontQuality` décide comment les lettres sont rastérisées -- comment les courbes
|
||
d'une lettre sont transformées en pixels -- et il y a trois choix. Essayez
|
||
chacun en le mettant au-dessus de la ligne `fontLoad` et en regardant le
|
||
résultat de près.
|
||
|
||
`FONT_QUALITY_SOLID` est le plus rapide. Chaque pixel fait partie de la lettre
|
||
ou n'en fait pas partie, donc les diagonales et les courbes sortent avec des
|
||
marches d'escalier visibles. En 48 points, ça fait bon marché. En 12 points sur
|
||
un petit overlay, ça peut paraître *plus net* que les autres choix, et c'est
|
||
parfois ce que vous voulez.
|
||
|
||
`FONT_QUALITY_SHADED` lisse les bords, mais il le fait en les mélangeant avec la
|
||
couleur de fond actuelle, et il dessine ce fond comme un rectangle plein
|
||
derrière toute la chaîne. Vous obtenez une étiquette entourée d'une boîte, que
|
||
vous en vouliez une ou non. C'est la bonne réponse pour un afficheur de débogage
|
||
par-dessus une image chargée, et la mauvaise pour presque tout le reste.
|
||
|
||
`FONT_QUALITY_BLENDED` lisse les bords et les garde transparents, donc les
|
||
lettres se posent sur ce qu'il y a derrière elles. C'est le plus coûteux et
|
||
c'est ce que vous voulez presque tout le temps. Utilisez-le à moins d'avoir une
|
||
raison de ne pas le faire.
|
||
|
||
Le réglage est global, pas propre à chaque police, et il s'applique à partir du
|
||
moment où vous l'appelez. Les sprites que vous avez déjà faits gardent la
|
||
qualité avec laquelle ils ont été faits. L'entrée de la Référence consacrée à
|
||
`fontQuality` liste les trois noms et leurs valeurs.
|
||
|
||
[#_measuring_so_you_can_center_things]
|
||
=== Mesurer, pour pouvoir centrer
|
||
|
||
Tôt ou tard -- en général au bout d'une dizaine de minutes -- vous voulez le titre
|
||
au milieu de l'écran. Vous savez que l'écran fait `overlayGetWidth()` de large.
|
||
Vous ne savez pas quelle largeur fait le texte, et il n'y a pas d'appel
|
||
`fontMeasure` pour le demander.
|
||
|
||
Ce qu'il y a à la place, c'est `fontToSprite`. Il fait le rendu d'une chaîne
|
||
avec la police sélectionnée, la qualité actuelle et la couleur actuelle, et au
|
||
lieu de la mettre à l'écran, il vous renvoie un *sprite* : le même genre de
|
||
handle que celui que vous donne `spriteLoad`, ce qui veut dire que chaque appel
|
||
`sprite*` que vous avez appris à la leçon neuf marche dessus. Y compris les deux
|
||
qui le mesurent.
|
||
|
||
[source,lua]
|
||
----
|
||
fontSelect(titleFont)
|
||
colorForeground(255, 211, 90)
|
||
local titleImage = fontToSprite("ASTEROID PATROL")
|
||
----
|
||
|
||
Puis, dans `onOverlayUpdate` :
|
||
|
||
[source,lua]
|
||
----
|
||
local x = (overlayGetWidth() - spriteGetWidth(titleImage)) / 2
|
||
spriteDraw(titleImage, x, 60)
|
||
----
|
||
|
||
C'est ça, centrer, et c'est tout le secret. Prenez la largeur de l'espace,
|
||
retirez la largeur de la chose, et la moitié de ce qui reste est l'écart de
|
||
chaque côté.
|
||
|
||
L'alignement à droite, c'est la même idée avec moins de calcul. Pour mettre du
|
||
texte contre la marge de droite :
|
||
|
||
[source,lua]
|
||
----
|
||
spriteDraw(scoreImage, overlayGetWidth() - MARGIN - spriteGetWidth(scoreImage), y)
|
||
----
|
||
|
||
`spriteGetHeight` mesure l'autre direction, pour empiler des lignes ou centrer
|
||
verticalement.
|
||
|
||
Il y a un piège dans le premier bloc de code, et c'est la deuxième chose que
|
||
tout le monde fait de travers. *La couleur est figée quand le sprite est
|
||
fabriqué, pas quand il est dessiné.* Un `colorForeground` au-dessus de
|
||
`fontToSprite` change le texte. Le mettre plutôt au-dessus de `spriteDraw` ne
|
||
fait rien du tout, parce que `colorForeground` est un réglage pour dessiner du
|
||
texte et des traits, et qu'un sprite est dessiné avec ses propres pixels. Si
|
||
votre titre soigneusement coloré sort en blanc, voilà pourquoi.
|
||
|
||
On peut recolorer un sprite après l'avoir fabriqué, mais avec un appel de
|
||
sprite, pas avec `colorForeground`. `spriteSetTint(image, red, green, blue)`
|
||
multiplie chaque pixel du sprite par cette couleur chaque fois qu'il est
|
||
dessiné, donc un texte rendu en blanc peut être dessiné en jaune aujourd'hui et
|
||
en rouge demain sans être refait. Le blanc est la couleur dans laquelle faire le
|
||
rendu quand vous comptez teinter : tout ce qui est multiplié par le blanc reste
|
||
ce qu'il était, donc la teinte est exactement la couleur que vous demandez.
|
||
|
||
[#_what_printing_costs]
|
||
=== Ce que coûte l'affichage
|
||
|
||
La première fois que `fontPrint` voit une chaîne, il fait un vrai travail : il
|
||
rastérise chaque lettre à partir des contours de la police et construit une
|
||
image de toute la chaîne. Puis il garde cette image. La fois suivante où vous
|
||
affichez la même chaîne, avec la même police, la même qualité et les mêmes
|
||
couleurs, il saute tout ça et copie l'image qu'il a déjà, ce qui coûte à peu
|
||
près autant que dessiner un sprite. Une chaîne que vous arrêtez d'afficher est
|
||
lâchée une seconde plus tard, donc rien ne s'accumule.
|
||
|
||
Ça veut dire que la façon évidente d'écrire un écran titre est aussi la plus
|
||
rapide. Afficher `"ASTEROID PATROL"` soixante fois par seconde n'en fait le
|
||
rendu qu'une fois. Un score qui change deux fois par seconde est rendu deux fois
|
||
par seconde, tout seul, sans aucune comptabilité de votre part.
|
||
|
||
Le seul cas qui coûte, c'est du texte qui est *différent* à chaque image -- un
|
||
minuteur en millisecondes, un compteur d'images -- parce que chaque image est une
|
||
chaîne que `fontPrint` n'a jamais vue. Sur un ordinateur de bureau, ça n'a pas
|
||
d'importance. Sur un Raspberry Pi ou une console portable, un plein écran de ce
|
||
genre de texte en aura. Le remède est de changer le texte moins souvent : un
|
||
minuteur en secondes entières est une nouvelle chaîne une fois par seconde, pas
|
||
soixante.
|
||
|
||
Alors pourquoi cette leçon a-t-elle utilisé `fontToSprite` ? Parce qu'un sprite
|
||
peut faire des choses que le texte affiché ne peut pas faire :
|
||
|
||
* *On peut le mesurer*, et c'est la seule façon de centrer du texte ou de
|
||
l'aligner contre le côté droit.
|
||
* *On peut le redimensionner et le faire tourner* avec les appels `sprite*` de
|
||
la leçon neuf.
|
||
* *Il peut survivre à sa police*, ce qui va compter dans un instant.
|
||
|
||
Utilisez `fontPrint` pour le texte, et `fontToSprite` quand vous avez besoin de
|
||
l'une de ces choses.
|
||
|
||
Le score de cette leçon a besoin de l'une d'elles : il est placé contre la marge
|
||
de droite, donc il doit être mesuré, donc il doit être un sprite. Et un sprite
|
||
de score n'est juste que jusqu'à ce que le score change. Quand il change, faites
|
||
le rendu d'un nouveau sprite et déchargez l'ancien -- l'oublier laisse fuir un
|
||
sprite à chaque changement :
|
||
|
||
[source,lua]
|
||
----
|
||
local function renderScore()
|
||
if scoreImage ~= nil then
|
||
spriteUnload(scoreImage)
|
||
end
|
||
fontSelect(scoreFont)
|
||
colorForeground(255, 255, 255)
|
||
scoreImage = fontToSprite(string.format("%06d", score))
|
||
scoreShown = score
|
||
end
|
||
----
|
||
|
||
`scoreShown` retient quel score ce sprite affiche, pour que le code de dessin
|
||
puisse savoir s'il est encore juste :
|
||
|
||
[source,lua]
|
||
----
|
||
if score ~= scoreShown then
|
||
renderScore()
|
||
end
|
||
----
|
||
|
||
`string.format("%06d", score)` est du Lua tout simple, pas du Singe. Il
|
||
transforme un nombre en chaîne, et `%06d` veut dire « en nombre entier, au moins
|
||
six chiffres, complété par des zéros », c'est pourquoi les scores d'arcade ont
|
||
des zéros devant. Changez-le en `%d` et les zéros disparaissent.
|
||
|
||
[#_putting_fonts_away]
|
||
=== Ranger les polices
|
||
|
||
`fontUnload` ferme une police et rend son handle invalide. Les sprites que vous
|
||
avez faits avec `fontToSprite` ne sont pas touchés -- une fois rendu, un sprite
|
||
n'a plus rien à voir avec la police -- donc vous pouvez décharger une police et
|
||
continuer de dessiner le texte fait avec elle.
|
||
|
||
Un point délicat : si vous déchargez la police qui était sélectionnée, *aucune*
|
||
police n'est sélectionnée ensuite, et le `fontPrint` suivant termine votre jeu
|
||
avec une erreur. Sélectionnez-en d'abord une autre si vous comptez continuer
|
||
d'afficher du texte.
|
||
|
||
L'endroit bien rangé pour tout ça, c'est `onShutdown`, un callback que le moteur
|
||
appelle une fois quand le jeu se ferme :
|
||
|
||
[source,lua]
|
||
----
|
||
function onShutdown()
|
||
spriteUnload(titleImage)
|
||
fontUnload(titleFont)
|
||
end
|
||
----
|
||
|
||
Singe fait le ménage derrière vous quand le jeu se termine, donc rien ne prend
|
||
feu si vous sautez cette étape. Écrivez-la quand même. Un jeu qui libère ce
|
||
qu'il a chargé est un jeu qu'on peut charger deux fois.
|
||
|
||
[#_the_whole_script]
|
||
=== Le script complet
|
||
|
||
[source,lua]
|
||
----
|
||
dofile("Singe/Framework.singe")
|
||
|
||
overlaySetResolution(discGetWidth(), discGetHeight())
|
||
|
||
local MARGIN = 24
|
||
local TITLE_Y = 60
|
||
local SCORE_Y = 170
|
||
local LABEL_Y = 186
|
||
local HINT_Y = 300
|
||
local CLOCK_Y = 430
|
||
|
||
local score = 0
|
||
local titleFont = nil
|
||
local scoreFont = nil
|
||
local smallFont = nil
|
||
local titleImage = nil
|
||
local hintImage = nil
|
||
local scoreImage = nil
|
||
local scoreShown = nil
|
||
|
||
|
||
local function centered(image)
|
||
return (overlayGetWidth() - spriteGetWidth(image)) / 2
|
||
end
|
||
|
||
|
||
local function renderScore()
|
||
if scoreImage ~= nil then
|
||
spriteUnload(scoreImage)
|
||
end
|
||
fontSelect(scoreFont)
|
||
colorForeground(255, 255, 255)
|
||
scoreImage = fontToSprite(string.format("%06d", score))
|
||
scoreShown = score
|
||
end
|
||
|
||
|
||
fontQuality(FONT_QUALITY_BLENDED)
|
||
|
||
titleFont = fontLoad("Singe/FreeSansBold.ttf", 48)
|
||
scoreFont = fontLoad("Singe/FreeSansBold.ttf", 36)
|
||
smallFont = fontLoad("Singe/FreeSansBold.ttf", 18)
|
||
|
||
fontSelect(titleFont)
|
||
colorForeground(255, 211, 90)
|
||
titleImage = fontToSprite("ASTEROID PATROL")
|
||
|
||
fontSelect(smallFont)
|
||
colorForeground(140, 200, 255)
|
||
hintImage = fontToSprite("Press the fire button to score")
|
||
|
||
renderScore()
|
||
|
||
|
||
function onInputPressed(what)
|
||
if what == SWITCH_BUTTON1 then
|
||
score = score + 125
|
||
end
|
||
end
|
||
|
||
|
||
function onOverlayUpdate()
|
||
overlayClear()
|
||
|
||
spriteDraw(titleImage, centered(titleImage), TITLE_Y)
|
||
|
||
if score ~= scoreShown then
|
||
renderScore()
|
||
end
|
||
spriteDraw(scoreImage, overlayGetWidth() - MARGIN - spriteGetWidth(scoreImage), SCORE_Y)
|
||
|
||
fontSelect(smallFont)
|
||
colorForeground(160, 160, 160)
|
||
fontPrint(MARGIN, LABEL_Y, "SCORE")
|
||
fontPrint(MARGIN, CLOCK_Y, "Running for " .. (singeGetTicks() // 1000) .. " seconds")
|
||
|
||
if (singeGetTicks() // 500) % 2 == 0 then
|
||
spriteDraw(hintImage, centered(hintImage), HINT_Y)
|
||
end
|
||
|
||
return OVERLAY_UPDATED
|
||
end
|
||
|
||
|
||
function onShutdown()
|
||
spriteUnload(titleImage)
|
||
spriteUnload(hintImage)
|
||
if scoreImage ~= nil then
|
||
spriteUnload(scoreImage)
|
||
end
|
||
fontUnload(titleFont)
|
||
fontUnload(scoreFont)
|
||
fontUnload(smallFont)
|
||
end
|
||
----
|
||
|
||
Lancez-le et maintenez le bouton de tir. Le titre et l'indication sont centrés,
|
||
le score grimpe contre la marge de droite et n'est rendu de nouveau que lorsqu'il
|
||
change, l'indication clignote, et l'étiquette et l'horloge sont affichées avec
|
||
`fontPrint` à chaque image -- l'horloge en secondes entières, donc ce n'est une
|
||
nouvelle chaîne qu'une fois par seconde.
|
||
|
||
[#_what_just_happened_15]
|
||
=== Ce qui vient de se passer
|
||
|
||
[source,lua]
|
||
----
|
||
titleFont = fontLoad("Singe/FreeSansBold.ttf", 48)
|
||
scoreFont = fontLoad("Singe/FreeSansBold.ttf", 36)
|
||
smallFont = fontLoad("Singe/FreeSansBold.ttf", 18)
|
||
----
|
||
|
||
Un fichier, trois tailles, trois handles. Chacun coûte de la mémoire pour son
|
||
propre jeu de glyphes rendus, donc chargez les tailles dont vous vous servez et
|
||
pas plus. Trois, c'est normal. Une douzaine, ça sent mauvais.
|
||
|
||
Après ces trois lignes, la police sélectionnée est `smallFont`, parce que
|
||
charger sélectionne. C'est pourquoi le code appelle `fontSelect(titleFont)` avant
|
||
de faire le rendu du titre : la dernière chose chargée n'est pas celle que vous
|
||
voulez en premier.
|
||
|
||
[source,lua]
|
||
----
|
||
local function centered(image)
|
||
return (overlayGetWidth() - spriteGetWidth(image)) / 2
|
||
end
|
||
----
|
||
|
||
Écrite une fois comme fonction parce qu'elle sert deux fois, et parce que la
|
||
prochaine fois que vous voudrez centrer quelque chose, vous en aurez de nouveau
|
||
besoin. Donnez un nom au calcul et vous n'aurez plus jamais à le relire.
|
||
|
||
[source,lua]
|
||
----
|
||
if (singeGetTicks() // 500) % 2 == 0 then
|
||
spriteDraw(hintImage, centered(hintImage), HINT_Y)
|
||
end
|
||
----
|
||
|
||
`singeGetTicks()` est le nombre de millisecondes écoulées depuis le démarrage du
|
||
moteur. `//` est la division qui jette le reste, donc `singeGetTicks() // 500`
|
||
compte des demi-secondes, et `% 2` est le reste de la division par deux, qui
|
||
vaut `0`, `1`, `0`, `1`... Donc l'indication est dessinée pendant une
|
||
demi-seconde et sautée pendant une demi-seconde. Ne rien dessiner, c'est comme
|
||
ça qu'on fait clignoter quelque chose.
|
||
|
||
[source,lua]
|
||
----
|
||
fontSelect(smallFont)
|
||
colorForeground(160, 160, 160)
|
||
fontPrint(MARGIN, LABEL_Y, "SCORE")
|
||
----
|
||
|
||
Les deux réglages sont nécessaires ici, et ça vaut la peine de dire clairement
|
||
pourquoi. Dessiner un sprite ne change ni la police sélectionnée ni la couleur,
|
||
donc les deux appels `spriteDraw` au-dessus les laissent exactement comme ils
|
||
les ont trouvées. Mais le code de mise en place a sélectionné trois polices
|
||
différentes à tour de rôle, et `renderScore` en sélectionne une autre chaque
|
||
fois que le score bouge, donc la sélection au moment où cette ligne s'exécute
|
||
est celle qui s'est trouvée choisie en dernier. N'essayez pas de la suivre.
|
||
Sélectionnez la police et réglez la couleur juste avant d'afficher, à chaque
|
||
fois. Ça fait deux lignes, et ça supprime toute une catégorie de soirées
|
||
perplexes.
|
||
|
||
[#_try_it_19]
|
||
=== Essayez
|
||
|
||
. *Changez la qualité.* Mettez `FONT_QUALITY_SOLID` dans la ligne
|
||
`fontQuality` et regardez la courbe du `S` dans `ASTEROID`. Puis essayez
|
||
`FONT_QUALITY_SHADED` et regardez ce qui se passe derrière chaque chaîne.
|
||
. *Mettez l'indication en jaune.* Déplacez la ligne
|
||
`colorForeground(140, 200, 255)` d'au-dessus de `fontToSprite` à juste
|
||
au-dessus du `spriteDraw` qui dessine `hintImage`, et changez les nombres.
|
||
Trouvez pourquoi rien ne se passe. Puis remettez-la, faites plutôt
|
||
l'indication en blanc, et rendez-la jaune avec `spriteSetTint` juste avant
|
||
qu'elle soit dessinée.
|
||
. *Alignez l'horloge à droite.* L'horloge est dessinée avec `fontPrint`, qu'on
|
||
ne peut pas mesurer. Mettez-la quand même contre la marge de droite. Il n'y a
|
||
qu'une seule façon de le faire, et la trouver, c'est tout l'intérêt.
|
||
. *Retirez la vérification de `scoreShown`*, pour que `renderScore()`
|
||
s'exécute plutôt à chaque image. Ça aura exactement la même allure, mais ça
|
||
fait maintenant le rendu du score soixante fois par seconde au lieu de le
|
||
faire quand il change -- exactement le travail que `fontPrint` vous épargne
|
||
quand vous le laissez faire. Puis retirez aussi le `spriteUnload` de
|
||
`renderScore`, laissez tourner une minute, et surveillez la mémoire
|
||
qu'utilise votre jeu.
|
||
. *Affichez une horloge en millisecondes.* Changez la ligne de l'horloge pour
|
||
qu'elle affiche `singeGetTicks()` sans le `// 1000`. Ça marche toujours ;
|
||
c'est maintenant une chaîne que `fontPrint` n'a jamais vue, à chaque image.
|
||
Sur une machine lente, c'est ce genre de texte qui coûte.
|
||
. *Utilisez votre propre police.* Trouvez un `.ttf` sur votre machine,
|
||
copiez-le à côté de votre script, et chargez-le avec `DIR .. "thatfont.ttf"`.
|
||
Certaines polices sont affreuses en 18 points et très bien en 48. C'est la
|
||
faute de la police, pas la vôtre.
|
||
|
||
[#_break_it_on_purpose_18]
|
||
=== Cassez-le exprès
|
||
|
||
Ajoutez une ligne `fontPrint(24, 60, "SCORE")` juste sous
|
||
`overlaySetResolution(discGetWidth(), discGetHeight())`, pour qu'elle s'exécute
|
||
avant tout `fontLoad`. Singe démarre et meurt aussitôt :
|
||
|
||
----
|
||
4:fontPrint: No font selected.
|
||
----
|
||
|
||
Lisez-le comme n'importe quelle autre erreur. `4` est la ligne. `fontPrint` est
|
||
l'appel du moteur qui s'est plaint -- un message de cette forme vient de
|
||
l'intérieur du moteur plutôt que de Lua, donc il nomme la fonction au lieu du
|
||
fichier. Le reste, c'est la plainte, et ici elle est exacte : vous lui avez
|
||
demandé d'afficher du texte et il n'y avait pas de police pour le faire.
|
||
|
||
Vous rencontrerez celle-ci pour de vrai, d'une façon moins évidente. Déchargez
|
||
la police sélectionnée pendant que le jeu tourne, et le `fontPrint` suivant dit
|
||
exactement la même chose, à plusieurs centaines de lignes du `fontUnload` qui
|
||
l'a causée.
|
||
|
||
[#_what_you_learned_19]
|
||
=== Ce que vous avez appris
|
||
|
||
* `fontLoad` ouvre un fichier TrueType à une taille en points fixe et renvoie
|
||
un handle. Une taille, un handle.
|
||
* Charger une police la sélectionne ; `fontSelect` choisit entre les polices
|
||
chargées.
|
||
* `fontPrint` compte en pixels et `overlayPrint` compte en cellules de
|
||
caractère.
|
||
* La couleur n'est pas un argument. `colorForeground` est un réglage ;
|
||
réglez-le, puis dessinez.
|
||
* `fontQuality` choisit comment les lettres sont rastérisées, et
|
||
`FONT_QUALITY_BLENDED` est la réponse habituelle.
|
||
* `fontPrint` garde ce dont il a fait le rendu, donc afficher le même texte à
|
||
chaque image coûte peu ; c'est le texte différent à chaque image qui coûte.
|
||
* `fontToSprite` fait le rendu dans un sprite, que `spriteGetWidth` mesure et
|
||
que les appels `sprite*` peuvent redimensionner et faire tourner. Utilisez-le
|
||
pour ça, pas pour la vitesse.
|
||
* Mesurer est la façon de centrer ou d'aligner du texte à droite, parce que rien
|
||
d'autre ne peut vous dire quelle largeur fera une chaîne.
|
||
* `colorForeground` atteint le texte quand il est fabriqué, jamais un sprite
|
||
quand il est dessiné ; `spriteSetTint` est la façon de recolorer un sprite
|
||
après coup.
|
||
* `fontUnload` libère une police ; les sprites qu'elle a servi à faire lui
|
||
survivent.
|
||
|
||
[#_next_time_19]
|
||
=== La prochaine fois
|
||
|
||
Du texte qui a de l'allure, c'est encore loin d'un menu qui marche. Une page
|
||
avec un titre, une liste dans laquelle on déplace un curseur, une glissière, et
|
||
un bouton, c'est une centaine de lignes de dessin et de calculs de curseur si
|
||
vous la construisez avec les appels de cette leçon. La leçon vingt ne la
|
||
construit pas du tout avec ces appels.
|