547 lines
21 KiB
Text
547 lines
21 KiB
Text
[#_lesson_19_text_that_looks_good]
|
|
== Lezione 19: Testo che fa bella figura
|
|
|
|
image::learn/19-text.png[La lezione finita, 480]
|
|
|
|
Ogni parola che hai messo sullo schermo dalla prima lezione è uscita da
|
|
`overlayPrint`, e `overlayPrint` ha esattamente un font: un blocco di pixel a
|
|
larghezza fissa incorporato nel motore, sempre della stessa dimensione, in
|
|
qualunque colore capiti. È perfetto per un contatore di fotogrammi. È sbagliato
|
|
per un titolo, un punteggio, un menu, o qualsiasi cosa che un giocatore deve
|
|
guardare invece di scorrere con gli occhi.
|
|
|
|
Questa lezione carica un vero font dal disco, ci stampa, lo colora, lo misura
|
|
così puoi centrare le cose, e ti mostra quanto costa davvero stampare del testo.
|
|
Alla fine avrai una schermata del titolo che sembra voluta invece che capitata
|
|
per caso.
|
|
|
|
[#_set_the_overlay_to_a_size_you_can_think_in]
|
|
=== Dai all'overlay una dimensione con cui ragionare
|
|
|
|
Crea un nuovo file chiamato `text.singe` con due righe:
|
|
|
|
[source,lua]
|
|
----
|
|
dofile("Singe/Framework.singe")
|
|
|
|
overlaySetResolution(discGetWidth(), discGetHeight())
|
|
----
|
|
|
|
La prima riga l'hai scritta molte volte; carica il framework, che definisce
|
|
`DIR`, i nomi `SWITCH_*` e i nomi `FONT_QUALITY_*` che stai per usare.
|
|
|
|
La seconda conta più di quanto sembri. L'overlay -- la superficie su cui finisce
|
|
tutto quello che disegni -- parte a *metà* della dimensione del mondo. Un gioco
|
|
senza video ha un canvas di 720 per 480, quindi l'overlay parte da 360 per 240,
|
|
e un font da 36 punti su un overlay largo 360 pixel è enorme.
|
|
`overlaySetResolution` sostituisce l'overlay con uno della dimensione che
|
|
chiedi, e da quel momento ogni coordinata che dai, e tutto ciò che ti dicono
|
|
`overlayGetWidth` e `overlayGetHeight`, sta in quello spazio.
|
|
|
|
`discGetWidth()` e `discGetHeight()` rispondono con la dimensione del video
|
|
quando c'è, e con quella del canvas quando non c'è, così quell'unica riga ti dà
|
|
un overlay che corrisponde al mondo, qualunque tipo di gioco sia questo.
|
|
Chiamala una volta, all'inizio, prima di disegnare qualsiasi cosa.
|
|
|
|
[#_load_a_font]
|
|
=== Carica un font
|
|
|
|
Sotto, aggiungi:
|
|
|
|
[source,lua]
|
|
----
|
|
fontQuality(FONT_QUALITY_BLENDED)
|
|
|
|
local titleFont = fontLoad("Singe/FreeSansBold.ttf", 48)
|
|
----
|
|
|
|
`fontLoad` apre un file di font TrueType e ti restituisce un numero -- un
|
|
*handle* -- che usi ogni volta che vuoi quel font. Il secondo argomento è la
|
|
dimensione in punti, ed è fissa per tutta la vita dell'handle. Non c'è modo di
|
|
cambiare la dimensione di un font già caricato. Se vuoi lo stesso carattere in
|
|
tre dimensioni, chiami `fontLoad` tre volte e tieni tre handle.
|
|
|
|
`Singe/FreeSansBold.ttf` è il font che il motore ha estratto nella cartella
|
|
`Singe` dentro la tua cartella di lavoro la prima volta che hai eseguito
|
|
qualcosa. Ce l'ha ogni copia di Singe, quindi puoi contarci. Qualsiasi altro
|
|
file `.ttf` funziona allo stesso modo; mettilo accanto al tuo script e caricalo
|
|
come `DIR .. "myfont.ttf"`.
|
|
|
|
Ora stampaci:
|
|
|
|
[source,lua]
|
|
----
|
|
function onOverlayUpdate()
|
|
overlayClear()
|
|
fontPrint(24, 60, "ASTEROID PATROL")
|
|
return OVERLAY_UPDATED
|
|
end
|
|
----
|
|
|
|
Eseguilo. Grandi lettere bianche vicino all'angolo in alto a sinistra.
|
|
|
|
Nota che cosa *non* hai fatto: non hai mai detto a `fontPrint` quale font
|
|
usare. `fontLoad` seleziona il font che ha appena caricato, e
|
|
`fontPrint` disegna sempre con quello selezionato. Con più di un font caricato
|
|
scegli tra loro con `fontSelect`, ed è quello che farai tra un momento.
|
|
|
|
Nota anche che `24` e `60` sono pixel, non celle di carattere. Questa è la
|
|
differenza tra `fontPrint` e `overlayPrint` che fa inciampare la gente:
|
|
`overlayPrint(2, 2, ...)` vuol dire due lettere verso destra e due righe verso
|
|
il basso, mentre `fontPrint(2, 2, ...)` vuol dire due pixel verso destra e due
|
|
pixel verso il basso, cioè schiacciato nell'angolo. La `y` che dai è la *cima*
|
|
del testo, non la linea di base su cui poggia.
|
|
|
|
[#_color]
|
|
=== Colore
|
|
|
|
`fontPrint` prende tre argomenti e nessuno di questi è un colore. Il colore
|
|
arriva da un'altra parte:
|
|
|
|
[source,lua]
|
|
----
|
|
colorForeground(255, 211, 90)
|
|
fontPrint(24, 60, "ASTEROID PATROL")
|
|
----
|
|
|
|
`colorForeground` imposta rosso, verde e blu da `0` a `255`, più un quarto
|
|
numero facoltativo per l'opacità. Non è un argomento di niente; è
|
|
un'impostazione che il motore si ricorda. Tutto ciò che disegni dopo esce in
|
|
quel colore finché non lo cambi di nuovo, esattamente come funzionano già
|
|
`overlayBox` e `overlayLine`. Quindi lo schema è sempre lo stesso: imposta il
|
|
colore, poi disegna.
|
|
|
|
Questa è la prima cosa che la gente sbaglia con i font, e la seconda arriva tra
|
|
qualche pagina.
|
|
|
|
[#_quality]
|
|
=== Qualità
|
|
|
|
`fontQuality` decide come vengono rasterizzate le lettere -- come le curve di
|
|
una lettera vengono trasformate in pixel -- e ci sono tre scelte. Provale una
|
|
per una mettendola sopra la riga di `fontLoad` e guardando bene il risultato.
|
|
|
|
`FONT_QUALITY_SOLID` è la più veloce. Ogni pixel o è la lettera o non lo è,
|
|
quindi le diagonali e le curve escono con dei gradini ben visibili. A 48 punti
|
|
sembra da quattro soldi. A 12 punti su un overlay piccolo può sembrare *più
|
|
nitida* delle alternative, ed è a volte quello che vuoi.
|
|
|
|
`FONT_QUALITY_SHADED` ammorbidisce i bordi, ma lo fa sfumandoli nel colore di
|
|
sfondo attuale, e disegna quello sfondo come un rettangolo pieno dietro tutta la
|
|
stringa. Ottieni un'etichetta con un riquadro intorno, che tu lo volessi o no.
|
|
È la risposta giusta per un indicatore di debug sopra un'immagine piena di
|
|
roba, e quella sbagliata per quasi tutto il resto.
|
|
|
|
`FONT_QUALITY_BLENDED` ammorbidisce i bordi e li lascia trasparenti, così le
|
|
lettere stanno sopra qualunque cosa ci sia dietro. È quella che costa di più ed
|
|
è quella che vuoi quasi sempre. Usala, a meno che tu non abbia un motivo per
|
|
non farlo.
|
|
|
|
L'impostazione è globale, non per font, e vale dal momento in cui la chiami in
|
|
poi. Gli sprite che hai già creato mantengono la qualità con cui sono stati
|
|
creati. La voce del Riferimento su `fontQuality` elenca i tre nomi e i loro valori.
|
|
|
|
[#_measuring_so_you_can_center_things]
|
|
=== Misurare, per poter centrare le cose
|
|
|
|
Prima o poi -- di solito dopo una decina di minuti -- vuoi il titolo in mezzo
|
|
allo schermo. Sai che lo schermo è largo `overlayGetWidth()`. Non sai quanto è
|
|
largo il testo, e non c'è nessuna chiamata `fontMeasure` a cui chiederlo.
|
|
|
|
Quello che c'è invece è `fontToSprite`. Disegna una stringa con il font
|
|
selezionato, la qualità attuale e il colore attuale, e invece di metterla sullo
|
|
schermo ti restituisce uno *sprite*: lo stesso tipo di handle che ti dà
|
|
`spriteLoad`, il che vuol dire che ogni chiamata `sprite*` che hai imparato
|
|
nella nona lezione funziona anche su questo. Comprese le due che lo misurano.
|
|
|
|
[source,lua]
|
|
----
|
|
fontSelect(titleFont)
|
|
colorForeground(255, 211, 90)
|
|
local titleImage = fontToSprite("ASTEROID PATROL")
|
|
----
|
|
|
|
E poi, in `onOverlayUpdate`:
|
|
|
|
[source,lua]
|
|
----
|
|
local x = (overlayGetWidth() - spriteGetWidth(titleImage)) / 2
|
|
spriteDraw(titleImage, x, 60)
|
|
----
|
|
|
|
Questo è centrare, ed è tutto il trucco. Prendi la larghezza dello spazio,
|
|
togli la larghezza della cosa, e metà di quello che resta è lo spazio vuoto da
|
|
ogni lato.
|
|
|
|
Allineare a destra è la stessa idea con meno aritmetica. Per mettere il testo
|
|
contro il margine destro:
|
|
|
|
[source,lua]
|
|
----
|
|
spriteDraw(scoreImage, overlayGetWidth() - MARGIN - spriteGetWidth(scoreImage), y)
|
|
----
|
|
|
|
`spriteGetHeight` misura l'altra direzione, per impilare righe o centrare in
|
|
verticale.
|
|
|
|
C'è un tranello in quel primo blocco di codice, ed è la seconda cosa che
|
|
sbagliano tutti. *Il colore viene fissato quando lo sprite viene creato, non
|
|
quando viene disegnato.* `colorForeground` sopra `fontToSprite` cambia il
|
|
testo. Metterlo invece sopra `spriteDraw` non fa proprio niente, perché
|
|
`colorForeground` è un'impostazione per disegnare testo e linee, e uno sprite
|
|
viene disegnato con i suoi pixel. Se il tuo titolo colorato con tanta cura esce
|
|
bianco, il motivo è questo.
|
|
|
|
Uno sprite si può ricolorare dopo averlo creato, ma con una chiamata degli
|
|
sprite, non con `colorForeground`. `spriteSetTint(image, red, green, blue)`
|
|
moltiplica ogni pixel dello sprite per quel colore ogni volta che viene
|
|
disegnato, così un testo creato in bianco si può disegnare giallo oggi e rosso
|
|
domani senza rifarlo. Il bianco è il colore in cui crearlo quando hai intenzione
|
|
di tingerlo: qualsiasi cosa moltiplicata per il bianco resta com'era, quindi la
|
|
tinta è esattamente il colore che chiedi.
|
|
|
|
[#_what_printing_costs]
|
|
=== Quanto costa stampare
|
|
|
|
La prima volta che `fontPrint` vede una stringa, fa un lavoro vero: rasterizza
|
|
ogni lettera dai contorni del font e costruisce un'immagine di tutta la
|
|
stringa. Poi quell'immagine se la tiene. La volta dopo che stampi la stessa
|
|
stringa, con lo stesso font, la stessa qualità e gli stessi colori, salta tutto
|
|
questo e copia l'immagine che ha già, che costa più o meno quanto disegnare uno
|
|
sprite. Una stringa che smetti di stampare viene lasciata andare un secondo
|
|
dopo, quindi non si accumula niente.
|
|
|
|
Questo vuol dire che il modo ovvio di scrivere una schermata del titolo è anche
|
|
quello veloce. Stampare `"ASTEROID PATROL"` sessanta volte al secondo lo
|
|
disegna una volta sola. Un punteggio che cambia due volte al secondo viene
|
|
disegnato due volte al secondo, da solo, senza che tu debba tenere nessun conto.
|
|
|
|
L'unico caso che costa è il testo che è *diverso* a ogni fotogramma -- un timer
|
|
in millisecondi, un contatore di fotogrammi -- perché ogni fotogramma è una
|
|
stringa che `fontPrint` non ha mai visto. Su un computer desktop non importa. Su
|
|
un Raspberry Pi o una console portatile, uno schermo pieno di testo così
|
|
importa. Il rimedio è cambiare il testo meno spesso: un timer in secondi interi
|
|
è una stringa nuova una volta al secondo, non sessanta.
|
|
|
|
Allora perché questa lezione ha usato `fontToSprite`? Perché uno sprite può fare
|
|
cose che il testo stampato non può fare:
|
|
|
|
* *Si può misurare*, ed è l'unico modo per centrare il testo o allinearlo contro
|
|
il lato destro.
|
|
* *Si può ridimensionare e ruotare* con le chiamate `sprite*` della nona lezione.
|
|
* *Può sopravvivere al suo font*, e tra un momento questo conterà.
|
|
|
|
Usa `fontPrint` per il testo, e `fontToSprite` quando ti serve una di queste
|
|
cose.
|
|
|
|
Il punteggio di questa lezione ne ha bisogno: sta contro il margine destro,
|
|
quindi va misurato, quindi deve essere uno sprite. E uno sprite di un punteggio
|
|
è giusto solo finché il punteggio non cambia. Quando cambia, crea un nuovo
|
|
sprite e scarica quello vecchio -- dimenticarsene lascia per strada uno sprite a
|
|
ogni cambio:
|
|
|
|
[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` si ricorda quale punteggio dice quello sprite, così il codice che
|
|
disegna può capire se è ancora giusto:
|
|
|
|
[source,lua]
|
|
----
|
|
if score ~= scoreShown then
|
|
renderScore()
|
|
end
|
|
----
|
|
|
|
`string.format("%06d", score)` è Lua puro, non Singe. Trasforma un numero in una
|
|
stringa, e `%06d` vuol dire «come numero intero, di almeno sei cifre,
|
|
riempito di zeri», ed è per questo che i punteggi da sala giochi hanno gli zeri
|
|
davanti. Cambialo in `%d` e gli zeri spariscono.
|
|
|
|
[#_putting_fonts_away]
|
|
=== Mettere via i font
|
|
|
|
`fontUnload` chiude un font e rende il suo handle non valido. Gli sprite che hai
|
|
creato con `fontToSprite` non ne risentono -- una volta disegnato, uno sprite non
|
|
ha più niente a che fare con il font -- quindi puoi scaricare un font e
|
|
continuare a disegnare il testo creato con lui.
|
|
|
|
Un'insidia: se scarichi il font che era selezionato, dopo non è
|
|
selezionato *nessun* font, e il `fontPrint` successivo chiude il tuo gioco con
|
|
un errore. Se hai intenzione di continuare a stampare, selezionane prima un
|
|
altro.
|
|
|
|
Il posto ordinato per tutto questo è `onShutdown`, una callback che il motore
|
|
chiama una volta quando il gioco si sta chiudendo:
|
|
|
|
[source,lua]
|
|
----
|
|
function onShutdown()
|
|
spriteUnload(titleImage)
|
|
fontUnload(titleFont)
|
|
end
|
|
----
|
|
|
|
Singe fa pulizia al posto tuo quando il gioco finisce, quindi non prende fuoco
|
|
niente se salti questa parte. Scrivila lo stesso. Un gioco che rilascia quello
|
|
che ha caricato è un gioco che puoi caricare due volte.
|
|
|
|
[#_the_whole_script]
|
|
=== Lo script completo
|
|
|
|
[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
|
|
----
|
|
|
|
Eseguilo e tieni premuto il pulsante di fuoco. Il titolo e il suggerimento
|
|
stanno al centro, il punteggio sale contro il margine destro e viene ridisegnato
|
|
solo quando cambia, il suggerimento lampeggia, e l'etichetta e l'orologio
|
|
vengono stampati con `fontPrint` a ogni fotogramma -- l'orologio in secondi
|
|
interi, così è una stringa nuova solo una volta al secondo.
|
|
|
|
[#_what_just_happened_15]
|
|
=== Cos'è appena successo
|
|
|
|
[source,lua]
|
|
----
|
|
titleFont = fontLoad("Singe/FreeSansBold.ttf", 48)
|
|
scoreFont = fontLoad("Singe/FreeSansBold.ttf", 36)
|
|
smallFont = fontLoad("Singe/FreeSansBold.ttf", 18)
|
|
----
|
|
|
|
Un file, tre dimensioni, tre handle. Ognuno costa memoria per il suo insieme di
|
|
glifi disegnati, quindi carica le dimensioni che usi e non di più. Tre è
|
|
normale. Una dozzina comincia a puzzare.
|
|
|
|
Dopo queste tre righe il font selezionato è `smallFont`, perché caricare
|
|
seleziona. Ecco perché il codice chiama `fontSelect(titleFont)` prima di
|
|
disegnare il titolo: l'ultima cosa caricata non è quella che vuoi per prima.
|
|
|
|
[source,lua]
|
|
----
|
|
local function centered(image)
|
|
return (overlayGetWidth() - spriteGetWidth(image)) / 2
|
|
end
|
|
----
|
|
|
|
Scritta una volta come funzione perché viene usata due volte, e perché la
|
|
prossima volta che vorrai centrare qualcosa la vorrai di nuovo. Dai un nome
|
|
all'aritmetica e non dovrai mai più rileggerla.
|
|
|
|
[source,lua]
|
|
----
|
|
if (singeGetTicks() // 500) % 2 == 0 then
|
|
spriteDraw(hintImage, centered(hintImage), HINT_Y)
|
|
end
|
|
----
|
|
|
|
`singeGetTicks()` è il numero di millisecondi dall'avvio del motore. `//`
|
|
è la divisione che butta via il resto, quindi `singeGetTicks() // 500` conta i
|
|
mezzi secondi, e `% 2` è il resto della divisione per due, che fa `0`,
|
|
`1`, `0`, `1`... Quindi il suggerimento viene disegnato per mezzo secondo e
|
|
saltato per mezzo secondo. Non disegnare niente è il modo per far lampeggiare
|
|
qualcosa.
|
|
|
|
[source,lua]
|
|
----
|
|
fontSelect(smallFont)
|
|
colorForeground(160, 160, 160)
|
|
fontPrint(MARGIN, LABEL_Y, "SCORE")
|
|
----
|
|
|
|
Qui servono tutte e due le impostazioni, e vale la pena capire bene perché.
|
|
Disegnare uno sprite non cambia il font selezionato né il colore, quindi le due
|
|
chiamate `spriteDraw` qui sopra li lasciano esattamente come li hanno trovati.
|
|
Ma il codice di preparazione ha selezionato tre font diversi uno dopo l'altro, e
|
|
`renderScore` ne seleziona un altro ogni volta che il punteggio si muove, quindi
|
|
quando questa riga viene eseguita è selezionato quello che è capitato di
|
|
scegliere per ultimo. Non cercare di tenerne traccia. Seleziona il font e
|
|
imposta il colore subito prima di stampare, ogni volta. Sono due righe, e
|
|
tolgono di mezzo un'intera categoria di serate passate a grattarsi la testa.
|
|
|
|
[#_try_it_19]
|
|
=== Prova tu
|
|
|
|
. *Cambia la qualità.* Metti `FONT_QUALITY_SOLID` nella riga di `fontQuality`
|
|
e guarda la curva della `S` in `ASTEROID`. Poi prova `FONT_QUALITY_SHADED` e
|
|
guarda che cosa succede dietro ogni stringa.
|
|
. *Fai diventare giallo il suggerimento.* Sposta la riga
|
|
`colorForeground(140, 200, 255)` da sopra `fontToSprite` a subito sopra lo
|
|
`spriteDraw` che disegna `hintImage`, e cambia i numeri. Capisci perché non
|
|
succede niente. Poi rimettila a posto, fai invece il suggerimento bianco, e
|
|
fallo diventare giallo con `spriteSetTint` subito prima che venga disegnato.
|
|
. *Allinea l'orologio a destra.* L'orologio è disegnato con `fontPrint`, che
|
|
non si può misurare. Mettilo contro il margine destro lo stesso. C'è un solo
|
|
modo, e trovarlo è il punto.
|
|
. *Togli il controllo su `scoreShown`*, così `renderScore()` viene eseguita a
|
|
ogni fotogramma. Sembrerà identico, ma adesso disegna il punteggio sessanta
|
|
volte al secondo invece di quando cambia -- esattamente il lavoro che
|
|
`fontPrint` ti risparmia quando lo lasci fare. Poi togli anche lo
|
|
`spriteUnload` in `renderScore`, lascialo girare per un minuto, e guarda la
|
|
memoria che usa il tuo gioco.
|
|
. *Stampa un orologio in millisecondi.* Cambia la riga dell'orologio perché
|
|
stampi `singeGetTicks()` senza il `// 1000`. Funziona ancora; adesso è una
|
|
stringa che `fontPrint` non ha mai visto, a ogni fotogramma. Su una macchina
|
|
lenta, è questo il tipo di testo che costa.
|
|
. *Usa un font tuo.* Trova un `.ttf` sul tuo computer, copialo accanto al tuo
|
|
script e caricalo con `DIR .. "thatfont.ttf"`. Certi font sono orribili a 18
|
|
punti e vanno benissimo a 48. È colpa del font, non tua.
|
|
|
|
[#_break_it_on_purpose_18]
|
|
=== Rompilo apposta
|
|
|
|
Aggiungi una riga con scritto `fontPrint(24, 60, "SCORE")` subito sotto
|
|
`overlaySetResolution(discGetWidth(), discGetHeight())`, così viene eseguita
|
|
prima di qualsiasi `fontLoad`. Singe parte e muore subito:
|
|
|
|
----
|
|
4:fontPrint: No font selected.
|
|
----
|
|
|
|
Leggilo come qualsiasi altro errore. `4` è la riga. `fontPrint` è la chiamata
|
|
del motore che si è lamentata -- un messaggio di questa forma arriva da dentro
|
|
il motore invece che da Lua, quindi nomina la funzione invece del file. Il resto
|
|
è la lamentela, e qui è precisa: gli hai chiesto di stampare e non c'era nessun
|
|
font con cui farlo.
|
|
|
|
Questo errore lo incontrerai sul serio in un modo meno ovvio. Scarica il font
|
|
selezionato mentre il gioco gira e il `fontPrint` successivo dice esattamente
|
|
la stessa cosa, a qualche centinaio di righe di distanza dal `fontUnload` che
|
|
l'ha causato.
|
|
|
|
[#_what_you_learned_19]
|
|
=== Cosa hai imparato
|
|
|
|
* `fontLoad` apre un file TrueType a una dimensione in punti fissa e
|
|
restituisce un handle. Una dimensione, un handle.
|
|
* Caricare un font lo seleziona; `fontSelect` sceglie tra i font caricati.
|
|
* `fontPrint` conta in pixel e `overlayPrint` conta in celle di carattere.
|
|
* Il colore non è un argomento. `colorForeground` è un'impostazione: impostala,
|
|
poi disegna.
|
|
* `fontQuality` sceglie come vengono rasterizzate le lettere, e
|
|
`FONT_QUALITY_BLENDED` è la risposta normale.
|
|
* `fontPrint` si tiene quello che disegna, quindi stampare lo stesso testo a
|
|
ogni fotogramma costa poco; quello che costa è il testo diverso a ogni
|
|
fotogramma.
|
|
* `fontToSprite` disegna dentro uno sprite, che `spriteGetWidth` misura e le
|
|
chiamate `sprite*` possono ridimensionare e ruotare. Usalo per questo, non per la
|
|
velocità.
|
|
* Misurare è il modo per centrare il testo o allinearlo a destra, perché
|
|
nient'altro può dirti quanto sarà larga una stringa.
|
|
* `colorForeground` arriva al testo quando viene creato, mai a uno sprite
|
|
quando viene disegnato; `spriteSetTint` è il modo per ricolorare uno sprite
|
|
dopo.
|
|
* `fontUnload` libera un font; gli sprite che ha creato gli sopravvivono.
|
|
|
|
[#_next_time_19]
|
|
=== La prossima volta
|
|
|
|
Un testo che fa bella figura è ben lontano da un menu che funziona. Una pagina
|
|
con un titolo, un elenco in cui sposti un cursore, uno slider e un pulsante è
|
|
un centinaio di righe di disegno e di aritmetica dei cursori, se la costruisci
|
|
con le chiamate di questa lezione. La ventesima lezione non la
|
|
costruisce affatto con quelle chiamate.
|