Lag et flykampspill i Corona Finishing Gameplay

Hva du skal skape

Introduksjon

I den fjerde og siste delen av denne serien fortsetter vi hvor vi igjen i den forrige opplæringen. Vi skaper fiendtlige planer spilleren trenger for å unngå eller skyte, og vi vil også opprette et spill over skjermen.

1. generateEnemys

De generateEnemys funksjon genererer et tall mellom tre og sju, og kaller generateEnemyPlane funksjon hver to sekunder for mange ganger numberOfEnemysToGenerate er lik. Skriv inn følgende kodestykke til gamelevel.lua.

funksjon generateEnemys () numberOfEnemysToGenerate = math.random (3,7) timer.performWithDelay (2000, generateEnemyPlane, numberOfEnemysToGenerate) slutten

Vi må også påkalle denne funksjonen i enterScene metode som vist nedenfor.

funksjonsscene: enterScene (event) --SNIP-- Runtime: addEventListener ("enterFrame", gameLoop) startTimers () generateEnemys () end

La oss se hva implementeringen av generateEnemyPlane ser ut som.

2. generateEnemyPlane

De generateEnemyPlane funksjon genererer ett fiendtlig fly. Det er tre typer fiendefly i dette spillet.

  • Regelmessig , beveger seg nedover skjermen i en rett linje
  • Waver, beveger seg i et bølgemønster på x-aksen
  • Chaser, jager spillerenes plan
funksjon generateEnemyPlane () hvis (gameOver ~ = true) deretter lokal randomGridSpace = math.random (11) lokal randomEnemyNumber = math.random (3) lokal tempEnemy hvis (planeGrid [randomGridSpace] ~ = 0) og genererEnemyPlane randomEnemyNumber == 1) then tempEnemy = display.newImage ("enemy1.png", (randomGridSpace * 65) -28, -60) tempEnemy.type = "vanlig" elseif (randomEnemyNumber == 2) deretter tempEnemy = display.newImage "enemy2.png", display.contentWidth / 2 -playerWidth / 2, -60) tempEnemy.type = "waver" annet tempEnemy = display.newImage ("enemy3.png", (randomGridSpace * 65) -28, -60) tempEnemy.type = "chaser" endeplanGrid [randomGridSpace] = 1 table.insert (enemyPlanes, tempEnemy) planeGroup: insert (tempEnemy) numberOfEnemysGenerated = numberOfEnemysGenerated + 1; avslutt hvis (numberOfEnemysGenerated == numberOfEnemysToGenerate) deretter numberOfEnemysGenerated = 0; resetPlaneGrid () timer.performWithDelay (2000, genererEnemys, 1) endeendens ende

Vi kontrollerer først for å sikre at spillet ikke er over ennå. Vi genererer deretter en randomGridSpace, et tall mellom 1 og 11, og en tilfeldig randomEnemyNumber, et tall mellom 1 og 3. De randomGridSpace brukes til å plassere flyet i en av elleve spor på toppen av skjermen på x-aksen. Hvis du tenker på at spillområdet er delt inn i elleve seksjoner, vil vi bare plassere nye fly i et spor som ikke er tatt ennå av et annet fly. De planeGrid Tabellen inneholder elleve 0s og når vi plasserer et nytt fly i en av sporene, setter vi tilsvarende posisjon i tabellen til 1 for å indikere at sporet er tatt av et fly.

Vi sjekker om indeksen til randomGridSpace i tabellen er ikke lik 0. Hvis ikke, vet vi at sporet er tatt for øyeblikket, og vi bør ikke fortsette, så vi ringer generateEnemyPlane og gå tilbake fra funksjonen.

Deretter sjekker vi hva randomEnemyNumber er lik og sett tempEnemy til en av de tre fiendens bilder, gir vi det også til en eiendom regelmessig, vakle, eller chaser. Fordi Lua er et dynamisk språk, kan vi legge til nye egenskaper til et objekt ved kjøring. Vi angir så uansett indeksen er lik randomGridSpace til 1 i planeGrid bord.

