Crea un gioco Space Defender - Game Logic

In questa serie di tutorial, impareremo come creare uno sparatutto spaziale proprio come il classico gioco Space Defender. Continuare a leggere!


Panoramica della serie

In questa versione di Space Defender, il giocatore dovrà difendere il suo spazio sparando ai nemici. Ogni volta che il giocatore distrugge un nemico, guadagna punti e quando il giocatore ha raggiunto 20 o 40 punti, la sua arma riceverà un aggiornamento. Per mescolare le cose, questo gioco invierà pacchetti bonus che valgono 5 punti. Per vedere il gioco in azione, guarda il breve video qui sopra.


Da dove siamo partiti ...

Nella parte 1 di questa serie, abbiamo imparato come impostare la nostra app, come utilizzare i caratteri personalizzati, come utilizzare uno storyboard e come impostare il nostro menu principale. Nella parte 2 di questa serie, impareremo come creare il gameplay della nostra app. Quindi iniziamo!

Il nostro primo passo è creare un nuovo file chiamato game.lua. Una volta creato, apri il file nel tuo editor preferito.


1. Aggiunta di librerie

Dal momento che stiamo iniziando una nuova scena, dobbiamo richiedere alcune librerie. Utilizzeremo il motore fisico integrato in Corona SDK per il rilevamento delle collisioni.

 storyboard locale = require ("storyboard") local scene = storyboard.newScene () local physics = require ("fisica")

2. Configurazione della fisica

Dopo aver configurato le nostre librerie, configureremo il motore fisico. Le impostazioni seguenti imposteranno la gravità su 0 (proprio come nello spazio) e imposteranno le iterazioni su 16. Il setPositionIterations indica che il motore passerà attraverso 16 posizioni per fotogramma. Qualsiasi valore superiore a 16 può influire negativamente sulle prestazioni del gioco.

 physics.start () physics.setGravity (0, 0) physics.setPositionIterations (16)

3. Utilizzo di un generatore casuale

Sebbene questo passaggio non sia necessario per questo tutorial, è buona norma "seminare" il generatore di numeri casuali. Mi piace usare l'ora corrente per avviare il generatore.

 math.randomseed (os.time ())

4. Impostazione delle variabili di gioco

Ora definiremo alcune variabili per il nostro gioco. Accanto a ciascuna variabile è presente un commento che spiega lo scopo della variabile.

 schermata localeW, schermataH, metàW, metàY = display.contentWidth, display.contentHeight, display.contentWidth * 0.5, display.contentHeight * 0.5 local gameover_returntomenu - avanti declard il nostro pulsante di gioco over - Imposta le impostazioni di gioco motionx = 0; - Variabile utilizzata per spostare il carattere lungo la velocità dell'asse y = 10; - Controlla la velocità della nave playerScore = 0; - Imposta il player score playerLives = 20; - Imposta il numero di vite per il giocatore slowEnemySpeed ​​= 2375; - Imposta la velocità di spostamento delle navi bianche attraverso lo schermo slowEnemySpawn = 2400; - Imposta la velocità di spawn della nave bianca fastEnemySpeed ​​= 1875; - Imposta la velocità con cui le navi verdi si spostano sullo schermo fastEnemySpawn = 1800; - Imposta la percentuale di spawn della nave verde bulletSpeed ​​= 325; - Imposta la velocità di avanzamento del proiettile attraverso lo schermo bulletSpawn = 250; - Imposta la velocità di spawn del proiettile

5. Crea la scena di gioco principale

Dopo aver creato le variabili, configureremo la scena all'interno della funzione scena: createScene. Se ti ricordi dalla Parte 1, questa funzione viene utilizzata per creare elementi visivi e logica di gioco. In una funzione successiva, chiameremo su queste funzioni per eseguire il gioco.

