502 lines
20 KiB
Text
502 lines
20 KiB
Text
[#_lesson_14_playing_video]
|
|
== Lección 14: Reproducir vídeo
|
|
|
|
image::learn/14-video.png[La lección terminada, 480]
|
|
|
|
Singe puede reproducir una película y ejecutar tu juego dentro de ella. La
|
|
imagen se convierte en el mundo, y el overlay en el que llevas dibujando desde
|
|
la lección uno se coloca encima. En esta lección pondrás en marcha un vídeo,
|
|
averiguarás qué fotograma se está mostrando, lo pararás y saltarás a cualquier
|
|
momento que quieras.
|
|
|
|
Antes de nada de eso, hay algo que conviene decir claramente, porque decide si
|
|
deberías leer siquiera esta parte del libro.
|
|
|
|
[#_a_game_plays_video_only_if_it_asks_to]
|
|
=== Un juego solo reproduce vídeo si lo pide
|
|
|
|
Singe es un motor de juegos. Todo lo que has construido en las partes primera y
|
|
segunda -- los sprites, el sonido, las colisiones, la puntuación, la carpeta del
|
|
juego -- es Singe entero, y nada de ello tiene que ver con el vídeo. Lo que
|
|
añade la tercera parte es un extra: un juego puede pedir que haya una película
|
|
reproduciéndose detrás, y entonces la película es el mundo en lugar de un lienzo
|
|
negro.
|
|
|
|
Nada más cambia. A `onOverlayUpdate` se le sigue llamando sesenta veces por
|
|
segundo. `overlayPrint` sigue contando en celdas de carácter. Tus sprites se
|
|
dibujan donde se dibujaban antes. Un juego con vídeo y un juego sin él se
|
|
escriben igual, con las mismas funciones, y comparten el mismo libro.
|
|
|
|
Así que, si el juego que quieres hacer no tiene vídeo, puedes cerrar esta parte
|
|
ahora e ir a la cuarta. Nada de la cuarta parte depende de nada de la tercera, y
|
|
las únicas lecciones que necesitan esta son las cuatro que vienen detrás.
|
|
|
|
¿Sigues aquí? Bien.
|
|
|
|
[#_you_already_have_a_video]
|
|
=== Ya tienes un vídeo
|
|
|
|
No necesitas buscar uno, ni comprarlo, ni hacerlo. La primera vez que ejecutaste
|
|
Singe en la lección uno, apareció una carpeta llamada `Singe` en tu carpeta de
|
|
trabajo. Ábrela y mira: entre los archivos que desempaquetó el motor está
|
|
`menuBackground.mkv`. Es el fondo que usa el propio menú del motor, mide 720 por
|
|
480 píxeles y dura 420 fotogramas -- unos catorce segundos.
|
|
|
|
Ese es el vídeo de esta lección y de las dos siguientes.
|
|
|
|
Crea una carpeta llamada `movie` en tu carpeta de trabajo, junto a las carpetas
|
|
de las otras lecciones, y dentro un archivo llamado `movie.singe`. No hay que
|
|
preparar nada más. Ejecutas Singe desde la carpeta de trabajo, como llevas
|
|
haciendo desde la lección uno, así que la carpeta `Singe`, vídeo incluido, está
|
|
justo ahí al lado de tu juego, y `Singe/menuBackground.mkv` es el nombre que le
|
|
darás al motor dentro de un momento. No hace falta copiar nada.
|
|
|
|
[#_type_this_in_2]
|
|
=== Escribe esto
|
|
|
|
[source,lua]
|
|
----
|
|
dofile("Singe/Framework.singe")
|
|
|
|
|
|
function onOverlayUpdate()
|
|
overlayClear()
|
|
overlayPrint(2, 2, "Frame " .. discGetFrame())
|
|
return OVERLAY_UPDATED
|
|
end
|
|
|
|
|
|
discPlay()
|
|
----
|
|
|
|
Guárdalo.
|
|
|
|
[#_run_it_2]
|
|
=== Ejecútalo
|
|
|
|
El vídeo no forma parte de tu script, así que no lo cargas en tu script. Se lo
|
|
das al motor en la línea de comandos, con `-v`:
|
|
|
|
----
|
|
Singe -R -v Singe/menuBackground.mkv movie
|
|
----
|
|
|
|
`-v` es la forma corta de `--framefile`, y nombra el vídeo que tiene que
|
|
reproducir el juego. El nombre de tu script va al final, como siempre. `-R` es
|
|
el mismo `-R` que llevas usando desde la lección uno, y sigue reiniciando el
|
|
juego cuando guardas.
|
|
|
|
Pedirlo es todo el sentido de `-v`. Si dejas un archivo de vídeo junto a tu
|
|
script y no dices nada, el motor lo ve, lo dice en la consola y ejecuta tu juego
|
|
sin él. Un juego solo es un juego de vídeo cuando alguien dice que lo es.
|
|
|
|
Se abre una ventana. La llena una bola de fuego naranja, un dragón se forma a
|
|
partir del fuego, la palabra `SINGE` aparece debajo, y luego una cuadrícula
|
|
magenta se aleja rodando bajo un sol naranja. En la esquina superior izquierda,
|
|
por encima de todo, hay un número que va subiendo.
|
|
|
|
Míralo hasta el final. Luego mira qué pasa al final.
|
|
|
|
[#_what_just_happened_10]
|
|
=== Qué acaba de pasar
|
|
|
|
[source,lua]
|
|
----
|
|
discPlay()
|
|
----
|
|
|
|
Singe llama al vídeo *el disco*, y todas las funciones que lo manejan empiezan
|
|
por `disc`. El nombre es histórico: el motor nació reproduciendo juegos que
|
|
guardaban su película en un laserdisc, y el nombre se quedó pegado a la idea. Es
|
|
un archivo de vídeo en tu disco duro y nada más.
|
|
|
|
Un juego tiene como mucho un disco, así que ninguna de las funciones `disc`
|
|
recibe un argumento que diga cuál. Solo hay uno.
|
|
|
|
Esta línea no está dentro de ninguna función. Está al final del archivo, sola, y
|
|
se ejecuta una vez cuando Singe carga tu script -- antes del primer
|
|
`onOverlayUpdate`, antes que cualquier otra cosa. Singe no tiene un callback
|
|
`onStartup` porque no lo necesita: un script de Lua *es* una lista de
|
|
instrucciones, y todo lo que escribas fuera de una función ocurre mientras se
|
|
lee el archivo. Por convención, el código de arranque va al final, debajo de las
|
|
funciones.
|
|
|
|
Necesitas `discPlay()` porque el disco no arranca solo. Cuando empieza el juego,
|
|
el motor aparca el disco en el fotograma uno y lo pausa ahí. Nada se mueve hasta
|
|
que lo pides.
|
|
|
|
[source,lua]
|
|
----
|
|
overlayPrint(2, 2, "Frame " .. discGetFrame())
|
|
----
|
|
|
|
`discGetFrame` responde a una pregunta: ¿qué fotograma se está mostrando ahora
|
|
mismo? No recibe argumentos y devuelve un número.
|
|
|
|
Fíjate en dónde se dibuja ese número. Columna 2, fila 2 -- exactamente donde fue
|
|
`Hello!` en la lección uno, y cae en el mismo sitio de la pantalla. El overlay
|
|
en el que dibujas mide la mitad que el vídeo en cada lado, lo que para un vídeo
|
|
de 720 por 480 son 360 por 240, que es exactamente el tamaño sobre el que
|
|
llevas dibujando trece lecciones. Tus coordenadas no se han movido. Si
|
|
alguna vez reproduces un vídeo de otro tamaño, el overlay cambia de tamaño con
|
|
él, y `overlaySetResolution` lo pone al tamaño que quieras; su entrada en la
|
|
referencia tiene los detalles.
|
|
|
|
Esta es toda la idea del overlay. El vídeo está debajo. Tu dibujo está encima, y
|
|
allí donde no has dibujado se ve la película. Por eso `overlayClear` importa
|
|
ahora todavía más que antes: limpia el overlay y estarás viendo la película.
|
|
|
|
[#_a_frame_is_an_address]
|
|
=== Un fotograma es una dirección
|
|
|
|
El número que devuelve `discGetFrame` es un número de *fotograma*. Un fotograma
|
|
es una imagen fija. Este vídeo tiene 420 y muestra unos treinta cada segundo,
|
|
así que dura unos catorce segundos.
|
|
|
|
Los fotogramas se cuentan desde cero, así que este vídeo tiene los fotogramas
|
|
del 0 al 419. La cantidad y el último número no son lo mismo, y eso pilla a todo
|
|
el mundo una vez. `discGetFrameCount` te dice la cantidad -- 420 -- y el
|
|
fotograma más alto que puedes pedir es uno menos.
|
|
|
|
Lo importante de un número de fotograma es que nunca cambia y nunca se desvía.
|
|
El fotograma 110 es el momento en que el dragón echa fuego, en esta copia del
|
|
archivo, en este ordenador, hoy y el año que viene. Los segundos se desvían,
|
|
porque un vídeo no siempre se reproduce exactamente a la velocidad que crees. Un
|
|
número de fotograma es la dirección de un momento, y los juegos de vídeo se
|
|
construyen con direcciones: apuntas el fotograma en el que aparece el dragón, y
|
|
a partir de ahí puedes mandar la película allí cuando quieras.
|
|
|
|
Encontrar esos números es un trabajo que haces una vez por vídeo, y el script
|
|
que has escrito es la herramienta para hacerlo: pon la película, mira el
|
|
contador, apunta los fotogramas en los que pasan cosas. Todo lo de la lección
|
|
dieciséis se construye sobre una lista de números encontrados exactamente así.
|
|
|
|
[#_what_happened_at_the_end]
|
|
=== Qué pasó al final
|
|
|
|
El contador llegó a 419, y luego volvió a ser 0 y el fuego regresó.
|
|
|
|
Cuando el disco se sale del final, el motor lo vuelve a empezar desde el
|
|
fotograma cero. No se para, y no te lo dice. Un juego de verdad nunca deja que
|
|
llegue ahí: vigila el número de fotograma y manda el disco a otro sitio antes de
|
|
que llegue el final. Olvidarse de hacerlo es la razón de que un juego de vídeo a
|
|
medio hacer reproduzca de repente su propia secuencia de título en mitad de una
|
|
pelea.
|
|
|
|
[#_stopping_jumping_and_asking]
|
|
=== Parar, saltar y preguntar
|
|
|
|
Añade esta función encima de `onOverlayUpdate`:
|
|
|
|
[source,lua]
|
|
----
|
|
function onInputPressed(what)
|
|
if what == SWITCH_BUTTON1 then
|
|
if discGetState() == DISC_PLAYING then
|
|
discPause()
|
|
else
|
|
discPlay()
|
|
end
|
|
elseif what == SWITCH_LEFT then
|
|
discSearch(0)
|
|
elseif what == SWITCH_RIGHT then
|
|
discSkipForward(30)
|
|
elseif what == SWITCH_START1 then
|
|
discStop()
|
|
end
|
|
end
|
|
----
|
|
|
|
Guarda y prueba cada una. La barra espaciadora reproduce y pausa. La izquierda
|
|
vuelve al principio. La derecha salta hacia delante más o menos un segundo. La
|
|
tecla `1` para el disco del todo, y lo que obtienes es una pantalla azul -- el
|
|
color que mostraba un reproductor de laserdisc cuando estaba parado, que se ha
|
|
conservado porque muchísimos juegos dibujaban encima su tabla de puntuaciones.
|
|
|
|
Ahí hay cinco llamadas nuevas, y se dividen en dos tipos.
|
|
|
|
*Preguntar.* `discGetState` devuelve uno de cuatro nombres que el motor ya ha
|
|
definido por ti: `DISC_PLAYING`, `DISC_PAUSED`, `DISC_STOPPED` y
|
|
`DISC_EJECTED`. Son como `OVERLAY_UPDATED` -- sin comillas, porque son nombres
|
|
de valores y no texto. Compáralos con `==` como comparabas números en la lección
|
|
tres.
|
|
|
|
*Ordenar.* `discPlay` empieza o reanuda. `discPause` se congela en el fotograma
|
|
que se está mostrando y lo deja en pantalla. `discStop` es la más contundente:
|
|
la imagen se sustituye por la pantalla azul, `discGetFrame` responde `0` hasta
|
|
que el disco vuelve a ponerse en marcha, y `discPause` y las llamadas de salto
|
|
se ignoran mientras está parado. Cualquiera de `discPlay`, `discSearch` o
|
|
`discSkipToFrame` lo saca de la parada.
|
|
|
|
Dos llamadas mandan el disco a un fotograma, y la diferencia entre ellas es lo
|
|
único de esta lección que vale la pena aprenderse de memoria:
|
|
|
|
* `discSearch(frame)` va allí y se *pausa* en él.
|
|
* `discSkipToFrame(frame)` va allí y *reproduce* desde él.
|
|
|
|
Busca cuando quieras aparcar en una imagen y esperar. Salta cuando quieras que
|
|
empiece una escena. `discSkipForward(30)` y `discSkipBackward(30)` son la misma
|
|
idea medida desde donde estás en lugar de desde el principio, y dejan el disco
|
|
reproduciéndose si se estaba reproduciendo y pausado si estaba pausado.
|
|
|
|
También existe `discPauseAtFrame`, que hace exactamente lo mismo que
|
|
`discSearch` con un nombre más antiguo. La referencia lo marca como alias heredado.
|
|
Lo verás en scripts de otras personas; en los tuyos, escribe `discSearch`.
|
|
|
|
[#_waiting_is_harder_than_it_sounds]
|
|
=== Esperar es más difícil de lo que parece
|
|
|
|
Esta es la frase de la que está hecho todo juego de vídeo:
|
|
|
|
____
|
|
Reproduce del fotograma 0 al fotograma 120 y después hazle una pregunta al
|
|
jugador.
|
|
____
|
|
|
|
Intenta escribirla. No puedes escribir «reproduce, después espera, después
|
|
pregunta», porque no tienes dónde escribirlo. Tienes `onOverlayUpdate`, a la que
|
|
Singe llama sesenta veces por segundo y que tiene que volver inmediatamente cada
|
|
una de esas veces. Una función que se sentara a esperar pararía el motor en
|
|
seco: ni dibujo, ni entrada, ni vídeo.
|
|
|
|
Así que no puedes decir «después». Tienes que convertir «después» en una
|
|
variable que recuerde por dónde vas, y comprobarla en cada una de esas sesenta
|
|
llamadas:
|
|
|
|
[source,lua]
|
|
----
|
|
local asking = false
|
|
|
|
|
|
function onOverlayUpdate()
|
|
overlayClear()
|
|
if not asking and discGetFrame() >= 120 then
|
|
discPause()
|
|
asking = true
|
|
end
|
|
if asking then
|
|
overlayPrint(2, 4, "Left or right?")
|
|
end
|
|
return OVERLAY_UPDATED
|
|
end
|
|
----
|
|
|
|
Eso funciona. Léelo y verás que funciona. Pero fíjate en lo que ha costado: una
|
|
variable, cuyo único trabajo es recordar en qué parte de la frase estás, y dos
|
|
`if` para mantenerla en orden.
|
|
|
|
Ahora añade el resto de la escena. El jugador responde, así que a continuación
|
|
se reproduce una de dos cosas. Cada una de ellas termina en algún sitio y hace
|
|
otra pregunta. Un juego con ramificaciones modesto tiene cuarenta de estos
|
|
momentos, y cada uno quiere su propia variable y su propio `if`. Acabas con un
|
|
script en el que la historia no está escrita en ninguna parte -- está repartida
|
|
entre una docena de indicadores, y para averiguar qué pasa después del dragón
|
|
tienes que leerlos todos.
|
|
|
|
Quédate con esa idea. La lección quince no es más que el arreglo para eso.
|
|
|
|
[#_when_there_is_no_video]
|
|
=== Cuando no hay vídeo
|
|
|
|
Vuelve a ejecutar tu script de la forma normal, sin `-v`:
|
|
|
|
----
|
|
Singe -R movie
|
|
----
|
|
|
|
No se cuelga nada. Te sale una ventana negra con `Frame 0` en la esquina.
|
|
|
|
Vale la pena entender esto, porque es la mitad tranquilizadora de la promesa del
|
|
principio de esta lección. Un juego sin disco sigue ejecutando cada llamada
|
|
`disc` que hagas; solo que no hacen nada útil. El motor documenta exactamente
|
|
qué responden, y eso nunca cambia: `discGetFrame` y `discGetFrameCount`
|
|
devuelven `0`, `discGetState` devuelve `DISC_EJECTED`, `discGetWidth` y
|
|
`discGetHeight` devuelven el tamaño del lienzo, y `discPlay`, `discPause`,
|
|
`discSearch` y las demás se apuntan en el registro y por lo demás se ignoran.
|
|
|
|
No puedes romper un script llamando a una función de disco cuando no hay disco.
|
|
|
|
Cuando un script necesita *saberlo*, pregunta a `SINGE_DISC`. Es otro nombre que
|
|
el motor define antes de que se ejecute tu script, y vale `true` cuando el juego
|
|
tiene vídeo y `false` cuando el mundo es un lienzo sin más. Es la forma correcta
|
|
de escribir un juego que pueda funcionar de las dos maneras:
|
|
|
|
[source,lua]
|
|
----
|
|
if not SINGE_DISC then
|
|
overlayPrint(2, 2, "This game has no video.")
|
|
overlayPrint(2, 4, "Run it again with -v Singe/menuBackground.mkv")
|
|
return OVERLAY_UPDATED
|
|
end
|
|
----
|
|
|
|
`not` le da la vuelta a un valor verdadero o falso, como en la lección tres.
|
|
|
|
[#_the_whole_thing_7]
|
|
=== Todo junto
|
|
|
|
Todo junto, con una pequeña función que convierte el estado en palabras y un
|
|
fotograma en el que detenerse, este es el script terminado. Es un buscador de
|
|
fotogramas: la herramienta que usarás cada vez que te encuentres con un vídeo
|
|
nuevo.
|
|
|
|
[source,lua]
|
|
----
|
|
dofile("Singe/Framework.singe")
|
|
|
|
local stopAt = 400
|
|
local held = false
|
|
|
|
|
|
local function stateName()
|
|
local state = discGetState()
|
|
|
|
if state == DISC_PLAYING then
|
|
return "playing"
|
|
elseif state == DISC_PAUSED then
|
|
return "paused"
|
|
elseif state == DISC_STOPPED then
|
|
return "stopped"
|
|
end
|
|
return "not there"
|
|
end
|
|
|
|
|
|
function onInputPressed(what)
|
|
if what == SWITCH_BUTTON1 then
|
|
if discGetState() == DISC_PLAYING then
|
|
discPause()
|
|
else
|
|
discPlay()
|
|
end
|
|
elseif what == SWITCH_LEFT then
|
|
discSearch(0)
|
|
held = false
|
|
elseif what == SWITCH_RIGHT then
|
|
discSkipForward(30)
|
|
elseif what == SWITCH_START1 then
|
|
discStop()
|
|
held = false
|
|
end
|
|
end
|
|
|
|
|
|
function onOverlayUpdate()
|
|
local frame = discGetFrame()
|
|
|
|
overlayClear()
|
|
if not SINGE_DISC then
|
|
overlayPrint(2, 2, "This game has no video.")
|
|
overlayPrint(2, 4, "Run it again with -v Singe/menuBackground.mkv")
|
|
return OVERLAY_UPDATED
|
|
end
|
|
|
|
if not held and frame >= stopAt then
|
|
discPause()
|
|
held = true
|
|
end
|
|
|
|
overlayPrint(2, 2, "Frame " .. frame .. " of " .. discGetFrameCount())
|
|
overlayPrint(2, 4, "The disc is " .. stateName() .. ".")
|
|
overlayPrint(2, 6, "Space plays and pauses. Left rewinds.")
|
|
overlayPrint(2, 7, "Right jumps on a second. 1 stops the disc.")
|
|
if held then
|
|
overlayPrint(2, 9, "Holding at frame " .. stopAt .. ".")
|
|
end
|
|
|
|
return OVERLAY_UPDATED
|
|
end
|
|
|
|
|
|
discPlay()
|
|
----
|
|
|
|
`held` es el mismo tipo de variable que `asking` más arriba, y está aquí por una
|
|
razón que puedes comprobar: quítala, deja `if frame >= stopAt then discPause()
|
|
end`, y luego intenta pulsar la barra espaciadora para seguir más allá del
|
|
fotograma 400. No puedes. El disco se reanuda durante una sexagésima de segundo
|
|
y el siguiente `onOverlayUpdate` lo vuelve a pausar, para siempre. `held` es la
|
|
forma de decir «esto ya lo he hecho una vez».
|
|
|
|
[#_try_it_14]
|
|
=== Pruébalo
|
|
|
|
. *Encuentra el dragón.* Ejecútalo, mira el contador y apunta el fotograma en el
|
|
que aparece el dragón por primera vez, el fotograma en el que echa fuego y el
|
|
fotograma en el que la cuadrícula se adueña de la pantalla. Querrás tener
|
|
estos tres números en la lección dieciséis.
|
|
. *Avanza paso a paso.* Cambia `discSkipForward(30)` por `discSkipForward(1)` y
|
|
mantén pulsada la flecha derecha. Ahora cambia `stopAt` a un fotograma en
|
|
mitad del dragón y usa `discSearch` y la tecla de flecha para caer exactamente
|
|
en él.
|
|
. *Haz que se repita.* Cambia la parada para que, en lugar de pausar en el
|
|
fotograma 400, llame a `discSkipToFrame(0)`. Has construido un modo de
|
|
atracción: una escena que se reproduce sin fin hasta que alguien pulsa un
|
|
botón.
|
|
. *Pide un fotograma que no existe.* Cambia la línea de `SWITCH_RIGHT` por
|
|
`discSkipToFrame(100000)`. No se cuelga. Lee la entrada de `discSkipToFrame`
|
|
en la referencia y busca la frase que dice por qué.
|
|
. *Juégalo sin el vídeo.* Ejecuta `Singe -R movie` y luego pulsa todas las
|
|
teclas. Nada de lo que pulses puede romperlo.
|
|
|
|
[#_break_it_on_purpose_13]
|
|
=== Rómpelo a propósito
|
|
|
|
El error que más tiempo cuesta aquí no es un error de Lua. Ejecuta esto:
|
|
|
|
----
|
|
Singe -R -v menuBackground.mkv movie
|
|
----
|
|
|
|
La ruta está mal -- se cuenta desde la carpeta de trabajo, y el vídeo está ahí
|
|
en la carpeta `Singe`, no suelto -- y Singe no arranca. Lo que hace en su lugar es imprimir su lista entera de
|
|
opciones de línea de comandos, lo que asusta la primera vez, y luego esto como
|
|
última línea:
|
|
|
|
----
|
|
Error: Unable to locate video.
|
|
----
|
|
|
|
Un muro de opciones así siempre significa lo mismo: el motor no llegó ni a tu
|
|
juego. Ignora el muro y lee la última línea.
|
|
|
|
Ese es fácil. La versión traicionera es ejecutarlo sin `-v` y olvidar que lo has
|
|
hecho. No hay ningún error, porque un juego sin vídeo es un juego perfectamente
|
|
bueno. Te sale una pantalla negra y un contador clavado en `0`, y te pasas
|
|
veinte minutos buscando el fallo en tu script.
|
|
|
|
Para eso está el bloque de `SINGE_DISC` del script terminado. Tres líneas, y el
|
|
juego te dice qué pasa en lugar de enfurruñarse. Pon una comprobación así en
|
|
cada script que escribas que necesite un vídeo.
|
|
|
|
[#_what_you_learned_14]
|
|
=== Lo que has aprendido
|
|
|
|
* Un juego solo reproduce vídeo cuando lo pide, y todo lo demás de Singe
|
|
funciona igual en los dos casos.
|
|
* Singe llama al vídeo el disco. Un juego tiene uno, y las funciones `disc` no
|
|
reciben ningún handle porque no hay nada entre lo que elegir.
|
|
* El vídeo va en la línea de comandos con `-v`, no en el script.
|
|
* El overlay se dibuja sobre la imagen, y la película se ve allí donde no has
|
|
dibujado.
|
|
* Un fotograma es una imagen fija, se cuentan desde cero, y un número de
|
|
fotograma es la dirección de un momento.
|
|
* `discGetFrame` pregunta dónde está el disco; `discGetFrameCount` dice cuántos
|
|
fotogramas hay; `discGetState` dice qué está haciendo el disco.
|
|
* `discPlay`, `discPause` y `discStop` lo manejan. `discSearch` va a un
|
|
fotograma y pausa; `discSkipToFrame` va a un fotograma y reproduce.
|
|
* Un disco que se sale del final vuelve a empezar en el fotograma cero sin
|
|
decírtelo.
|
|
* Sin disco, toda llamada `disc` es inofensiva, y `SINGE_DISC` es como un script
|
|
sabe en qué tipo de juego está.
|
|
* No puedes esperar dentro de `onOverlayUpdate`, así que el «después» hay que
|
|
escribirlo como una variable.
|
|
|
|
[#_next_time_14]
|
|
=== La próxima vez
|
|
|
|
Ese último punto es el que duele. Un juego de vídeo es una lista de pasos --
|
|
reproduce esto, pregunta aquello, reproduce lo otro -- y acabas de ver que el
|
|
modelo de callbacks no puede decir «después» sin un indicador y un `if` para
|
|
cada paso. La lección quince te enseña la otra forma que te deja Singe de
|
|
escribir un script, en la que «después» es una línea nueva.
|