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.
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 à.
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..
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 0
et 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é.
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
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
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
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.
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
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
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
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
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
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..
explosionListener
le explosionListener
La fonction est utilisée pour supprimer le sprite. Si la un événement
de 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
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
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
.
doGameOver
le doGameOver
fonction raconte le storyboard pour aller à la jeu terminé scène.
fonction doGameOver () storyboard.gotoScene ("gameover") fin
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 transition
de itérations
propriété en combinaison avec ses onComplete
gestionnaire. Lisez la documentation pour en savoir plus sur cette approche alternative..
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.
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.
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
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
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.
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
showButton
le showButton
la fonction cache la gameOverText
et montre le newGameButton
.
fonction showButton () gameOverText.isVisible = false newGameButton.isVisible = true fin
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
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
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)
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.