543 lines
21 KiB
Text
543 lines
21 KiB
Text
[#_lesson_19_text_that_looks_good]
|
|
== Lección 19: Texto con buen aspecto
|
|
|
|
image::learn/19-text.png[La lección terminada, 480]
|
|
|
|
Cada palabra que has puesto en pantalla desde la lección uno ha salido de
|
|
`overlayPrint`, y `overlayPrint` tiene exactamente una fuente: un bloque de
|
|
píxeles de ancho fijo integrado en el motor, siempre del mismo tamaño, del color
|
|
que le toque. Es perfecta para un contador de fotogramas. No sirve para un
|
|
título, una puntuación, un menú ni nada que el jugador tenga que mirar en lugar
|
|
de leer de pasada.
|
|
|
|
Esta lección carga una fuente de verdad desde el disco, imprime con ella, le da
|
|
color, la mide para que puedas centrar las cosas y te enseña lo que cuesta de
|
|
verdad imprimir texto. Al final tendrás una pantalla de título que parece hecha
|
|
a propósito y no por casualidad.
|
|
|
|
[#_set_the_overlay_to_a_size_you_can_think_in]
|
|
=== Pon el overlay a un tamaño con el que puedas pensar
|
|
|
|
Empieza un archivo nuevo llamado `text.singe` con dos líneas:
|
|
|
|
[source,lua]
|
|
----
|
|
dofile("Singe/Framework.singe")
|
|
|
|
overlaySetResolution(discGetWidth(), discGetHeight())
|
|
----
|
|
|
|
La primera línea la has escrito muchas veces; carga el framework, que define
|
|
`DIR`, los nombres `SWITCH_*` y los nombres `FONT_QUALITY_*` que estás a punto
|
|
de usar.
|
|
|
|
La segunda importa más de lo que parece. El overlay -- la superficie sobre la que
|
|
cae todo lo que dibujas -- empieza con la *mitad* del tamaño del mundo. Un juego
|
|
sin vídeo tiene un lienzo de 720 por 480, así que el overlay empieza en 360 por
|
|
240, y una fuente de 36 puntos en un overlay de 360 píxeles de ancho es enorme.
|
|
`overlaySetResolution` cambia el overlay por uno del tamaño que pidas, y a partir
|
|
de ahí cada coordenada que das, y todo lo que te dicen `overlayGetWidth` y
|
|
`overlayGetHeight`, está en ese espacio.
|
|
|
|
`discGetWidth()` y `discGetHeight()` responden con el tamaño del vídeo cuando lo
|
|
hay y con el tamaño del lienzo cuando no, así que esa única línea te da un
|
|
overlay que encaja con el mundo sea cual sea el tipo de juego. Llámala una vez,
|
|
al principio, antes de dibujar nada.
|
|
|
|
[#_load_a_font]
|
|
=== Carga una fuente
|
|
|
|
Debajo de eso, añade:
|
|
|
|
[source,lua]
|
|
----
|
|
fontQuality(FONT_QUALITY_BLENDED)
|
|
|
|
local titleFont = fontLoad("Singe/FreeSansBold.ttf", 48)
|
|
----
|
|
|
|
`fontLoad` abre un archivo de fuente TrueType y te devuelve un número -- un
|
|
*handle* -- que usas cada vez que quieres esa fuente. El segundo argumento es el
|
|
tamaño en puntos, y queda fijo mientras viva el handle. No hay forma de cambiar
|
|
después el tamaño de una fuente cargada. Si quieres el mismo tipo de letra en
|
|
tres tamaños, llamas a `fontLoad` tres veces y guardas tres handles.
|
|
|
|
`Singe/FreeSansBold.ttf` es la fuente que el motor desempaquetó en la carpeta
|
|
`Singe` de tu carpeta de trabajo la primera vez que ejecutaste algo. Todas las
|
|
copias de Singe la tienen, así que puedes contar con ella. Cualquier otro archivo
|
|
`.ttf` funciona igual; ponlo junto a tu script y cárgalo como
|
|
`DIR .. "myfont.ttf"`.
|
|
|
|
Ahora imprime con ella:
|
|
|
|
[source,lua]
|
|
----
|
|
function onOverlayUpdate()
|
|
overlayClear()
|
|
fontPrint(24, 60, "ASTEROID PATROL")
|
|
return OVERLAY_UPDATED
|
|
end
|
|
----
|
|
|
|
Ejecútalo. Letras blancas grandes cerca de la esquina superior izquierda.
|
|
|
|
Fíjate en lo que *no* has hecho: no le has dicho a `fontPrint` qué fuente usar.
|
|
`fontLoad` deja seleccionada la fuente que acaba de cargar, y `fontPrint` dibuja
|
|
siempre con la que esté seleccionada. Con más de una fuente cargada, eliges entre
|
|
ellas con `fontSelect`, que es lo que harás dentro de un momento.
|
|
|
|
Fíjate también en que `24` y `60` son píxeles, no celdas de carácter. Esta es la
|
|
diferencia entre `fontPrint` y `overlayPrint` que hace tropezar a la gente:
|
|
`overlayPrint(2, 2, ...)` significa dos letras hacia dentro y dos líneas hacia
|
|
abajo, mientras que `fontPrint(2, 2, ...)` significa dos píxeles hacia dentro y
|
|
dos píxeles hacia abajo, que es estar metido en la esquina. La `y` que das es la
|
|
parte de *arriba* del texto, no la línea base sobre la que se apoya.
|
|
|
|
[#_color]
|
|
=== Color
|
|
|
|
`fontPrint` recibe tres argumentos y ninguno de ellos es un color. El color sale
|
|
de otro sitio:
|
|
|
|
[source,lua]
|
|
----
|
|
colorForeground(255, 211, 90)
|
|
fontPrint(24, 60, "ASTEROID PATROL")
|
|
----
|
|
|
|
`colorForeground` pone el rojo, el verde y el azul de `0` a `255`, y un cuarto
|
|
número opcional para la opacidad. No es un argumento de nada; es un ajuste que el
|
|
motor recuerda. Todo lo que se dibuja después sale de ese color hasta que lo
|
|
vuelvas a cambiar, exactamente igual que ya funcionan `overlayBox` y
|
|
`overlayLine`. Así que el patrón es siempre el mismo: pon el color y luego
|
|
dibuja.
|
|
|
|
Esto es lo primero que la gente hace mal con las fuentes, y lo segundo llega
|
|
dentro de unas páginas.
|
|
|
|
[#_quality]
|
|
=== Calidad
|
|
|
|
`fontQuality` decide cómo se rasterizan las letras -- cómo se convierten en
|
|
píxeles las curvas de una letra -- y hay tres opciones. Prueba cada una
|
|
poniéndola encima de la línea de `fontLoad` y mirando el resultado de cerca.
|
|
|
|
`FONT_QUALITY_SOLID` es la más rápida. Cada píxel o es de la letra o no lo es,
|
|
así que las diagonales y las curvas salen con escalones visibles. A 48 puntos
|
|
queda barato. A 12 puntos en un overlay pequeño puede verse *más nítida* que las
|
|
otras, y a veces es justo lo que quieres.
|
|
|
|
`FONT_QUALITY_SHADED` suaviza los bordes, pero lo hace mezclándolos con el color
|
|
de fondo actual, y dibuja ese fondo como un rectángulo sólido detrás de toda la
|
|
cadena. Te sale una etiqueta con una caja alrededor, la quisieras o no. Es la
|
|
respuesta correcta para una lectura de depuración sobre una imagen recargada, y
|
|
la equivocada para casi todo lo demás.
|
|
|
|
`FONT_QUALITY_BLENDED` suaviza los bordes y los deja transparentes, así que las
|
|
letras se asientan sobre lo que haya detrás. Es la que más cuesta y es la que
|
|
quieres casi siempre. Úsala a menos que tengas un motivo para no hacerlo.
|
|
|
|
El ajuste es global, no de cada fuente, y se aplica desde el momento en que lo
|
|
llamas en adelante. Los sprites que ya has creado conservan la calidad con la
|
|
que se crearon. La entrada de la referencia de `fontQuality` enumera los tres nombres y
|
|
sus valores.
|
|
|
|
[#_measuring_so_you_can_center_things]
|
|
=== Medir, para poder centrar las cosas
|
|
|
|
Tarde o temprano -- normalmente a los diez minutos -- querrás el título en el
|
|
centro de la pantalla. Sabes que la pantalla mide `overlayGetWidth()` de ancho.
|
|
No sabes cuánto mide de ancho el texto, y no hay ninguna llamada `fontMeasure` a
|
|
la que preguntar.
|
|
|
|
Lo que hay en su lugar es `fontToSprite`. Dibuja una cadena con la fuente
|
|
seleccionada, la calidad actual y el color actual, y en lugar de ponerla en
|
|
pantalla te devuelve un *sprite*: el mismo tipo de handle que te da
|
|
`spriteLoad`, lo que significa que todas las llamadas `sprite*` que aprendiste en
|
|
la lección nueve funcionan con él. Incluidas las dos que lo miden.
|
|
|
|
[source,lua]
|
|
----
|
|
fontSelect(titleFont)
|
|
colorForeground(255, 211, 90)
|
|
local titleImage = fontToSprite("ASTEROID PATROL")
|
|
----
|
|
|
|
Y luego, en `onOverlayUpdate`:
|
|
|
|
[source,lua]
|
|
----
|
|
local x = (overlayGetWidth() - spriteGetWidth(titleImage)) / 2
|
|
spriteDraw(titleImage, x, 60)
|
|
----
|
|
|
|
Eso es centrar, y ese es todo el truco. Toma el ancho del espacio, réstale el
|
|
ancho de la cosa, y la mitad de lo que queda es el hueco de cada lado.
|
|
|
|
Alinear a la derecha es la misma idea con menos cuentas. Para poner texto contra
|
|
el margen derecho:
|
|
|
|
[source,lua]
|
|
----
|
|
spriteDraw(scoreImage, overlayGetWidth() - MARGIN - spriteGetWidth(scoreImage), y)
|
|
----
|
|
|
|
`spriteGetHeight` mide la otra dirección, para apilar líneas o centrar en
|
|
vertical.
|
|
|
|
Hay una trampa en ese primer bloque de código, y es lo segundo que todo el mundo
|
|
hace mal. *El color se queda grabado cuando se crea el sprite, no cuando se
|
|
dibuja.* Un `colorForeground` encima de `fontToSprite` cambia el texto. Ponerlo
|
|
en cambio encima de `spriteDraw` no hace nada de nada, porque `colorForeground`
|
|
es un ajuste para dibujar texto y líneas, y un sprite se dibuja con sus propios
|
|
píxeles. Si tu título, coloreado con tanto cuidado, sale blanco, es por esto.
|
|
|
|
Un sprite se puede volver a colorear después de crearlo, pero con una llamada de
|
|
sprite, no con `colorForeground`. `spriteSetTint(image, red, green, blue)`
|
|
multiplica cada píxel del sprite por ese color cada vez que se dibuja, así que un
|
|
texto dibujado en blanco se puede pintar amarillo hoy y rojo mañana sin volver a
|
|
crearlo. El blanco es el color con el que hay que dibujarlo cuando piensas
|
|
teñirlo: cualquier cosa multiplicada por blanco se queda como estaba, así que el
|
|
tinte es exactamente el color que pides.
|
|
|
|
[#_what_printing_costs]
|
|
=== Lo que cuesta imprimir
|
|
|
|
La primera vez que `fontPrint` ve una cadena, trabaja de verdad: rasteriza cada
|
|
letra a partir de los contornos de la fuente y construye una imagen de toda la
|
|
cadena. Luego se guarda esa imagen. La siguiente vez que imprimes la misma
|
|
cadena, con la misma fuente, calidad y colores, se salta todo eso y copia la
|
|
imagen que ya tiene, que cuesta más o menos lo mismo que dibujar un sprite. Una
|
|
cadena que dejas de imprimir se suelta un segundo después, así que no se
|
|
acumula nada.
|
|
|
|
Eso significa que la forma obvia de escribir una pantalla de título es también
|
|
la rápida. Imprimir `"ASTEROID PATROL"` sesenta veces por segundo la dibuja una
|
|
vez. Una puntuación que cambia dos veces por segundo se dibuja dos veces por
|
|
segundo, ella sola, sin que tengas que llevar ninguna cuenta.
|
|
|
|
El único caso que cuesta es el texto que es *distinto* en cada fotograma -- un
|
|
temporizador en milisegundos, un contador de fotogramas -- porque cada fotograma
|
|
es una cadena que `fontPrint` no ha visto nunca. En un ordenador de escritorio da
|
|
igual. En una Raspberry Pi o en una consola portátil, una pantalla llena de eso
|
|
se notará. El remedio es cambiar el texto menos a menudo: un temporizador en
|
|
segundos enteros es una cadena nueva una vez por segundo, no sesenta.
|
|
|
|
Entonces, ¿por qué ha usado esta lección `fontToSprite`? Porque un sprite puede
|
|
hacer cosas que el texto impreso no puede:
|
|
|
|
* *Se puede medir*, que es la única forma de centrar texto o alinearlo contra el
|
|
lado derecho.
|
|
* *Se puede escalar y girar* con las llamadas `sprite*` de la lección nueve.
|
|
* *Puede sobrevivir a su fuente*, lo que importará dentro de un momento.
|
|
|
|
Usa `fontPrint` para el texto, y `fontToSprite` cuando necesites una de esas
|
|
cosas.
|
|
|
|
La puntuación de esta lección necesita una de ellas: va contra el margen derecho,
|
|
así que hay que medirla, así que tiene que ser un sprite. Y un sprite de una
|
|
puntuación solo es correcto hasta que la puntuación cambia. Cuando cambie, crea
|
|
un sprite nuevo y descarga el viejo -- si se te olvida, pierdes un sprite en cada
|
|
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` recuerda qué puntuación dice ese sprite, para que el código de
|
|
dibujo pueda saber si sigue siendo correcto:
|
|
|
|
[source,lua]
|
|
----
|
|
if score ~= scoreShown then
|
|
renderScore()
|
|
end
|
|
----
|
|
|
|
`string.format("%06d", score)` es Lua normal, no Singe. Convierte un número en
|
|
una cadena, y `%06d` significa «como número entero, con al menos seis cifras,
|
|
rellenando con ceros», que es por lo que las puntuaciones de las recreativas
|
|
llevan ceros delante. Cámbialo por `%d` y los ceros desaparecen.
|
|
|
|
[#_putting_fonts_away]
|
|
=== Recoger las fuentes
|
|
|
|
`fontUnload` cierra una fuente y deja su handle inválido. Los sprites que creaste
|
|
con `fontToSprite` no se ven afectados -- una vez dibujado, un sprite ya no tiene
|
|
nada que ver con la fuente -- así que puedes descargar una fuente y seguir
|
|
dibujando el texto que hiciste con ella.
|
|
|
|
Un borde afilado: si descargas la fuente que estaba seleccionada, después no
|
|
queda *ninguna* fuente seleccionada, y el siguiente `fontPrint` termina tu juego
|
|
con un error. Selecciona otra antes si vas a seguir imprimiendo.
|
|
|
|
El sitio ordenado para todo esto es `onShutdown`, un callback que el motor llama
|
|
una vez cuando el juego se está cerrando:
|
|
|
|
[source,lua]
|
|
----
|
|
function onShutdown()
|
|
spriteUnload(titleImage)
|
|
fontUnload(titleFont)
|
|
end
|
|
----
|
|
|
|
Singe recoge lo tuyo cuando el juego termina, así que no se quema nada si te
|
|
saltas esto. Escríbelo de todos modos. Un juego que suelta lo que ha cargado es
|
|
un juego que puedes cargar dos veces.
|
|
|
|
[#_the_whole_script]
|
|
=== El 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
|
|
----
|
|
|
|
Ejecútalo y mantén pulsado el botón de disparo. El título y la pista se quedan
|
|
centrados, la puntuación sube contra el margen derecho y solo se vuelve a dibujar
|
|
cuando cambia, la pista parpadea, y la etiqueta y el reloj se imprimen con
|
|
`fontPrint` en cada fotograma -- el reloj en segundos enteros, así que solo es
|
|
una cadena nueva una vez por segundo.
|
|
|
|
[#_what_just_happened_15]
|
|
=== Qué acaba de pasar
|
|
|
|
[source,lua]
|
|
----
|
|
titleFont = fontLoad("Singe/FreeSansBold.ttf", 48)
|
|
scoreFont = fontLoad("Singe/FreeSansBold.ttf", 36)
|
|
smallFont = fontLoad("Singe/FreeSansBold.ttf", 18)
|
|
----
|
|
|
|
Un archivo, tres tamaños, tres handles. Cada uno cuesta memoria para su propio
|
|
juego de glifos dibujados, así que carga los tamaños que uses y ni uno más. Tres
|
|
es normal. Una docena huele mal.
|
|
|
|
Después de estas tres líneas la fuente seleccionada es `smallFont`, porque cargar
|
|
selecciona. Por eso el código llama a `fontSelect(titleFont)` antes de dibujar el
|
|
título: lo último que se ha cargado no es lo que quieres primero.
|
|
|
|
[source,lua]
|
|
----
|
|
local function centered(image)
|
|
return (overlayGetWidth() - spriteGetWidth(image)) / 2
|
|
end
|
|
----
|
|
|
|
Escrita una vez como función porque se usa dos veces, y porque la próxima vez
|
|
que quieras centrar algo la vas a volver a querer. Ponle nombre a la cuenta y no
|
|
tendrás que volver a leerla nunca.
|
|
|
|
[source,lua]
|
|
----
|
|
if (singeGetTicks() // 500) % 2 == 0 then
|
|
spriteDraw(hintImage, centered(hintImage), HINT_Y)
|
|
end
|
|
----
|
|
|
|
`singeGetTicks()` es el número de milisegundos desde que arrancó el motor. `//`
|
|
es una división que tira el resto, así que `singeGetTicks() // 500` cuenta
|
|
medios segundos, y `% 2` es el resto de dividir entre dos, que es `0`, `1`, `0`,
|
|
`1`... Así que la pista se dibuja durante medio segundo y se salta durante medio
|
|
segundo. No dibujar nada es como se hace que algo parpadee.
|
|
|
|
[source,lua]
|
|
----
|
|
fontSelect(smallFont)
|
|
colorForeground(160, 160, 160)
|
|
fontPrint(MARGIN, LABEL_Y, "SCORE")
|
|
----
|
|
|
|
Aquí hacen falta los dos ajustes, y vale la pena tener claro por qué. Dibujar un
|
|
sprite no cambia la fuente seleccionada ni el color, así que las dos llamadas
|
|
`spriteDraw` de arriba los dejan exactamente como los encontraron. Pero el código
|
|
de preparación seleccionó tres fuentes distintas una tras otra, y `renderScore`
|
|
selecciona otra cada vez que se mueve la puntuación, así que la selección cuando
|
|
se ejecuta esta línea es la que haya tocado elegir la última. No intentes seguirle
|
|
la pista. Selecciona la fuente y pon el color justo antes de imprimir, siempre.
|
|
Son dos líneas, y te quitan de encima toda una categoría de tardes de
|
|
desconcierto.
|
|
|
|
[#_try_it_19]
|
|
=== Pruébalo
|
|
|
|
. *Cambia la calidad.* Pon `FONT_QUALITY_SOLID` en la línea de `fontQuality` y
|
|
mira la curva de la `S` de `ASTEROID`. Luego prueba `FONT_QUALITY_SHADED` y
|
|
fíjate en lo que pasa detrás de cada cadena.
|
|
. *Pon la pista en amarillo.* Mueve la línea `colorForeground(140, 200, 255)`
|
|
de encima de `fontToSprite` a justo encima del `spriteDraw` que dibuja
|
|
`hintImage`, y cambia los números. Averigua por qué no pasa nada. Luego
|
|
devuélvela a su sitio, haz la pista blanca y vuélvela amarilla con
|
|
`spriteSetTint` justo antes de dibujarla.
|
|
. *Alinea el reloj a la derecha.* El reloj se dibuja con `fontPrint`, que no se
|
|
puede medir. Ponlo contra el margen derecho de todos modos. Solo hay una forma,
|
|
y encontrarla es lo importante.
|
|
. *Quita la comprobación de `scoreShown`*, para que `renderScore()` se ejecute en
|
|
cada fotograma. Se verá igual, pero ahora dibuja la puntuación sesenta veces
|
|
por segundo en lugar de cuando cambia -- justo el trabajo que `fontPrint` te
|
|
ahorra cuando le dejas. Luego quita también el `spriteUnload` de
|
|
`renderScore`, déjalo en marcha un minuto y vigila la memoria que usa tu juego.
|
|
. *Imprime un reloj en milisegundos.* Cambia la línea del reloj para que imprima
|
|
`singeGetTicks()` sin el `// 1000`. Sigue funcionando; ahora es una cadena que
|
|
`fontPrint` no ha visto nunca, en cada fotograma. En un ordenador lento, ese es
|
|
el tipo de texto que cuesta.
|
|
. *Usa tu propia fuente.* Busca un `.ttf` en tu ordenador, cópialo junto a tu
|
|
script y cárgalo con `DIR .. "thatfont.ttf"`. Algunas fuentes quedan fatal a
|
|
18 puntos y bien a 48. Eso es culpa de la fuente, no tuya.
|
|
|
|
[#_break_it_on_purpose_18]
|
|
=== Rómpelo a propósito
|
|
|
|
Añade una línea que diga `fontPrint(24, 60, "SCORE")` justo debajo de
|
|
`overlaySetResolution(discGetWidth(), discGetHeight())`, para que se ejecute
|
|
antes de que haya habido ningún `fontLoad`. Singe arranca y se muere al
|
|
instante:
|
|
|
|
----
|
|
4:fontPrint: No font selected.
|
|
----
|
|
|
|
Léelo igual que cualquier otro error. `4` es la línea. `fontPrint` es la llamada
|
|
del motor que se ha quejado -- este tipo de mensaje sale de dentro del motor y no
|
|
de Lua, así que nombra la función en lugar del archivo. El resto es la queja, y
|
|
aquí es exacta: le has pedido que imprima y no había ninguna fuente con la que
|
|
imprimir.
|
|
|
|
Te encontrarás con este de verdad de una forma menos evidente. Descarga la fuente
|
|
seleccionada mientras el juego está en marcha y el siguiente `fontPrint` dirá
|
|
exactamente lo mismo, a varios cientos de líneas del `fontUnload` que lo
|
|
provocó.
|
|
|
|
[#_what_you_learned_19]
|
|
=== Lo que has aprendido
|
|
|
|
* `fontLoad` abre un archivo TrueType a un tamaño en puntos fijo y devuelve un
|
|
handle. Un tamaño, un handle.
|
|
* Cargar una fuente la selecciona; `fontSelect` elige entre las fuentes
|
|
cargadas.
|
|
* `fontPrint` cuenta en píxeles y `overlayPrint` cuenta en celdas de carácter.
|
|
* El color no es un argumento. `colorForeground` es un ajuste; ponlo y luego
|
|
dibuja.
|
|
* `fontQuality` elige cómo se rasterizan las letras, y `FONT_QUALITY_BLENDED`
|
|
es la respuesta habitual.
|
|
* `fontPrint` guarda lo que dibuja, así que imprimir el mismo texto en cada
|
|
fotograma es barato; lo que cuesta es el texto que cambia en cada fotograma.
|
|
* `fontToSprite` dibuja en un sprite, que `spriteGetWidth` mide y que las
|
|
llamadas `sprite*` pueden escalar y girar. Úsalo para eso, no por velocidad.
|
|
* Medir es la forma de centrar o alinear texto a la derecha, porque nada más te
|
|
puede decir cuánto va a medir de ancho una cadena.
|
|
* `colorForeground` llega al texto cuando se crea, nunca a un sprite cuando se
|
|
dibuja; `spriteSetTint` es la forma de volver a colorear un sprite después.
|
|
* `fontUnload` libera una fuente; los sprites que creó sobreviven.
|
|
|
|
[#_next_time_19]
|
|
=== La próxima vez
|
|
|
|
Un texto con buen aspecto está muy lejos de un menú que funcione. Una página con
|
|
un título, una lista por la que mueves un cursor, un deslizador y un botón son
|
|
cien líneas de dibujo y de cuentas con el cursor si la construyes con las
|
|
llamadas de esta lección. La lección veinte no la construye con esas llamadas en
|
|
absoluto.
|