Créer un jeu de combat aérien dans Corona Terminer le gameplay

Ce que vous allez créer

introduction

Dans la quatrième et dernière partie de cette série, nous continuons là où nous avions laissé le tutoriel précédent. Nous allons créer des avions ennemis que le joueur doit éviter ou tirer, et nous créerons également un jeu sur écran.

1. générerEnemys

le générerEnemys la fonction génère un nombre entre Trois et Sept, et appelle le générerEnemyPlane fonctionner chaque deux secondes pour cependant plusieurs fois numberOfEnemysToGenerate est égal à. Entrez l'extrait de code suivant pour gamelevel.lua.

function generateEnemys () numberOfEnemysToGenerate = math.random (3,7) timer.performWithDelay (2000, generateEnemyPlane, numberOfEnemysToGenerate) fin

Nous devons également appeler cette fonction dans le enterScene méthode comme indiqué ci-dessous.

scène de fonction: enterScene (événement) --SNIP-- Runtime: addEventListener ("enterFrame", gameLoop) startTimers () generateEnemys () end

Voyons ce que la mise en œuvre de générerEnemyPlane ressemble à.

2. générerEnemyPlane

le générerEnemyPlane la fonction génère un avion ennemi. Il y a trois types d'avions ennemis dans ce jeu..

  • Ordinaire , se déplace vers le bas de l'écran en ligne droite
  • Vaciller, se déplace dans un motif d'onde sur l'axe des x
  • Chaser, poursuit l'avion du joueur
function generateEnemyPlane () if (gameOver ~ = true) alors local randomGridSpace = math.random (11) local randomEnemyNumber = math.random (3) local tempEnemy if (planeGrid [randomGridSpace] ~ = 0) puis generateEnemyPlane () renvoie le résultat suivant if ( randomEnemyNumber == 1) puis tempEnemy = display.newImage ("ennemis1.png", (randomGridSpace * 65) -28, -60) tempEnemy.type = "normal" elseif (randomEnemyNumber == 2) puis tempEnemy = display.newImage ( "ennemi2.png", display.contentWidth / 2 -playerWidth / 2, -60) tempEnemy.type = "waver" sinon tempEnemy = display.newImage ("ennemi3.png", (randomGridSpace * 65) -28, -60) tempEnemy.type = "chaser" end planeGrid [randomGridSpace] = 1 table.insert (ennemiPlanes, tempEnemy) planeGroup: insert (tempEnemy) numberOfEnemysGenerated = numberOfEnemysGenerated + 1; end if (numberOfEnemysGenerated == numberOfEnemysToGenerate), alors numberOfEnemysGenerated = 0; resetPlaneGrid () timer.performWithDelay (2000, generateEnemys, 1) end end end

Nous vérifions d’abord que le jeu n’est pas encore terminé. Nous générons ensuite un randomGridSpace, un nombre entre 1 et 11, et un hasard randomEnemyNumber, un nombre entre 1 et 3. le randomGridSpace est utilisé pour positionner le plan dans l’un des onze emplacements en haut de l’écran sur l’axe des x. Si vous pensez que la zone de jeu est divisée en onze sections, nous voulons uniquement placer de nouveaux avions dans une fente qui n'a pas encore été prise par un autre avion. le planeGrid la table contient onze 0et lorsque nous plaçons un nouvel avion dans l’un des slots, nous définissons la position correspondante dans le tableau sur 1 pour indiquer que la fente a été prise par un avion.

Nous vérifions si l'index de la randomGridSpace dans la table n'est pas égal à 0. Si ce n’est pas le cas, nous savons que cet emplacement est actuellement pris et nous ne devrions pas continuer. Nous appelons donc générerEnemyPlane et revenir de la fonction.

Ensuite, nous vérifions quoi randomEnemyNumber est égal et défini tempEnemy à l'une des trois images ennemies, nous lui donnons également une propriété ordinaire, vaciller, ou chasseur. Lua étant un langage dynamique, nous pouvons ajouter de nouvelles propriétés à un objet lors de l'exécution. Nous définissons ensuite quel que soit l'indice est égal à randomGridSpace à 1 dans le planeGrid table.