Nel seguente codice, stiamo creando il scena: createScene funzione e aggiungendo lo sfondo e le pareti superiore / inferiore. Entrambe le pareti sono impostate come oggetti fisici statici per impedire al giocatore di uscire dallo schermo.

 function scene: createScene (event) local group = self.view - Imposta elementi visivi e wall locali bg = display.newImageRect ("images / BKG.png", 480, 320) bg.x = halfW bg.y = halfY gruppo: insert (bg) topwall = display.newRect (0,0, screenW, 20) topwall.y = -5 topwall: setFillColor (0,0,0) topwall.alpha = 0.01 physics.addBody (topwall, "static ") group: insert (topwall) local bottomwall = display.newRect (0,0, screenW, 20) bottomwall.y = 325 bottomwall: setFillColor (0,0,0) bottomwall.alpha = 0,01 physics.addBody (bottomwall," static ") group: insert (bottomwall) end

6. Aggiunta di elementi HUD

All'interno dello stesso scena: createScene funzione, ma dopo il bottomwall mostra oggetto, stiamo per aggiungere altri quattro oggetti di visualizzazione. Ecco una spiegazione dello scopo di ciascun oggetto.

  • btn_up, btn_down: Questi oggetti di visualizzazione fungeranno da pulsanti sul lato sinistro dello schermo e ciascun oggetto sposterà la nave rispettivamente verso l'alto o verso il basso. Tuttavia, non sono operativi fino a quando non impostiamo la funzione di spostamento.
  • enemyHitBar: Questo oggetto di visualizzazione è impostato come un sensore e reagirà solo alle collisioni fisiche. Quando reagisce alle collisioni, rimuoverà l'oggetto nemico e lo sottrarrà dalle vite dei giocatori.
 locale btn_up = display.newRect (0,0,75,160) btn_up: setReferencePoint (display.TopLeftReferencePoint) btn_up.x, btn_up.y = 0,0; btn_up.alpha = 0,01 gruppo: insert (btn_up) local btn_down = display.newRect (0,0,75,160) btn_down: setReferencePoint (display.BottomLeftReferencePoint) btn_down.x, btn_down.y = 0, screenH; btn_down.alpha = 0,01 gruppo: insert (btn_down) local enemyHitBar = display.newRect (-20,0,20,320) enemyHitBar: setFillColor (0,0,0) enemyHitBar.name = "enemyHitBar" physics.addBody (enemyHitBar, isSensor = true) gruppo: insert (enemyHitBar)

Subito dopo l'oggetto di visualizzazione enemyHitBar, aggiungeremo alcuni elementi della GUI per visualizzare il punteggio del giocatore e le vite dei giocatori. Mostreremo anche il testo sullo schermo che dice "Move Up" e "Move Down" per notificare al giocatore dove devono toccare per spostare la nave su o giù.

 local gui_score = display.newText ("Punteggio:" ... playerScore, 0,0, "Kemco Pixel", 16) gui_score: setReferencePoint (display.TopRightReferencePoint) gui_score.x = screenW group: insert (gui_score) local gui_lives = display.newText ("Vite:" ... playerLives, 0,0, "Kemco Pixel", 16) gui_lives: setReferencePoint (display.BottomRightReferencePoint) gui_lives.x = screenW gui_lives.y = screenH gruppo: insert (gui_lives) local gui_moveup = display.newText ( "Sposta in alto", 0,0,50,100, "Kemco Pixel", 16) gruppo: insert (gui_moveup) local gui_movedown = display.newText ("Sposta giù", 0,0,50,23, "Kemco Pixel", 16 ) gui_movedown: setReferencePoint (display.BottomLeftReferencePoint) gui_movedown.y = screenH group: insert (gui_movedown)

7. Aggiunta della nave giocatore

Successivamente, aggiungeremo la nave del giocatore allo schermo. La nave verrà aggiunta come oggetto di fisica dinamica in modo che possa reagire alle collisioni con altri oggetti di fisica. Andremo più in profondità sulle collisioni più avanti in questo tutorial.

 nave locale = display.newImageRect ("images / spaceShip.png", 29, 19) ship.x, ship.y = 75, 35 ship.name = "ship" physics.addBody (nave, "dynamic", friction = 0.5, bounce = 0) gruppo: insert (ship)

