Dans cette série de didacticiels, nous allons apprendre à créer un jeu de tir spatial comme le jeu classique Space Defender. Continuer à lire!
Dans cette version de Space Defender, le joueur devra défendre son espace en tirant sur des ennemis. Chaque fois que le joueur réussit à détruire un ennemi, il gagne des points et lorsque le joueur a atteint 20 ou 40 points, son arme à feu reçoit une mise à niveau. Pour mélanger les choses, ce jeu enverra des packages bonus qui valent 5 points. Pour voir le jeu en action, regardez la courte vidéo ci-dessus.
Dans la première partie de cette série, nous avons appris à configurer notre application, à utiliser des polices personnalisées, à utiliser un scénarimage et à configurer notre menu principal. Dans la deuxième partie de cette série, nous allons apprendre à créer le gameplay de notre application. Alors, commençons!
Notre première étape consiste à créer un nouveau fichier appelé game.lua. Une fois créé, ouvrez le fichier dans votre éditeur préféré..
Puisque nous commençons une nouvelle scène, nous devons avoir besoin de bibliothèques. Nous utiliserons le moteur physique intégré à Corona SDK pour la détection de collision.
storyboard local = require ("storyboard") scène locale = storyboard.newScene () physique locale = require ("physique")
Après avoir configuré nos bibliothèques, nous allons configurer le moteur physique. Les paramètres ci-dessous définiront la gravité sur 0 (comme dans l'espace) et les itérations sur 16. La méthode setPositionIterations signifie que le moteur passera par 16 positions par image. Tout ce qui dépasse 16 peut nuire aux performances du jeu.
physics.start () physics.setGravity (0, 0) physics.setPositionIterations (16)
Bien que cette étape ne soit pas nécessaire pour ce didacticiel, il est judicieux de "créer" le générateur de nombres aléatoires. J'aime utiliser l'heure actuelle pour ensemencer le générateur.
math.randomseed (os.time ())
Nous allons maintenant définir certaines variables pour notre jeu. Chaque variable a un commentaire à côté d'elle expliquant le but de la variable.
local screenW, screenH, halfW, halfY = display.contentWidth, display.contentHeight, display.contentWidth * 0.5, display.contentHeight * 0.5 local gameover_returntomenu - en avant notre bouton de jeu - Configurer les paramètres de jeu motionx = 0; - Variable utilisée pour déplacer le caractère le long de l’axe des y = 10; - Contrôle la vitesse du navire playerScore = 0; - Définit le score du joueur playerLives = 20; - Définit le nombre de vies pour le lecteur slowEnemySpeed = 2375; - Définit la vitesse à laquelle les navires blancs se déplacent à travers l'écran slowEnemySpawn = 2400; - Définit le taux de réapparition des navires blancs fastEnemySpeed = 1875; - Définit la vitesse à laquelle les navires verts se déplacent à travers l'écran fastEnemySpawn = 1800; - Définit le taux de réapparition des navires verts bulletSpeed = 325; - Définit la vitesse à laquelle la balle parcourt l'écran bulletSpawn = 250; - Définit le taux de réapparition des balles
Après avoir créé les variables, nous allons configurer la scène dans la fonction scène: createScene
. Si vous vous rappelez de la partie 1, cette fonction est utilisée pour créer des éléments visuels et une logique de jeu. Dans une fonction ultérieure, nous ferons appel à ces fonctions pour lancer le jeu..
Dans le code suivant, nous créons le scène: createScene
fonction et en ajoutant les murs de fond et haut / bas. Les deux murs sont configurés en tant qu’objets physiques statiques pour empêcher le lecteur de sortir de l’écran..
scène de fonction: createScene (événement) groupe local = self.view - Configure les éléments visuels et les murs locaux bg = display.newImageRect ("images / BKG.png", 480, 320) bg.x = halfW bg.y = halfY groupe: insert (bg) topwall local = display.newRect (0,0, screenW, 20) topwall.y = -5 topwall: setFillColor (0,0,0) topwall.alpha = 0.01 physics.addBody (topwall, "statique ") groupe: insérer (topwall) local bottomwall = display.newRect (0,0, screenW, 20) bottomwall.y = 325 bottomwall: setFillColor (0,0,0) bottomwall.alpha = 0.01 physics.addBody (bottomwall," static ") group: insert (bottomwall) end
À l'intérieur de la même scène: createScene
fonction, mais après la fond
objet d'affichage, nous allons ajouter quatre autres objets d'affichage. Voici une explication du but de chaque objet.
btn_up
, btn_down
: Ces objets d'affichage agiront comme des boutons sur le côté gauche de l'écran et chaque objet déplacera le navire vers le haut ou le bas, respectivement. Cependant, ils ne sont utilisables que lorsque nous avons configuré la fonction de déplacement.ennemiBar
: Cet objet d'affichage est configuré en tant que capteur et réagit uniquement aux collisions physiques. Quand il réagit aux collisions, il enlève l'objet ennemi et soustrait un de la vie du joueur.local btn_up = display.newRect (0,0,75,160) btn_up: setReferencePoint (display.TopLeftReferencePoint) btn_up.x, btn_up.y = 0,0; btn_up.alpha = 0.01 groupe: insert (btn_up) local btn_down = display.newRect (0,0,75,160) btn_down: setReferencePoint (display.BottomLeftReferencePoint) btn_down.x, btn_down.y = 0, screenH; btn_down.alpha = 0.01 groupe: insérer (btn_down) ennemi localHitBar = display.newRect (-20,0,20,320) ennemisHitBar: setFillColor (0,0,0) ennemisHitBar.name = "ennemisHitBar" physics.addBody (ennemisHitBar,, isSensor = true) group: insert (ennemiHitBar)
Juste après l'objet d'affichage ennemisHitBar, nous allons ajouter des éléments d'interface graphique pour afficher le score du joueur et ses vies. Nous allons également afficher un texte à l'écran indiquant "Monter" et "Descendre" pour indiquer au joueur où il doit toucher pour déplacer le navire vers le haut ou le bas..
local gui_score = display.newText ("Score:"… playerScore, 0,0, "Kemco Pixel", 16) gui_score: setReferencePoint (display.TopRightReferencePoint) gui_score.x = screenW groupe: insert (gui_score) local gui_lives = display.newText ("Lives:"… playerLives, 0,0, "Kemco Pixel", 16) gui_lives: setReferencePoint (display.BottomRightereferPoint) gui_lives.x = screenW gui_lives.y = screenH groupe: insérer (gui_lives) local gui_moveup = displayWne.Text ( "Move Up", 0,050,100, "Kemco Pixel", 16) groupe: insérer (gui_moveup) local gui_movedown = display.newText ("Move Down", 0,0,50,23, "Kemco Pixel", 16 ) gui_movedown: setReferencePoint (display.BottomLeftReferencePoint) gui_movedown.y = groupe screenH: insert (gui_movedown)
Ensuite, nous ajouterons le vaisseau du joueur à l'écran. Le vaisseau sera ajouté en tant qu’objet de physique dynamique afin de pouvoir réagir aux collisions avec d’autres objets de physique. Nous approfondirons les collisions plus loin dans ce tutoriel..
local ship = display.newImageRect ("images / spaceShip.png", 29, 19) ship.x, ship.y = 75, 35 ship.name = "ship" physics.addBody (ship, "dynamique", friction = 0.5, bounce = 0) group: insert (ship)
Vous souvenez-vous du btn_up
et btn_down
afficher les objets que nous avons ajoutés? Nous allons maintenant ajouter des écouteurs d'événements à ces objets pour faciliter le déplacement du joueur. Quand btn_up
est touché, nous allons rendre notre vitesse variable négative et quand btn_down
est touché nous allons rendre notre vitesse positive. En rendant cette variable positive et négative, nous disons à notre prochaine fonction de faire monter ou descendre le navire.
-- Lorsque vous appuyez sur le bouton haut, définissez notre mouvement pour déplacer la fonction d'expédition vers le haut. Btn_up: touch () motionx = -speed; end btn_up: addEventListener ("touch", btn_up) - Lorsque le bouton Bas est touché, définissez notre mouvement pour déplacer la fonction de navire vers le bas btn_down: touch () motionx = speed; end btn_down: addEventListener ("touch", btn_down)
Après avoir ajouté des auditeurs d'événements à notre btn_up
et btn_down
afficher des objets, nous allons créer deux écouteurs d’événements d’exécution avec leurs fonctions respectives. Ces fonctions exécuteront chaque image et la seule capture avec les fonctions d'exécution est que vous devez spécifier quand les arrêter. Nous couvrirons cela plus tard. Pour l'instant, la fonction stop va définir la variable motionx
à 0 (car aucun bouton n'est touché) et le moveguy
la fonction ajoutera la variable motionx
à notre navire y
position.
fonction locale stop (event) si event.phase == "terminé" alors motionx = 0; end end Runtime: addEventListener ("touch", stop) - Cette fonction déplacera le navire en fonction de la fonction de mouvement locale moveguy (événement) ship.y = ship.y + motionx; end Runtime: addEventListener ("enterFrame", moveguy)
Nous avons maintenant notre navire en mouvement, mais ce n'est pas le tir! Pour que le navire soit prêt à tirer des balles, nous devons créer le fireShip ()
une fonction. Cette fonction créera de nouveaux objets d'affichage réagissant aux collisions physiques et déplacera également l'objet sur l'écran de gauche à droite..
Pour rendre le jeu plus intéressant, nous allons permettre au joueur de tirer plus de balles quand il atteint un certain score. Lorsque le joueur atteint 20, le navire tire deux balles et lorsque le joueur atteint 40, le navire tire une troisième balle qui tire vers le bas en diagonale.
fonction fireShip () bullet = display.newImageRect ("images / bullet.png", 13, 8) bullet.x = ship.x + 9 bullet.y = ship.y + 6 bullet: toFront () bullet.name = " bullet "physics.addBody (bullet, isSensor = true) transition.to (bullet, time = bulletSpeed, x = 500, onComplete = fonction (self) self.parent: remove (self); self = nil; end; ) if (playerScore> = 20) alors secondBullet = display.newImageRect ("images / bullet.png", 13, 8) secondBullet.x = ship.x + 9 secondBullet.y = ship.y + 12 secondBullet: toFront ( ) secondBullet.name = "bullet" physics.addBody (secondBullet, isSensor = true) transition.to (secondBullet, time = bulletSpeed, x = 500, onComplete = fonction (self) self.parent: remove (auto); self = nil; end;) end if (playerScore> = 40), alors thirdBullet = display.newImageRect ("images / bullet.png", 13, 8) thirdBullet.x = ship.x + 9 thirdBullet.y = ship. y + 12 thirdBullet: toFront () thirdBullet.name = "bullet" physics.addBody (thirdBullet, isSensor = true) transition.to (thirdBullet, time = bulletSpeed, x = 500, y = ship.y + 100, onComplete = fonction (self) self.parent: remove (self); soi = nul; fin; )
Une fois que nous avons configuré notre vaisseau pour tirer, nous devons donner au joueur des ennemis sur lesquels tirer! Nous allons créer deux fonctions différentes - createSlowEnemy ()
et createFastEnemy ()
. Les deux fonctions créeront un objet d'affichage physique qui se déplace de droite à gauche avec la vitesse de l'ennemi et l'image étant la seule différence..
function createSlowEnemy () ennemis = display.newImageRect ("images / ennemis.png", 32, 26) ennemis.rotation = 180 ennemis.x = 500 ennemis.y = math.random (10, screenH-10) ennemis.nom = "ennemis" physics.addBody (ennemi, isSensor = true) transition.to (ennemi, heure = slowEnemySpeed, x = -20) fin de la fonction createFastEnemy () ennemi = display.newImageRect ("images / fastEnemy.png" , 32, 26) ennemis.rotation = 180 ennemis.x = 500 ennemis.y = math.random (10, screenH-10) ennemis.nom = "ennemis" physics.addBody (ennemis, isSensor = true). to (ennemi, time = fastEnemySpeed, x = -20) end
Ensuite, nous allons créer des packages de bonus que notre joueur pourra saisir dans la fonction. createBonus ()
. le createBonus ()
la fonction créera des objets d'affichage physique qui se déplacent de droite à gauche et chaque package de bonus que le joueur récupère gagne 5 points.
function createBonus () bonus = display.newImageRect ("images / bonus.png", 18, 18) bonus.rotation = 180 bonus.x = 500 bonus.y = math.random (10, screenH-10) bonus.name = "bonus" physics.addBody (bonus, isSensor = true) transition.to (bonus, time = 1475, x = -20, onComplete = fonction () display.remove (bonus) bonus = nil end;) fin
Notre dernière fonction est la fonction updateLives (). Cette fonction sera appelée chaque fois qu'un ennemi dépasse le joueur pour lui donner le but de défendre son côté de l'espace. Si le nombre de vies est supérieur à 0, cette fonction soustraira une vie et mettra à jour le texte à l'écran. Sinon, il en résultera une scène de game over.
Dans la scène de la fin de la partie, nous annulons tous nos chronomètres et retirons tous les écouteurs de nos événements. Avec le SDK Corona, il est très important de vous rappeler que vous devez explicitement indiquer à votre application à quel moment il faut supprimer les écouteurs d'exécution et les minuteurs (uniquement lorsque le minuteur est en cours d'exécution). Une fois que ceux-ci ont été supprimés, nous afficherons un message de fin de partie pour permettre au joueur de revenir au menu..
function updateLives () if (playerLives> = 0) alors playerLives = playerLives - 1 gui_lives.text = "Lives:"… playerLives gui_lives.x = screenW min timer.cancel (tmr_fireShip) timer.cancel (tmr_sendSlowSemennes). ) timer.cancel (tmr_sendFastEnemies) timer.cancel (tmr_sendBonus) Durée: removeEventListener ("collision", onCollision) Durée: removeEventListener ("enterFrame", moveguy) Durée: removeEventListener ("stop") - Affichage de récit de défense gameover_message = display.newText ("Game Over!", 0,0, "Kemco Pixel", 32) gameover_message.x = halfW gameover_message.y = halfY - 15 groupe: insertion (gameover_message) fonction returnToMenuTouch (événement) si (événement. phase == "a commencé") puis storyboard.gotoScene ("menu", "slideRight", "1000") fin end gameover_returntomenu = display.newText ("Retour au menu", 0,0, "Kemco Pixel", 28) gameover_returntomenu .x = halfW gameover_returntomenu.y = halfY + 35 gameover_returntomenu: addEventListener ("touch", returnToMenuTouch) groupe: dans sert (gameover_returntomenu) end end
Nous sommes prêts pour notre fonction finale à l'intérieur de notre scène: fonction createScene ()! Cette fonction gérera toute notre détection de collision en comparant la propriété mon nom
de object1 à celui de l'objet 2. Chaque objet est passé en paramètre à cette fonction sous le nom de la variable un événement
.
Pour vous faciliter la tâche, j'ai décomposé les cinq cas de collision..
function onCollision (event) if (event.object1.name == "bullet" et event.object2.name == "ennemi") puis display.remove (event.object2) playerScore = playerScore + 1 elseif (event.object1.name == "ennemi" et event.object2.name == "bullet") puis display.remove (event.object1) playerScore = playerScore + 1 elseif (event.object1.name == "ship" et event.object2.name = = "bonus") puis display.remove (event.object2) playerScore = playerScore + 5 elseif (event.object1.name == "ennemis" et event.object2.name == "ennemisHitBar") puis display.remove (événements. object1) updateLives () elseif (event.object1.name == "ennemisHitBar" et event.object2.name == "ennemis"), puis display.remove (event.object2) updateLives () et finit gui_score.text = "Score:" … PlayerScore gui_score.x = screenW end
Puisque tout est prêt pour notre jeu, il suffit de tout faire bouger! À l'intérieur de la fonction scène: enterScene ()
- rappelez-vous que le enterScene
la fonction est en dehors de la createScene
fonction - nous allons créer 5 timers et un auditeur d’exécution. Les timers enverront les balles, les ennemis et les bonus tandis que l'auditeur d'exécution traitera la détection de collision.
fonction scène: enterScene (événement) groupe local = self.view tmr_fireShip = timer.performWithDelD (bulletSpawn, fireShip, 0) tmr_sendSlowEnemies = timer.perform createSlowEnemy, 0) tmr_sendFastEnemies = timer.performWithDelay (fastEnemySpawn, createFastEnemy, 0) tmr_sendBonus = timer.performWithDelay (2500, createBonus, 0) Durée: addEventListener ("collision", fin)
Le dernier ajout (promis!) Est le scène: destroyScene ()
fonction et les auditeurs d'événements de la scène. La fonction de destruction de la scène s'assurera que la physique est supprimée une fois que le joueur quitte la scène. Les auditeurs de la scène appellent le createScene
, enterScene
, et destroyScene
respectivement.
scène de fonction: destroyScene (événement) groupe local = self.view package.loaded [physics] = nil physics = nil scène de fin: addEventListener ("createScene", scène) scène: addEventListener ("enterScene", scène) scène: addEventListener (" destroyScene ", scène) retour scène
Toutes nos félicitations! Vous avez appris beaucoup de choses telles que le storyboard, la physique, les collisions et bien plus encore de Corona! Ce sont des compétences précieuses qui peuvent être appliquées à presque tous les jeux. Si vous souhaitez créer ce jeu pour votre appareil, je vous recommande fortement les documents officiels de Corona sur la création d'un appareil..
Merci beaucoup d'avoir lu! Si vous avez des questions, s'il vous plaît laissez-les dans les commentaires ci-dessous.