singe/docs/fr/lessons/21-subtitles.adoc

580 lines
24 KiB
Text
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

[#_lesson_21_subtitles]
== Leçon 21 : Les sous-titres
image::learn/21-subtitles.png[La leçon terminée, 480]
Quelqu'un qui n'entend pas va jouer à votre jeu.
Si votre jeu a des paroles et pas de sous-titres, cette personne ne le pourra
pas. Pas « ce sera plus difficile pour elle » -- elle arrivera à la première
scène où un personnage dit ce qui explique au joueur quoi faire, et elle
s'arrêtera là. C'est tout, et c'est la raison pour laquelle cette leçon existe
au lieu d'être un paragraphe de la leçon dix-neuf.
L'autre raison est pratique. Ajouter des sous-titres après coup à un jeu
terminé est une misère. Il faut retrouver chaque réplique, déterminer sur
quelle image elle tombe, et découvrir que la moitié de vos synchronisations
sont fausses parce que votre vidéo a été réencodée au milieu du projet. Fait
pendant que vous écrivez le jeu, c'est un après-midi. Singe vous donne un
système qui fait la partie difficile, et cette leçon explique comment vous en
servir.
[#_it_is_not_ordinary_text_drawing]
=== Ce n'est pas du texte ordinaire
Vous pourriez faire les sous-titres vous-même. Garder une liste de lignes avec
leurs heures de début et de fin, regarder l'horloge dans `onOverlayUpdate`, et
dessiner la ligne du moment avec `fontToSprite`. Certains l'ont fait. Voici
pourquoi vous ne devriez pas.
Les sous-titres sont synchronisés sur *le disque*, pas sur une horloge. Quand
`srtLoad` lit votre fichier, elle convertit chaque horodatage en numéro d'image
du disque, sur-le-champ, en utilisant la fréquence d'images du disque chargé. À
partir de là, le moteur ne regarde plus du tout une horloge ; à chaque image
dessinée, il demande quel sous-titre couvre l'image sur laquelle se trouve le
disque.
Cette seule décision vous offre ce qu'un système fait maison ne réussit
jamais : *un sous-titre survit à un saut*. Envoyez le disque n'importe où --
`discSearch`, `discSkipForward`, un pas, un embranchement vers un autre
segment -- et la bonne ligne est affichée à l'image dessinée suivante, en
avant comme en arrière, sans rien recharger ni remettre aucune horloge à zéro.
Dans un jeu à embranchements de la leçon seize, où les choix du joueur font
sauter le disque sans arrêt, c'est la différence entre ce qui marche et ce qui
marche presque.
Le système dessine aussi au bon endroit. Le document des sous-titres se place
par-dessus l'image, au-dessus de votre overlay, en dessous de toute GUI en
espace écran, et il suit l'image partout où les réglages du joueur la mettent
-- une image réduite, une image décalée, une borne tournée, un trou dans un
bezel. Il ne prend aucune entrée de la souris, donc un tir de pistolet optique
le traverse sans s'arrêter. Réussir tout ça vous-même, c'est une semaine que
vous n'aviez pas prévue.
[#_write_the_file]
=== Écrivez le fichier
Les sous-titres vont dans un fichier SubRip, qui se termine par `.srt` et qui
est du texte brut que vous pouvez taper dans n'importe quel éditeur. C'est l'un
des formats les plus simples qui existent. Créez un fichier appelé
`subtitles.srt` à côté de votre script :
----
1
00:00:01,000 --> 00:00:04,000
They told us the cave was empty.
2
00:00:04,200 --> 00:00:08,000
They were wrong about
a great many things.
3
00:00:08,500 --> 00:00:13,000
Keep your light on the floor.
----
Un sous-titre, c'est quatre choses. Un numéro seul sur sa ligne. Une ligne avec
deux horodatages et, entre eux, une flèche faite de deux traits d'union et d'un
signe supérieur. Une ou plusieurs lignes de texte. Une ligne vide pour finir.
Un horodatage s'écrit `HH:MM:SS,mmm` -- les heures, les minutes et les
secondes séparées par des deux-points, puis une virgule, puis les
millisecondes. La virgule n'est pas une faute de frappe, et un point ne fera
pas l'affaire. `00:00:04,200`, c'est quatre secondes et un cinquième après le
début.
Les numéros des sous-titres sont lus puis jetés, donc ils n'ont pas besoin
d'être dans l'ordre, ni même d'être justes. Numérotez-les quand même ; un jour,
vous chercherez le sous-titre quarante et un.
Le sous-titre deux montre à quoi ressemble un sous-titre de deux lignes. Les
lignes sont affichées comme vous les avez écrites, donc c'est *vous* qui
décidez où une longue ligne se coupe, ce qui compte plus que vous ne le
croiriez et qui est expliqué plus bas.
L'analyseur est volontairement tolérant envers ce qui tourne mal dans un
fichier modifié à la main. Les fins de ligne Windows se chargent telles
quelles, et les espaces au début et à la fin de chaque ligne sont retirés. Un
sous-titre dont les horodatages ne s'analysent pas est sauté et le reste du
fichier continue de se charger, ce qui est sympathique jusqu'au moment où vous
vous demandez où est passée la ligne douze. Quelques choses ne sont pas prises
en charge : les balises `<i>` et `<b>` que certains outils écrivent sont
affichées comme du texte au lieu d'être appliquées, et les coordonnées de
positionnement que certains outils ajoutent à la ligne de temps sont ignorées.
La section Sous-titres de la Référence en donne la liste complète.
[#_load_it_and_switch_it_on]
=== Chargez-les et allumez-les
Trois appels, et l'ordre compte.
[source,lua]
----
if srtLoad(DIR .. "subtitles.srt") then
srtPosition(80)
srtEnable(true)
end
----
`srtLoad` lit le fichier et répond `true` ou `false`. Elle répond `false`, sans
rien avoir chargé, dans trois cas : le fichier ne peut pas être lu, rien dedans
ne s'analyse, ou *il n'y a pas de disque*. Dans les trois cas, elle affiche à
la console un avertissement qui nomme le fichier. C'est une bonne conception et
vous devriez vous en servir : un fichier de sous-titres manquant ne vaut pas
qu'on tue un jeu pour ça, mais livrer sans s'en apercevoir un jeu sans
sous-titres vaut qu'on le remarque.
Le cas « pas de disque » est celui qui vous attrapera. Les horodatages doivent
devenir des numéros d'image à la fréquence d'images du disque, et tant qu'aucun
disque n'est ouvert, il n'y a pas de fréquence d'images à utiliser. Chargez
donc les sous-titres *après* le disque, ce qui, dans un script ordinaire, veut
dire depuis le corps du script, là où s'exécute tout ce que vous avez écrit au
niveau principal. Il s'ensuit qu'un `.srt` découpé pour une vidéo à une
certaine fréquence d'images tombe au mauvais endroit sur une vidéo à une autre.
Synchronisez votre fichier sur la vidéo que vous allez vraiment livrer.
`srtEnable(true)` allume les sous-titres chargés. Un script démarre toujours
avec les sous-titres éteints, donc un jeu qui les charge doit les allumer. Les
éteindre de nouveau retire ce qui est affiché, et les rallumer affiche ce qui
couvre l'image sur laquelle se trouve le disque à cet instant -- rien à
chercher, aucun état à garder.
Laisser les sous-titres allumés est un réglage, pas une décision que vous
prenez à la place du joueur. Mettez-le dans votre page d'options de la leçon
vingt, enregistrez-le avec `saveSet` de la leçon douze, et passez à `srtEnable`
ce que le joueur a choisi en dernier.
[#_where_they_sit]
=== Où ils se placent
[source,lua]
----
srtPosition(70)
----
`srtPosition` est un pourcentage de la hauteur de l'image, compté depuis le
haut, de `1` à `95`, et la valeur par défaut est `80`. Il s'applique au
sous-titre affiché à cet instant et à tous ceux qui suivent. Un nombre hors de
cette plage est ignoré au lieu d'être ramené dans les limites, ce qui veut dire
qu'une faute de frappe laisse la position exactement où elle était, sans rien
dire, alors vérifiez les vôtres.
Quatre-vingts pour cent, c'est bas, et c'est bas exprès : le bas de l'image est
en général la partie la moins intéressante du plan. Remontez-les quand le bas
n'est *pas* ennuyeux -- quand votre jeu dessine les scores le long du bord
inférieur, ou quand le réticule du pistolet optique du joueur vit par là.
[#_taking_one_down]
=== En retirer un
[source,lua]
----
srtClear()
----
`srtClear` retire immédiatement ce qui est à l'écran, et laisse tranquilles les
sous-titres chargés et l'état allumé. La ligne qui était affichée reste
retirée jusqu'à ce que le disque atteigne le sous-titre suivant. Servez-vous-en
quand une scène se termine plus tôt et que vous ne voulez pas que la dernière
réplique traîne sur le plan suivant.
Un autre appel partage la mécanique des sous-titres, et vous vous en êtes
peut-être déjà servi :
[source,lua]
----
overlayBanner("Extra life!", 20)
----
`overlayBanner` affiche un court message par-dessus l'image pendant une
trentaine d'images dessinées, puis le retire. Il est dessiné par le même
document que les sous-titres, donc un bandeau ressemble à un sous-titre, et
tant qu'il est affiché il prend la place de tout sous-titre, qui revient quand
le bandeau expire. Son propre argument de hauteur est distinct de
`srtPosition` et vaut `47` par défaut, le milieu de l'image. Les messages de
plus de soixante caractères sont purement et simplement ignorés, donc un
message long a l'air de ne pas marcher du tout.
[#_subtitles_already_inside_the_video]
=== Des sous-titres déjà dans la vidéo
Certains fichiers vidéo transportent leurs propres pistes de sous-titres. Si
c'est le cas du vôtre, vous n'avez pas du tout besoin d'un `.srt` :
[source,lua]
----
if discGetSubtitleTracks() > 0 then
srtLoadTrack(0)
else
srtLoad(DIR .. "subtitles.srt")
end
srtEnable(true)
----
`srtLoadTrack` prend un numéro de piste de `0` à `discGetSubtitleTracks() - 1`
et, une fois le chargement fait, tout est identique.
`discGetSubtitleLanguage(track)` vous dit dans quelle langue une piste est
étiquetée, sous la forme d'un code de trois lettres comme `eng` ou `fra`, pour
qu'un jeu puisse proposer au joueur ce que sa vidéo se trouve transporter.
Deux mises en garde. Lire une piste parcourt une fois tout le fichier vidéo,
sans décoder aucune image, donc un disque de la longueur d'un long métrage
prend un moment : faites-le pendant que quelque chose d'autre est à l'écran,
pas entre deux images de jeu. Et les pistes de sous-titres qui contiennent des
*images* des mots au lieu des mots -- le genre qu'utilisent les DVD et les
Blu-ray -- ne peuvent pas être lues. Elles sont comptées, pour que la
numérotation corresponde à ce que montrent les autres lecteurs, mais
`srtLoadTrack` répond `false` pour elles.
[#_the_whole_script_3]
=== Le script complet
Celui-ci a besoin d'une vidéo. Vous en avez une : `Singe/menuBackground.mkv`,
que le moteur a extraite dans votre dossier de travail la première fois que
vous avez lancé quoi que ce soit. Dites à Singe de s'en servir comme disque
avec `--framefile`, qui allume aussi le disque :
----
Singe --framefile=Singe/menuBackground.mkv subtitles
----
[source,lua]
----
dofile("Singe/Framework.singe")
overlaySetResolution(discGetWidth(), discGetHeight())
local HUD_X = 16
local STEP = 5
local TOP_LIMIT = 10
local LOW_LIMIT = 95
local hudFont = fontLoad("Singe/FreeSansBold.ttf", 18)
local loaded = false
local showing = false
local height = 80
if discGetSubtitleTracks() > 0 then
loaded = srtLoadTrack(0)
end
if not loaded then
loaded = srtLoad(DIR .. "subtitles.srt")
end
if loaded then
srtPosition(height)
showing = true
srtEnable(true)
end
discPlay()
function onInputPressed(what)
if not loaded then
return
end
if what == SWITCH_BUTTON1 then
showing = not showing
srtEnable(showing)
if showing then
overlayBanner("Subtitles on", 20)
else
overlayBanner("Subtitles off", 20)
end
elseif what == SWITCH_UP then
height = math.max(TOP_LIMIT, height - STEP)
srtPosition(height)
elseif what == SWITCH_DOWN then
height = math.min(LOW_LIMIT, height + STEP)
srtPosition(height)
elseif what == SWITCH_LEFT then
srtClear()
discSearch(0)
discPlay()
end
end
function onOverlayUpdate()
overlayClear()
fontSelect(hudFont)
colorForeground(255, 255, 255)
if loaded then
if showing then
fontPrint(HUD_X, 16, "Frame " .. discGetFrame() .. " subtitles ON at " .. height .. "%")
else
fontPrint(HUD_X, 16, "Frame " .. discGetFrame() .. " subtitles OFF")
end
fontPrint(HUD_X, 40, "Fire toggles them, up and down move them, left rewinds")
else
fontPrint(HUD_X, 16, "No subtitles loaded.")
fontPrint(HUD_X, 40, "Run with --framefile and check subtitles.srt is beside the script.")
end
return OVERLAY_UPDATED
end
function onShutdown()
fontUnload(hudFont)
end
----
Appuyez sur tir pour les allumer ou les éteindre, sur haut et bas pour
déplacer les lignes, et sur gauche pour revenir au début. C'est le retour au
début qu'il faut regarder : le disque saute à l'image zéro et le bon
sous-titre est affiché dès l'image suivante, sans que vous ayez rien fait pour
ça.
[#_what_just_happened_17]
=== Ce qui vient de se passer
[source,lua]
----
if discGetSubtitleTracks() > 0 then
loaded = srtLoadTrack(0)
end
if not loaded then
loaded = srtLoad(DIR .. "subtitles.srt")
end
----
Prenez les sous-titres de la vidéo elle-même quand elle en a, rabattez-vous
sur le fichier à côté du script quand elle n'en a pas, et finissez en sachant
lequel des deux a marché -- si l'un des deux a marché. Les deux appels
répondent par un booléen, donc le repli tient en deux instructions `if`, sans
cas particulier. `menuBackground.mkv` ne transporte aucune piste de
sous-titres, donc ce que vous obtiendrez réellement, c'est le fichier que vous
avez tapé.
[source,lua]
----
function onInputPressed(what)
if not loaded then
return
end
----
Un `return` sans rien après quitte la fonction immédiatement. Sans
sous-titres chargés, aucune de ces touches n'a rien à faire, et partir tôt est
plus clair que d'envelopper tout le corps dans un `if`. Appeler `srtEnable`
sans rien de chargé est sans danger, mais écrire du code qui n'a de sens que
lorsqu'il ne fait rien est une habitude qu'il vaut mieux ne pas prendre.
[source,lua]
----
elseif what == SWITCH_LEFT then
srtClear()
discSearch(0)
discPlay()
----
Le `srtClear` n'est pas nécessaire. Le moteur affichera de toute façon le bon
sous-titre pour l'image zéro à l'image dessinée suivante. Il est là pour que
vous puissiez le supprimer et regarder ce qui se passe : l'ancienne ligne reste
affichée une fraction de seconde pendant que le disque arrive là où il va. Sur
un saut lent, ce flash de la mauvaise ligne se voit, et effacer d'abord est la
façon de l'éviter.
[#_making_text_readable_over_a_moving_picture]
=== Rendre le texte lisible sur une image qui bouge
Le système met les mots à l'écran. Que quelqu'un arrive à les lire, c'est
votre problème, et ça vaut pour chaque ligne de texte que vous dessinez
par-dessus de la vidéo -- une consigne de la leçon dix-huit, un score, un choix
d'embranchement -- pas seulement pour les sous-titres.
*Restez loin des bords.* Les téléviseurs coupent les bords des images depuis
soixante-dix ans, et les moniteurs d'arcade sont pires, pas meilleurs. Partez
du principe que les cinq pour cent extérieurs de l'image, de chaque côté,
peuvent ne pas exister du tout sur l'écran de quelqu'un. Gardez à l'intérieur
tout ce qu'un joueur doit lire. C'est pour ça que `srtPosition` s'arrête à
`95` et pas à `100`.
*Le contraste n'est pas un choix de couleur, c'est une garantie.* Du texte
blanc sur une image, c'est du texte blanc sur ce que l'image est en train de
montrer, et dans deux secondes ce sera peut-être un champ de neige. Le style
de sous-titres fourni règle ça comme le font les sous-titres de cinéma : un
panneau sombre qui épouse le texte, plus une ombre d'un pixel. Si vous
dessinez votre propre texte sur de la vidéo, faites l'un ou l'autre -- un
`overlayBox` derrière, ou une copie sombre du texte décalée d'un pixel. Ne
comptez pas sur l'image.
*Coupez vos lignes vous-même.* Deux lignes courtes se lisent plus vite qu'une
longue, et une ligne qui passe à la ligne là où vous ne l'avez pas choisi se
coupe en général au mauvais endroit. Visez une quarantaine de caractères par
ligne et pas plus de deux lignes à la fois. Coupez à une pause naturelle :
`They were wrong about / a great many things` se lit bien ; `They
were wrong / about a great many things` trébuche.
*Laissez-les assez longtemps.* Une vitesse de lecture confortable tourne autour
de quinze caractères par seconde, donc une ligne de quarante caractères
demande environ trois secondes. Regardez le sous-titre un dans le fichier
ci-dessus : trente-deux caractères, trois secondes. Si une réplique est courte
et rapide, garder le sous-titre affiché un temps de plus après la fin de la
parole est plus gentil que de le faire disparaître d'un coup.
*Mettez-les là où l'action n'est pas.* Si ce qui compte dans le plan se trouve
en bas de l'image, remontez les sous-titres pour cette scène avec
`srtPosition`. Vous pouvez l'appeler aussi souvent que vous voulez, et le
joueur ne remarquera jamais que vous l'avez fait.
[#_styling_them]
=== Leur mise en forme
Les lignes sont dessinées par un document, du même genre que ceux de la leçon
vingt, et deux fichiers de votre dossier `Singe` décident de leur apparence :
* `Singe/subtitle.rml` -- le document. Il contient un seul élément, `#slot`,
dans lequel le moteur écrit le sous-titre ou le bandeau du moment.
* `Singe/subtitle.rcss` -- le style. `.cue` est le bloc qui porte la hauteur
demandée par `srtPosition`, et `.cue .text` est la barre de texte à
l'intérieur : blanc, gras, sur un panneau sombre arrondi qui épouse le texte,
avec une ombre.
Ouvrez `subtitle.rcss` et lisez-le -- il fait une quarantaine de lignes. Puis
laissez-le tranquille, parce qu'une modification faite là ne dure pas. À chaque
démarrage, Singe remet sa propre copie de tout ce qui a été modifié dans le
dossier `Singe`, et le dit :
----
>>> Updated File: Singe/subtitle.rcss
----
Ce dossier appartient au moteur. Votre jeu apporte plutôt sa propre copie.
Copiez les deux fichiers depuis `Singe` dans votre dossier `subtitles`, à côté
du script. Puis ouvrez votre copie de `subtitle.rml` et changez la seule ligne
de son `<head>` qui relie le style, pour qu'elle se lise :
[source,html]
----
<link type="text/rcss" href="subtitle.rcss"/>
----
Un nom à l'intérieur d'un document part du dossier du document lui-même, sauf
un nom qui commence par `Singe/`, qui désigne toujours le dossier du moteur. Le
lien tel que vous l'avez copié, `Singe/subtitle.rcss`, va toujours chercher le
style du moteur. Le nouveau va chercher le vôtre.
Quand Singe trouve un `subtitle.rml` à côté de votre script, il dessine les
sous-titres avec celui-ci au lieu du sien. Ajoutez `-p` à la commande et Singe
décrit ce qu'il fait au fur et à mesure, une ligne à la fois :
----
Singe -p --framefile=Singe/menuBackground.mkv subtitles
----
Quand le premier sous-titre apparaît, l'une de ces lignes est celle-ci :
----
Using the game's own subtitles/subtitle.rml
----
Changez maintenant la `font-size`, la `color`, la `background-color`, le
`border-radius` ou le `font-effect` dans votre `subtitle.rcss`, enregistrez, et
relancez. Le moteur ne touche jamais qu'au contenu de `#slot` et à la hauteur
de `.cue`, donc tout le reste de vos deux fichiers est à vous, et rien de ce que
fait Singe à son démarrage n'y touchera. Si `-p` dit qu'il utilise les vôtres et
que l'apparence n'a pas changé, relisez le lien : une copie qui dit encore
`Singe/` utilise en silence le style du moteur.
Comme `overlayBanner` utilise le même document, changer le style des
sous-titres change aussi celui de vos bandeaux, ce qui est en général ce que
vous voulez et qui vaut d'être su avant que vous vous demandiez pourquoi.
Une limite : le document des sous-titres est un document, et les documents ont
besoin d'un GPU. Sur une machine qui n'en a pas, `srtLoad` lit quand même
votre fichier et répond quand même honnêtement, et rien n'est dessiné. Un
avertissement le dit une fois.
[#_try_it_21]
=== Essayez
. *Ajoutez un quatrième sous-titre* à `subtitles.srt`, entre onze et treize
secondes. Enregistrez et lancez. Puis mettez-le dans le désordre, en tant que
sous-titre `99`, en premier dans le fichier, et vérifiez qu'il marche
toujours.
. *Donnez à un sous-titre un mauvais horodatage* -- écrivez `00:00:05.500` avec
un point au lieu d'une virgule -- et lancez. Regardez quelle ligne disparaît,
et remarquez que rien du tout n'est dit à ce sujet. Un fichier qui se charge
n'est pas un fichier juste.
. *Changez leur style.* Dans votre propre `subtitle.rcss` -- la copie qui est à
côté de votre script, pas celle de `Singe` -- changez `.cue .text` pour
avoir du texte jaune sur un fond complètement transparent, puis ajoutez une
ombre plus marquée. Ensuite, regardez-le par-dessus la vidéo et décidez
honnêtement s'il est encore lisible.
. *Cassez la synchronisation exprès.* Changez chaque horodatage du fichier pour
qu'il soit en retard de deux secondes, et regardez un système qui marche
parfaitement livrer des sous-titres inutiles. Voilà ce que vous fait une
vidéo réencodée.
. *Mettez l'interrupteur dans un menu.* Reprenez la page de pause de la leçon
vingt, ajoutez une case à cocher, et branchez-la sur `srtEnable`. Puis
enregistrez le choix avec `saveSet` pour qu'il soit encore allumé la fois
suivante. C'est la version que vos joueurs veulent vraiment.
[#_break_it_on_purpose_20]
=== Cassez-le exprès
Lancez le script sans l'option `--framefile` :
----
Singe subtitles
----
La fenêtre s'ouvre, noire, avec votre message de repli dessus, et la console
dit :
----
Warning: No subtitles were loaded from subtitles/subtitles.srt.
----
Pas de plantage et pas de boîte de dialogue d'erreur, parce que `srtLoad` a
répondu `false` et que votre script s'en est occupé.
C'est l'échec le plus précieux de cette leçon, parce que c'est celui qui
arrive sans que personne ne le remarque. Il n'y a pas de disque, donc pas de
fréquence d'images, donc rien en quoi transformer les horodatages, donc rien
ne se charge. Si vous aviez écrit `srtLoad(...)` tout seul en jetant la
réponse, votre jeu aurait tourné parfaitement sans montrer aucun sous-titre, et
vous l'auriez découvert quand quelqu'un vous l'aurait dit.
Servez-vous de la réponse. À chaque fois.
[#_what_you_learned_21]
=== Ce que vous avez appris
* Un jeu avec des paroles et sans sous-titres est un jeu auquel certaines
personnes ne peuvent pas jouer du tout.
* Mettre les sous-titres au fur et à mesure, c'est un après-midi ; les ajouter
après coup, non.
* Les sous-titres sont un fichier `.srt` : un numéro, deux horodatages avec
`-->` entre eux, le texte, et une ligne vide.
* `srtLoad` transforme les horodatages en numéros d'image du disque au
chargement, et c'est pour ça qu'un sous-titre survit à n'importe quel saut.
* Il faut charger les sous-titres après le disque, et l'appel répond `false`
quand il ne peut pas charger, y compris quand il n'y a pas de disque.
Vérifiez la réponse.
* `srtEnable` les allume, et un script démarre toujours avec les sous-titres
éteints.
* `srtPosition` les place à un pourcentage de la hauteur de l'image, de `1` à
`95`.
* `srtClear` retire ce qui est affiché, et `overlayBanner` partage le même
document.
* `srtLoadTrack` lit les sous-titres dans le fichier vidéo lui-même quand il en
contient.
* Singe réécrit son dossier `Singe` à chaque démarrage, donc pour changer le
style des sous-titres, vous copiez `subtitle.rml` et `subtitle.rcss` à côté de
votre script, vous faites pointer le lien de la copie vers votre propre
feuille, et c'est celle-ci que vous modifiez.
* Du texte sur une image qui bouge a besoin d'une marge de sécurité, de son
propre contraste, de lignes coupées par vous, et de temps pour être lu.
[#_next_time_21]
=== La prochaine fois
C'en est fini du texte. La quatrième partie continue sans que vous ayez à la
lire dans l'ordre, et la prochaine leçon ouvre une porte restée fermée pendant
vingt leçons : `overlayGetWidth` et `overlayGetHeight` sont deux nombres, et
une scène en a trois. La leçon vingt-deux parle du troisième.