Nel tutorial precedente di questa serie, abbiamo iniziato a implementare il gameplay del gioco e siamo già riusciti a far muovere l'aereo sullo schermo. In questo tutorial, continueremo ad implementare il gameplay. Facciamo un tuffo proprio con il startTimers
funzione.
startTimers
Come indica il nome, il startTimers
la funzione avvia i timer. Aggiungere il seguente codice a gamelevel.lua.
funzione startTimers () end
Invoca questa funzione nel enterScene
metodo come mostrato di seguito.
function scene: enterScene (evento) local planeSound = audio.loadStream ("planesound.mp3") planeSoundChannel = audio.play (planeSound, loops = -1) Runtime: addEventListener ("enterFrame", gameLoop) startTimers () fine
firePlayerBullet
Il firePlayerBullet
la funzione crea un proiettile per il giocatore.
function firePlayerBullet () local tempBullet = display.newImage ("bullet.png", (player.x + playerWidth / 2) - bulletWidth, player.y-bulletHeight) table.insert (playerBullets, tempBullet); planeGroup: inserire (tempBullet) end
Qui usiamo l'oggetto Display nuova immagine
metodo per creare il proiettile. Lo posizioniamo in modo tale che si trovi al centro del piano sull'asse x e nella parte superiore del piano sull'asse y. Il proiettile viene quindi inserito nel playerBullets
tavolo per riferimento futuro e anche nel planeGroup
.
firePlayerBullet
Dobbiamo chiamare il firePlayerBullet
funzione periodicamente per assicurarsi che l'aereo del giocatore stia sparando automaticamente proiettili. Aggiungi il seguente snippet di codice nel file startTimers
funzione.
function startTimers () firePlayerBulletTimer = timer.performWithDelay (2000, firePlayerBullet, -1) end
Come indica il nome, il timer performWithDelay
il metodo richiama una funzione specificata dopo che è trascorso un periodo di tempo. Il tempo è in millisecondi, quindi qui stiamo chiamando il firePlayerBullet
funzione ogni due secondi. Passando -1
come terzo argomento, il timer si ripeterà per sempre.
Se stai testando il gioco ora, dovresti vedere che ogni due secondi appare un proiettile. Tuttavia, non si stanno ancora muovendo. Ci prenderemo cura di ciò nei prossimi passi.
movePlayerBullets
Nel movePlayerBullets
, passiamo attraverso il playerBullets
tavolo e cambia il y
coordinata di ogni proiettile. Per prima cosa controlliamo per accertarci che playerBullets
la tabella contiene proiettili. Il #
prima playerBullets
è chiamato il lunghezza operatore e restituisce la lunghezza dell'oggetto su cui è chiamato. È utile sapere che il #
l'operatore lavora anche sulle stringhe.
function movePlayerBullets () if (#playerBullets> 0) quindi per i = 1, # playerBullets do playerBullets [i]. y = playerBullets [i] .y - 7 end end end
Dobbiamo invocare movePlayerBullets
nel gameLoop
funzione come mostrato di seguito.
function gameLoop () --SNIP-- numberOfTicks = numberOfTicks + 1 movePlayer () movePlayerBullets () end
checkPlayerBulletsOutOfBounds
Quando un proiettile va fuori campo, non è più rilevante per il gioco. Tuttavia, fanno ancora parte del playerBullets
tabella e continuare a muoversi come qualsiasi altro proiettile nella tabella. Questo è uno spreco di risorse e, se il gioco dovesse durare a lungo, risulterebbe in centinaia o migliaia di oggetti non utilizzati.
Per ovviare a ciò, monitoriamo i proiettili e, una volta spostati fuori dallo schermo, li rimuoviamo dal playerBullets
tavolo così come dal display. Dai un'occhiata all'implementazione di checkPlayerBulletsOutOfBounds
.
function checkPlayerBulletsOutOfBounds () if (#playerBullets> 0) quindi per i = # playerBullets, 1, -1 do if (playerBullets [i] .y < -18) then playerBullets[i]:removeSelf() playerBullets[i] = nil table.remove(playerBullets,i) end end end end
È importante notare che stiamo scorrendo attraverso il playerBullets
tavolo all'indietro. Se eseguiamo il ciclo di inoltro della tabella, quando rimuoviamo uno dei proiettili, l'indice di looping viene eliminato e causa un errore. Eseguendo il ciclo sulla tabella in ordine inverso, l'ultimo punto è già stato elaborato. Il removeSelf
metodo rimuove l'oggetto di visualizzazione e libera la sua memoria. Come best practice, dovresti impostare qualsiasi oggetto zero
dopo aver chiamato removeSelf
.
Invochiamo questa funzione nel gameLoop
funzione.
function gameLoop () --SNIP-- movePlayer () movePlayerBullets () checkPlayerBulletsOutOfBounds () end
Se vuoi vedere se questa funzione funziona correttamente, puoi inserire temporaneamente un print ("Rimozione del proiettile")
dichiarazione immediatamente dopo l'impostazione dell'oggetto di visualizzazione zero
.
generateIsland
Per rendere il gioco più interessante, generiamo un'isola ogni tanto, e spostiamolo lungo lo schermo per dare l'aspetto dell'aereo che sorvola le isole. Aggiungi il seguente snippet di codice per generateIsland
funzione.
function generateIsland () local tempIsland = display.newImage ("island1.png", math.random (0, display.contentWidth - islandWidth), - islandHeight) table.insert (isole, tempIsland) islandGroup: insert (tempIsland) end
Facciamo uso del nuova immagine
metodo ancora una volta e posizionare l'isola impostando un valore negativo per il islandHeight
. Per il X
posizione, usiamo il Math.random
metodo per generare un numero tra 0
e il display
'S contentWidth
meno il islandWidth
. Il motivo per cui sottrarre la larghezza dell'isola è assicurarsi che l'isola sia completamente sullo schermo. Se non volessimo sottrarre la larghezza dell'isola, ci sarebbe la possibilità che parte dell'isola non sia sullo schermo.
Abbiamo bisogno di avviare un timer per generare un'isola ogni tanto. Aggiungi il seguente snippet al startTimers
funzione che abbiamo creato in precedenza. Come puoi vedere, stiamo generando un'isola ognicinque secondi. Nel prossimo passo, faremo muovere le isole.
function startTimers () firePlayerBulletTimer = timer.performWithDelay (2000, firePlayerBullet, -1) generateIslandTimer = timer.performWithDelay (5000, generateIsland, -1) fine
moveIslands
L'implementazione di moveIslands
è quasi identico al movePlayerBullets
funzione. Controlliamo se il isole
il tavolo contiene alcune isole e, se lo fa, lo attraversiamo e spostiamo ogni isola un po '.
function moveIslands () if (#islands> 0) quindi per i = 1, #islands do isole [i] .y = isole [i] .y + 3 end end end
checkIslandsOutOfBounds
Proprio come controlliamo se i proiettili del giocatore sono stati spostati fuori dallo schermo, controlliamo se una delle isole si è spostata fuori dallo schermo. L'implementazione di checkIslandsOutOfBounds
dovrebbe quindi sembrarti familiare. Controlliamo se le isole y
la posizione è maggiore di display.contentHeight
e se lo è, sappiamo che l'isola è stata spostata fuori dallo schermo e può quindi essere rimossa.
function checkIslandsOutOfBounds () if (#islands> 0) quindi per i = # isole, 1, -1 do if (isole [i] .y> display.contentHeight) quindi isole [i]: removeSelf () isole [i] = nil table.remove (isole, i) fine fine fine
generateFreeLife
Ogni tanto, il giocatore ha la possibilità di ottenere una vita libera. Per prima cosa generiamo un'immagine di vita libera e se il giocatore collide con l'immagine ottiene una vita extra. Il giocatore può avere un massimo di sei vite.
function generateFreeLife () if (numberOfLives> = 6) quindi restituisce end local freeLife = display.newImage ("newlife.png", math.random (0, display.contentWidth - 40), 0); table.insert (freeLifes, freeLife) planeGroup: insert (freeLife) end
Se il giocatore ha già sei vite, non facciamo nulla ritornando presto dalla funzione. In caso contrario, creiamo una nuova immagine di vita e la aggiungiamo allo schermo. Simile a come abbiamo posizionato le isole in precedenza, abbiamo impostato l'immagine su un valore negativo y
posiziona e genera un valore casuale per l'immagine X
posizione. Quindi lo inseriamo nel freeLifes
tavolo per poterlo consultare più tardi.
Dobbiamo chiamare questa funzione ogni tanto. Aggiungi il seguente snippet al startTimers
funzione.
function startTimers () firePlayerBulletTimer = timer.performWithDelay (2000, firePlayerBullet, -1) generateIslandTimer = timer.performWithDelay (5000, generateIsland, -1) generateFreeLifeTimer = timer.performWithDelay (7000, generateFreeLife, - 1) fine
moveFreeLives
L'implementazione di moveFreeLifes
dovrebbe sembrare familiare Stiamo collegando il freeLifes
tabella e spostare ogni immagine in esso.
function moveFreeLifes () if (#freeLifes> 0) quindi per i = 1, # freeLifes do freeLifes [i] .y = freeLifes [i] .y +5 end end end
Tutto ciò che dobbiamo fare è chiamare moveFreeLifes
nel gameLoop
funzione.
function gameLoop () --SNIP-- checkIslandsOutOfBounds () moveFreeLifes () fine
checkFreeLifesOutOfBounds
Il seguente frammento di codice dovrebbe anche sembrarti familiare. Controlliamo se una qualsiasi delle immagini nel freeLifes
il tavolo si è spostato fuori dallo schermo e rimuove quelli che lo hanno.
function checkFreeLifesOutOfBounds () if (#freeLifes> 0) quindi per i = # freeLifes, 1, -1 do if (freeLifes [i] .y> display.contentHeight) quindi freeLifes [i]: removeSelf () freeLifes [i] = nil table.remove (freeLifes, i) end end end end
Chiamiamo questa funzione nel gameLoop
funzione.
function gameLoop () --SNIP-- checkIslandsOutOfBounds () moveFreeLifes () checkFreeLifesOutOfBounds () fine
hasCollided
Dobbiamo essere in grado di dire quando gli oggetti del gioco si scontrano tra di loro, come il piano del giocatore e le immagini della vita libera, i proiettili e gli aerei, ecc. Mentre Corona offre un motore fisico molto robusto che può gestire facilmente le collisioni tra gli oggetti di visualizzazione per noi, fare così aggiunge un po 'di overhead con i calcoli che il motore deve fare ogni frame.
Ai fini di questo gioco, utilizzeremo un semplice sistema di rilevamento delle collisioni. Che cosa fa questa funzione, assicurati che i rettangoli o le caselle di delimitazione attorno a due oggetti non si sovrappongano. Se lo fanno, gli oggetti si scontrano. Questa logica è implementata nel hasCollided
funzione.
function hasCollided (obj1, obj2) if (obj1 == nil) quindi restituisce false end if (obj2 == nil) quindi restituisce false end local left = obj1.contentBounds.xMin <= obj2.contentBounds.xMin and obj1.contentBounds.xMax >= obj2.contentBounds.xMin right locale = obj1.contentBounds.xMin> = obj2.contentBounds.xMin e obj1.contentBounds.xMin <= obj2.contentBounds.xMax local up = obj1.contentBounds.yMin <= obj2.contentBounds.yMin and obj1.contentBounds.yMax >= obj2.contentBounds.yMin local down = obj1.contentBounds.yMin> = obj2.contentBounds.yMin e obj1.contentBounds.yMin <= obj2.contentBounds.yMax return (left or right) and (up or down) end
Ho trovato questo snippet di codice sul sito web di CoronaLabs. Funziona molto bene, perché gli oggetti del gioco nel nostro gioco sono rettangolari. Se stai lavorando con oggetti che non sono rettangolari, allora è meglio sfruttare il motore fisico di Corona poiché il rilevamento delle collisioni è ottimizzato molto bene per questo.
checkPlayerCollidesWithFreeLife
Vogliamo verificare se l'aereo del giocatore si è scontrato con un oggetto di vita libero. Se è così, assegniamo al giocatore una vita libera.
function checkPlayerCollidesWithFreeLife () if (#freeLifes> 0) quindi per i = # freeLifes, 1, -1 do if (hasCollided (freeLifes [i], player)) then freeLifes [i]: removeSelf () freeLifes [i] = nil table.remove (freeLifes, i) numberOfLives = numberOfLives + 1 hideLives () showLives () fine fine fine fine
Nel checkPlayerCollidesWithFreeLife
funzione, passiamo attraverso il freeLives
tabella indietro per la stessa ragione che ho descritto in precedenza. Chiamiamo il hasCollided
funzione e passa nell'immagine corrente e nel piano del giocatore. Se i due oggetti si scontrano, rimuoviamo l'immagine di vita libera, incrementiamo il numberOfLives
variabile e chiama il hideLives
e showLives
funzione.
Invochiamo questa funzione nel gameLoop
funzione.
function gameLoop () --SNIP-- moveFreeLifes () checkFreeLifesOutOfBounds () checkPlayerCollidesWithFreeLife () fine
hideLives
Il hideLives
la funzione scorre attraverso il livesImages
tavolo e imposta il è visibile
proprietà di ogni immagine di vita a falso
.
function hideLives () per i = 1, 6 do livesImages [i] .isVisible = false end end
showLives
Il showLives
la funzione scorre attraverso il livesImages
tabella e imposta ogni immagine è visibile
proprietà a vero
.
function showLives () per i = 1, numberOfLives do livesImages [i] .isVisible = true; fine fine
Ciò porta alla conclusione la terza parte di questa serie. Nella prossima e ultima puntata di questa serie, creeremo aerei nemici e finalizzeremo il gameplay del gioco. Grazie per aver letto e ci vediamo lì.