503 lines
19 KiB
Text
503 lines
19 KiB
Text
[#_lesson_14_playing_video]
|
|
== Lezione 14: Riprodurre video
|
|
|
|
image::learn/14-video.png[La lezione finita, 480]
|
|
|
|
Singe sa riprodurre un filmato e far girare il tuo gioco al suo interno.
|
|
L'immagine diventa il mondo, e l'overlay su cui disegni dalla prima lezione
|
|
viene steso sopra. In questa lezione avvierai un video, scoprirai quale
|
|
fotogramma si vede, lo fermerai e salterai a qualunque momento ti piaccia.
|
|
|
|
Prima di tutto questo, c'è una cosa da dire chiaramente, perché decide se devi
|
|
leggere questa parte del libro.
|
|
|
|
[#_a_game_plays_video_only_if_it_asks_to]
|
|
=== Un gioco riproduce video solo se lo chiede
|
|
|
|
Singe è un motore di gioco. Tutto ciò che hai costruito nella prima e nella
|
|
seconda parte -- gli sprite, il suono, le collisioni, il punteggio, la cartella
|
|
del gioco -- è Singe per intero, e niente di tutto ciò riguarda il video. Quello
|
|
che la terza parte aggiunge è un extra: un gioco può chiedere che dietro di lui
|
|
scorra un filmato, e allora il mondo è il filmato invece di un canvas nero.
|
|
|
|
Non cambia nient'altro. `onOverlayUpdate` viene ancora chiamata sessanta volte
|
|
al secondo. `overlayPrint` conta ancora in celle di carattere. I tuoi sprite si
|
|
disegnano dove si disegnavano prima. Un gioco con il video e uno senza si
|
|
scrivono allo stesso modo, con le stesse funzioni, e condividono lo stesso
|
|
libro.
|
|
|
|
Quindi se il gioco che vuoi fare non ha video, puoi chiudere adesso questa parte
|
|
e passare alla quarta. Niente nella quarta parte dipende da qualcosa della
|
|
terza, e le uniche lezioni che hanno bisogno di questa sono le quattro che la
|
|
seguono.
|
|
|
|
Ancora qui? Bene.
|
|
|
|
[#_you_already_have_a_video]
|
|
=== Hai già un video
|
|
|
|
Non devi trovarne uno, comprarne uno o farne uno. La prima volta che hai
|
|
eseguito Singe nella prima lezione, nella tua cartella di lavoro è comparsa una
|
|
cartella chiamata `Singe`. Aprila e guarda: tra i file che il motore ha estratto
|
|
c'è `menuBackground.mkv`. È lo sfondo che usa il menu del motore stesso, è di
|
|
720 per 480 pixel, ed è lungo 420 fotogrammi -- circa quattordici secondi.
|
|
|
|
È il video di questa lezione e delle due successive.
|
|
|
|
Crea una cartella chiamata `movie` nella tua cartella di lavoro, accanto alle
|
|
cartelle delle altre lezioni, e dentro un file chiamato `movie.singe`. Non c'è
|
|
nient'altro da preparare. Avvii Singe dalla cartella di lavoro, come fai dalla
|
|
prima lezione, quindi la cartella `Singe`, video compreso, è proprio lì accanto
|
|
al tuo gioco, e `Singe/menuBackground.mkv` è il nome che fra un momento darai al
|
|
motore. Non c'è niente da copiare.
|
|
|
|
[#_type_this_in_2]
|
|
=== Scrivi questo
|
|
|
|
[source,lua]
|
|
----
|
|
dofile("Singe/Framework.singe")
|
|
|
|
|
|
function onOverlayUpdate()
|
|
overlayClear()
|
|
overlayPrint(2, 2, "Frame " .. discGetFrame())
|
|
return OVERLAY_UPDATED
|
|
end
|
|
|
|
|
|
discPlay()
|
|
----
|
|
|
|
Salvalo.
|
|
|
|
[#_run_it_2]
|
|
=== Avvialo
|
|
|
|
Il video non fa parte del tuo script, quindi non lo carichi nel tuo script. Lo
|
|
passi al motore sulla riga di comando, con `-v`:
|
|
|
|
----
|
|
Singe -R -v Singe/menuBackground.mkv movie
|
|
----
|
|
|
|
`-v` è l'abbreviazione di `--framefile`, e indica il video che il gioco deve
|
|
riprodurre. Il nome del tuo script viene per ultimo, come sempre. `-R` è lo
|
|
stesso `-R` che usi dalla prima lezione, e fa ancora ripartire il gioco quando
|
|
salvi.
|
|
|
|
Il senso di `-v` sta tutto nel chiedere. Se metti un file video accanto al tuo
|
|
script e non dici niente, il motore se ne accorge, lo dice sulla console ed
|
|
esegue il tuo gioco senza. Un gioco è un gioco video solo quando qualcuno dice
|
|
che lo è.
|
|
|
|
Si apre una finestra. Una palla di fuoco arancione la riempie, dal fuoco prende
|
|
forma un drago, sotto compare la parola `SINGE`, e poi una griglia magenta
|
|
scorre via sotto un sole arancione. Nell'angolo in alto a sinistra, sopra tutto
|
|
quanto, c'è un numero che sale.
|
|
|
|
Guardalo fino alla fine. Poi guarda che cosa succede alla fine.
|
|
|
|
[#_what_just_happened_10]
|
|
=== Cos'è appena successo
|
|
|
|
[source,lua]
|
|
----
|
|
discPlay()
|
|
----
|
|
|
|
Singe chiama il video *il disco*, e ogni funzione che lo comanda comincia con
|
|
`disc`. Il nome è storico: il motore è nato facendo girare giochi che tenevano
|
|
il loro filmato su un laserdisc, e il nome è rimasto attaccato all'idea. È un
|
|
file video sul tuo disco rigido e niente di più.
|
|
|
|
Un gioco ha al massimo un disco, quindi nessuna delle funzioni `disc` prende un
|
|
argomento che dica quale. Ce n'è uno solo.
|
|
|
|
Questa riga non sta dentro nessuna funzione. Sta in fondo al file, da sola, e
|
|
viene eseguita una volta quando Singe carica il tuo script -- prima del primo
|
|
`onOverlayUpdate`, prima di qualunque altra cosa. Singe non ha una callback
|
|
`onStartup` perché non gliene serve una: uno script Lua *è* un elenco di
|
|
istruzioni, e tutto ciò che scrivi fuori da una funzione avviene mentre il file
|
|
viene letto. Per convenzione, il codice di avvio va in fondo, sotto le funzioni.
|
|
|
|
Ti serve `discPlay()` perché il disco non parte da solo. Quando il gioco
|
|
comincia, il motore parcheggia il disco sul fotogramma uno e lo mette in pausa
|
|
lì. Non si muove niente finché non lo chiedi.
|
|
|
|
[source,lua]
|
|
----
|
|
overlayPrint(2, 2, "Frame " .. discGetFrame())
|
|
----
|
|
|
|
`discGetFrame` risponde a una domanda: quale fotogramma si vede in questo
|
|
momento? Non prende argomenti e restituisce un numero.
|
|
|
|
Nota dove viene disegnato quel numero. Colonna 2, riga 2 -- esattamente dove
|
|
andava `Hello!` nella prima lezione, e finisce nello stesso punto dello
|
|
schermo. L'overlay su cui disegni è grande la metà del video su ogni lato, cioè
|
|
360 per 240 per un video di 720 per 480, che è esattamente la dimensione su cui
|
|
disegni da tredici lezioni. Le tue coordinate non si sono
|
|
spostate. Se un giorno riproduci un video di un'altra dimensione, l'overlay
|
|
cambia dimensione con lui, e `overlaySetResolution` lo imposta a quello che
|
|
vuoi; la voce nel Riferimento ha i dettagli.
|
|
|
|
È tutta qui l'idea dell'overlay. Il video sta sotto. Il tuo disegno sta sopra, e
|
|
dovunque non hai disegnato si vede il filmato. Ecco perché `overlayClear`
|
|
adesso conta ancora più di prima: pulisci l'overlay e stai guardando il
|
|
filmato.
|
|
|
|
[#_a_frame_is_an_address]
|
|
=== Un fotogramma è un indirizzo
|
|
|
|
Il numero che `discGetFrame` restituisce è un numero di *fotogramma*. Un
|
|
fotogramma è un'immagine ferma. Questo video ne contiene 420 e ne mostra circa
|
|
trenta al secondo, quindi dura circa quattordici secondi.
|
|
|
|
I fotogrammi si contano da zero, quindi questo video ha i fotogrammi da 0 a
|
|
419. Il conteggio e l'ultimo numero non sono la stessa cosa, e questo frega
|
|
tutti una volta. `discGetFrameCount` ti dice il conteggio -- 420 -- e il
|
|
fotogramma più alto che puoi chiedere è uno di meno.
|
|
|
|
La cosa importante di un numero di fotogramma è che non cambia mai e non slitta
|
|
mai. Il fotogramma 110 è il momento in cui il drago sputa fuoco, in questa copia
|
|
del file, su questa macchina, oggi e l'anno prossimo. I secondi slittano, perché
|
|
un video non va sempre esattamente alla velocità che pensi. Un numero di
|
|
fotogramma è l'indirizzo di un momento, e gli indirizzi sono la materia di cui è
|
|
fatto un gioco video: annoti il fotogramma in cui compare il drago, e da lì in
|
|
poi puoi mandarci il filmato quando vuoi.
|
|
|
|
Trovare quei numeri è un lavoro che fai una volta per ogni video, e lo script
|
|
che hai scritto è lo strumento per farlo: fai andare il filmato, guarda il
|
|
contatore, annota i fotogrammi in cui succedono le cose. Tutto ciò che c'è nella
|
|
sedicesima lezione è costruito su un elenco di numeri trovati esattamente così.
|
|
|
|
[#_what_happened_at_the_end]
|
|
=== Che cosa è successo alla fine
|
|
|
|
Il contatore è arrivato a 419, poi è tornato a 0 e il fuoco è ricomparso.
|
|
|
|
Quando il disco arriva in fondo, il motore lo fa ripartire dal fotogramma zero.
|
|
Non si ferma, e non te lo dice. Un vero gioco non lo lascia mai arrivare lì:
|
|
tiene d'occhio il numero del fotogramma e manda il disco da un'altra parte prima
|
|
che arrivi la fine. Dimenticarsene è il motivo per cui un gioco video finito a
|
|
metà all'improvviso riproduce la propria sequenza dei titoli nel bel mezzo di un
|
|
combattimento.
|
|
|
|
[#_stopping_jumping_and_asking]
|
|
=== Fermare, saltare e chiedere
|
|
|
|
Aggiungi questa funzione sopra `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
|
|
----
|
|
|
|
Salva, e provali uno per uno. La barra spaziatrice avvia e mette in pausa.
|
|
Sinistra torna all'inizio. Destra salta avanti di circa un secondo. Il tasto `1`
|
|
ferma del tutto il disco, e quello che ottieni è una schermata blu -- il colore
|
|
che mostrava un lettore laserdisc quando era fermo, mantenuto perché moltissimi
|
|
giochi ci disegnavano sopra la tabella dei punteggi.
|
|
|
|
Lì ci sono cinque nuove chiamate, e si dividono in due tipi.
|
|
|
|
*Chiedere.* `discGetState` restituisce uno di quattro nomi che il motore ha già
|
|
definito per te: `DISC_PLAYING`, `DISC_PAUSED`, `DISC_STOPPED` e
|
|
`DISC_EJECTED`. Sono come `OVERLAY_UPDATED` -- niente virgolette, perché sono
|
|
nomi di valori e non testo. Confrontali con `==` come confrontavi i numeri nella
|
|
terza lezione.
|
|
|
|
*Comandare.* `discPlay` avvia o riprende. `discPause` si blocca sul fotogramma
|
|
che si vede e lo lascia sullo schermo. `discStop` è quella più pesante:
|
|
l'immagine viene sostituita dalla schermata blu, `discGetFrame` risponde `0`
|
|
finché il disco non riparte, e `discPause` e le chiamate di salto vengono
|
|
ignorate finché è fermo. Una qualunque tra `discPlay`, `discSearch` e
|
|
`discSkipToFrame` lo rimette in moto.
|
|
|
|
Due chiamate mandano il disco a un fotogramma, e la differenza tra le due è
|
|
l'unica cosa di questa lezione che valga la pena imparare a memoria:
|
|
|
|
* `discSearch(frame)` ci va e lì si *mette in pausa*.
|
|
* `discSkipToFrame(frame)` ci va e da lì *riproduce*.
|
|
|
|
Cerca quando vuoi parcheggiare su un'immagine e aspettare. Salta quando vuoi
|
|
che cominci una scena. `discSkipForward(30)` e `discSkipBackward(30)` sono la
|
|
stessa idea misurata da dove sei invece che dall'inizio, e lasciano il disco in
|
|
riproduzione se era in riproduzione e in pausa se era in pausa.
|
|
|
|
C'è anche `discPauseAtFrame`, che fa esattamente quello che fa `discSearch` con
|
|
un nome più vecchio. Il Riferimento lo segna come alias storico. Lo vedrai negli
|
|
script degli altri; nei tuoi scrivi `discSearch`.
|
|
|
|
[#_waiting_is_harder_than_it_sounds]
|
|
=== Aspettare è più difficile di quanto sembri
|
|
|
|
Ecco la frase di cui è fatto ogni gioco video:
|
|
|
|
____
|
|
Riproduci dal fotogramma 0 al fotogramma 120, poi fai una domanda al giocatore.
|
|
____
|
|
|
|
Prova a scriverla. Non puoi scrivere «riproduci, poi aspetta, poi chiedi»,
|
|
perché non hai nessun posto dove scriverlo. Hai `onOverlayUpdate`, che Singe
|
|
chiama sessanta volte al secondo e che deve tornare subito, ogni singola volta.
|
|
Una funzione che se ne stesse lì ad aspettare fermerebbe il motore di colpo:
|
|
niente disegno, niente comandi, niente video.
|
|
|
|
Quindi non puoi dire «poi». Devi trasformare il «poi» in una variabile che
|
|
ricorda a che punto sei arrivato, e controllarla in ognuna di quelle sessanta
|
|
chiamate:
|
|
|
|
[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
|
|
----
|
|
|
|
Funziona. Leggilo e vedi che funziona. Ma nota quanto è costato: una variabile,
|
|
il cui unico lavoro è ricordare in quale parte della frase ti trovi, e due
|
|
istruzioni `if` per tenerla in riga.
|
|
|
|
Ora aggiungi il resto della scena. Il giocatore risponde, quindi dopo parte una
|
|
di due cose. Ognuna di queste finisce da qualche parte e fa un'altra domanda. Un
|
|
modesto gioco a diramazioni ha quaranta di questi momenti, e ognuno vuole la sua
|
|
variabile e il suo `if`. Finisci con uno script in cui la storia non è scritta
|
|
da nessuna parte -- è sparpagliata su una dozzina di flag, e per scoprire che
|
|
cosa succede dopo il drago devi leggerli tutti.
|
|
|
|
Tieni a mente questo pensiero. La quindicesima lezione non è nient'altro che la
|
|
soluzione.
|
|
|
|
[#_when_there_is_no_video]
|
|
=== Quando non c'è il video
|
|
|
|
Esegui di nuovo il tuo script nel modo semplice, senza `-v`:
|
|
|
|
----
|
|
Singe -R movie
|
|
----
|
|
|
|
Non va in crash niente. Ottieni una finestra nera con `Frame 0` nell'angolo.
|
|
|
|
Vale la pena capirlo, perché è la metà rassicurante della promessa all'inizio di
|
|
questa lezione. Un gioco senza disco esegue comunque ogni chiamata `disc` che
|
|
fai; semplicemente non fanno niente di utile. Il motore documenta esattamente
|
|
che cosa rispondono, e non cambia mai: `discGetFrame` e `discGetFrameCount`
|
|
restituiscono `0`, `discGetState` restituisce `DISC_EJECTED`, `discGetWidth` e
|
|
`discGetHeight` restituiscono la dimensione del canvas, e `discPlay`,
|
|
`discPause`, `discSearch` e le altre vengono annotate nel log e per il resto
|
|
ignorate.
|
|
|
|
Non puoi rompere uno script chiamando una funzione del disco quando il disco non
|
|
c'è.
|
|
|
|
Quando uno script ha bisogno di *saperlo*, chiede a `SINGE_DISC`. È un altro
|
|
nome che il motore definisce prima che parta il tuo script, e vale `true` quando
|
|
il gioco ha un video e `false` quando il mondo è un semplice canvas. È il modo
|
|
giusto di scrivere un gioco che possa funzionare in tutti e due i modi:
|
|
|
|
[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` capovolge un valore vero o falso, come nella terza lezione.
|
|
|
|
[#_the_whole_thing_7]
|
|
=== Tutto insieme
|
|
|
|
Messo insieme, con una piccola funzione che trasforma lo stato in parole e un
|
|
fotogramma su cui fermarsi, questo è lo script finito. È un cercatore di
|
|
fotogrammi: lo strumento che userai ogni volta che incontri un nuovo video.
|
|
|
|
[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` è lo stesso tipo di variabile di `asking` qui sopra, ed è qui per un
|
|
motivo che puoi verificare: toglila, lascia `if frame >= stopAt then discPause()
|
|
end`, e poi prova a premere la barra spaziatrice per andare oltre il fotogramma
|
|
400. Non ci riesci. Il disco riparte per un sessantesimo di secondo e il
|
|
`onOverlayUpdate` successivo lo rimette in pausa, per sempre. `held` è il modo
|
|
in cui dici «questo l'ho già fatto una volta».
|
|
|
|
[#_try_it_14]
|
|
=== Prova tu
|
|
|
|
. *Trova il drago.* Eseguilo, guarda il contatore e annota il fotogramma in cui
|
|
il drago compare per la prima volta, quello in cui sputa fuoco e quello in cui
|
|
la griglia si prende lo schermo. Questi tre numeri ti serviranno nella
|
|
sedicesima lezione.
|
|
. *Procedi un passo alla volta.* Cambia `discSkipForward(30)` in
|
|
`discSkipForward(1)` e tieni premuto il tasto freccia destra. Ora cambia
|
|
`stopAt` in un fotogramma a metà del drago e usa `discSearch` e il tasto
|
|
freccia per atterrarci esattamente.
|
|
. *Fallo ripetere.* Cambia la fermata in modo che invece di mettere in pausa al
|
|
fotogramma 400 chiami `discSkipToFrame(0)`. Hai costruito una modalità
|
|
attract: una scena che si ripete per sempre finché qualcuno non preme un
|
|
pulsante.
|
|
. *Chiedi un fotogramma che non c'è.* Cambia la riga di `SWITCH_RIGHT` in
|
|
`discSkipToFrame(100000)`. Non va in crash. Leggi la voce di
|
|
`discSkipToFrame` nel Riferimento e trova la frase che dice perché.
|
|
. *Giocaci senza il video.* Esegui `Singe -R movie` e poi premi tutti i tasti.
|
|
Niente di quello che premi può romperlo.
|
|
|
|
[#_break_it_on_purpose_13]
|
|
=== Rompilo apposta
|
|
|
|
L'errore che costa più tempo qui non è un errore di Lua. Esegui questo:
|
|
|
|
----
|
|
Singe -R -v menuBackground.mkv movie
|
|
----
|
|
|
|
Il percorso è sbagliato -- parte dalla cartella di lavoro, e lì il video sta
|
|
nella cartella `Singe`, non in giro da solo -- e Singe non parte. Quello che fa invece è stampare l'intero elenco
|
|
delle sue opzioni da riga di comando, cosa che la prima volta mette in allarme,
|
|
e poi questa come ultimissima riga:
|
|
|
|
----
|
|
Error: Unable to locate video.
|
|
----
|
|
|
|
Un muro di opzioni come quello vuol dire sempre la stessa cosa: il motore non è
|
|
mai arrivato fino al tuo gioco. Ignora il muro e leggi l'ultima riga.
|
|
|
|
Quello è facile. La versione meno simpatica è eseguire senza nessun `-v` e
|
|
dimenticarsi di averlo fatto. Non c'è nessun errore, perché un gioco senza video
|
|
è un gioco perfettamente valido. Ottieni uno schermo nero e un contatore fermo
|
|
su `0`, e passi venti minuti a cercare il bug nel tuo script.
|
|
|
|
È a questo che serve il blocco `SINGE_DISC` nello script finito. Tre righe, e il
|
|
gioco ti dice che cosa non va invece di tenere il muso. Metti un controllo come
|
|
quello in ogni script che scrivi e che ha bisogno di un video.
|
|
|
|
[#_what_you_learned_14]
|
|
=== Cosa hai imparato
|
|
|
|
* Un gioco riproduce video solo quando lo chiede, e tutto il resto di Singe
|
|
funziona allo stesso modo in entrambi i casi.
|
|
* Singe chiama il video il disco. Un gioco ne ha uno, e le funzioni `disc` non
|
|
prendono un handle perché non c'è niente tra cui scegliere.
|
|
* Il video va sulla riga di comando con `-v`, non nello script.
|
|
* L'overlay viene disegnato sopra l'immagine, e il filmato si vede dovunque non
|
|
hai disegnato.
|
|
* Un fotogramma è un'immagine ferma, contata da zero, e un numero di fotogramma
|
|
è l'indirizzo di un momento.
|
|
* `discGetFrame` chiede dove si trova il disco; `discGetFrameCount` dice quanti
|
|
fotogrammi ci sono; `discGetState` dice che cosa sta facendo il disco.
|
|
* `discPlay`, `discPause` e `discStop` lo comandano. `discSearch` va a un
|
|
fotogramma e si mette in pausa; `discSkipToFrame` va a un fotogramma e
|
|
riproduce.
|
|
* Un disco che arriva in fondo riparte dal fotogramma zero senza dirtelo.
|
|
* Senza disco ogni chiamata `disc` è innocua, e `SINGE_DISC` è il modo in cui
|
|
uno script capisce in che tipo di gioco si trova.
|
|
* Non puoi aspettare dentro `onOverlayUpdate`, quindi il «poi» deve essere
|
|
scritto per esteso, come variabile.
|
|
|
|
[#_next_time_14]
|
|
=== La prossima volta
|
|
|
|
Quell'ultimo punto è quello dolente. Un gioco video è un elenco di passi --
|
|
riproduci questo, chiedi quello, riproduci l'altro -- e hai appena visto che il
|
|
modello a callback non sa dire «poi» senza un flag e un `if` per ogni passo. La
|
|
quindicesima lezione ti mostra l'altro modo in cui Singe ti lascia scrivere uno
|
|
script, in cui «poi» è una riga nuova.
|