8. Spostamento della nave giocatore

Ti ricordi di btn_up e btn_down mostra oggetti che abbiamo aggiunto? Ora aggiungeremo gli ascoltatori di eventi a questi oggetti per aiutare a far muovere il giocatore. quando btn_up è toccato, renderemo la nostra variabile variabile in negativo e quando btn_down è toccato renderemo la nostra velocità positiva. Rendendo questa variabile positiva e negativa, stiamo dicendo alla nostra prossima funzione di spostare la nave su o giù.

 -- Quando si tocca il pulsante su, imposta il movimento per spostare la funzione di sollevamento su btn_up: touch () motionx = -speed; end btn_up: addEventListener ("touch", btn_up) - Quando si tocca il pulsante giù, imposta il movimento per spostare la funzione di discesa della nave btn_down: touch () motionx = speed; fine btn_down: addEventListener ("touch", btn_down)

9. Abilitazione del movimento

Dopo aver aggiunto gli ascoltatori di eventi al nostro btn_up e btn_down mostra oggetti, creeremo due listener di eventi runtime con le loro rispettive funzioni. Queste funzioni eseguiranno ogni frame e quella che si cattura con le funzioni di runtime è che devi specificare quando fermarle. Lo copriremo più tardi. Per ora, la funzione stop imposterà la variabile MotionX a 0 (perché nessuno dei due pulsanti viene toccato) e il moveguy la funzione aggiungerà la variabile MotionX alla nostra nave y posizione.

 funzione locale stop (evento) se event.phase == "ended" then motionx = 0; end end Runtime: addEventListener ("touch", stop) - Questa funzione sposta effettivamente la nave in base al movimento mové (evento) della funzione locale ship.y = ship.y + motionx; end Runtime: addEventListener ("enterFrame", moveguy)

10. Sparare i proiettili

Ormai, abbiamo la nostra nave in movimento, ma non sta sparando! Per far sì che la nave sia pronta a sparare proiettili, dobbiamo creare il Fireship () funzione. Questa funzione creerà nuovi oggetti di visualizzazione che reagiscono alle collisioni fisiche e questa funzione sposterà anche l'oggetto sullo schermo da sinistra a destra.

Per rendere il gioco più interessante, consentiremo al giocatore di sparare più proiettili quando raggiungono un determinato punteggio. Quando il giocatore raggiunge i 20, la nave sparerà due proiettili e quando il giocatore raggiungerà i 40, la nave sparerà un terzo proiettile che spara verso il basso in diagonale.

 function fireShip () bullet = display.newImageRect ("images / bullet.png", 13, 8) bullet.x = ship.x + 9 bullet.y = ship.y + 6 bullet: toFront () bullet.name = " bullet "physics.addBody (bullet, isSensor = true) transition.to (bullet, time = bulletSpeed, x = 500, onComplete = function (self) self.parent: remove (self); self = nil; end; ) if (playerScore> = 20) then secondBullet = display.newImageRect ("images / bullet.png", 13, 8) secondBullet.x = ship.x + 9 secondBullet.y = ship.y + 12 secondBullet: toFront ( ) secondBullet.name = "bullet" physics.addBody (secondBullet, isSensor = true) transition.to (secondBullet, time = bulletSpeed, x = 500, onComplete = function (self) self.parent: remove (self); self = nil; end;) end if (playerScore> = 40) then thirdBullet = display.newImageRect ("images / bullet.png", 13, 8) thirdBullet.x = ship.x + 9 thirdBullet.y = ship. y + 12 thirdBullet: toFront () thirdBullet.name = "bullet" physics.addBody (thirdBullet, isSensor = true) transition.to (thirdBullet, time = bulletSpeed, x = 500, y = ship.y + 100, onComplete = function (self) self.parent: remove (self); auto = zero; fine; )

11. Creazione di nemici

