Î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.
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.
generateEnemyPlane
generateEnemyPlane
funcția generează un avion inamic. Există trei tipuri de avioane inamice în acest joc.
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.
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
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
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
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.
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
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
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
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
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
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.
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
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
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
.
doGameOver
doGameOver
funcția spune storyboard-ul pentru a merge la joc încheiat scenă.
funcția doGameOver () storyboard.gotoScene ("gameover") sfârșitul
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ă.
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.
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.
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
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
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.
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)
showButton
showButton
funcția ascunde gameOverText
și arată newGameButton
.
funcția showButton () gameOverText.isVisible = false newGameButton.isVisible = sfârșitul adevărat
startNewGame
startNewGame
funcția spune tabloului de bord să treacă la gamelevel scenă.
funcția startNewGame () storyboard.gotoScene ("gamelevel") sfârșitul
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
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"
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ă.