Nella quarta e ultima parte di questa serie, continuiamo dove abbiamo lasciato nel tutorial precedente. Creeremo gli aerei nemici che il giocatore deve evitare o sparare, e creeremo anche un gioco sullo schermo.
generateEnemys
Il generateEnemys
la funzione genera un numero tra tre e Sette, e chiama il generateEnemyPlane
funzione ogni due secondi per molte volte numberOfEnemysToGenerate
è uguale a. Immettere il seguente frammento di codice su gamelevel.lua.
function generateEnemys () numberOfEnemysToGenerate = math.random (3,7) timer.performWithDelay (2000, generateEnemyPlane, numberOfEnemysToGenerate) end
Abbiamo anche bisogno di invocare questa funzione nel enterScene
metodo come mostrato di seguito.
function scene: enterScene (evento) --SNIP-- Runtime: addEventListener ("enterFrame", gameLoop) startTimers () generateEnemys () fine
Vediamo quale è l'implementazione di generateEnemyPlane
sembra.
generateEnemyPlane
Il generateEnemyPlane
la funzione genera un aereo nemico. Ci sono tre tipi di aerei nemici in questo gioco.
function generateEnemyPlane () if (gameOver ~ = true) then randomGridSpace = math.random (11) local randomEnemyNumber = math.random (3) local tempEnemy if (planeGrid [randomGridSpace] ~ = 0) then generateEnemyPlane () restituisce else if ( randomEnemyNumber == 1) then tempEnemy = display.newImage ("enemy1.png", (randomGridSpace * 65) -28, -60) tempEnemy.type = "regular" elseif (randomEnemyNumber == 2) then tempEnemy = display.newImage ( "enemy2.png", display.contentWidth / 2 -playerWidth / 2, -60) tempEnemy.type = "waver" else tempEnemy = display.newImage ("enemy3.png", (randomGridSpace * 65) -28, -60) tempEnemy.type = "chaser" end planeGrid [randomGridSpace] = 1 table.insert (enemyPlanes, tempEnemy) planeGroup: insert (tempEnemy) numberOfEnemysGenerated = numberOfEnemysGenerated + 1; end if (numberOfEnemysGenerated == numberOfEnemysToGenerate) then numberOfEnemysGenerated = 0; resetPlaneGrid () timer.performWithDelay (2000, generateEnemys, 1) end end end
Prima controlliamo per assicurarci che il gioco non sia ancora finito. Quindi generiamo a randomGridSpace
, un numero tra 1 e 11, e un casuale randomEnemyNumber
, un numero tra 1 e 3. Il randomGridSpace
viene utilizzato per posizionare il piano in uno degli undici slot nella parte superiore dello schermo sull'asse x. Se pensi che l'area di gioco sia divisa in undici sezioni, allora vogliamo solo posizionare nuovi piani in uno slot che non è stato ancora preso da un altro piano. Il planeGrid
la tabella contiene undici 0
e quando posizioniamo un nuovo piano in uno degli slot, impostiamo la posizione corrispondente nella tabella 1
per indicare che lo slot è stato preso da un aereo.
Controlliamo se l'indice del randomGridSpace
nella tabella non è uguale a 0
. Se non lo è, sappiamo che attualmente lo slot è occupato e non dovremmo continuare, quindi chiamiamo generateEnemyPlane
e ritorno dalla funzione.
Successivamente, controlliamo cosa randomEnemyNumber
è uguale a e impostato tempEnemy
a una delle tre immagini nemiche, le diamo anche una proprietà di entrambi regolare
, vacillare
, o inseguitore
. Poiché Lua è un linguaggio dinamico, possiamo aggiungere nuove proprietà a un oggetto in fase di runtime. Quindi impostiamo qualsiasi indice uguale a randomGridSpace
a 1
nel planeGrid
tavolo.
Inseriamo tempEnemy
nel enemyPlanes
tabella per riferimento e incremento successivi numberOfEnemysGenerated
. Se numberOfEnemysGenerated
è uguale a numberOfEnemysToGenerate
, abbiamo ripristinato numberOfEnemysGenerated
a 0
, invocare resetPlaneGrid
, e impostare un timer che chiamerà generateEnemys
di nuovo dopo due secondi. Questo processo si ripete fintanto che il gioco non è finito.
moveEnemyPlanes
Come avrai intuito, il moveEnemyPlanes
la funzione è responsabile per lo spostamento degli aerei nemici. A seconda del piano genere
, viene chiamata la funzione appropriata.
function moveEnemyPlanes () if (#enemyPlanes> 0) quindi per i = 1, #enemyPlanes fa if (enemyPlanes [i] .type == "regular") quindi moveRegularPlane (enemyPlanes [i]) elseif (enemyPlanes [i] .type == "waver") quindi moveWaverPlane (enemyPlanes [i]) else moveChaserPlane (enemyPlanes [i]) end end end end
Questa funzione deve essere invocata in gameLoop
funzione.
function gameLoop () --SNIP-- checkFreeLifesOutOfBounds () checkPlayerCollidesWithFreeLife () moveEnemyPlanes () fine
moveRegularPlane
Il moveRegularPlane
sposta semplicemente l'aereo lungo lo schermo lungo l'asse y.
function moveRegularPlane (plane) plane.y = plane.y + 4 end
moveWaverPlane
Il moveWaverPlane
la funzione sposta l'aereo lungo lo schermo lungo l'asse y e, in un modello d'onda, attraverso l'asse x. Questo si ottiene usando il cos
funzione della biblioteca matematica di Lua.
Se questo concetto ti è estraneo, Michael James Williams ha scritto una grande introduzione a Sinusoidal Motion. Si applicano gli stessi concetti, l'unica differenza è che stiamo usando coseno. Dovresti pensare seno quando si ha a che fare con l'asse y e coseno quando si ha a che fare con l'asse x.
function moveWaverPlane (plane) plane.y = plane.y + 4 plane.x = (display.contentWidth / 2) + 250 * math.cos (numberOfTicks * 0.5 * math.pi / 30) end
Nel frammento sopra, usiamo il numberOfTicks
variabile. Abbiamo bisogno di incrementare questo ogni volta il gameLoop
la funzione è chiamata. Aggiungi quanto segue come prima linea nel file gameLoop
funzione.
function gameLoop () numberOfTicks = numberOfTicks + 1 end
moveChaserPlane
Il moveChaserPlane
la funzione ha il piano caccia il giocatore. Si sposta lungo l'asse y a una velocità costante e si sposta verso la posizione del giocatore sull'asse x. Dai un'occhiata all'implementazione di moveChaserPlane
per chiarire.
function moveChaserPlane (plane) if (plane.x < player.x)then plane.x =plane.x +4 end if(plane.x > player.x) quindi plane.x = plane.x - 4 end plane.y = plane.y + 4 end
Se stai testando il gioco ora, dovresti vedere gli aerei muoversi verso il basso sullo schermo.
fireEnemyBullets
Ogni tanto, vogliamo che gli aerei nemici sparino un proiettile. Non vogliamo che sparino tutti allo stesso tempo, quindi, scegliamo solo un paio di aerei da sparare.
function fireEnemyBullets () if (#enemyPlanes> = 2) then numberOfEnemyPlanesToFire = math.floor (# enemyPlanes / 2) local tempEnemyPlanes = table.copy (enemyPlanes) function locale fireBullet () local randIndex = math.random (#tempEnemyPlanes) local tempBullet = display.newImage ("bullet.png", (tempEnemyPlanes [randIndex] .x + playerWidth / 2) + bulletWidth, tempEnemyPlanes [randIndex] .y + playerHeight + bulletHeight) tempBullet.rotation = 180 planeGroup: tabella insert (tempBullet) .Insert (enemyBullets, tempBullet); table.remove (tempEnemyPlanes, randIndex) end per i = 0, numberOfEnemyPlanesToFire do fireBullet () end end end
Per prima cosa controlliamo per accertarci che enemyPlanes
la tabella ha più di due piani in essa. Se lo fa, otteniamo il numberOfEnemyPlanes
sparare prendendo la lunghezza del enemyPlanes
tabella, dividerlo per due e arrotondarlo verso il basso. Facciamo anche una copia del enemyPlanes
tabella, in modo che possiamo manipolarlo separatamente.
Il fireBullet
la funzione sceglie un piano dal tempEnemyPlanes
tavolo e fa sparare un proiettile all'aereo. Generiamo un numero casuale basato sulla lunghezza del file tempEnemyPlanes
tabella, creare un'immagine bullet e posizionarla utilizzando qualsiasi piano randIndex
nel tempEnemyPlanes
tavolo. Quindi rimuoviamo quell'aereo dalla tabella temporanea per assicurarci che non venga scelto di nuovo la prossima volta fireBullet
è chiamato.
Ripetiamo questo processo comunque molte volte numerOfEnemyPlanesToFire
è uguale a e chiama il fireBullet
funzione.
Abbiamo bisogno di avviare il timer che chiama questa funzione ogni tanto. Per fare ciò, aggiungi quanto segue al startTimers
funzione.
function startTimers () firePlayerBulletTimer = timer.performWithDelay (2000, firePlayerBullet, -1) generateIslandTimer = timer.performWithDelay (5000, generateIsland, -1) generateFreeLifeTimer = timer.performWithDelay (7000, generateFreeLife, - 1) fireEnemyBulletsTimer = timer.performWithDelay (2000 , fireEnemyBullets, -1) end
moveEnemyBullets
Abbiamo anche bisogno di spostare i proiettili nemici che sono sullo schermo. Questo è abbastanza semplice utilizzando il seguente frammento di codice.
function moveEnemyBullets () if (#enemyBullets> 0) quindi per i = 1, # enemyBullets do enemyBullets [i]. y = enemyBullets [i] .y + 7 end end end
Invoca questa funzione nel gameLoop
funzione.
function gameLoop () --SNIP-- checkPlayerCollidesWithFreeLife () moveEnemyPlanes () moveEnemyBullets () end
checkEnemyBulletsOutOfBounds
Oltre a spostare i proiettili nemici, dobbiamo controllare quando i proiettili nemici sono andati fuori campo e rimuoverli quando lo fanno. L'implementazione di checkEnemyBulletsOutOfBounds
dovrebbe sentirsi familiare ormai.
function checkEnemyBulletsOutOfBounds () if (#enemyBullets> 0) quindi per i = # enemyBullets, 1, -1 do if (enemyBullets [i] .y> display.contentHeight quindi enemyBullets [i]: removeSelf () enemyBullets [i] = nil table.remove (enemyBullets, i) end end end end
Invoca questa funzione nel gameLoop
funzione.
function gameLoop () --SNIP-- moveEnemyBullets () checkEnemyBulletsOutOfBounds () end
checkEnemyPlanesOutOfBounds
Dovremmo anche controllare se gli aerei nemici sono stati spostati fuori dallo schermo.
function checkEnemyPlanesOutOfBounds () if (#enemyPlanes> 0) quindi per i = # enemyPlanes, 1, -1 do if (enemyPlanes [i] .y> display.contentHeight quindi enemyPlanes [i]: removeSelf () enemyPlanes [i] = nil table.remove (enemyPlanes, i) end end end end
Invoca questa funzione nel gameLoop
funzione
function gameLoop () --SNIP-- moveEnemyBullets () checkEnemyBulletsOutOfBounds () checkEnemyPlanesOutOfBounds () end
checkPlayerBulletsCollideWithEnemyPlanes
Il checkPlayerBulletCollidesWithEnemyPlanes
la funzione usa il hasCollided
funzione per verificare se qualcuno dei proiettili del giocatore si è scontrato con uno qualsiasi degli aerei nemici.
function checkPlayerBulletsCollideWithEnemyPlanes () if (#playerBullets> 0 e #enemyPlanes> 0) quindi per i = # playerBullets, 1, -1 do per j = # enemyPlanes, 1, -1 do if (hasCollided (playerBullets [i], enemyPlanes [ j])) then playerBullets [i]: removeSelf () playerBullets [i] = nil table.remove (playerBullets, i) generateExplosion (enemyPlanes [j] .x, enemyPlanes [j] .y) enemyPlanes [j]: removeSelf ( ) enemyPlanes [j] = nil table.remove (enemyPlanes, j) local explosion = audio.loadStream ("explosion.mp3") local backgroundMusicChannel = audio.play (esplosione, fadein = 1000) fine fine fine fine fine
Questa funzione utilizza due nidificati per
loop per verificare se gli oggetti sono entrati in collisione. Per ciascuno dei playerBullets
, corriamo attraverso tutti gli aerei nel enemyPlanes
tavolo e chiama il hasCollided
funzione. Se c'è una collisione, rimuoviamo il proiettile e l'aereo, chiamiamo il generateExplosion
funzione e carica e riproduce un suono esplosivo.
Invoca questa funzione nel gameLoop
funzione.
function gameLoop () --SNIP-- checkEnemyBulletsOutOfBounds () checkEnemyPlanesOutOfBounds () checkPlayerBulletsCollideWithEnemyPlanes () fine
generateExplosion
Il generateExplosion
la funzione utilizza la classe SpriteObject di Corona. Gli sprite consentono sequenze animate di fotogrammi che si trovano su fogli di immagini o di sprite. Raggruppando le immagini in una singola immagine, puoi estrarre determinati fotogrammi da quella immagine e creare una sequenza di animazione.
function generateExplosion (xPosition, yPosition) opzioni locali = width = 60, height = 49, numFrames = 6 local explosionSheet = graphics.newImageSheet ("explosion.png", options) local sequenceData = name = "explosion", start = 1, count = 6, time = 400, loopCount = 1 local explosionSprite = display.newSprite (explosionSheet, sequenceData) explosionSprite.x = xPosition explosionSprite.y = yPosition explosionSprite: addEventListener ("sprite", explosionListener) explosionSprite: play () fine
Il newImageSheet
il metodo prende come parametri il percorso dell'immagine e una tabella di opzioni per il Foglio Sprite. Le opzioni che impostiamo sono le larghezza
, il altezza
, e il numFrames
, quante immagini singole compongono questo foglio. Ci sono sei immagini di esplosione separate come mostrato nell'immagine qui sotto.
Successivamente, impostiamo un tavolo, sequenceData
, che è necessario dal SpriteObject
. Abbiamo impostato il inizio
proprietà a 1
, il contare
a 6
, e tempo per 400
. Il inizio
proprietà è il frame su cui inizierà l 'animazione, il contare
è quanti fotogrammi include l'animazione e il tempo
proprietà è quanto tempo impiega l'animazione per giocare.
Quindi creiamo il SpriteObject
passando nel explosionSheet
e sequenceData
, imposta le posizioni xey e aggiungi un listener allo sprite. Il listener verrà utilizzato per rimuovere lo sprite una volta completata l'animazione.
explosionListener
Il explosionListener
la funzione è usata per rimuovere lo sprite. Se la evento
'S fase
la proprietà è uguale a conclusa
, allora sappiamo che lo sprite ha terminato la sua animazione e possiamo rimuoverlo.
function explosionListener (event) if (event.phase == "ended") then explosion locale = event.target explosion: removeSelf () explosion = nil end end
checkEnemyBulletsCollideWithPlayer
Il checkEnemyBulletsCollideWithPlayer
controlla se qualcuno dei proiettili nemici si è scontrato con l'aereo del giocatore.
function checkEnemyBulletsCollideWithPlayer () if (#enemyBullets> 0) quindi per i = # enemyBullets, 1, -1 do if (hasCollided (enemyBullets [i], player)) then enemyBullets [i]: removeSelf () enemyBullets [i] = nil table.remove (enemyBullets, i) if (playerIsInvincible == false) then killPlayer () fine fine fine fine fine
Passiamo attraverso il enemyBullets
tabella e controllare se qualcuno di essi è entrato in collisione con il giocatore. Se è vero, rimuoviamo quel particolare proiettile e, se playerIsInvincible
è falso
, noi invochiamo killPlayer
.
Invoca questa funzione nel gameLoop
funzione.
function gameLoop () --SNIP-- checkEnemyPlanesOutOfBounds () checkPlayerBulletsCollideWithEnemyPlanes () checkEnemyBulletsCollideWithPlayer () end
killPlayer
Il killPlayer
la funzione è responsabile per controllare se il gioco è finito e generare un nuovo giocatore se non lo è.
function killPlayer () numberOfLives = numberOfLives- 1; if (numberOfLives == 0) then gameOver = true doGameOver () else spawnNewPlayer () hideLives () showLives () playerIsInvincible = true end end
Iniziamo a decrementare numberOfLives
di 1
, e, se è uguale a 0
, noi chiamiamo il gioco finito
funzione. Il giocatore ha una vita sinistra, noi chiamiamo spawnNewPlayer
, seguito da hideLives
, showLives
, e impostare playerIsInvincible
a vero
.
doGameOver
Il doGameOver
la funzione dice allo storyboard di andare al gioco finito scena.
function doGameOver () storyboard.gotoScene ("gameover") fine
spawnNewPlayer
Il spawnNewPlayer
la funzione è responsabile della generazione di un nuovo giocatore dopo la sua morte. L'aereo del giocatore lampeggia per alcuni secondi per mostrare che è temporaneamente invincibile.
function spawnNewPlayer () local numberOfTimesToFadePlayer = 5 local numberOfTimesPlayerHasFaded = 0 local function fadePlayer () player.alpha = 0; transition.to (player, time = 200, alpha = 1) numberOfTimesPlayerHasFaded = numberOfTimesPlayerHasFaded + 1 if (numberOfTimesPlayerHasFaded == numberOfTimesToFadePlayer) then playerIsInvincible = false end end timer.performWithDelay (400, fadePlayer, numberOfTimesToFadePlayer) end
Per far lampeggiare l'aereo del giocatore, lo sbiadiamo in entrata e in uscita cinque volte. Nel fadePlayer
funzione, impostiamo l'aereo alfa
proprietà a 0
, che lo rende trasparente. Quindi usiamo la libreria di transizione per dissolvere il alfa
di nuovo a 1
per un periodo di 200 millisecondi. Il a
metodo del transizione
oggetto prende una tabella di opzioni. Nel nostro esempio, la tabella delle opzioni include un tempo in millisecondi e la proprietà che desideriamo animare, alfa
, e il valore desiderato, 1
.
Aumentiamo numberOfTimesThePlayerHasFaded
e controlla se è uguale al numero di volte che volevamo che il giocatore svanisse. Abbiamo quindi impostato playerIsInvincible
a falso
. Usiamo un timer per chiamare il fadePlayer
funzione tuttavia molte volte numberOfTimerToFadePlayer
è uguale a.
C'è un modo per fare tutto questo senza usare il timer e cioè usando il transizione
'S iterazioni
proprietà in combinazione con il suo onComplete
handler. Leggi la documentazione per saperne di più su questo approccio alternativo.
checkEnemyPlaneCollidesWithPlayer
C'è un altro controllo di collisione che dovremmo fare e cioè vedere se un aereo nemico si scontra con l'aereo del giocatore.
function checkEnemyPlaneCollideWithPlayer () if (#enemyPlanes> 0) quindi per i = # enemyPlanes, 1, -1 do if (hasCollided (enemyPlanes [i], player)) then enemyPlanes [i]: removeSelf () enemyPlanes [i] = nil table.remove (enemyPlanes, i) if (playerIsInvincible == false) then killPlayer () fine fine fine fine fine
Attraversiamo gli aerei nemici e vediamo se qualcuno di loro si scontra con l'aereo del giocatore. Se è vero, rimuoviamo quell'aereo nemico e chiamiamo killPlayer
. Se pensi che renda il gioco più interessante, potresti anche generare un'esplosione qui.
exitScene
Quando il gioco è finito, passiamo al gioco finito scena. Ricorda da prima nel tutorial, il exitScene
la funzione è dove si rimuovono gli ascoltatori di eventi, si fermano i timer e si interrompe l'audio riprodotto.
function scene: exitScene (evento) local group = self.view rectUp: removeEventListener ("touch", movePlane) rectDown: removeEventListener ("touch", movePlane) rectLeft: removeEventListener ("touch", movePlane) rectRight: removeEventListener ("touch" , movePlane) audio.stop (planeSoundChannel) audio.dispose (planeSoundChannel) Runtime: removeEventListener ("enterFrame", gameLoop) cancelTimers () fine scena: addEventListener ("exitScene", scena)
In pratica stiamo annullando ciò che abbiamo fatto nel enterScene
funzione. Chiamiamo il disporre
metodo sul Audio
oggetto di rilasciare la memoria associata al canale audio. chiamata Stop
da solo non rilascia la memoria.
cancelTimers
Come indica il nome, il cancelTimers
la funzione fa il contrario di startTimers
, cancella tutti i timer.
function cancelTimers () timer.cancel (firePlayerBulletTimer) timer.cancel (generateIslandTimer) timer.cancel (fireEnemyBulletsTimer) fine.cancel (generateFreeLifeTimer) fine
È tempo di creare il gioco finito scena. Inizia aggiungendo un nuovo file Lua al tuo progetto chiamato gameover.lua, e aggiungere il seguente codice ad esso.
storyboard locale = require ("storyboard") local scene = storyboard.newScene () local gameOverText local newgameButton return scene
createScene
Aggiungi il seguente a gameover.lua sopra scena di ritorno
. Da qui in poi, tutto il codice dovrebbe essere posizionato sopra il scena di ritorno
dichiarazione.
scena della funzione: createScene (evento) local group = self.view local background = display.newRect (0, 0, display.contentWidth, display.contentHeight) background: setFillColor (0, .39, .75) gruppo: insert (background) gameOverText = display.newText ("Game Over", display.contentWidth / 2.400, native.systemFont, 16) gameOverText: setFillColor (1, 1, 0) gameOverText.anchorX = .5 gameOverText.anchorY = .5 group: insert (gameOverText ) newGameButton = display.newImage ("newgamebutton.png", 264,670) gruppo: insert (newGameButton) newGameButton.isVisible = false end
Come abbiamo fatto nelle due scene precedenti, diamo il gioco finito scena uno sfondo blu. Quindi creiamo a TextObject
istanza chiamando newText
sopra display
. Il newText
metodo prende alcune opzioni, il testo per l'oggetto, la sua posizione e il tipo di carattere da usare. Diamo un colore giallo invocando SetFillColor
, passando in valori RGB come percentuali. Infine, creiamo un pulsante e lo nascondiamo per il momento.
enterScene
Quando lo storyboard è completamente passato al gioco finito scena, il enterScene
il metodo è chiamato.
Nel enterScene
, rimuoviamo la scena precedente dallo storyboard. Usiamo il metodo di convenienza scaleTo
dalla Transition Library per scalare il gameOverText
di un fattore di 4
. Aggiungiamo un onComplete
ascoltatore della transizione che chiama ilshowButton
funzione una volta completata la transizione. Infine, aggiungiamo un listener di eventi tap al pulsante di gioco che richiama il startNewGame
funzione.
function scene: enterScene (evento) local group = self.view storyboard.removeScene ("gamelevel") transition.scaleTo (gameOverText, xScale = 4.0, yScale = 4.0, time = 2000, onComplete = showButton) newGameButton: addEventListener (" tocca ", startNewGame) fine
showButton
Il showButton
la funzione nasconde il gameOverText
e mostra il newGameButton
.
function showButton () gameOverText.isVisible = false newGameButton.isVisible = true end
startNewGame
Il startNewGame
la funzione dice allo storyboard di passare a gamelevel scena.
function startNewGame () storyboard.gotoScene ("gamelevel") fine
exitScene
Dobbiamo fare un po 'di pulizia quando lasceremo il gioco finito scena. Rimuoviamo il listener dell'evento tap che abbiamo aggiunto in precedenza a newGameButton
.
function scene: exitScene (event) local group = self.view newGameButton: removeEventListener ("tap", startNewGame) end
Il pezzo finale del puzzle sta aggiungendo gli ascoltatori di eventi di scena di cui abbiamo parlato in precedenza. Per fare ciò, aggiungere il seguente frammento di codice a gameover.lua.
scena: addEventListener (scena "createScene", scena): addEventListener (scena "enterScene", scena): addEventListener ("exitScene", scena)
Siamo giunti alla fine di questa serie e ora abbiamo un gioco di combattimento aereo completamente funzionale. Spero che tu abbia trovato questi tutorial utili e hai imparato qualcosa lungo la strada. Grazie per aver letto.