Creați un joc de combatere a avioanelor în Corona Joc de terminare

Ce veți crea

Introducere

În cea de-a patra și ultima parte a acestei serii, vom continua de unde am plecat în tutorialul anterior. Vom crea avioane inamice pe care jucătorul trebuie să le evite sau să le împușcă și vom crea, de asemenea, un joc pe ecran.

1. generateEnemys

generateEnemys funcția generează un număr între Trei și Șapte, și sună generateEnemyPlane funcționează fiecare două secunde pentru că de multe ori numberOfEnemysToGenerate este egal cu. Introduceți următorul fragment de cod în gamelevel.lua.

funcția generateEnemys () numberOfEnemysToGenerate = math.random (3,7) timer.performWithDelay (2000, generateEnemyPlane, numberOfEnemysToGenerate) sfârșit

De asemenea, trebuie să invocăm această funcție în enterScene așa cum se arată mai jos.

funcția scena: enterScene (eveniment) --SNIP-- Runtime: addEventListener ("enterFrame", gameLoop) startTimers () generateEnemys

Să vedem ce implementare generateEnemyPlane se pare ca.

2. generateEnemyPlane

generateEnemyPlane funcția generează un avion inamic. Există trei tipuri de avioane inamice în acest joc.

  • Regulat , se mișcă în jos pe ecran în linie dreaptă
  • Waver, se mișcă într-un model de undă pe axa x
  • urmăritor, urmărește avionul jucătorului
funcția generateEnemyPlane () dacă (gameOver ~ = true), apoi local randomGridSpace = math.random (11) local randomEnemyNumber = math.random (3) tempEnemy local dacă (planeGrid [randomGridSpace] randomEnemyNumber == 1) apoi tempEnemy = display.newImage ("enemy1.png", (randomGridSpace * 65) -28, -60) tempEnemy.type = "regular" elseif (randomEnemyNumber == 2) apoi tempEnemy = display.newImage "enemy2.png", display.contentWidth / 2 -playerWidth / 2, -60) tempEnemy.type = "oscilați" altceva tempEnemy = display.newImage ("enemy3.png", (randomGridSpace * 65) -28, -60) tempEnemy.type = "chaser" sfârșitul planeGrid [randomGridSpace] = 1 table.insert (enemyPlanes, tempEnemy) planeGroup: insert (tempEnemy) numberOfEnemysGenerated = numberOfEnemysGenerated + 1; în cazul în care (numberOfEnemysGenerated == numberOfEnemysToGenerate) then numberOfEnemysGenerated = 0; resetPlaneGrid () timer.performWithDelay (2000, generateEnemys, 1) end end end

Mai întâi verificăm să ne asigurăm că jocul nu sa terminat încă. Apoi, generăm a randomGridSpace, un număr între 1 și 11, și o întâmplare randomEnemyNumber, un număr între 1 și 3. randomGridSpace este folosit pentru a poziționa planul într-una din cele unsprezece fante din partea superioară a ecranului pe axa x. Dacă vă gândiți că zona de joc este împărțită în unsprezece secțiuni, atunci vrem doar să aducem noi avioane într-un slot care nu a fost încă luat de un alt avion. planeGrid tabelul conține unsprezece 0și când plasăm un nou avion într-unul dintre sloturi, setăm poziția corespunzătoare din tabel la 1 pentru a indica faptul că slotul a fost preluat de un avion.

Verificăm dacă indexul randomGridSpace în tabel nu este egal cu 0. Dacă nu este, știm că slotul este în prezent luat și nu ar trebui să continuăm, așa că sunăm generateEnemyPlane și reveniți din funcție.

Apoi, verificăm ce randomEnemyNumber este egal și stabilit tempEnemy la una dintre cele trei imagini inamice, îi dăm și ea o proprietate regulat, șovăi, sau urmăritor. Deoarece Lua este un limbaj dinamic, putem adăuga proprietăți noi unui obiect în timpul execuției. Apoi stabilim ce indice este egal cu randomGridSpace la 1 în planeGrid masa.

