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.
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.
generateEnemyPlane
De generateEnemyPlane
funksjon genererer ett fiendtlig fly. Det er tre typer fiendefly i dette spillet.
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 0
s 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.
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
moveRegularPlane
De moveRegularPlane
Flytter bare flyet nedover skjermen over y-aksen.
Funksjon moveRegularPlane (fly) planet.y = plan.y + 4 ende
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
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.
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
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
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
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
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
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.
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
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
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
.
doGameOver
De doGameOver
funksjonen forteller storyboardet å gå til spillet er slutt scene.
funksjon doGameOver () storyboard.gotoScene ("gameover") slutten
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.
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.
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.
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
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
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.
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
showButton
De showButton
funksjonen skjuler gameOverText
og viser newGameButton
.
funksjon showButton () gameOverText.isVisible = false newGameButton.isVisible = true end
startNewGame
De startNewGame
funksjonen forteller storyboardet til overgang til gamelevel scene.
funksjon startNewGame () storyboard.gotoScene ("gamelevel") slutten
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
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)
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.