Dopo che abbiamo impostato la nostra nave per sparare, dobbiamo dare al giocatore alcuni nemici per sparare! Creeremo due diverse funzioni - createSlowEnemy () e createFastEnemy (). Entrambe le funzioni creeranno un oggetto di visualizzazione fisica che si sposta da destra a sinistra con la velocità del nemico e l'immagine è l'unica differenza.

 function createSlowEnemy () enemy = display.newImageRect ("images / enemy.png", 32, 26) enemy.rotation = 180 enemy.x = 500 enemy.y = math.random (10, screenH-10) enemy.name = "enemy" physics.addBody (enemy, isSensor = true) transition.to (enemy, time = slowEnemySpeed, x = -20) end function createFastEnemy () enemy = display.newImageRect ("images / fastEnemy.png" , 32, 26) enemy.rotation = 180 enemy.x = 500 enemy.y = math.random (10, screenH-10) enemy.name = "nemico" physics.addBody (nemico, isSensor = true) transizione. a (nemico, time = fastEnemySpeed, x = -20) fine

12. Crea pacchetti bonus

Successivamente, creeremo pacchetti bonus per il nostro giocatore per accedere alla funzione createBonus (). Il createBonus () la funzione creerà un oggetto di visualizzazione fisica che si sposta da destra a sinistra e ogni pacchetto bonus che il giocatore prende, guadagnerà 5 punti.

 function createBonus () bonus = display.newImageRect ("images / bonus.png", 18, 18) bonus.rotation = 180 bonus.x = 500 bonus.y = math.random (10, screenH-10) bonus.name = "bonus" physics.addBody (bonus, isSensor = true) transition.to (bonus, time = 1475, x = -20, onComplete = function () display.remove (bonus) bonus = zero fine;) fine

13. Aggiornamento della vita del giocatore

La nostra ultima penultima funzione è la funzione updateLives (). Questa funzione verrà chiamata ogni volta che un nemico supererà il giocatore per dare al giocatore l'obiettivo di difendere il proprio lato dello spazio. Se il numero di vite è superiore a 0, questa funzione sottrarrà una vita e aggiornerà il testo sullo schermo. Altrimenti, si tradurrà in una partita sulla scena.

Nel gioco sulla scena, stiamo cancellando tutti i nostri timer e rimuoviamo tutti i nostri ascoltatori di eventi. Con Corona SDK, è molto importante ricordare che devi dire esplicitamente alla tua app quando rimuovere i listener e i timer di runtime (solo quando il timer è in esecuzione). Dopo che questi sono stati rimossi, mostreremo un messaggio di gioco e consentirai al giocatore di tornare al menu.

 function updateLives () if (playerLives> = 0) then playerLives = playerLives - 1 gui_lives.text = "Lives:" ... playerLives gui_lives.x = screenW else timer.cancel (tmr_fireShip) timer.cancel (tmr_sendSlowEnemies) timer.cancel (tmr_sendSlowEnemies2 ) timer.cancel (tmr_sendFastEnemies) timer.cancel (tmr_sendBonus) Runtime: removeEventListener ("collision", onCollision) Runtime: removeEventListener ("enterFrame", moveguy) Runtime: removeEventListener ("touch", stop) - Visualizza il gioco sullo schermo locale gameover_message = display.newText ("Game Over!", 0,0, "Kemco Pixel", 32) gameover_message.x = halfW gameover_message.y = halfY - 15 gruppo: insert (gameover_message) function returnToMenuTouch (event) if (event. phase == "started") then storyboard.gotoScene ("menu", "slideRight", "1000") end end gameover_returntomenu = display.newText ("Return To Menu", 0,0, "Kemco Pixel", 28) gameover_returntomenu .x = halfW gameover_returntomenu.y = halfY + 35 gameover_returntomenu: addEventListener ("touch", returnToMenuTouch) group: in sert (gameover_returntomenu) end end

14. Rilevamento collisione

Siamo pronti per la nostra funzione finale all'interno della nostra scena: funzione createScene ()! Questa funzione gestirà tutto il rilevamento delle collisioni confrontando la proprietà il mio nome dell'oggetto1 a quello contenuto nell'oggetto 2. Ogni oggetto viene passato come parametro a questa funzione sotto il nome della variabile evento.

