singe/docs/de/lessons/19-text.adoc

535 lines
22 KiB
Text

[#_lesson_19_text_that_looks_good]
== Lektion 19: Text, der gut aussieht
image::learn/19-text.png[Die fertige Lektion, 480]
Jedes Wort, das du seit Lektion eins auf den Bildschirm gebracht hast, kam aus
`overlayPrint`, und `overlayPrint` hat genau eine Schriftart: einen Pixelblock fester
Breite, der in die Engine eingebaut ist, für immer gleich groß, in welcher Farbe er gerade
eben ist. Für einen Frame-Zähler ist sie perfekt. Für einen Titel, eine Punktzahl, ein Menü
oder alles, was ein Spieler ansehen und nicht einfach überlesen soll, ist sie falsch.
Diese Lektion lädt eine echte Schriftart von der Festplatte, gibt Text mit ihr aus, färbt
ihn ein, misst ihn aus, damit du Dinge zentrieren kannst, und zeigt dir, was das Ausgeben
von Text wirklich kostet. Am Ende hast du einen Titelbildschirm, der gewollt aussieht statt
zufällig.
[#_set_the_overlay_to_a_size_you_can_think_in]
=== Gib dem Overlay eine Größe, in der du denken kannst
Fang eine neue Datei namens `text.singe` mit zwei Zeilen an:
[source,lua]
----
dofile("Singe/Framework.singe")
overlaySetResolution(discGetWidth(), discGetHeight())
----
Die erste Zeile hast du schon oft geschrieben; sie lädt das Framework, das `DIR`, die
Namen `SWITCH_*` und die Namen `FONT_QUALITY_*` definiert, die du gleich benutzt.
Die zweite ist wichtiger, als sie aussieht. Das Overlay -- die Fläche, auf der alles
landet, was du zeichnest -- ist am Anfang *halb* so groß wie die Welt. Ein Spiel ohne Video
hat eine Zeichenfläche von 720 mal 480, also ist das Overlay am Anfang 360 mal 240 groß,
und eine Schrift in 36 Punkt ist auf einem Overlay, das 360 Pixel breit ist, riesig.
`overlaySetResolution` ersetzt das Overlay durch eines in der Größe, um die du bittest, und
von da an liegt jede Koordinate, die du angibst, und alles, was `overlayGetWidth` und
`overlayGetHeight` dir sagen, in diesem Raum.
`discGetWidth()` und `discGetHeight()` antworten mit der Größe des Videos, wenn es eines
gibt, und mit der Größe der Zeichenfläche, wenn nicht, also bekommst du mit dieser einen
Zeile ein Overlay, das zur Welt passt, egal welche Art von Spiel das hier ist. Ruf es
einmal auf, am Anfang, bevor du irgendetwas zeichnest.
[#_load_a_font]
=== Lade eine Schriftart
Schreib darunter:
[source,lua]
----
fontQuality(FONT_QUALITY_BLENDED)
local titleFont = fontLoad("Singe/FreeSansBold.ttf", 48)
----
`fontLoad` öffnet eine TrueType-Schriftdatei und gibt dir eine Zahl zurück -- ein *Handle*
-- und die benutzt du immer, wenn du diese Schriftart willst. Das zweite Argument ist die
Punktgröße, und die steht fest, solange es das Handle gibt. Es gibt keine Möglichkeit,
die Größe einer geladenen Schriftart nachträglich zu ändern. Wenn du dieselbe Schrift in
drei Größen willst, rufst du `fontLoad` dreimal auf und behältst drei Handles.
`Singe/FreeSansBold.ttf` ist die Schriftart, die die Engine in den Ordner `Singe` in
deinem Arbeitsordner entpackt hat, als du zum ersten Mal irgendetwas gestartet hast. Jede
Kopie von Singe hat sie, also kannst du dich auf sie verlassen. Jede andere `.ttf`-Datei
funktioniert genauso; leg sie neben dein Skript und lade sie als `DIR .. "myfont.ttf"`.
Gib jetzt Text mit ihr aus:
[source,lua]
----
function onOverlayUpdate()
overlayClear()
fontPrint(24, 60, "ASTEROID PATROL")
return OVERLAY_UPDATED
end
----
Starte es. Große weiße Buchstaben oben links.
Achte darauf, was du *nicht* getan hast: Du hast `fontPrint` nie gesagt, welche
Schriftart es benutzen soll. `fontLoad` macht die Schriftart, die es gerade geladen hat,
zur ausgewählten, und `fontPrint` zeichnet immer mit der, die gerade ausgewählt ist. Wenn
mehr als eine Schriftart geladen ist, wählst du mit `fontSelect` zwischen ihnen, und das
machst du gleich.
Achte auch darauf, dass `24` und `60` Pixel sind, keine Zeichenzellen. Das ist der
Unterschied zwischen `fontPrint` und `overlayPrint`, über den die Leute stolpern:
`overlayPrint(2, 2, ...)` heißt zwei Buchstaben nach rechts und zwei Zeilen nach unten,
`fontPrint(2, 2, ...)` dagegen zwei Pixel nach rechts und zwei Pixel nach unten, also
eingequetscht in die Ecke. Das `y`, das du angibst, ist die *Oberkante* des Texts, nicht
die Grundlinie, auf der er steht.
[#_color]
=== Farbe
`fontPrint` nimmt drei Argumente, und keines davon ist eine Farbe. Die Farbe kommt von
woanders:
[source,lua]
----
colorForeground(255, 211, 90)
fontPrint(24, 60, "ASTEROID PATROL")
----
`colorForeground` setzt Rot, Grün und Blau von `0` bis `255` und, wenn du willst, eine
vierte Zahl für die Deckkraft. Es ist kein Argument für irgendetwas; es ist eine
Einstellung, die sich die Engine merkt. Alles, was danach gezeichnet wird, kommt in dieser
Farbe heraus, bis du sie wieder änderst, genau so, wie `overlayBox` und `overlayLine`
schon funktionieren. Das Muster ist also immer dasselbe: Setz die Farbe, dann zeichne.
Das ist das Erste, was die Leute bei Schriftarten falsch machen, und das Zweite kommt in
ein paar Seiten.
[#_quality]
=== Qualität
`fontQuality` entscheidet, wie die Buchstaben gerastert werden, also wie die Kurven eines
Buchstabens zu Pixeln werden. Es gibt drei Möglichkeiten. Probier jede aus, indem
du sie über die Zeile mit `fontLoad` setzt und dir das Ergebnis genau ansiehst.
`FONT_QUALITY_SOLID` ist die schnellste. Jedes Pixel gehört entweder zum Buchstaben oder
nicht, also kommen die Diagonalen und Kurven mit sichtbaren Treppenstufen heraus. Bei 48
Punkt sieht das billig aus. Bei 12 Punkt auf einem kleinen Overlay kann es *schärfer*
aussehen als die anderen, und manchmal willst du genau das.
`FONT_QUALITY_SHADED` glättet die Kanten, aber dazu mischt es sie mit der aktuellen
Hintergrundfarbe, und diesen Hintergrund zeichnet es als volles Rechteck hinter den ganzen
String. Du bekommst ein Etikett mit einem Kasten drumherum, ob du einen wolltest oder
nicht. Für ein Debug-Anzeigefeld über einem unruhigen Bild ist das die richtige Antwort und
für fast alles andere die falsche.
`FONT_QUALITY_BLENDED` glättet die Kanten und lässt sie durchsichtig, also sitzen die
Buchstaben auf dem, was gerade hinter ihnen ist. Sie kostet am meisten, und sie ist das,
was du fast immer willst. Nimm sie, außer du hast einen Grund dagegen.
Die Einstellung ist global, nicht pro Schriftart, und sie gilt ab dem Moment, in dem du
sie aufrufst. Sprites, die du schon gemacht hast, behalten die Qualität, mit der sie
gemacht wurden. Der Eintrag zu `fontQuality` in der Referenz nennt die drei Namen und ihre
Werte.
[#_measuring_so_you_can_center_things]
=== Ausmessen, damit du Dinge zentrieren kannst
Früher oder später -- meistens nach etwa zehn Minuten -- willst du den Titel in der Mitte
des Bildschirms haben. Du weißt, dass der Bildschirm `overlayGetWidth()` breit ist. Du
weißt nicht, wie breit der Text ist, und es gibt keinen Aufruf `fontMeasure`, den du fragen
könntest.
Was es stattdessen gibt, ist `fontToSprite`. Es rendert einen String mit der ausgewählten
Schriftart, der aktuellen Qualität und der aktuellen Farbe, und statt ihn auf den
Bildschirm zu bringen, gibt es dir ein *Sprite* zurück: dieselbe Art von Handle, die dir
`spriteLoad` gibt, und das heißt, jeder `sprite*`-Aufruf, den du in Lektion neun gelernt
hast, funktioniert damit. Auch die beiden, die es ausmessen.
[source,lua]
----
fontSelect(titleFont)
colorForeground(255, 211, 90)
local titleImage = fontToSprite("ASTEROID PATROL")
----
Und dann, in `onOverlayUpdate`:
[source,lua]
----
local x = (overlayGetWidth() - spriteGetWidth(titleImage)) / 2
spriteDraw(titleImage, x, 60)
----
Das ist Zentrieren, und das ist schon der ganze Trick. Nimm die Breite des Platzes, zieh
die Breite des Dings ab, und die Hälfte von dem, was übrig bleibt, ist der Abstand auf
jeder Seite.
Rechtsbündig ist dieselbe Idee mit weniger Rechnerei. So setzt du Text an den rechten
Rand:
[source,lua]
----
spriteDraw(scoreImage, overlayGetWidth() - MARGIN - spriteGetWidth(scoreImage), y)
----
`spriteGetHeight` misst die andere Richtung, um Zeilen zu stapeln oder senkrecht zu
zentrieren.
In diesem ersten Codeblock steckt ein Haken, und das ist das Zweite, was alle falsch
machen. *Die Farbe wird eingebacken, wenn das Sprite gemacht wird, nicht wenn es
gezeichnet wird.* `colorForeground` über `fontToSprite` ändert den Text. Setzt du es
stattdessen über `spriteDraw`, tut es überhaupt nichts, denn `colorForeground` ist eine
Einstellung für das Zeichnen von Text und Linien, und ein Sprite wird mit seinen eigenen
Pixeln gezeichnet. Wenn dein sorgfältig eingefärbter Titel weiß herauskommt, liegt es
daran.
Ein Sprite lässt sich umfärben, nachdem es gemacht wurde, aber mit einem Sprite-Aufruf,
nicht mit `colorForeground`. `spriteSetTint(image, red, green, blue)` multipliziert jedes
Pixel des Sprites bei jedem Zeichnen mit dieser Farbe, also kann Text, der in Weiß gerendert
wurde, heute gelb und morgen rot gezeichnet werden, ohne neu gemacht zu werden. Weiß ist
die Farbe, in der du renderst, wenn du einfärben willst: Alles, was mit Weiß multipliziert
wird, bleibt, was es war, also ist die Tönung genau die Farbe, um die du bittest.
[#_what_printing_costs]
=== Was Ausgeben kostet
Wenn `fontPrint` einen String zum ersten Mal sieht, leistet es echte Arbeit: Es rastert
jeden Buchstaben aus den Umrissen der Schriftart und baut ein Bild des ganzen Strings.
Dann behält es dieses Bild. Wenn du denselben String das nächste Mal ausgibst, mit
derselben Schriftart, Qualität und denselben Farben, überspringt es all das und kopiert
das Bild, das es schon hat, und das kostet ungefähr so viel wie das Zeichnen eines
Sprites. Ein String, den du nicht mehr ausgibst, wird eine Sekunde später losgelassen,
also häuft sich nichts an.
Das heißt, die naheliegende Art, einen Titelbildschirm zu schreiben, ist auch die
schnelle. `"ASTEROID PATROL"` sechzigmal pro Sekunde auszugeben rendert ihn einmal. Eine
Punktzahl, die sich zweimal pro Sekunde ändert, wird zweimal pro Sekunde gerendert, von
selbst, ohne dass du Buch führen musst.
Der eine Fall, der etwas kostet, ist Text, der in jedem Frame *anders* ist (ein Timer in
Millisekunden, ein Frame-Zähler), denn jeder Frame ist ein String, den `fontPrint` noch
nie gesehen hat. Auf einem Desktop-Rechner spielt das keine Rolle. Auf einem Raspberry Pi
oder einem Handheld spielt ein Bildschirm voll davon sehr wohl eine. Die Abhilfe ist, den
Text seltener zu ändern: Ein Timer in ganzen Sekunden ist einmal pro Sekunde ein neuer
String, nicht sechzigmal.
Warum hat diese Lektion dann überhaupt `fontToSprite` benutzt? Weil ein Sprite Dinge kann,
die ausgegebener Text nicht kann:
* *Es kann ausgemessen werden*, und das ist die einzige Möglichkeit, Text zu zentrieren
oder an der rechten Seite auszurichten.
* *Es kann skaliert und gedreht werden*, mit den `sprite*`-Aufrufen aus Lektion neun.
* *Es kann seine Schriftart überleben*, und das ist gleich wichtig.
Nimm `fontPrint` für Text und `fontToSprite`, wenn du eines davon brauchst.
Die Punktzahl in dieser Lektion braucht eines davon: Sie sitzt am rechten Rand, also muss
sie ausgemessen werden, also muss sie ein Sprite sein. Und ein Sprite einer Punktzahl
stimmt nur, bis sich die Punktzahl ändert. Wenn sie das tut, render ein neues Sprite und
entlade das alte -- vergisst du das, bleibt bei jeder Änderung ein Sprite als Leck zurück:
[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` merkt sich, welche Punktzahl dieses Sprite zeigt, damit der Zeichencode
erkennen kann, ob es noch stimmt:
[source,lua]
----
if score ~= scoreShown then
renderScore()
end
----
`string.format("%06d", score)` ist reines Lua, nicht Singe. Es macht aus einer Zahl einen
String, und `%06d` heißt „als ganze Zahl, mindestens sechs Stellen, mit Nullen
aufgefüllt“, und darum haben Arcade-Punktzahlen führende Nullen. Ändere es in `%d`, und
die Nullen verschwinden.
[#_putting_fonts_away]
=== Schriftarten wegräumen
`fontUnload` schließt eine Schriftart und macht ihr Handle ungültig. Sprites, die du mit
`fontToSprite` gemacht hast, sind davon nicht betroffen (ist ein Sprite einmal gerendert,
hat es mit der Schriftart nichts mehr zu tun), also kannst du eine Schriftart entladen
und weiter Text zeichnen, der aus ihr gemacht wurde.
Eine scharfe Kante: Wenn du die ausgewählte Schriftart entlädst, ist danach *keine*
Schriftart ausgewählt, und das nächste `fontPrint` beendet dein Spiel mit einem Fehler.
Wähl zuerst eine andere aus, wenn du weiter Text ausgeben willst.
Der ordentliche Platz für all das ist `onShutdown`, ein Callback, den die Engine einmal
aufruft, wenn das Spiel beendet wird:
[source,lua]
----
function onShutdown()
spriteUnload(titleImage)
fontUnload(titleFont)
end
----
Singe räumt hinter dir auf, wenn das Spiel endet, also fängt nichts Feuer, wenn du das
weglässt. Schreib es trotzdem. Ein Spiel, das freigibt, was es geladen hat, ist ein Spiel,
das du zweimal laden kannst.
[#_the_whole_script]
=== Das ganze Skript
[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
----
Starte es und halt den Feuerknopf gedrückt. Der Titel und der Hinweis sitzen zentriert,
die Punktzahl klettert am rechten Rand hoch und wird nur neu gerendert, wenn sie sich
ändert, der Hinweis blinkt, und das Etikett und die Uhr werden in jedem Frame mit
`fontPrint` ausgegeben -- die Uhr in ganzen Sekunden, also ist sie nur einmal pro Sekunde
ein neuer String.
[#_what_just_happened_15]
=== Was gerade passiert ist
[source,lua]
----
titleFont = fontLoad("Singe/FreeSansBold.ttf", 48)
scoreFont = fontLoad("Singe/FreeSansBold.ttf", 36)
smallFont = fontLoad("Singe/FreeSansBold.ttf", 18)
----
Eine Datei, drei Größen, drei Handles. Jedes kostet Speicher für seinen eigenen Satz
gerenderter Glyphen, also lade die Größen, die du benutzt, und keine weiteren. Drei sind
normal. Ein Dutzend riecht verdächtig.
Nach diesen drei Zeilen ist `smallFont` die ausgewählte Schriftart, denn Laden wählt aus.
Darum ruft der Code `fontSelect(titleFont)` auf, bevor er den Titel rendert: Das zuletzt
Geladene ist nicht das, was du zuerst willst.
[source,lua]
----
local function centered(image)
return (overlayGetWidth() - spriteGetWidth(image)) / 2
end
----
Einmal als Funktion geschrieben, weil es zweimal benutzt wird und weil du es beim
nächsten Mal, wenn du etwas zentriert haben willst, wieder brauchst. Gib der Rechnung
einen Namen, und du musst sie nie wieder lesen.
[source,lua]
----
if (singeGetTicks() // 500) % 2 == 0 then
spriteDraw(hintImage, centered(hintImage), HINT_Y)
end
----
`singeGetTicks()` ist die Zahl der Millisekunden seit dem Start der Engine. `//` ist eine
Division, die den Rest wegwirft, also zählt `singeGetTicks() // 500` halbe Sekunden, und
`% 2` ist der Rest nach dem Teilen durch zwei, also `0`, `1`, `0`, `1` ... Der Hinweis
wird also eine halbe Sekunde lang gezeichnet und eine halbe Sekunde lang ausgelassen.
Nichts zu zeichnen ist die Art, wie man etwas blinken lässt.
[source,lua]
----
fontSelect(smallFont)
colorForeground(160, 160, 160)
fontPrint(MARGIN, LABEL_Y, "SCORE")
----
Hier werden beide Einstellungen gebraucht, und es lohnt sich, klar zu sehen, warum. Ein
Sprite zu zeichnen ändert weder die ausgewählte Schriftart noch die Farbe, also lassen die
beiden `spriteDraw`-Aufrufe darüber beides genau so, wie sie es vorgefunden haben. Aber der
Code am Anfang hat nacheinander drei verschiedene Schriftarten ausgewählt, und
`renderScore` wählt jedes Mal eine weitere aus, wenn sich die Punktzahl bewegt, also ist
die Auswahl, wenn diese Zeile läuft, die, die zufällig zuletzt gewählt wurde. Versuch
nicht, darüber Buch zu führen. Wähl die Schriftart aus und setz die Farbe unmittelbar
bevor du etwas ausgibst, jedes Mal. Das sind zwei Zeilen, und sie ersparen dir eine ganze
Kategorie ratloser Abende.
[#_try_it_19]
=== Probier es aus
. *Ändere die Qualität.* Schreib `FONT_QUALITY_SOLID` in die Zeile mit `fontQuality` und
sieh dir die Kurve des `S` in `ASTEROID` an. Dann probier `FONT_QUALITY_SHADED` und
achte darauf, was hinter jedem String passiert.
. *Mach den Hinweis gelb.* Verschieb die Zeile `colorForeground(140, 200, 255)` von über
`fontToSprite` direkt über das `spriteDraw`, das `hintImage` zeichnet, und ändere die
Zahlen. Finde heraus, warum nichts passiert. Dann setz sie zurück, mach den Hinweis
stattdessen weiß und färb ihn mit `spriteSetTint` gelb, direkt bevor er gezeichnet wird.
. *Richte die Uhr rechtsbündig aus.* Die Uhr wird mit `fontPrint` gezeichnet, und das
lässt sich nicht ausmessen. Bring sie trotzdem an den rechten Rand. Es gibt nur einen
Weg, und ihn zu finden ist der Sinn der Sache.
. *Nimm die Prüfung von `scoreShown` heraus*, sodass `renderScore()` stattdessen in jedem
Frame läuft. Es sieht genauso aus, aber jetzt wird die Punktzahl sechzigmal pro Sekunde
gerendert statt dann, wenn sie sich ändert -- genau die Arbeit, die `fontPrint` dir
erspart, wenn du es lässt. Dann nimm auch das `spriteUnload` in `renderScore` heraus,
lass es eine Minute laufen und beobachte, wie viel Speicher dein Spiel benutzt.
. *Gib eine Uhr in Millisekunden aus.* Ändere die Zeile der Uhr so, dass sie
`singeGetTicks()` ohne das `// 1000` ausgibt. Es funktioniert immer noch; es ist jetzt
in jedem Frame ein String, den `fontPrint` noch nie gesehen hat. Auf einem langsamen
Rechner ist das die Art von Text, die etwas kostet.
. *Nimm deine eigene Schriftart.* Such dir eine `.ttf` auf deinem Rechner, kopier sie neben
dein Skript und lade sie mit `DIR .. "thatfont.ttf"`. Manche Schriftarten sehen bei 18
Punkt furchtbar aus und bei 48 gut. Das ist die Schuld der Schriftart, nicht deine.
[#_break_it_on_purpose_18]
=== Mach es absichtlich kaputt
Füg eine Zeile `fontPrint(24, 60, "SCORE")` direkt unter
`overlaySetResolution(discGetWidth(), discGetHeight())` ein, sodass sie läuft, bevor
irgendein `fontLoad` gelaufen ist. Singe startet und stirbt sofort:
----
4:fontPrint: No font selected.
----
Lies das wie jeden anderen Fehler. `4` ist die Zeile. `fontPrint` ist der Engine-Aufruf,
der sich beschwert hat -- Meldungen in dieser Form kommen aus dem Inneren der Engine und
nicht von Lua, darum nennen sie die Funktion statt der Datei. Der Rest ist die Beschwerde,
und hier ist sie genau: Du hast verlangt, etwas auszugeben, und es gab keine Schriftart,
mit der das ging.
Diesem Fehler wirst du in echt auf eine weniger offensichtliche Art begegnen. Entlade die
ausgewählte Schriftart, während das Spiel läuft, und das nächste `fontPrint` sagt genau
dasselbe, mehrere hundert Zeilen entfernt von dem `fontUnload`, das es verursacht hat.
[#_what_you_learned_19]
=== Was du gelernt hast
* `fontLoad` öffnet eine TrueType-Datei in einer festen Punktgröße und gibt ein Handle
zurück. Eine Größe, ein Handle.
* Eine Schriftart zu laden wählt sie aus; `fontSelect` wählt zwischen geladenen
Schriftarten.
* `fontPrint` zählt in Pixeln und `overlayPrint` in Zeichenzellen.
* Farbe ist kein Argument. `colorForeground` ist eine Einstellung; setz sie, dann zeichne.
* `fontQuality` bestimmt, wie Buchstaben gerastert werden, und `FONT_QUALITY_BLENDED` ist
die übliche Antwort.
* `fontPrint` behält, was es rendert, also ist es billig, in jedem Frame denselben Text
auszugeben; Text, der in jedem Frame anders ist, ist das, was kostet.
* `fontToSprite` rendert in ein Sprite, das `spriteGetWidth` ausmisst und die
`sprite*`-Aufrufe skalieren und drehen können. Nimm es dafür, nicht für Geschwindigkeit.
* Ausmessen ist die Art, wie du Text zentrierst oder rechtsbündig ausrichtest, denn sonst
kann dir nichts sagen, wie breit ein String wird.
* `colorForeground` erreicht Text, wenn er gemacht wird, nie ein Sprite, wenn es
gezeichnet wird; mit `spriteSetTint` färbst du ein Sprite nachträglich um.
* `fontUnload` gibt eine Schriftart frei; die Sprites, die sie gemacht hat, überleben sie.
[#_next_time_19]
=== Beim nächsten Mal
Text, der gut aussieht, ist noch weit entfernt von einem Menü, das funktioniert. Eine
Seite mit einem Titel, einer Liste, durch die du einen Cursor bewegst, einem Schieberegler
und einem Knopf sind hundert Zeilen Zeichnen und Cursor-Rechnerei, wenn du sie aus den
Aufrufen dieser Lektion baust. Lektion zwanzig baut sie überhaupt nicht aus diesen
Aufrufen.