Introducem tempEnemy în enemyPlanes tabel pentru referință ulterioară și incrementare numberOfEnemysGenerated. Dacă numberOfEnemysGenerated este egal cu  numberOfEnemysToGenerate, ne reinițializăm numberOfEnemysGenerated la 0, invoca resetPlaneGrid, și setați un cronometru care va apela generateEnemys din nou după două secunde. Acest proces se repetă atât timp cât jocul nu sa terminat.

3. moveEnemyPlanes

După cum probabil ați ghicit, moveEnemyPlanes funcția este responsabilă pentru mutarea avioanelor inamice. În funcție de avion tip, se numește funcția corespunzătoare.

funcția moveEnemyPlanes () dacă (#enemyPlanes> 0) atunci pentru i = 1, #enemyPlanes face dacă (enemyPlanes [i] .type == "regulat") apoi moveRegularPlane (enemyPlanes [i]) elseif (enemyPlanes [ == "waver") apoi moveWaverPlane (inamicPlanes [i]) altceva moveChaserPlane (enemyPlanes [i]) end end end end

Această funcție trebuie invocată în gameLoop funcţie.

funcția gameLoop () --SNIP-- checkFreeLifesOutOfBounds () checkPlayerCollidesWithFreeLife () mutareEnemyPlanes () sfârșit

4. moveRegularPlane

moveRegularPlane pur și simplu deplasează planul în jos pe ecran peste axa y.

funcția moveRegularPlane (plan) plane.y = plane.y + 4 sfârșit

5. moveWaverPlane

moveWaverPlane funcția deplasează planul în jos pe ecran pe axa y și, într-un model de undă, pe axa x. Acest lucru se realizează prin utilizarea funcției cos funcția bibliotecii matematice a lui Lua.

Dacă acest concept este străin pentru dvs., Michael James Williams a scris o introducere minunată a mișcării sinusoidale. Aceleași concepte se aplică, singura diferență este aceea pe care o folosim cosinus. Ar trebui să te gândești sinus atunci când se ocupă de axa y și cosinus atunci când se ocupă de axa x.

Funcția moveWaverPlane (plan) plane.y = plan.y + 4 plane.x = (display.contentWidth / 2) + 250 * math.cos (numberOfTicks * 0.5 * math.pi / 30)

În fragmentul de mai sus, folosim numberOfTicks variabil. Trebuie să creștem acest lucru de fiecare dată gameLoop se numește funcția. Adăugați următoarele ca prima linie în gameLoop funcţie.

funcția gameLoop () numberOfTicks = numberOfTicks + 1 end

6. moveChaserPlane

moveChaserPlane funcția are planul alungare jucătorul. Se mișcă în jos pe axa y la o viteză constantă și se deplasează spre poziția jucătorului pe axa x. Aruncați o privire la punerea în aplicare a moveChaserPlane pentru clarificare.

funcția moveChaserPlane (plan) dacă (plane.x < player.x)then plane.x =plane.x +4 end if(plane.x > player.x), apoi plane.x = plane.x - 4 end plane.y = plane.y + 4 end

Dacă încercați jocul acum, ar trebui să vedeți avioanele care se deplasează în jos pe ecran.

7. fireEnemyBullets

Atât de des, vrem ca avioanele inamice să tragă un glonț. Nu vrem ca toata lumea sa traga in acelasi timp, deci alegem doar cateva avioane de foc.

funcția fireEnemyBullets () dacă (#enemyPlanes> = 2), apoi numărul localOfEnemyPlanesToFire = math.floor (# enemyPlanes / 2) local tempEnemyPlanes = table.copy (enemyPlanes) funcția locală fireBullet () local randIndex = math.random (#tempEnemyPlanes) tempBullet = display.newImage ("bullet.png", tempEnemyPlanes [randIndex] .x + playerWidth / 2) + bulletWidth, tempEnemyPlanes [randIndex] .y + playerHeight + bulletHeight) tempBullet.rotation = 180 planeGroup: .insert (enemyBullets, tempBullet); table.remove (tempEnemyPlanes, randIndex) sfârșit pentru i = 0, numberOfEnemyPlanesToFire do fireBullet () end end end

Mai întâi verificăm să vă asigurați că enemyPlanes tabelul are mai mult de două planuri în el. Dacă da, obținem numberOfEnemyPlanes pentru a trage prin luarea lungimii enemyPlanes tabelul, împărțiți-l cu două și rotunjiți-l. Facem, de asemenea, o copie a textului enemyPlanes tabel, astfel încât să putem manipula separat.

fireBullet funcția alege un plan de la tempEnemyPlanes masa și face planul să tragă un glonț. Noi generăm un număr aleator pe baza lungimii tempEnemyPlanes tabelă, creați o imagine glonț și poziționați-o folosind oricare avion aflat la randIndex în tempEnemyPlanes masa. Apoi, eliminăm planul din masa temporară pentru a ne asigura că nu va mai fi ales data viitoare fireBullet se numește.

Repetăm ​​acest proces de multe ori numerOfEnemyPlanesToFire este egal cu și cheamă fireBullet funcţie.

Trebuie să pornim cronometrul care sună frecvent această funcție. Pentru a realiza acest lucru, adăugați următoarele elemente la startTimers funcţie.

Funcția startTimers () FirePlayerBulletTimer = Timer.performWithDelay (2000, FirePlayerBullet, -1) generateIslandTimer = Timer.performWithDelay (5000, generateIsland, -1) generateFreeLifeTimer = Timer.performWithDelay (7000, generateFreeLife, 1) fireEnemyBulletsTimer = Timer.performWithDelay , fireEnemyBullets, -1) sfârșit

8. moveEnemyBullets

De asemenea, trebuie să mutăm gloanțele inamice care sunt pe ecran. Acest lucru este destul de simplu, utilizând următorul fragment de cod.

funcția moveEnemyBullets () dacă (#enemyBullets> 0) atunci pentru i = 1, # enemyBullets face enemyBullets [i]. y = enemyBullets [i] .y + 7 sfârșitul capătului final

Invocați această funcție în gameLoop funcţie.

funcția gameLoop () --SNIP-- checkPlayerCollidesWithFreeLife () mutareEnemyPlanes () endEnemyBullets () end

9. checkEnemyBulletsOutOfBounds

În plus față de mutarea gloanelor inamice, trebuie să verificăm când gloanțele inamice au ieșit în afara ecranului și să le elimine atunci când o fac. Implementarea sistemului checkEnemyBulletsOutOfBounds ar trebui să se simtă familiar până acum.

()) if (#enemyBullets> 0) atunci pentru i = # enemyBullets, 1, -1 face if (enemyBullets [i] .y> display.contentHeight) nil table.remove (enemyBullets, i) end end end end

Invocați această funcție în gameLoop funcţie.

funcția gameLoop () --SNIP-- moveEnemyBullets () checkEnemyBulletsOutOfBounds () sfârșit

10. checkEnemyPlanesOutOfBounds

De asemenea, ar trebui să verificăm dacă avioanele inamice s-au mutat off-screen.

()) if (#enemyPlanes> 0) atunci pentru i = # enemyPlanes, 1, -1 face if (enemyPlanes [i] .y> display.contentHeight) atunci enemyPlanes [i]: removeSelf nil table.remove (inamicPlanes, i) sfarsitul sfarsitului

Invocați această funcție în gameLoop funcţie

funcția gameLoop () --SNIP-- moveEnemyBullets () checkEnemyBulletsOutOfBounds () checkEnemyPlanesOutOfBounds () sfârșit

11. checkPlayerBulletsCollideWithEnemyPlanes

checkPlayerBulletCollidesWithEnemyPlanes funcția utilizează hasCollided pentru a verifica dacă oricare dintre gloanțele jucătorului s-a ciocnit cu oricare dintre avioanele inamice.

funcția checkPlayerBulletsCollideWithEnemyPlanes () dacă (#playerBullets> 0 și #enemyPlanes> 0) apoi pentru i = # playerBullets, 1, -1 face pentru j = # enemyPlanes, 1, -1 do if (hasCollided (playerBullets [i], enemyPlanes [ j]), apoi playerBullets [i]: removeSelf () playerBullet [i] = nil table.remove (playerBullets, i) generateExplosion (enemyPlanes [j] .x, enemyPlanes [ ) enemyPlanes [j] = nil table.remove (enemyPlanes, j) explozie locală = audio.loadStream ("explosion.mp3") fundal localMusicChannel = audio.play (explozie, fadein = 1000

Această funcție utilizează două imbricate pentru pentru a verifica dacă obiectele s-au ciocnit. Pentru fiecare dintre playerBullets, navigăm prin toate avioanele din enemyPlanes și apelați hasCollided funcţie. Dacă există o coliziune, eliminăm glontul și avionul, sunăm generateExplosion funcția și încărcați și redați un sunet de explozie.

Invocați această funcție în gameLoop funcţie.

funcția gameLoop () --SNIP-- checkEnemyBulletsOutOfBounds () checkEnemyPlanesOutOfBounds () checkPlayerBulletsCollideWithEnemyPlanes () sfârșit

12. generateExplosion

generateExplosion funcția utilizează clasa SpriteObject a Coronei. Sprite permit secvențe animate de cadre care se găsesc pe fișiere imagine sau sprite. Prin gruparea imaginilor într-o singură imagine, puteți trage anumite cadre din acea imagine și puteți crea o secvență de animație.

funcția generateExplosion (xPosition, yPosition) opțiuni locale = width = 60, height = 49, numFrames = 6 explozie localăSheet = graphics.newImageSheet ("exploion.png" = 1, count = 6, time = 400, loopCount = 1 explozie localăSprite = display.newSprite (explosionSheet, sequenceData) explozieSprite.x = x exploziePoziție explozieSprite.y = yexpoziție explozieSprite: addEventListener () Sfârșit

newImageSheet metoda ia ca parametri calea către imagine și o tabelă de opțiuni pentru foaia Sprite. Opțiunile pe care le-am setat sunt lăţime, înălţime, si numFrames, câte imagini individuale alcătuiesc această foaie. Există șase imagini explozive separate, după cum se arată în imaginea de mai jos.

Apoi, am stabilit o masă, sequenceData, care este necesar de către SpriteObject. Am setat start proprietate la 1, numara la 6, și timpul să 400 start proprietatea este cadrul pe care va începe animația, numara este numărul de cadre pe care animația le include și timp proprietatea este cât timp are nevoie animația pentru a juca.

Apoi creăm SpriteObject trecerea în explosionSheet și sequenceData, setați pozițiile x și y și adăugați un ascultător sprite. Ascultătorul va fi folosit pentru a elimina sprite-ul după terminarea animației.

13. explosionListener

explosionListener funcția este utilizată pentru a elimina sprite. În cazul în care eveniment„s fază proprietatea este egală cu încheiat, atunci știm că sprite a terminat animația și o putem elimina.

(event.phase == "ended"), apoi explozie locală = explozie event.target: removeSelf () explozie = sfârșit de sfârșit zero

14. checkEnemyBulletsCollideWithPlayer

checkEnemyBulletsCollideWithPlayer verifică dacă vreunul dintre gloanțele dușmanilor s-a ciocnit cu avionul jucătorului.

()) if (#enemyBullets> 0) atunci pentru i = # enemyBullets, 1, -1 face if (hasCollided (enemyBullets [i], player)) then enemyBullets [i]: removeSelf table.remove (enemyBullets, i) dacă (playerIsInvincible == false) apoi killPlayer () sfârșitul capătului end

Vom trece prin enemyBullets și verificați dacă vreuna dintre ele s-a ciocnit cu jucătorul. Dacă este adevărat, eliminăm acel glonț și, dacă playerIsInvincible este fals, noi invocăm killPlayer.

Invocați această funcție în gameLoop funcţie.

funcția gameLoop () --SNIP-- checkEnemyPlanesOutOfBounds () checkPlayerBulletsCollideWithEnemyPlanes () checkEnemyBulletsCollideWithPlayer () sfârșit

15. killPlayer

killPlayer funcția este responsabilă pentru a verifica dacă jocul se termină și dacă se întâlnește un alt jucător.

funcția killPlayer () numberOfLives = numberOfLives-1; dacă (numberOfLives == 0), atunci gameOver = adevărat doGameOver () else spawnNewPlayer () hideLives () showLives () playerIsInvincible = true end end

Mai întâi decrementăm numberOfLives de 1, și, dacă e egal cu 0, noi numim joc încheiat funcţie. Jucătorul are vieți rămase, sunăm spawnNewPlayer, urmat de hideLives, showLives, și stabilit playerIsInvincible la Adevărat.

16. doGameOver

doGameOver funcția spune storyboard-ul pentru a merge la joc încheiat scenă.

funcția doGameOver () storyboard.gotoScene ("gameover") sfârșitul

17. spawnNewPlayer

spawnNewPlayer funcția este responsabil pentru reproducerea unui nou jucător după ce a murit. Avionul jucătorului clipește câteva secunde pentru a arăta că este temporar invincibil.

funcția spawnNewPlayer () number localOfTimesToFadePlayer = 5 număr localOfTimesPlayerHasFaded = 0 funcție locală fadePlayer () player.alpha = 0; transition.to (jucător, time = 200, alpha = 1) numberOfTimesPlayerHasFaded = numberOfTimesPlayerHasFaded + 1 dacă (numberOfTimesPlayerHasFaded == numberOfTimesToFadePlayer) then playerIsInvincible = end end timer.performWithDelay (400, fadePlayer, numberOfTimesToFadePlayer) end

Pentru a face ca planul jucătorului să clipească, l-am stins și ieșit de cinci ori. În fadePlayer funcția, am setat avionul alfa proprietate la 0, ceea ce îl face transparentă. Apoi folosim biblioteca de tranziție pentru a estompa alfa înapoi la 1 pe o perioadă de 200 de milisecunde. la metodă a tranziție obiect ia un tabel de opțiuni. În exemplul nostru, tabelul cu opțiuni include un timp în milisecunde și proprietatea pe care dorim să o animăm, alfa, și valoarea dorită, 1.

Noi creștem numberOfTimesThePlayerHasFaded și verificați dacă este egal cu numărul de ori pe care am vrut ca jucătorul să-l estompeze. Apoi am stabilit playerIsInvincible la fals. Folosim un cronometru pentru a apela fadePlayer funcționează totuși de multe ori numberOfTimerToFadePlayer este egal cu.

Există o modalitate de a face toate aceste lucruri fără a utiliza temporizatorul și care este prin utilizarea tranziție„s iterații proprietate în combinație cu proprietatea onComplete handler. Citiți documentația pentru a afla mai multe despre această abordare alternativă.

18. checkEnemyPlaneCollidesWithPlayer

Există încă un test de coliziune pe care ar trebui să-l facem și asta pentru a vedea dacă un avion inamic se ciocnește cu avionul jucătorului.

funcția checkEnemyPlaneCollideWithPlayer () dacă (#enemyPlanes> 0), apoi pentru i = # enemyPlanes, 1, -1 face if (hasCollided (enemyPlanes [i], player)) then enemyPlanes [i]: removeSelf () enemyPlanes [ table.remove (enemyPlanes, i) dacă (playerIsInvincible == false) apoi killPlayer () end end

Ne batem prin avioanele inamice și vedem dacă unul dintre ele se ciocnește cu avionul jucătorului. Dacă este adevărat, eliminăm planul inamic și sunăm killPlayer. Dacă credeți că face jocul mai interesant, puteți genera și o explozie aici.

19. exitScene

Când jocul se termină, trecem la joc încheiat scenă. Rețineți din mai devreme în tutorial, exitScene funcția este aceea în care eliminați ascultătorii de evenimente, opriți cronometrele și opriți redarea audio.

("touch", movePlane) rectLeft: removeEventListener ("touch", movePlane) rectRight: removeEventListener ("atinge" , movePlane) audio.stop (planeSoundChannel) audio.dispunere (planeSoundChannel) Durata de executie: removeEventListener ("enterFrame", gameLoop) cancelTimers () scena finala: addEventListener 

În esență, ne rănim ceea ce am făcut în enterScene funcţie. Noi numim dispune metoda pe audio obiect pentru a elibera memoria asociată cu canalul audio. apel Stop singurul nu eliberează memoria.

20. cancelTimers

După cum indică și numele, cancelTimers funcția face opusul  startTimers, anulează toate cronometrele.

funcția cancelTimers () timer.cancel (firePlayerBulletTimer) timer.cancel (generateIslandTimer) timer.cancel (fireEnemyBulletsTimer) timer.cancel (generateFreeLifeTimer) sfârșit

21. Joc peste scenă

Este timpul să creați joc încheiat scenă. Începeți prin adăugarea unui nou fișier Lua proiectului dvs. numit gameover.lua, și adăugați codul următor.

scriptura locală = necesită ("storyboard") scenă locală = storyboard.newScene () joc localOverText local newGameButton scenă de întoarcere 

22. createScene

Adăugați următoarele la gameover.lua de mai sus întoarceți scena. De aici, toate codurile trebuie plasate deasupra întoarceți scena afirmație.

(0, 0, display.contentWidth, display.contentHeight) fundal: setFillColor (0, .39, .75) grup: inserați (fundal) jocOverText = display.newText ("Game Over", display.contentWidth / 2,400, native.systemFont, 16) gameOverText: setFillColor (1, 1, 0) gameOverText.anchorX = .5 groupOverText.anchorY = .5 group: insert ) newGameButton = display.newImage ("newgamebutton.png", 264,670) grup: introduceți (newGameButton) newGameButton.isVisible = sfarsit false

Așa cum am făcut în cele două scene anterioare, le dăm joc încheiat scena cu un fundal albastru. Apoi creăm un TextObject exemplu prin apel newText pe afişa. newText metoda ia câteva opțiuni, textul pentru obiect, poziția sa și fontul de utilizat. Îi dăm o culoare galbenă invocând setFillColor, trecând în valori RGB ca procente. În cele din urmă, creăm un buton și îl ascundem pentru moment.

23. enterScene

Când tabloul de bord a trecut pe deplin la joc încheiat scena, enterScene se numește metoda.

În enterScene, eliminăm scena anterioară din storyboard. Folosim metoda de comoditate scaleTo de la Biblioteca de tranziție pentru a scala gameOverText cu un factor de 4. Adăugăm un onComplete ascultător al tranziției care cheamăshowButton după terminarea tranziției. În cele din urmă, adăugăm un ascultător al evenimentului la butonul de joc care invocă startNewGame funcţie.

(de exemplu, gameSpace = 4.0, yScale = 4.0, time = 2000, onComplete = showButton)) newGameButton: addEventListener (" tap ", startNewGame)

24. showButton

showButton funcția ascunde gameOverText și arată newGameButton.

 funcția showButton () gameOverText.isVisible = false newGameButton.isVisible = sfârșitul adevărat

25. startNewGame

startNewGame funcția spune tabloului de bord să treacă la gamelevel scenă.

funcția startNewGame () storyboard.gotoScene ("gamelevel") sfârșitul

26. exitScene

Trebuie să facem o curățare când plecăm joc încheiat scenă. Eliminăm ascultătorul evenimentului de la robinet pe care l-am adăugat mai devreme la newGameButton.

scena funcției: exitScene (eveniment) grup local = auto.view newGameButton: removeEventListener ("atingeți", startNewGame) sfârșit

27. Adăugați ascultători de scenă

Ultimul fragment al puzzle-ului este adăugarea ascultătorilor evenimentului despre care am vorbit mai devreme. Pentru aceasta, adăugați următorul fragment de cod în gameover.lua.

scena: addEventListener ("createScene", scenă) scena: addEventListener ("enterScene", scenă) scena: addEventListener ("exitScene"

Concluzie

Am ajuns la finalul acestei serii și acum avem un joc de luptă pe deplin funcțional. Sper că ați găsit aceste tutoriale utile și ați învățat ceva de-a lungul drumului. Vă mulțumim pentru lectură.


Cod