Per rendere più facile per te, ho suddiviso i cinque casi di collisione.

  • Caso 1 - L'oggetto 1 è un proiettile e l'oggetto 2 è un nemico
    Cosa sta succedendo: Rimuovi nemico e aggiorna punteggio
  • Caso 2 - L'oggetto 1 è un nemico e l'oggetto 2 è un proiettile
    Cosa sta succedendo: Rimuovi nemico e aggiorna punteggio
  • Caso 3 - L'oggetto 1 è una nave e l'oggetto 2 è un bonus
    Cosa sta succedendo: Rimuovi bonus e aggiorna il punteggio
  • Caso 4 - L'oggetto 1 è un nemico e l'oggetto 2 è un nemicoHitBar
    Cosa sta succedendo: Rimuovi il nemico e aggiorna le vite
  • Caso 5 - L'oggetto 1 è un nemicoHitBar e l'oggetto 2 è un nemico
    Cosa sta succedendo: Rimuovi il nemico e aggiorna le vite
 function onCollision (event) if (event.object1.name == "bullet" ed event.object2.name == "enemy") then display.remove (event.object2) playerScore = playerScore + 1 elseif (event.object1.name == "nemico" ed event.object2.name == "bullet") then display.remove (event.object1) playerScore = playerScore + 1 elseif (event.object1.name == "ship" ed event.object2.name = = "bonus") then display.remove (event.object2) playerScore = playerScore + 5 elseif (event.object1.name == "enemy" e event.object2.name == "enemyHitBar") then display.remove (event. object1) updateLives () elseif (event.object1.name == "enemyHitBar" ed event.object2.name == "enemy") then display.remove (event.object2) updateLives () end gui_score.text = "Punteggio:" ... playerScore gui_score.x = screenW end

15. Sincronizzazione dei timer di movimento

Dal momento che abbiamo tutto pronto per il nostro gioco, abbiamo solo bisogno di far muovere tutto! All'interno della funzione scena: enterScene () - ricorda che il enterScene la funzione è al di fuori del createScene funzione: creeremo 5 timer e un listener di runtime. I timer invieranno proiettili, nemici e bonus mentre il listener di runtime gestirà il rilevamento delle collisioni.

 function scene: enterScene (event) local group = self.view tmr_fireShip = timer.performWithDelay (bulletSpawn, fireShip, 0) tmr_sendSlowEnemies = timer.performWithDelay (slowEnemySpawn, createSlowEnemy, 0) tmr_sendSlowEnemies2 = timer.performWithDelay (slowEnemySpawn + (slowEnemySpawn * 0.5), createSlowEnemy, 0) tmr_sendFastEnemies = timer.performWithDelay (fastEnemySpawn, createFastEnemy, 0) tmr_sendBonus = timer.performWithDelay (2500, createBonus, 0) Runtime: addEventListener ("collision", onCollision) fine

16. Distruggere la scena

L'aggiunta finale (lo prometto!) È la scena: destroyScene () funzione e gli ascoltatori di eventi di scena. La funzione Distruggi scena assicurerà che la fisica venga rimossa una volta che il giocatore lascia la scena. Gli ascoltatori dell'evento di scena chiameranno il createScene, enterScene, e destroyScene rispettivamente.

 function scene: destroyScene (evento) local group = self.view package.loaded [fisica] = nil physics = nil end scene: addEventListener (scena "createScene"): addEventListener (scena "enterScene", scena): addEventListener (" destroyScene ", scene) scena di ritorno

Conclusione

Congratulazioni! Hai imparato molte cose come la funzione di storyboard di Corona, la fisica, le collisioni e molto altro ancora! Queste sono abilità preziose che possono essere applicate a quasi tutti i giochi e, se vuoi creare questo gioco per il tuo dispositivo, ti consiglio vivamente i documenti ufficiali di Corona sulla costruzione del dispositivo.

Grazie mille per la lettura! Se avete domande, per favore lasciatele nei commenti qui sotto.