Nous insérons tempEnemy dans le plans ennemis tableau pour référence ultérieure et incrément numberOfEnemysGenerated. Si numberOfEnemysGenerated est égal à  numberOfEnemysToGenerate, nous réinitialisons numberOfEnemysGenerated à 0, invoquer réinitialiserPlaneGrid, et définir une minuterie qui appellera générerEnemys à nouveau après deux secondes. Ce processus se répète tant que le jeu n'est pas terminé.

3. moveEnemyPlanes

Comme vous l'avez peut-être deviné, le moveEnemyPlanes la fonction est responsable du déplacement des avions ennemis. En fonction de l'avion type, la fonction appropriée est appelée.

function moveEnemyPlanes () if (#enemyPlanes> 0) alors pour i = 1, #enemyPlanes faire si (ennemisPlans [i] .type == "normal") puis moveRégulateursPlane (ennemisPlans [i]) elseif (ennemisPlanes [i]. types == "waver") puis moveWaverPlane (ennemiPlanes [i]) sinon moveChaserPlane (ennemiPlanes [i]) fin fin fin fin

Cette fonction doit être appelée dans le gameLoop une fonction.

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

4. moveRegularPlane

le moveRegularPlane déplace simplement le plan en bas de l'écran sur l'axe des y.

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

5. moveWaverPlane

le moveWaverPlane function déplace le plan vers le bas de l’écran sur l’axe des y et, dans un motif d’onde, sur l’axe des x. Ceci est réalisé en utilisant le cos fonction de la bibliothèque mathématique de Lua.

Si ce concept vous est étranger, Michael James Williams a écrit une excellente introduction au mouvement sinusoïdal. Les mêmes concepts s'appliquent, la seule différence est que nous utilisons cosinus. Tu devrais penser sinus lorsqu'il s'agit de l'axe des y et cosinus lorsqu'il s'agit de l'axe x.

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

Dans l'extrait ci-dessus, nous utilisons le numberOfTicks variable. Nous devons incrémenter cela chaque fois que le gameLoop la fonction est appelée. Ajouter ce qui suit comme la toute première ligne du gameLoop une fonction.

fonction gameLoop () numberOfTicks = numberOfTicks + 1 end

6. moveChaserPlane

le moveChaserPlane la fonction a l'avion ciselure le joueur. Il descend l'axe des y à une vitesse constante et se déplace vers la position du joueur sur l'axe des x. Jetez un coup d’œil à la mise en œuvre de moveChaserPlane pour clarification.

fonction moveChaserPlane (plane) if (plane.x < player.x)then plane.x =plane.x +4 end if(plane.x > player.x) alors plane.x = plane.x - 4 extrémité plane.y = avion.y + 4 extrémité

Si vous testez le jeu maintenant, vous devriez voir les avions défiler vers le bas de l'écran.

7. fireEnemyBullets

De temps en temps, nous voulons que les avions ennemis tirent une balle. Cependant, nous ne voulons pas que tous tirent en même temps, alors nous ne choisissons que quelques avions à tirer..

function fireEnemyBullets () if (#enemyPlanes> = 2) puis numero localOfEnemyPlanesToFire = math.floor (# ennemiPlanes / 2) local tempEnemyPlanes = table.copy (ennemi) local fonction fireBullet () local randIndex = math.random (#tempEnemyPlanes) local tempBullet = display.newImage ("bullet.png", (tempEnemyPlanes [randIndex] .x + playerWidth / 2) + bulletWidth, tempEnemyPlanes [randIndex] .y + playerHeight + bulletHeight) tempBullet.rotation = 180 planeGroup: table (tempBullet) .insert (ennemisBullets, tempBullet); table.remove (tempEnemyPlanes, randIndex) end pour i = 0, numberOfEnemyPlanesToFire do fireBullet () end end end

Nous vérifions d’abord pour nous assurer que le plans ennemis la table a plus de deux plans. Si c'est le cas, nous obtenons le numberOfEnemyPlanes tirer en prenant la longueur de la plans ennemis table, divisez-la par deux et arrondissez-la. Nous faisons également une copie du plans ennemis table, afin que nous puissions le manipuler séparément.

le fireBullet fonction choisit un avion de la tempEnemyPlanes table et fait l'avion tirer une balle. Nous générons un nombre aléatoire basé sur la longueur de la tempEnemyPlanes table, créer une image de balle et la positionner en utilisant le plan qui est au randIndex dans le tempEnemyPlanes table. Nous retirons ensuite cet avion de la table temporaire pour nous assurer qu'il ne sera pas choisi à nouveau la prochaine fois. fireBullet est appelé.

Nous répétons ce processus cependant plusieurs fois numerOfEnemyPlanesToFire est égal à et appelle le fireBullet une fonction.

Nous devons démarrer le minuteur qui appelle cette fonction de temps en temps. Pour ce faire, ajoutez ce qui suit au startTimers une fonction.

function startTimers () firePlayerBulletTimer = timer.performWithDelay (2000, firePlayerBullet, -1) generateIslandTimer = timer.performWithDelay (5000, generateIsland, -1) , fireEnemyBullets, -1) fin

8. moveEnemyBullets

Nous devons également déplacer les balles ennemies affichées à l'écran. Ceci est assez simple en utilisant l'extrait de code suivant.

function moveEnemyBullets () if (#enemyBullets> 0) alors pour i = 1, # ennemiBullets font ennemiBullets [i]. y = balles ennemies [i] .y + 7 fin fin fin

Invoquer cette fonction dans le gameLoop une fonction.

fonction gameLoop () --SNIP-- checkPlayerCollidesWithFreeLife () moveEnemyPlanes () moveEnemyBullets () end

9. checkEnemyBulletsOutOfBounds

En plus de déplacer les balles ennemies, nous devons vérifier si les balles ennemies sont sorties de l'écran et les supprimer lorsqu'elles le font. L'implémentation de checkEnemyBulletsOutOfBounds devrait se sentir familier maintenant.

function checkEnemyBulletsOutOfBounds () if (#enemyBullets> 0), puis pour i = # ennemiBullets, 1, -1 faire si (ennemiBullets [i] .y> display.contentHeight) alors ennemiBullets [i]: supprimeSelf () ennemiBullets [i] = nil table.remove (ennemisBullets, i) fin fin fin fin

Invoquer cette fonction dans le gameLoop une fonction.

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

dix. checkEnemyPlanesOutOfBounds

Nous devrions également vérifier si les avions ennemis sont sortis de l'écran.

function checkEnemyPlanesOutOfBounds () if (#enemyPlanes> 0) puis pour i = # ennemiPlanes, 1, -1 faire si (ennemiPlanes [i] .y> display.contentHeight) alors ennemiPlanes [i]: supprime soi-même () ennemiPlanes [i] = nil table.remove (ennemisPlanes, i) fin fin fin fin

Invoquer cette fonction dans le gameLoop une fonction

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

11. checkPlayerBulletsCollideWithEnemyPlanes

le checkPlayerBulletCollidesWithEnemyPlanes la fonction utilise le hasCollided fonction permettant de vérifier si l'une des balles du joueur est entrée en collision avec l'un des avions ennemis.

function checkPlayerBulletsCollideWithEnemyPlanes () if (#playerBullets> 0 et #enemyPlanes> 0), puis pour i = # playerBullets, 1, -1 pour j = # ennemiPlanes, 1, -1 si (hasCollided (playerBullets [i], ennemis, 1), j])) puis playerBullets [i]: removeSelf () playerBullets [i] = nil table.remove (playerBullets, i) générerExplosion (ennemiPlanes [j] .x, ennemiPlanes [j] .y) ennemiPlanes [j]: se retirer ( ) ennemisPlanes [j] = nil table.remove (ennemis ennemis, j) explosion locale = audio.loadStream ("explosion.mp3") local backgroundMusicChannel = audio.play (explosion, fadein = 1000) fin fin fin fin

Cette fonction utilise deux imbriqués pour boucles pour vérifier si les objets sont entrés en collision. Pour chacun des playerBullets, nous courons à travers tous les avions dans le plans ennemis table et appelez le hasCollided une fonction. S'il y a une collision, nous retirons la balle et l'avion, appelez le générer une explosion fonction, et charge et joue un son d'explosion.

Invoquer cette fonction dans le gameLoop une fonction.

fonction gameLoop () --SNIP-- checkEnemyBulletsOutOfBounds () checkEnemyPlanesOutOfBounds () checkPlayerBulletsCollideWithEnemyPlanes () end

12. générer une explosion

le générer une explosion Cette fonction utilise la classe SpriteObject de Corona. Les sprites permettent d’animer des séquences d’images qui résident sur des feuilles d’image ou des sprites. En regroupant les images en une seule image, vous pouvez extraire certaines images de cette image et créer une séquence d'animation..

function generateExplosion (xPosition, yPosition) options locales = largeur = 60, hauteur = 49, numFrames = 6 explosionSheet locale = graphics.newImageSheet ("explosion.png", options) séquence localeData = nom = "explosion", début = 1, compte = 6, heure = 400, loopCount = 1 explosionSprite locale = display.newSprite (explosionSheet, sequenceData) explosionSprite.x = position xPosition explosionSprite.y = yPosition explosionSprite: addEventListener ("sprite", explosionListener) explosionSprite: play () fin

le newImageSheet La méthode prend comme paramètres le chemin de l’image et un tableau d’options pour la feuille Sprite. Les options que nous avons définies sont les largeur, la la taille, et le numFrames, combien d'images individuelles composent cette feuille. Il y a six images d'explosion distinctes, comme indiqué dans l'image ci-dessous..

Ensuite, nous mettons en place une table, sequenceData, qui est nécessaire par le SpriteObject. Nous avons mis le début propriété à 1, la compter à 6, et le temps de 400.  le début propriété est le cadre sur lequel l'animation commencera, la compter est le nombre d'images incluses dans l'animation et la temps propriété est combien de temps l'animation prend pour jouer à travers.

Nous créons ensuite le SpriteObject en passant dans le feuille d'explosion et sequenceData, définissez les positions x et y et ajoutez un écouteur à l'image-objet. L’auditeur sera utilisé pour supprimer le sprite une fois l’animation terminée..

13. explosionListener

le explosionListener La fonction est utilisée pour supprimer le sprite. Si la un événementde phase propriété est égale à terminé, alors nous savons que le sprite a terminé son animation et nous pouvons le supprimer.

fonction explosionListener (event) if (event.phase == "terminé"), puis explosion locale = event.target explosion: removeSelf () explosion = nil end end

14. checkEnemyBulletsCollideWithPlayer

le checkEnemyBulletsCollideWithPlayer vérifie si les balles des ennemis sont entrées en collision avec l'avion du joueur.

function checkEnemyBulletsCollideWithPlayer () if (#enemyBullets> 0), puis pour i = # ennemiBullets, 1, -1 faire si (hasCollide (ennemiBullets [i], joueur)), alors ennemiBullets [i]: supprimerSoi () ennemiBullets [i] = nil table.remove (ennemisBullets, i) si (playerIsInvincible == false), alors killPlayer () end end end end end

Nous parcourons le balles ennemies table et vérifiez si l’un d’entre eux est entré en collision avec le joueur. Si vrai, nous supprimons cette balle particulière, et, si playerIsInvincible est faux, nous invoquons killPlayer.

Invoquer cette fonction dans le gameLoop une fonction.

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

15. killPlayer

le killPlayer la fonction est chargée de vérifier si le jeu est terminé et de créer un nouveau joueur si ce n'est pas le cas.

fonction killPlayer () numberOfLives = numberOfLives- 1; if (numberOfLives == 0) alors gameOver = true doGameOver () else spawnNewPlayer () hideLives () showLives () playerIsInvincible = true fin fin

Nous décrémentons d'abord nombre de vies par 1, et, si c'est égal à 0, nous appelons le jeu terminé une fonction. Si le joueur a encore des vies, nous appelons spawnNewPlayer, suivi par hideLives, showLives, Et mettre playerIsInvincible à vrai.

16. doGameOver

le doGameOver fonction raconte le storyboard pour aller à la jeu terminé scène.

fonction doGameOver () storyboard.gotoScene ("gameover") fin

17. spawnNewPlayer

le spawnNewPlayer La fonction est responsable de la création d'un nouveau joueur après sa mort. L'avion du joueur clignote pendant quelques secondes pour indiquer qu'il est temporairement invincible.

function spawnNewPlayer () local numberOfTimesToFadePlayer = 5 local numberOfTimesPlayerHasFaded = 0 fonction locale fadePlayer () player.alpha = 0; transition.to (player, time = 200, alpha = 1) numberOfTimesPlayerHasFaded = numberOfTimesPlayerHasFaded + 1 if (nombre de jours);

Pour faire clignoter l'avion du joueur, nous le faisons fondre à cinq reprises. dans le fadePlayer fonction, nous définissons l'avion alpha propriété à 0, ce qui le rend transparent. Nous utilisons ensuite la bibliothèque de transition pour atténuer les effets alpha retour à 1 sur une période de 200 millisecondes. le à méthode du transition objet prend une table d'options. Dans notre exemple, la table des options inclut une durée en millisecondes et la propriété que nous souhaitons animer., alpha, et la valeur souhaitée, 1.

Nous incrémentons numberOfTimesThePlayerHasFaded et vérifiez si c'est égal au nombre de fois où nous avons voulu que le joueur disparaisse. Nous avons ensuite mis playerIsInvincible à faux. Nous utilisons une minuterie pour appeler le fadePlayer fonctionner cependant plusieurs fois numberOfTimerToFadePlayer est égal à.

Il existe un moyen de faire tout cela sans utiliser la minuterie, c’est-à-dire en utilisant le transitionde itérations propriété en combinaison avec ses onComplete gestionnaire. Lisez la documentation pour en savoir plus sur cette approche alternative..

18. checkEnemyPlaneCollidesWithPlayer

Une autre vérification de collision que nous devrions faire est de savoir si un avion ennemi entre en collision avec l'avion du joueur..

function checkEnemyPlaneCollideWithPlayer () if (#enemyPlanes> 0), puis pour i = # ennemiPlanes, 1, -1 faire si (hasCollided (ennemiPlanes [i], joueur)), puis ennemiPlanes [i]: supprime soi-même () ennemiPlanes [i] = nil table.remove (ennemisPlanes, i) si (playerIsInvincible == false), alors killPlayer () end end end end end

Nous parcourons les avions ennemis et voyons si l'un d'entre eux entre en collision avec l'avion du joueur. Si vrai, nous retirons cet avion ennemi et appelons killPlayer. Si vous pensez que cela rend le jeu plus intéressant, vous pouvez également générer une explosion ici.

19. exitScene

Lorsque le jeu est terminé, nous passons à la jeu terminé scène. Rappelez-vous que plus tôt dans le tutoriel, le exitScene la fonction est l'endroit où vous supprimez les écouteurs d'événements, arrêtez les minuteries et arrêtez l'audio en cours de lecture.

scène de fonction: exitScene (événement) groupe local = auto.view rectUp: removeEventListener ("touch", movePlane) rectDown: removeEventListener ("touch", movePlane) rectLeft: removeEventListener ("touch", movePlane) rectRight: removeEventListener ("touch") , movePlane) audio.stop (planeSoundChannel) audio.dispose (planeSoundChannel) Durée: removeEventListener ("enterFrame", gameLoop) cancelTimers () end scene: addEventListener ("exitScene", scene) 

Nous sommes en train de défaire ce que nous avons fait dans le enterScene une fonction. Nous appelons le disposer méthode sur le l'audio objet pour libérer la mémoire associée au canal audio. Appel Arrêtez seul ne libère pas la mémoire.

20. cancelTimers

Comme son nom l'indique, le cancelTimers la fonction fait le contraire de  startTimers, il annule tous les minuteries.

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

21. Game Over Scene

Il est temps de créer le jeu terminé scène. Commencez par ajouter un nouveau fichier Lua à votre projet nommé gameover.lua, et ajoutez le code suivant.

storyboard local = nécessite ("storyboard") scène locale = storyboard.newScene () scène locale gameOverText local newGameButton retour scène 

22. createScene

Ajouter ce qui suit à gameover.lua au dessus de scène de retour. A partir de là, tout le code devrait être placé au-dessus de la scène de retour déclaration.

scène de fonction: createScene (événement) groupe local = self.view local fond = display.newRect (0, 0, display.contentWidth, display.contentHeight) arrière-plan: setFillColor (0, .39, .75) groupe: insert (arrière-plan) gameOverText = display.newText ("Game Over", display.contentWidth / 2 400, native.systemFont, 16) gameOverText: setFillColor (1, 1, 0) gameOverText.anchorX = .5 gameOverText.anchorY = .5 groupe: insérer (gameOverText). ) newGameButton = display.newImage ("newgamebutton.png", 264,670) groupe: insérer (newGameButton) newGameButton.isVisible = false fin

Comme nous l'avons fait dans les deux scènes précédentes, nous donnons au jeu terminé scène un fond bleu. Nous créons ensuite un TextObject par exemple en appelant nouveauTexte sur afficher. le nouveauTexte La méthode prend quelques options, le texte de l'objet, sa position et la police à utiliser. Nous lui donnons une couleur jaune en invoquant setFillColor, en passant des valeurs RVB sous forme de pourcentages. Enfin, nous créons un bouton et le cachons pour le moment.

23. enterScene

Lorsque le storyboard est complètement passé à la jeu terminé scène, la enterScene la méthode s'appelle.

Dans enterScene, on enlève la scène précédente du storyboard. Nous utilisons la méthode de la commodité échellePour de la bibliothèque de transition à l'échelle du gameOverText par un facteur de 4. Nous ajoutons un onComplete auditeur à la transition qui appelle leshowButton fonctionner une fois la transition terminée. Enfin, nous ajoutons un écouteur d’événement tap au bouton de jeu qui invoque le Commencer une nouvelle partie une fonction.

scène de fonction: enterScene (événement) groupe local = self.view storyboard.removeScene ("gamelevel") transition.scaleTo (gameOverText, xScale = 4.0, yScale = 4.0, time = 2000, onComplete = showButton) newGameButton: addEventListener (" appuyez sur ", startNewGame) end

24. showButton

le showButton la fonction cache la gameOverText et montre le newGameButton.

 fonction showButton () gameOverText.isVisible = false newGameButton.isVisible = true fin

25. Commencer une nouvelle partie

le Commencer une nouvelle partie fonction indique au storyboard de passer à la gamelevel scène.

fonction startNewGame () storyboard.gotoScene ("gamelevel") end

26. exitScene

Nous devons faire un peu de nettoyage lorsque nous quittons la jeu terminé scène. Nous supprimons l'écouteur d'événement tap que nous avons ajouté précédemment à la newGameButton.

scène de fonction: exitScene (événement) groupe local = self.view newGameButton: removeEventListener ("tap", startNewGame) end

27. Ajouter des auditeurs de scène

La dernière pièce du casse-tête consiste à ajouter à la scène les auditeurs d'événements dont nous avons parlé plus tôt. Pour ce faire, ajoutez l’extrait de code suivant à gameover.lua.

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

Conclusion

Nous sommes arrivés à la fin de cette série et avons maintenant un jeu de combat aérien entièrement fonctionnel. J'espère que vous avez trouvé ces tutoriels utiles et que vous avez appris quelque chose en cours de route. Merci d'avoir lu.