Vi setter inn tempEnemy inn i det enemyPlanes bord for senere referanse og økning numberOfEnemysGenerated. Hvis numberOfEnemysGenerated er lik  numberOfEnemysToGenerate, vi tilbakestilles numberOfEnemysGenerated til 0, påberope resetPlaneGrid, og sett en tidtaker som vil ringe generateEnemys igjen etter to sekunder. Denne prosessen gjentar så lenge spillet ikke er over.

3. moveEnemyPlanes

Som du kanskje har gjettet, moveEnemyPlanes funksjonen er ansvarlig for å flytte fiendens fly. Avhengig av flyets type, Den aktuelle funksjonen kalles.

funksjon moveEnemyPlanes () hvis (#enemyPlanes> 0) så for i = 1, #enemyPlanes gjør hvis (fiendPlanes [i] .type == "regular") deretter moveRegularPlane (enemyPlanes [i]) elseif (enemyPlanes [i] .type == "waver") så moveWaverPlane (enemyPlanes [i]) else moveChaserPlane (enemyPlanes [i]) slutten slutten slutten

Denne funksjonen må påberopes i gameLoop funksjon.

funksjon gameLoop () --SNIP - checkFreeLifesOutOfBounds () checkPlayerCollidesWithFreeLife () moveEnemyPlanes () end

4. moveRegularPlane

De moveRegularPlane Flytter bare flyet nedover skjermen over y-aksen.

Funksjon moveRegularPlane (fly) planet.y = plan.y + 4 ende

5. moveWaverPlane

De moveWaverPlane funksjon flytter flyet nedover skjermen over y-aksen og i et bølgemønster over x-aksen. Dette oppnås ved å bruke cos funksjon av Luas matematikkbibliotek.

Hvis dette konseptet er fremmed for deg, skrev Michael James Williams en flott introduksjon til Sinusoidal Motion. De samme begrepene gjelder, den eneste forskjellen er at vi bruker cosinus. Du burde tenke sinus når du arbeider med y-aksen og cosinus når det gjelder x-aksen.

funksjon moveWaverPlane (fly) planet.y = planet.y + 4 plane.x = (display.contentWidth / 2) + 250 * math.cos (numberOfTicks * 0.5 * math.pi / 30) slutten

I den ovennevnte koden bruker vi numberOfTicks variabel. Vi må øke dette hver gang gameLoop funksjon kalles. Legg til følgende som den aller første linjen i gameLoop funksjon.

funksjon gameLoop () numberOfTicks = numberOfTicks + 1 end

6. moveChaserPlane

De moveChaserPlane funksjonen har flyet jage spilleren. Den beveger seg nedover y-aksen med konstant fart, og den beveger seg mot spillerens posisjon på x-aksen. Ta en titt på implementeringen av moveChaserPlane for avklaring.

funksjon moveChaserPlane (plan) hvis (plane.x < player.x)then plane.x =plane.x +4 end if(plane.x > player.x) så plane.x = plan.x - 4 endeplan.y = plan.y + 4 ende

Hvis du tester spillet nå, bør du se at flyene beveger seg nedover skjermen.

7. fireEnemyBullets

Hver så ofte vil vi at fiendens planer brenner en kule. Vi vil ikke at alle skal skyte på samme tid, men vi velger bare et par fly til ild.

funksjon fireEnemyBullets () hvis (#enemyPlanes> = 2) så lokalt nummerOfEnemyPlanesToFire = math.floor (# enemyPlanes / 2) lokal tempEnemyPlanes = table.copy (enemyPlanes) lokal funksjon fireBullet () local randIndex = math.random (#tempEnemyPlanes) tempBullet = display.newImage ("bullet.png", (tempEnemyPlanes [randIndex] .x + playerWidth / 2) + bulletWidth, tempEnemyPlanes [randIndex] .y + playerHight + bulletHeight) tempBullet.rotation = 180 planeGroup: sett inn (tempBullet) bord .insert (enemyBullets, tempBullet); table.remove (tempEnemyPlanes, randIndex) ende for i = 0, numberOfEnemyPlanesToFire gjør fireBullet () endeendens ende

Vi kontrollerer først for å sikre at enemyPlanes bordet har mer enn to fly i den. Hvis det gjør, får vi numberOfEnemyPlanes å brenne ved å ta lengden på enemyPlanes bord, divisjon det med to, og runde det ned. Vi lager også en kopi av enemyPlanes bord, slik at vi kan manipulere det separat.

De fireBullet funksjonen velger et fly fra tempEnemyPlanes bord og gjør flyet til en kule. Vi genererer et tilfeldig tall basert på lengden på tempEnemyPlanes bord, opprett et kulebilde og plasser det ved å bruke hvilket fly som er på randIndex i tempEnemyPlanes bord. Vi fjerner deretter flyet fra det midlertidige bordet for å sikre at det ikke blir valgt igjen neste gang fireBullet er kalt.

Vi gjentar denne prosessen men mange ganger numerOfEnemyPlanesToFire er lik og ringe til fireBullet funksjon.

Vi må starte timeren som kaller denne funksjonen hver så ofte. For å oppnå dette, legg til følgende i startTimers funksjon.

funksjon 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) ende

8. moveEnemyBullets

Vi må også flytte fiendens kuler som er på skjermen. Dette er ganske enkelt ved hjelp av følgende kodestykke.

funksjon moveEnemyBullets () hvis (#enemyBullets> 0) så for i = 1, # enemyBullets gjør fiendeBullets [i]. y = enemyBullets [i] .y + 7 endeendens ende

Oppgi denne funksjonen i gameLoop funksjon.

funksjon gameLoop () --SNIP-- checkPlayerCollidesWithFreeLife () moveEnemyPlanes () moveEnemyBullets () ende

9. checkEnemyBulletsOutOfBounds

I tillegg til å flytte fiendens kuler må vi sjekke når fiendens kuler har gått på skjermen og fjern dem når de gjør det. Gjennomføringen av checkEnemyBulletsOutOfBounds burde føle seg kjent nå.

funksjonscheckEnemyBulletsOutOfBounds () hvis (#enemyBullets> 0) så for i = # enemyBullets, 1, -1 gjør hvis (fiendeBullets [i] .y> display.contentHeight) then enemyBullets [i]: removeSelf () enemyBullets [i] = nil table.remove (fiendeBullets, i) slutten slutten slutten

Oppgi denne funksjonen i gameLoop funksjon.

funksjon gameLoop () --SNIP-- moveEnemyBullets () checkEnemyBulletsOutOfBounds () slutten

10. checkEnemyPlanesOutOfBounds

Vi bør også sjekke om fiendens fly har flyttet på skjermen.

funksjonskontrollEnemyPlanesOutOfBounds () hvis (#enemyPlanes> 0) then for i = # enemyPlanes, 1, -1 gjør hvis (fiendPlanes [i] .y> display.contentHeight) then enemyPlanes [i]: removeSelf () enemyPlanes [i] = nul table.remove (fiendPlanes, i) slutten slutten slutten

Oppgi denne funksjonen i gameLoop funksjon

funksjon gameLoop () --SNIP-- moveEnemyBullets () checkEnemyBulletsOutOfBounds () checkEnemyPlanesOutOfBounds () end

11. checkPlayerBulletsCollideWithEnemyPlanes

De checkPlayerBulletCollidesWithEnemyPlanes funksjonen bruker hasCollided funksjon for å sjekke om noen av spillernes kuler har kollidert med noen av fiendens fly.

FunksjonskontrollPlayerBulletsCollideWithEnemyPlanes () hvis (#playerBullets> 0 og #enemyPlanes> 0) så for i = # playerBullets, 1, -1 gjør for j = # enemyPlanes, 1, -1 gjør hvis (hasCollided (playerBullets [i], enemyPlanes [ j])) da spillerBullets [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) lokal eksplosjon = audio.loadStream ("explosion.mp3") lokal bakgrunnMusicChannel = audio.play (eksplosjon, fadein = 1000) endeendens ende ende

Denne funksjonen bruker to nestede til løkker for å sjekke om objektene har kollidert. For hver av playerBullets, vi løper gjennom alle flyene i enemyPlanes bord og ring på hasCollided funksjon. Hvis det er en kollisjon, fjerner vi kulen og flyet, ring til generateExplosion funksjon, og last og spill en eksplosjonslyd.

Oppgi denne funksjonen i gameLoop funksjon.

funksjon gameLoop () --SNIP - checkEnemyBulletsOutOfBounds () checkEnemyPlanesOutOfBounds () checkPlayerBulletsCollideWithEnemyPlanes () slutten

12. generateExplosion

De generateExplosion funksjonen bruker Coronas SpriteObject-klasse. Sprites tillater animerte sekvenser av rammer som ligger på Image eller Sprite Sheets. Ved å gruppere bilder i et enkelt bilde, kan du trekke bestemte rammer fra bildet og lage en animasjonssekvens.

funksjon generereExplosion (xPosition, yPosition) lokale alternativer = bredde = 60, høyde = 49, numFrames = 6 lokale eksplosjonsark = graphics.newImageSheet ("explosion.png", alternativer) local sequenceData = name = "explosion" = 1, telle = 6, tid = 400, loopCount = 1 lokal eksplosjonSprit = display.newSprite (eksplosjonSkala, sekvensData) eksplosjonSprite.x = xStilling eksplosjonSprite.y = yStilling eksplosjonSprite: addEventListener (eksplosjonListener) eksplosjonSprit: spill () slutt

De newImageSheet Metoden tar som parametre banen til bildet og en tabell med alternativer for Sprite Sheet. Alternativene vi angir er bredde, de høyde, og numFrames, hvor mange individuelle bilder utgjør dette arket. Det er seks separate eksplosjonsbilder som vist på bildet nedenfor.

Deretter satte vi opp et bord, sequenceData, som er nødvendig av SpriteObject. Vi satte start eiendom til 1, de telle til 6, og tid til 400.  De start Egenskapen er rammen som animasjonen vil starte på, den telle er hvor mange rammer animasjonen inneholder, og tid Eiendommen er hvor lenge animasjonen tar for å spille gjennom.

Vi lager deretter SpriteObject passerer i explosionSheet og sequenceData, sett x- og y-posisjonene, og legg til en lytter til sprite. Lytten vil bli brukt til å fjerne sprite når den er ferdig med animasjonen.

1. 3. explosionListener

De explosionListener funksjonen brukes til å fjerne sprite. Hvis begivenhet's fase Eiendommen er lik endte, da vet vi at sprite er ferdig med animasjonen, og vi kan fjerne den.

funksjon eksplosjonListener (hendelse) hvis (event.phase == "endte") deretter lokal eksplosjon = event.target eksplosjon: removeSelf () eksplosjon = null ende

14. checkEnemyBulletsCollideWithPlayer

De checkEnemyBulletsCollideWithPlayer sjekker for å se om noen av fienderens kuler har kollidert med spillerens fly.

FunksjonskontrollEnemyBulletsCollideWithPlayer () hvis (#enemyBullets> 0) så for i = # enemyBullets, 1, -1 gjør hvis (hasCollided (enemyBullets [i], spiller)) then enemyBullets [i]: removeSelf () enemyBullets [i] = null table.remove (enemyBullets, i) hvis (playerIsInvincible == false) da killPlayer () slutten slutten endeenden

Vi sløyfe gjennom enemyBullets bord og sjekk om noen av dem har kollidert med spilleren. Hvis sant, fjerner vi den aktuelle kule, og hvis playerIsInvincible er falsk, vi påberoper killPlayer.

Oppgi denne funksjonen i gameLoop funksjon.

funksjon gameLoop () --SNIP - checkEnemyPlanesOutOfBounds () checkLayerBulletsCollideWithEnemyPlanes () checkEnemyBulletsCollideWithPlayer () slutten

15. killPlayer

De killPlayer funksjonen er ansvarlig for å kontrollere om spillet er over og gyte en ny spiller hvis det ikke er det.

funksjon killPlayer () numberOfLives = numberOfLives-1; hvis (numberOfLives == 0) then gameOver = true doGameOver () annet spawnNewPlayer () hideLives () showLives () playerIsInvincible = true end end

Vi første nedringning numberOfLives av 1, og, hvis det er lik 0, vi kaller spillet er slutt funksjon. Det spilleren har liv igjen, vi kaller spawnNewPlayer, etterfulgt av hideLives, showLives, og sett playerIsInvincible til ekte.

16. doGameOver

De doGameOver funksjonen forteller storyboardet å gå til spillet er slutt scene.

funksjon doGameOver () storyboard.gotoScene ("gameover") slutten

17. spawnNewPlayer

De spawnNewPlayer funksjonen er ansvarlig for å gyte en ny spiller etter at den er død. Spillerens fly blinker i noen sekunder for å vise at det er midlertidig uovervinnelig.

funksjon spawnNewPlayer () lokalnummerOfTimesToFadePlayer = 5 lokalnummerOfTimesPlayerHasFaded = 0 lokal funksjon fadePlayer () player.alpha = 0; overgang.to (spiller, time = 200, alfa = 1) numberOfTimesPlayerHasFaded = numberOfTimesPlayerHasFaded + 1 hvis (numberOfTimesPlayerHasFaded == numberOfTimesToFadePlayer) så playerIsInvincible = false end end timer.performWithDelay (400, fadePlayer, numberOfTimesToFadePlayer)

For å få spilleren til å blinke, blinker vi inn og ut fem ganger. I fadePlayer funksjon, vi satte flyets alfa eiendom til 0, som gjør det gjennomsiktig. Vi bruker deretter overgangsbiblioteket til å falme alfa tilbake til 1 over en periode på 200 millisekunder. De til metode av overgang objektet tar en tabell med alternativer. I vårt eksempel inneholder alternativtabellen en tid i millisekunder og eiendommen vi ønsker å animere, alfa, og ønsket verdi, 1.

Vi øker numberOfTimesThePlayerHasFaded og sjekk om det er lik antall ganger vi ønsket at spilleren skulle forsvinne. Vi setter da playerIsInvincible til falsk. Vi bruker en timer til å ringe fadePlayer Fungerer imidlertid mange ganger numberOfTimerToFadePlayer er lik.

Det er en måte å gjøre alt dette på uten å bruke timeren, og det er ved å bruke overgang's gjentakelser eiendom i kombinasjon med sin onComplete behandleren. Les gjennom dokumentasjonen for å lære mer om denne alternative tilnærmingen.

18. checkEnemyPlaneCollidesWithPlayer

Det er en ekstra kollisjonskontroll vi bør gjøre, og det er å se om et fiendtlig fly kolliderer med spillerens fly.

funksjonskontrollEnemyPlaneCollideWithPlayer () hvis (#enemyPlanes> 0) then for i = # enemyPlanes, 1, -1 gjør hvis (harCollided (enemyPlanes [i], spiller)) da fiendPlanes [i]: removeSelf () enemyPlanes [i] = null table.remove (enemyPlanes, i) hvis (playerIsInvincible == false) deretter killPlayer () slutten slutten endeenden

Vi går gjennom fiendens fly og ser om noen av dem kolliderer med spillerens fly. Hvis sant, fjerner vi det fiendens fly og ringer killPlayer. Hvis du tror det gjør spillet mer interessant, kan du også generere en eksplosjon her.

19. exitScene

Når spillet er over, overgår vi til spillet er slutt scene. Husk fra tidligere i opplæringen, den exitScene funksjonen er hvor du fjerner hendelseslyttere, stopper tidtakere og stopper lyden som spilles.

funksjonsscene: exitScene (event) lokal gruppe = self.view rectUp: removeEventListener ("touch", movePlane) rectDown: removeEventListener ("touch", movePlane) rectLeft: removeEventListener ("touch", movePlane) rectRight: removeEventListener , movePlane) audio.stop (planetSoundChannel) audio.dispose (planeSoundChannel) Runtime: removeEventListener ("enterFrame", spillLoop) cancelTimers () sluttscene: addEventListener ("exitScene", scene) 

Vi er i utgangspunktet å angre hva vi gjorde i enterScene funksjon. Vi kaller avhende metode på lyd motsette seg å frigjøre minnet som er tilknyttet lydkanalen. ringe Stoppe alene frigjør ikke minnet.

20. cancelTimers

Som navnet antyder, er cancelTimers funksjon gjør det motsatte av  startTimers, det avbryter alle tidtakere.

funksjon avbryte Timer () timer.cancel (firePlayerBulletTimer) timer.cancel (generateIslandTimer) timer.cancel (fireEnemyBulletsTimer) timer.cancel (generateFreeLifeTimer) slutten

21. Spill over scene

Det er på tide å lage spillet er slutt scene. Start med å legge til en ny Lua-fil i prosjektet ditt gameover.lua, og legg til følgende kode for den.

lokal storyboard = kreve ("storyboard") lokal scene = storyboard.newScene () lokal gameOverText lokal newGameButton retur scene 

22. createScene

Legg til følgende til gameover.lua ovenfor retur scene. Herfra skal all kode plasseres over retur scene uttalelse.

funksjonsscene: createScene (event) lokal gruppe = self.view lokal bakgrunn = display.newRect (0, 0, display.contentWidth, display.contentHeight) bakgrunn: setFillColor (0, .39, .75) gruppe: gameOverText = display.newText ("Spill over", display.contentWidth / 2,400, native.systemFont, 16) gameOverText: setFillColor (1, 1, 0) gameOverText.anchorX = .5 gameOverText.anchorY = .5 gruppe: Sett inn (gameOverText ) newGameButton = display.newImage ("newgamebutton.png", 264.670) gruppe: sett inn (newGameButton) newGameButton.isVisible = false end

Som vi gjorde i de to foregående scenene, gir vi spillet er slutt scene en blå bakgrunn. Vi lager deretter en TextObject eksempel ved å ringe newText på vise. De newText Metoden tar noen alternativer, teksten til objektet, dens posisjon og skrifttypen som skal brukes. Vi gir den en gul farge ved å påkalle setFillColor, sender inn RGB-verdier som prosentandel. Til slutt lager vi en knapp og gjemmer den for tiden.

23. enterScene

Når storyboardet har fullstendig overgått til spillet er slutt scene, den enterScene Metoden kalles.

I enterScene, Vi fjerner forrige scene fra storyboardet. Vi bruker bekvemmelighetsmetoden scaleTo fra overgangsbiblioteket for å skalere gameOverText med en faktor på 4. Vi legger til en onComplete lytter til overgangen som kallershowButton fungere når overgangen har fullført. Til slutt legger vi til en trykkhendelse lytter til spillknappen som påkaller startNewGame funksjon.

funksjonsscene: enterScene (event) lokal gruppe = self.view storyboard.removeScene (gamelevel) transition.scaleTo (gameOverText, xScale = 4.0, yScale = 4,0, tid = 2000, onComplete = showButton) newGameButton: addEventListener trykk ", startNewGame) slutten

24. showButton

De showButton funksjonen skjuler gameOverText og viser newGameButton.

 funksjon showButton () gameOverText.isVisible = false newGameButton.isVisible = true end

25. startNewGame

De startNewGame funksjonen forteller storyboardet til overgang til gamelevel scene.

funksjon startNewGame () storyboard.gotoScene ("gamelevel") slutten

26. exitScene

Vi må gjøre noe opprydding når vi forlater spillet er slutt scene. Vi fjerner taphendelse lytteren vi la til tidligere til newGameButton.

funksjonsscene: exitScene (event) lokal gruppe = self.view newGameButton: removeEventListener ("trykk", startNewGame) avslutte

27. Legg til scenelyttere

Det siste stykket av puslespillet er å legge til scenehendelse lytterne vi snakket om tidligere. For å gjøre dette, legg til følgende kodestykke til gameover.lua.

scene: addEventListener ("createScene" scene) scene: addEventListener ("enterScene" scene) scene: addEventListener ("exitScene" scene)

Konklusjon

Vi har kommet til slutten av denne serien og har nå et fullt funksjonelt fly kampspill. Jeg håper du har funnet disse opplæringene nyttige og har lært noe underveis. Takk for at du leste.