Maak een vliegtuiggevecht in Corona het voltooien van de gameplay

Wat je gaat creëren

Invoering

In het vierde en laatste deel van deze serie gaan we verder waar we gebleven waren in de vorige tutorial. We zullen vijandelijke vliegtuigen maken die de speler moet vermijden of schieten, en we zullen ook een game over het scherm maken.

1. generateEnemys

De generateEnemys functie genereert een getal tussen drie en zeven, en roept de generateEnemyPlane functioneer elke twee seconden voor vele keren numberOfEnemysToGenerate is gelijk aan. Voer het volgende codefragment in gamelevel.lua.

functie generateEnemys () numberOfEnemysToGenerate = math.random (3,7) timer.performWithDelay (2000, generateEnemyPlane, numberOfEnemysToGenerate) end

We moeten deze functie ook aanroepen in de enterScene methode zoals hieronder getoond.

functie scène: enterScene (event) --SNIP-- Runtime: addEventListener ("enterFrame", gameLoop) startTimers () generateEnemys () einde

Laten we eens kijken wat de implementatie is van generateEnemyPlane lijkt op.

2. generateEnemyPlane

De generateEnemyPlane functie genereert een vijandelijk vlak. Er zijn drie soorten vijandelijke vliegtuigen in dit spel.

  • regelmatig , beweegt in een rechte lijn over het scherm
  • Wankelen, beweegt in een golfpatroon op de x-as
  • jager, jaagt het vliegtuig van de speler achterna
function generateEnemyPlane () if (gameOver ~ = true) then local randomGridSpace = math.random (11) local randomEnemyNumber = math.random (3) local tempEnemy if (planeGrid [randomGridSpace] ~ = 0) en vervolgensEnemyPlane () retourneren else if ( randomEnemyNumber == 1) then tempEnemy = display.newImage ("enemy1.png", (randomGridSpace * 65) -28, -60) tempEnemy.type = "regular" elseif (randomEnemyNumber == 2) then tempEnemy = display.newImage ( "enemy2.png", display.contentWidth / 2 -playerWidth / 2, -60) tempEnemy.type = "waver" else tempEnemy = display.newImage ("enemy3.png", (randomGridSpace * 65) -28, -60) tempEnemy.type = "chaser" end planeGrid [randomGridSpace] = 1 table.insert (enemyPlanes, tempEnemy) planeGroup: insert (tempEnemy) numberOfEnemysGenerated = numberOfEnemysGenerated + 1; end if (numberOfEnemysGenerated == numberOfEnemysToGenerate) then numberOfEnemysGenerated = 0; resetPlaneGrid () timer.performWithDelay (2000, generateEnemys, 1) end end end

We controleren eerst of het spel nog niet is afgelopen. We genereren vervolgens een randomGridSpace, een getal tussen 1 en 11, en een willekeurig randomEnemyNumber, een getal tussen 1 en 3. De randomGridSpace wordt gebruikt om het vlak in een van de elf slots aan de bovenkant van het scherm op de x-as te plaatsen. Als je bedenkt dat het speelveld verdeeld is in elf secties, dan willen we alleen nieuwe vlakken plaatsen in een slot dat nog niet is ingenomen door een ander vlak. De planeGrid tabel bevat elf 0's en wanneer we een nieuw vlak in een van de slots plaatsen, stellen we de overeenkomstige positie in de tabel in 1 om aan te geven dat een slot is ingenomen door een vliegtuig.

We controleren of de index van de randomGridSpace in de tabel is niet gelijk aan 0. Als dat niet het geval is, weten we dat er momenteel een gokkast wordt gebruikt en we moeten niet doorgaan, dus we bellen generateEnemyPlane en terugkeren van de functie.

Vervolgens kijken we wat randomEnemyNumber is gelijk aan en ingesteld tempEnemy voor een van de drie vijandige afbeeldingen, geven we het ook een eigenschap van een van beide regelmatig, wankelen, of jager. Omdat Lua een dynamische taal is, kunnen we tijdens runtime nieuwe eigenschappen aan een object toevoegen. Vervolgens stellen we in welke index gelijk is randomGridSpace naar 1 in de planeGrid tafel.

