[LEZIONE 3] Controllo Oggetti e collisioni

« Older   Newer »
 
  Share  
.
  1. KiNgOfUnIvErS
        Like  
     
    .

    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.



    ---------------------------
     
    .
0 replies since 8/1/2008, 17:17   300 views
  Share  
.