-
KiNgOfUnIvErS.
User deleted
Nel precedente tutorial avevamo visto come animare un elemento.
Un elemento animato può essere,nel nostro programma/gioco un cursore,un icona,un missile che parte dalla nostra navicella o altro.
Finora abbiamo visto come rendere animato un oggetto,questa volta vedremo come prenderne il controllo e farla interagire con gli altri oggetti a schermo.vedremo anche come creare un semplice motore asincrono che gestisca molti oggetti animati in modo indipendente.
Partiamo con questo programma che ha il doppio scopo di introdurre all'uso delle funzioni per ordinare il codice e quello di mostrare come si prende il controllo di un elemento a schermo e come si creano interazioni tra i vari elementi.
Questo semplice gioco prevede infatti di muovere una X bianca per lo schermo tramite il D-pad della psp.l'interattività che andiamo a cercare è costituita dal fatto che possiamo muovere la nostra X bianca SOLO all'interno dello schermo (nei limiti 280,270 pixels),e qualora questa passi sopra il nemico (una E rossa) mostrare a schermo una scritta.
Vediamo velocemente il codice.
-- inizializzo le variabili
--colori
green = Color.new(0, 255, 0)
red= Color.new(255,0, 0)
white = Color.new(255,255,255)
--variabili scritte
tempo_scritta=0
--varibili di moto
xspeed=4
yspeed=4
x=10
y=10
--posizione nemico
ex=math.random(470)
ey=math.random(260)
---funzioni
function collide()
if (x>=ex-5 and x<=ex+5 and y>=ey-5 and y<=ey+5) then
tempo_scritta=30
return true
end
end
function limiti_bordi()
if x>=470 then
x=470
end
if x<=0 then
x=0
end
if y>=260 then
y=260
end
if y<=0 then
y=0
end
end
function disegna()
--disegna il cursore
screen:print(x, y,"X", white)
--disegna l'obiettivo
screen:print(ex, ey,"O", red)
end
function informazioni()
--se la collisione avviene o è appena avvenuta
if (collide() or tempo_scritta>0) then
screen:print(0, 15,"COLPITO!", red)
tempo_scritta=tempo_scritta-1
end
end
-- ciclo principale
while true do
screen:clear()
disegna()
informazioni()
pad = Controls.read()
if pad:start() then
break
end
if pad:left() then
x=x-xspeed
end
if pad:right() then
x=x+xspeed
end
if pad:up() then
y=y-yspeed
end
if pad:down() then
y=y+yspeed
end
limiti_bordi()
screen.waitVblankStart()
screen.flip()
end
Nel ciclo principale:
while true do
screen:clear()
disegna()
[...]
Come si può vedere anche dal primo codice mostrato,nel ciclo principale tutte le azioni del programma sono raggruppate in funzioni, come:
disegna()
informazioni()
..
limiti_bordi()
Il vantaggio di raggruppare le attivita pricipali in funzioni stà nel fatto, oltre che al semplice guadagno in termini di ordine e pulizia del codice, che la stessa sequenza di operazioni può essere richiamata in più parti del codice senza dover necessariamente essere riscritta.
Nel caso del nostro semplice programma mettiamo in sequenza le operazioni base da eseguire ad ogni iterazione del ciclo principale.
in parole povere quello che serve per eseguire il tutto è:
-disegnare a schermo la X e la E
-mostrare le informazioni (colpito o no?)
-leggere i controlli e atturare azioni relative
-limitare le azioni (limiti bordi,collisioni)
E' chiaro che questa divisione interna è arbitraria,e poteva essere arrangiata in modi diversi a secondo di che azioni andavamo a delegare ad ogni function,oppure a quante function volevamo gestire.
l'ideale per una corretta frammentazione delle operazioni è un rapporto di codice che tende,anche a seconda della applicazione,a rendere flessibile quanto basta il codice,senza necessariamente frammentarlo troppo,ma ricondurlo ad azioni base.
Ma torniamo all'obiettivo del tutorial.
Per controllare la nostra X bianca,dobbiamo intercettare i controlli. cioò avviene nel ciclo principale,quando per ogni direzione del d-pad che viene premuta,viene eseguita una azione sulle coordinate della X bianca.
vediamone il codice relativo:
pad = Controls.read()
if pad:start() then
break
end
if pad:left() then
x=x-xspeed
end
if pad:right() then
x=x+xspeed
end
if pad:up() then
y=y-yspeed
end
if pad:down() then
y=y+yspeed
end
Come consueto,chiamando
pad = Controls.read()
creiamo l'oggetto pad,che contiene la lista dei controlli premuti.
if pad:left() then
x=x-xspeed
end
il codice qui sopra per esempio chiede se è vero che pad:left() è TRUE. tale funzione(che è una funzione riservata del lua,non programmata da noi)è una funzione booleana.
in parole povere risponde alla logica binaria del vero o del falso.se il tasto left (sinistra) è stato premuto ,allora tale funzione ritornerà VERO (TRUE).
altri modi per scrivere la stessa cosa sono
if pad:left()==1 then [...]
if pad:left()==true then [...]
if pad:left()~=0 then [...]
if pad:left()~=false then [...]
Ovvero confermare altre forme della stessa funzione o negarne l'opposto (~= significa "diverso da"),ma la forma più semplice e immediata rimane quella usata nel codice,sebbene sia comunque necessario sapere i significati di TRUE,FALSE 0 e 1.
Nel momento in cui premiamo il tasto direzionale sinistro,la funzione pad:left() diventerà TRUE,e la condizione sarà soddisfatta,facendo spostare la variabile orizzontale x della nostra "X" bianca.
x=x-xspeed
ad x viene assegnato il valore di sè stessa meno xspeed.
xspeed è una variabile che manteniamo fissa durante tutto il programma.
è assegnata uguale a 4 nell'inizializzazioni delle variabili,in cima al codice.
indica la velocità orizzontale che avrà la nostra "X" bianca quando si sposterà a sinistra.
è negativa,perchè deve diminuire la x delle coordinate,ovvero spostarsi a sinistra.
Stessa cosa avviene con pad:right(), ma qui
xspeed è positiva perchè la variabile x deve aumentare e spostare la nostra "X" bianca a destra.
Per quanto riguarda pad:up() e pad:down() restituiscono vero se viene premuto rispettivamente il tasto direzionale SU o GIU.
se viene premuto SU, pad:up() sarà vera, e la variabile Y sarà diminuita del valore yspeed, e quindi la nostra "X" bianca tornerà verso l'alto dello schermo,dove appunto la variabile Y è minore.
yspeed e yspeed ,che hanno valore fisso 4,indicano in pratica quanto sarà da togliere o aggiungere a x o y a seconda della direzione scelta sul pad.
In realtà non è necessario specificare due diverse variabili di velocità per la x e la x,ma introducendo xspeed e yspeed si più fare in modo ad esempio che il movimento verticale sia più lento di quello orizzontale.questo chiaramente in un gioco può tornare utile,se per esempio dobbiamo fare in modo che una ipotetica astronave "faccia fatica" a salire e a scendere,mentre per muoversi di lato non abbia problemi.
ed ora,come anticipato prima,dopo che i controlli sono stati interpretati subentra il controllo della loro effettiva validità.
limiti_bordi()
viene chiamata la funzione "limiti_bordi()".
la funzione risiede al di fuori del ciclo principale e viene chiamata ad ogni giro.
il compito di questa funzione è controllare che la nostra "X" bianca non si possa spostare fuori dallo schermo,che risulterebbe nell'impossibilità di vederla.
per fare ciò controlla le sue variabili di posizione x e y,e come nella lezione precedente,dove facevamo rimbalzare il puntino sui bordi dello schermo,controlliamo se il bordo è stato raggiunto o superato.
function limiti_bordi()
if x>=470 then
x=470
end
if x<=0 then
x=0
end
if y>=260 then
y=260
end
if y<=0 then
y=0
end
end
semplicemente si controlla per ogni margine (destro sinistro,alto e basso) se una delle due variabili ha raggiunto il valore di limite (0 o 470 per la x , 0 e 260 per la y).
In caso uno dei margini sia raggiunto o superato allora la variabile che lo ha superato viene settata uguale al margine.
in questo modo la variabile non può superare quel valore,che in termini visivi si traduce nell'impossibilità di superare il bordo dello schermo.
il ciclo principale dopo questo controllo finisce e re-inizia da capo dopo aver refreshato lo schermo.
(mi scuso per il percorso ritroso che sto facendo nello spiegare il codice,ma saltare la prima parte mi ha permesso di lasciare a dopo la parte interessante,che miglioreremo negli esempi successivi)
le prime funzioni che troviamo nel giro dopo sono:
disegna()
informazioni()
disegna() è una funzione, che come dice il nome semplicemente disegna a schermo i due oggetti principali: la nostra "X" bianca e la "E" rossa.
Notare che la posizone del nemico è assegnata dalle variabili:
--posizione nemico
ex=math.random(470)
ey=math.random(260)
ex e ey (e sta per Enemy,Nemico , per differenziarla dalla x del "portagonista",la nostra "X" bianca controllata da noi)
sono due variabili il cui valore è assegnato a randoma ad ogni inizializzazione del programma.
la funzione math.random restituisce un
numero random compreso tra 0 e 1. se a questa funzione passiamo un parametro (tra parentesi)avremo una generazione casuale di un numero tra 0 e il numero dato.
nel caso di ex=math.random(470) chiediamo che il nemico "E" stia orizzontalmente tra 0 e 470.
nel caso di ex=math.random(470) chiediamo che il nemico "E" stia verticalmente tra 0 e 260.
nel complesso chiediamo semplicemente che il nemico sia all'interno dello schermo e non oltre il limite di 480*270 pixels.
E adesso veniamo alla parte interessante.
la prossima funzione che viene chiamata è
informazioni()
nonostante il nome innocuo è questa funzione che ci dirà se la E nemica è colpita dalla X bianca.
function informazioni()
--se la collisione avviene o è appena avvenuta
if (collide() or tempo_scritta>0) then
screen:print(0, 15,"COLPITO!", red)
tempo_scritta=tempo_scritta-1
end
end
chiamiamo la funzione collide().
come detto prima,if collide()dice in pratica: se [il contenuto della funzione] è vero,allora...
facciamo un salto a vedere collide(),per spiegare successivamente cosa avverrà nel programma se la funzione è vera.
function collide()
if (x>=ex-5 and x<=ex+5 and y>=ey-5 and y<=ey+5) then
tempo_scritta=30
return true
end
end
nell'If primario controlliamo che la x e la y
della nostra "X" bianca siano all'interno di un quadrato immaginario di 10 pixel di lato avente come centro la posizione del nemico.
perchè questo quadrato immaginario?
dal punto di vista visivo se la "X" bianca tocca la E (quindi stessa posizione)per noi "umani" è un colpo andato a segno.
dal punto di vista informatico/matematico,invece il colpo andrebbe a centro solo se le due coordinate sono identiche.ciò vuol dire che il giocatore dovrebbe riuscire a colpire non la E rossa di 3 millimetri,ma il pixel che corrisponde al centro,di circa 0,1 mm, per giunta con un altro pixel (il centro della nostra X bianca).Una cosa dle genere risulterebbe assai ardua.
Ecco quindi che diventa indispensabile creare uno spazio maggiore (quel quadrato immaginario,appunto) dove poter intercettare la collisione tra i due oggetti.
se la nostra X bianca ha coordiante che stanno all'interno di quel quadrato,allora abbiamo colpito la E.
tornando alla nostra funzione,se la condizione (la nostra x nel quadrato immaginario) è soddisfatta,allora
[...]then
tempo_scritta=30
return true
end
setto la variabile tempo_scritta=30 in modo che la scritta (o comunque l'evento che succederà dopo la collisione) duri 30 giri di loop principale.
inoltre eseguo:
Return True
che dà valore True (vero) alla nostra funzione.
questo TRUE, o tempo_scritta maggiore di 0 è quello che serve alla funzione informazioni() per entrare nel suo if principale.
if (collide() or tempo_scritta>0) then
screen:print(0, 15,"COLPITO!", red)
tempo_scritta=tempo_scritta-1
end
Se quindi la collisione avviene in questo momento oppure è avvenuta da meno di 30 cicli di programma (che dureranno circa 1 secondo a 30 frames al secondo)mostra a schermo la scritta "COLPITO" ,con colore rosso e in posizione 0,15.
Allo steso tempo diminuisce la variabile tempo_scritta,così che ad ogni giro diminuisca fino a risultare 0 e non innescare più la visualizzazione della scritta "COLPITO".
Nella prossima lezione vedremo come usare questo gioco base per costruirvi sopra un semplice sparatutto,gestendo quindi spari e collisioni tra oggetti diversi.
---------------------------.