We voegen in tempEnemy in de enemyPlanes tabel voor latere referentie en toename numberOfEnemysGenerated. Als numberOfEnemysGenerated is gelijk aan  numberOfEnemysToGenerate, we resetten numberOfEnemysGenerated naar 0, beroep doen op resetPlaneGrid, en stel een timer in die zal bellen generateEnemys opnieuw na twee seconden. Dit proces wordt herhaald zolang het spel nog niet voorbij is.

3. moveEnemyPlanes

Zoals je misschien al geraden had, de moveEnemyPlanes functie is verantwoordelijk voor het verplaatsen van de vijandelijke vliegtuigen. Afhankelijk van het vliegtuig type, de juiste functie wordt aangeroepen.

functie moveEnemyPlanes () if (#enemyPlanes> 0) then for i = 1, #enemyPlanes do if (enemyPlanes [i] .type == "regular") then moveRegularPlane (enemyPlanes [i]) elseif (enemyPlanes [i] .type == "waver") en dan moveWaverPlane (enemyPlanes [i]) else moveChaserPlane (enemyPlanes [i]) end end end end

Deze functie moet worden aangeroepen in de gameLoop functie.

function gameLoop () --SNIP-- checkFreeLifesOutOfBounds () checkPlayerCollidesWithFreeLife () moveEnemyPlanes () einde

4. moveRegularPlane

De moveRegularPlane verplaatst het vliegtuig eenvoudig over het scherm langs de y-as.

functie moveRegularPlane (plane) plane.y = plane.y + 4 end

5. moveWaverPlane

De moveWaverPlane functie verplaatst het vlak langs het scherm langs de y-as en, in een golfpatroon, over de x-as. Dit wordt bereikt door de cos functie van Lua's wiskundebibliotheek.

Als dit concept iets voor jou is, schreef Michael James Williams een geweldige inleiding tot Sinusoïdale beweging. Dezelfde concepten zijn van toepassing, het enige verschil is dat we gebruiken cosinus. Je zou moeten denken sinus bij het omgaan met de y-as en cosinus wanneer het om de x-as gaat.

functie moveWaverPlane (plane) plane.y = plane.y + 4 plane.x = (display.contentWidth / 2) + 250 * math.cos (numberOfTicks * 0.5 * math.pi / 30) einde

In het bovenstaande fragment gebruiken we de numberOfTicks variabel. We moeten dit telkens verhogen als het gameLoop functie wordt aangeroepen. Voeg het volgende toe als de allereerste regel in de gameLoop functie.

function gameLoop () numberOfTicks = numberOfTicks + 1 end

6. moveChaserPlane

De moveChaserPlane functie heeft het vliegtuig achtervolgen de speler. Het beweegt langs de y-as met een constante snelheid en het beweegt in de richting van de positie van de speler op de x-as. Bekijk de implementatie van moveChaserPlane Ter verduidelijking.

function moveChaserPlane (plane) if (plane.x < player.x)then plane.x =plane.x +4 end if(plane.x > player.x) en vervolgens plane.x = plane.x - 4 end plane.y = plane.y + 4 end

Als je het spel nu test, zou je de vliegtuigen over het scherm moeten zien bewegen.

7. fireEnemyBullets

Om de zoveel tijd willen we dat de vijandelijke vliegtuigen een kogel afvuren. We willen echter niet dat ze allemaal tegelijk schieten, dus kiezen we slechts een paar vliegtuigen om te vuren.

function fireEnemyBullets () if (#enemyPlanes> = 2) en local numberOfEnemyPlanesToFire = math.floor (# enemyPlanes / 2) local tempEnemyPlanes = table.copy (enemyPlanes) lokale functie fireBullet () local randIndex = math.random (#tempEnemyPlanes) lokaal tempBullet = display.newImage ("bullet.png", (tempEnemyPlanes [randIndex] .x + playerWidth / 2) + bulletWidth, tempEnemyPlanes [randIndex] .y + playerHeight + bulletHeight) tempBullet.rotation = 180 planeGroup: insert (tempBullet) table .Gebruik (enemyBullets, tempBullet); table.remove (tempEnemyPlanes, randIndex) einde voor i = 0, numberOfEnemyPlanesToFire do fireBullet () end end end

We controleren eerst om zeker te zijn van de enemyPlanes tafel heeft meer dan twee vliegtuigen. Als dat zo is, krijgen we de numberOfEnemyPlanes om te schieten door de lengte van de te nemen enemyPlanes tafel, deel het door twee en rond het naar beneden. We maken ook een kopie van de enemyPlanes tafel, zodat we het afzonderlijk kunnen manipuleren.

De fireBullet functie kiest een vlak uit de tempEnemyPlanes tafel en laat het vliegtuig een kogel schieten. We genereren een willekeurig getal op basis van de lengte van de tempEnemyPlanes tabel maken, een opsommingstekenafbeelding maken en deze positioneren met het vlak waarop het vlak zich bevindt randIndex in de tempEnemyPlanes tafel. Vervolgens verwijderen we dat vlak uit de tijdelijke tabel om ervoor te zorgen dat het de volgende keer niet opnieuw wordt gekozen fireBullet wordt genoemd.

We herhalen dit proces echter vele malen numerOfEnemyPlanesToFire is gelijk aan en roept de fireBullet functie.

We moeten de timer starten die deze functie af en toe oproept. Om dit te bereiken, voeg het volgende toe aan de startTimers functie.

function 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) einde

8. moveEnemyBullets

We moeten ook de kogels van de vijand verplaatsen die op het scherm verschijnen. Dit is vrij eenvoudig met behulp van het volgende codefragment.

functie moveEnemyBullets () if (#enemyBullets> 0) then for i = 1, # enemyBullets do enemyBullets [i]. y = enemyBullets [i] .y + 7 end end end

Roep deze functie aan in de gameLoop functie.

function gameLoop () --SNIP-- checkPlayerCollidesWithFreeLife () moveEnemyPlanes () moveEnemyBullets () einde

9. checkEnemyBulletsOutOfBounds

Naast het verplaatsen van de vijandelijke kogels, moeten we controleren wanneer de kogels van de vijand van het scherm zijn verdwenen en ze verwijderen wanneer ze dat doen. De implementatie van checkEnemyBulletsOutOfBounds moet nu vertrouwd aanvoelen.

functie checkEnemyBulletsOutOfBounds () if (#enemyBullets> 0) en dan voor i = # enemyBullets, 1, -1 do if (vijandBullets [i] .y> display.contentHeight) then enemyBullets [i]: removeSelf () enemyBullets [i] = nul table.remove (enemyBullets, i) einde end end end

Roep deze functie aan in de gameLoop functie.

function gameLoop () --SNIP-- moveEnemyBullets () checkEnemyBulletsOutOfBounds () end

10. checkEnemyPlanesOutOfBounds

We moeten ook controleren of de vijandelijke vliegtuigen van het scherm zijn verplaatst.

functie checkEnemyPlanesOutOfBounds () if (#enemyPlanes> 0) en dan voor i = # enemyPlanes, 1, -1 do if (enemyPlanes [i] .y> display.contentHeight) then enemyPlanes [i]: removeSelf () enemyPlanes [i] = nihil table.remove (enemyPlanes, i) end end end end

Roep deze functie aan in de gameLoop functie

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

11. checkPlayerBulletsCollideWithEnemyPlanes

De checkPlayerBulletCollidesWithEnemyPlanes functie gebruikt de hasCollided functie om te controleren of een van de kogels van de speler in botsing is gekomen met een van de vijandelijke vliegtuigen.

function checkPlayerBulletsCollideWithEnemyPlanes () if (#playerBullets> 0 and #enemyPlanes> 0) en dan voor i = # playerBullets, 1, -1 do voor j = # enemyPlanes, 1, -1 do if (hasCollided (playerBullets [i], enemyPlanes [ j])) then playerBullets [i]: removeSelf () playerBullets [i] = nihil table.remove (playerBullets, i) generateExplosion (enemyPlanes [j] .x, enemyPlanes [j] .y) enemyPlanes [j]: removeSelf ( ) enemyPlanes [j] = nihil table.remove (enemyPlanes, j) local explosion = audio.loadStream ("explosion.mp3") local backgroundMusicChannel = audio.play (explosion, fadein = 1000) end end end end end

Deze functie maakt gebruik van twee geneste voor lussen om te controleren of de objecten zijn gebotst. Voor elk van de playerBullets, we lopen door alle vliegtuigen in de enemyPlanes tafel en bel de hasCollided functie. Als er een botsing is, verwijderen we de kogel en het vliegtuig, noem het generateExplosion functie, en laad en speel een explosiegeluid.

Roep deze functie aan in de gameLoop functie.

function gameLoop () --SNIP-- checkEnemyBulletsOutOfBounds () checkEnemyPlanesOutOfBounds () checkPlayerBulletsCollideWithEnemyPlanes () einde

12. generateExplosion

De generateExplosion function gebruikt de SpriteObject-klasse van Corona. Sprites maken geanimeerde sequenties van frames mogelijk die zich op Image- of Sprite-bladen bevinden. Door afbeeldingen in één afbeelding te groeperen, kunt u bepaalde frames uit die afbeelding trekken en een animatiereeks maken.

functie generateExplosion (xPosition, yPosition) lokale opties = width = 60, height = 49, numFrames = 6 local explosionSheet = graphics.newImageSheet ("explosion.png", options) local sequenceData = name = "explosion", start = 1, count = 6, time = 400, loopCount = 1 local explosionSprite = display.newSprite (explosionSheet, sequenceData) explosionSprite.x = xPosition explosionSprite.y = yPosition explosionSprite: addEventListener ("sprite", explosionListener) explosionSprite: play () einde

De newImageSheet methode neemt als parameters het pad naar de afbeelding en een tabel met opties voor het Sprite-blad. De opties die we instellen zijn de breedte, de hoogte, en de numFrames, hoeveel individuele afbeeldingen dit blad vormen. Er zijn zes afzonderlijke explosiebeelden, zoals in de onderstaande afbeelding.

Vervolgens zetten we een tafel op, sequenceData, wat nodig is door de SpriteObject. We hebben de begin eigendom aan 1, de tellen naar 6, en tijd om 400.  De begin eigenschap is het frame waarop de animatie zal starten, de tellen is het aantal frames dat de animatie bevat, en de tijd eigenschap is hoe lang de animatie duurt om door te spelen.

We maken vervolgens de SpriteObject passeren in de explosionSheet en sequenceData, stel de x- en y-posities in en voeg een luisteraar toe aan de sprite. De luisteraar wordt gebruikt om de Sprite te verwijderen zodra de animatie is voltooid.

13. explosionListener

De explosionListener functie wordt gebruikt om de sprite te verwijderen. Als het evenement's fase eigendom is gelijk aan beëindigde, dan weten we dat de sprite zijn animatie heeft beëindigd en we deze kunnen verwijderen.

functie explosionListener (event) if (event.phase == "ended") then local explosion = event.target explosion: removeSelf () explosion = nil end end

14. checkEnemyBulletsCollideWithPlayer

De checkEnemyBulletsCollideWithPlayer controleert of een van de kogels van de vijand in botsing is gekomen met het vliegtuig van de speler.

functie checkEnemyBulletsCollideWithPlayer () if (#enemyBullets> 0) en dan voor i = # enemyBullets, 1, -1 do if (hasCollided (enemyBullets [i], player)) then enemyBullets [i]: removeSelf () enemyBullets [i] = nil table.remove (enemyBullets, i) if (playerIsInvincible == false) en killPlayer () end end end end end

We lopen door de enemyBullets tafel en controleer of een van hen is gebotst met de speler. Als dit waar is, verwijderen we die specifieke opsommingsteken en, zo ja playerIsInvincible is vals, we roepen aan killPlayer.

Roep deze functie aan in de gameLoop functie.

function gameLoop () --SNIP-- checkEnemyPlanesOutOfBounds () checkPlayerBulletsCollideWithEnemyPlanes () checkEnemyBulletsCollideWithPlayer () end

15. killPlayer

De killPlayer De functie is verantwoordelijk voor het controleren of het spel afgelopen is en het uitzetten van een nieuwe speler als dit niet het geval is.

function killPlayer () numberOfLives = numberOfLives- 1; if (numberOfLives == 0) dan gameOver = true doGameOver () else spawnNewPlayer () hideLives () showLives () playerIsInvincible = true end end

We verlagen eerst numberOfLives door 1, en, als het gelijk is aan 0, we noemen het spel is over functie. Als de speler nog levens heeft, bellen we spawnNewPlayer, gevolgd door hideLives, showLives, En instellen playerIsInvincible naar waar.

16. doGameOver

De doGameOver functie vertelt het storyboard om naar de spel is over tafereel.

functie doGameOver () storyboard.gotoScene ("gameover") einde

17. spawnNewPlayer

De spawnNewPlayer functie is verantwoordelijk voor het uitzetten van een nieuwe speler nadat deze is overleden. Het vlak van de speler knippert enkele seconden om aan te geven dat het tijdelijk onoverwinnelijk is.

function spawnNewPlayer () local numberOfTimesToFadePlayer = 5 local numberOfTimesPlayerHasFaded = 0 lokale functie fadePlayer () player.alpha = 0; transition.to (speler, time = 200, alpha = 1) numberOfTimesPlayerHasFaded = numberOfTimesPlayerHasFaded + 1 if (numberOfTimesPlayerHasFaded == numberOfTimesTo FadePlayer) then playerIsInvincible = false end end timer.performWithDelay (400, fadePlayer, numberOfTimesToFadePlayer) einde

Om het vlak van de speler te laten knipperen, vervagen we het vijf keer in en uit. In de fadePlayer functie, we plaatsen het vliegtuig alpha eigendom aan 0, waardoor het transparant is. Vervolgens gebruiken we de transitiebibliotheek om het alpha terug naar 1 over een periode van 200 milliseconden. De naar methode van de overgang object neemt een tabel met opties. In ons voorbeeld bevat de optietabel een tijd in milliseconden en de eigenschap die we willen animeren, alpha, en de gewenste waarde, 1.

We verhogen numberOfTimesThePlayerHasFaded en controleer of het gelijk is aan het aantal keren dat we wilden dat de speler vervaagde. We zijn dan begonnen playerIsInvincible naar vals. We gebruiken een timer om het te bellen fadePlayer functioneert echter vele malen numberOfTimerToFadePlayer is gelijk aan.

Er is een manier om dit allemaal te doen zonder de timer te gebruiken en dat is door het gebruik van de overgang's iteraties eigendom in combinatie met zijn onComplete handler. Lees de documentatie voor meer informatie over deze alternatieve aanpak.

18. checkEnemyPlaneCollidesWithPlayer

Er is nog een botsingcontrole die we zouden moeten doen en dat is om te zien of een vijandelijk vliegtuig botst met het vliegtuig van de speler.

functie checkEnemyPlaneCollideWithPlayer () if (#enemyPlanes> 0) en dan voor i = # enemyPlanes, 1, -1 do if (hasCollided (enemyPlanes [i], player)) then enemyPlanes [i]: removeSelf () enemyPlanes [i] = nil table.remove (enemyPlanes, i) if (playerIsInvincible == false) en killPlayer () end end end end end

We lopen door de vijandelijke vliegtuigen en kijken of een van hen botst met het vliegtuig van de speler. Als dat waar is, verwijderen we dat vijandelijke vliegtuig en bellen killPlayer. Als je denkt dat het de game interessanter maakt, kun je hier ook een explosie genereren.

19. exitScene

Wanneer het spel voorbij is, gaan we over naar de spel is over tafereel. Onthoud van eerder in de tutorial, de exitScene De functie is waar u eventlisteners verwijdert, timers stopt en audio die wordt afgespeeld stopt.

functiescène: exitScene (gebeurtenis) lokale groep = self.view rectUp: removeEventListener ("touch", movePlane) rectDown: removeEventListener ("touch", movePlane) rectLeft: removeEventListener ("touch", movePlane) rectRight: removeEventListener ("touch") , movePlane) audio.stop (planeSoundChannel) audio.dispose (planeSoundChannel) Runtime: removeEventListener ("enterFrame", gameLoop) cancelTimers () eindscène: addEventListener ("exitScene", scène) 

We zijn in feite ongedaan maken wat we deden in de enterScene functie. We noemen het vervreemden methode op de audio object om het geheugen vrij te geven dat is gekoppeld aan het audiokanaal. Roeping hou op alleen geeft het geheugen niet vrij.

20. cancelTimers

Zoals de naam al aangeeft, is de cancelTimers functie doet het tegenovergestelde van  startTimers, het annuleert alle timers.

function cancelTimers () timer.cancel (firePlayerBulletTimer) timer.cancel (generateIslandTimer) timer.cancel (fireEnemyBulletsTimer) timer.cancel (generateFreeLifeTimer) einde

21. Spel over scène

Het is tijd om het te maken spel is over tafereel. Begin met het toevoegen van een nieuw Lua-bestand aan uw project met de naam gameover.lua, en voeg de volgende code eraan toe.

lokaal storyboard = vereisen ("storyboard") lokale scène = storyboard.newScene () local gameOverText local newGameButton return scene 

22. createScene

Voeg het volgende toe aan gameover.lua bovenstaande terugkeer scène. Vanaf hier zou alle code boven de terugkeer scène uitspraak.

function scene: createScene (event) local group = self.view local background = display.newRect (0, 0, display.contentWidth, display.contentHeight) background: setFillColor (0, .39, .75) group: insert (background) gameOverText = display.newText ("Game Over", display.contentWidth / 2.400, native.systemFont, 16) gameOverText: setFillColor (1, 1, 0) gameOverText.anchorX = .5 gameOverText.anchorY = .5 group: insert (gameOverText ) newGameButton = display.newImage ("newgamebutton.png", 264.670) group: insert (newGameButton) newGameButton.isVisible = false end

Zoals we in de vorige twee scènes hebben gedaan, geven we de spel is over scène een blauwe achtergrond. We maken vervolgens een textobject bijvoorbeeld door te bellen newText op tonen. De newText methode neemt een paar opties, de tekst voor het object, de positie ervan en het lettertype dat moet worden gebruikt. We geven het een gele kleur door aan te roepen SetFillColor, RGB-waarden doorgeven als percentages. Ten slotte maken we een knop en verbergen deze voorlopig.

23. enterScene

Wanneer het storyboard volledig is overgegaan naar de spel is over scène, de enterScene methode wordt genoemd.

In enterScene, we verwijderen de vorige scène van het storyboard. We gebruiken de gemaksmethode scaleTo van de Transitiebibliotheek om het gameOverText met een factor 4. We voegen een toe onComplete luisteraar voor de overgang die deshowButton functie zodra de overgang is voltooid. Ten slotte voegen we een tikken gebeurtenis luisteraar toe aan de spelknop die de nieuw spel starten functie.

functie scène: enterScene (evenement) lokale groep = self.view storyboard.removeScene ("gamelevel") transition.scaleTo (gameOverText, xScale = 4.0, yScale = 4.0, time = 2000, onComplete = showButton) newGameButton: addEventListener (" tik op ", startNewGame) einde

24. showButton

De showButton functie verbergt de gameOverText en toont de newGameButton.

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

25. nieuw spel starten

De nieuw spel starten functie vertelt het storyboard om over te schakelen naar de gamelevel tafereel.

function startNewGame () storyboard.gotoScene ("gamelevel") einde

26. exitScene

We moeten opruimen als we weggaan spel is over tafereel. We verwijderen de tikgebeurtenisluisteraar die we eerder aan de. Hebben toegevoegd newGameButton.

function scene: exitScene (event) local group = self.view newGameButton: removeEventListener ("tap", startNewGame) einde

27. Voeg scène-luisteraars toe

Het laatste stukje van de puzzel is het toevoegen van de scène-evenement luisteraars waar we eerder over gesproken hebben. Voeg hiervoor het volgende codefragment toe aan gameover.lua.

scène: addEventListener (scène "createScene", scène): addEventListener (scène "enterScene", scène): addEventListener ("exitScene", scène)

Conclusie

We zijn aan het einde van deze serie gekomen en hebben nu een volledig functioneel vliegtuiggevecht. Ik hoop dat je deze tutorials nuttig hebt gevonden en onderweg iets hebt geleerd. Bedankt voor het lezen.