Concevoir un jeu d'exploration dans l'espace - Partie 02
< < Concevoir un jeu d'exploration dans l'espace - Partie 01
Concevoir un jeu d'exploration dans l'espace - Partie 03 > >
Ce tutoriel a été réalisé avec la version 3.1.7 du moteur Dina.
Rappel du tutoriel précédent
Notre objectif est de concevoir un jeu qui se composera des éléments suivants :
- un écran pour afficher un logo (celui de votre futur studio, par exemple)
- un écran principal
- un écran pour les crédits (toujours sympa de citer les personnes qui nous ont aidées, par exemple les créateurs d'images ou de musique)
- le jeu en lui-même qui sera découpé comme suit :
- une introduction (pour essayer de mettre le joueur dans l'ambiance du jeu)
- la phase d'exploration qui se limitera à éviter des astéroïdes
- une animation lorsqu'on decouvrira une planète
- un mini-jeu pour se poser sur la planète (de type Lunar-landing)
- un écran de fin
Pour l'instant, voici ce que nous avons :
- un écran pour afficher un logo
- un écran principal
- un début de jeu qui contient pour le moment :
- une phase de jeu où on peut déplacer notre vaisseau
Ajout des astéroïdes
Ici, nous allons faire apparaitre un astéroïde toutes les 3 secondes. Pour cela, nous allons rajouter, dans le fichier game.lua, les variables suivantes :
- une variable
asteroidTimerpour connaitre la durée écoulée - une variable
asteroidDelaypour connaitre le délai entre chaque astéroïde (les 3 secondes) - une table
Asteroidsqui contiendra tous les astéroïdes
Ces 3 variables doivent etre déclarées en dessous de la variable Dina.
local Asteroids = {}
local asteroidTimer = 0
local asteroidDelay = 3
Maintenant, dans la fonction Game:update, juste en dessous de la remise à zéro de la rotation du vaisseau, nous allons calculer le délai écoulé. Si on dépasse les 3 secondes (nextAsteroid), on devra créer un astéroïde.
asteroidTimer = asteroidTimer + dt
if asteroidTimer > asteroidDelay then
asteroidTimer = 0
-- Création de l'astéroïde
self:createAsteroid()
end
N"oubliez pas de remettre le timer à zéro !
Maintenant, lançons nous dans la création de notre astéroïde en rajoutant la fonction createAsteroid dans Game :
function Game:createAsteroid()
end
Ayant récupéré plusieurs images intéressantes, on va les utiliser de manière aléatoire.
Commençons par rajouter dans Game:createAsteroid comment récupérer un chiffre entre 1 et 9 (le nombre d'images récupérées).
local rnd = love.math.random(9)
Ensuite, nous allons créer une image dont l'origine sera son centre :
local asteroid = Dina("Image", "datas/images/game/rock_"..rnd..".png")
asteroid:centerOrigin()
Pour rendre le jeu un peu plus "fun", on va faire en sorte que les astéroïdes n'apparaissent pas tous au même endroit.
On va déterminer leur position de départ de manière aléatoire :
local x = love.math.random(Dina.width)
Dina.width correspond à la largeur de l'écran
On va ensuite les positionner au dessus de l'écran pour qu'ils ne soient pas visibles au moment de leur création.
local _, ah = asteroid:getDimensions()
asteroid:setPosition(x, 0 - ah)
Maintenant que notre astéroïde a été créé, on va lui donner une vitesse (comprise entre 1 et 10) et une rotation (comprise entre -5 et 5).
asteroid.vy = love.math.random(10)
asteroid.rspeed = love.math.random(-5, 5)
Et pour finir notre fonction createAsteroid, on va insérer notre nouvel astéroïde dans la table Asteroids :
table.insert(Asteroids, asteroid)
Si vous lancez le jeu, vous ne remarquerez aucun changement.
Vous pouvez toujours déplacer votre vaisseau mais aucun astéroïde n'apparait.
C"est tout à fait normal ! On les a créé mais ils ne bougent tout simplement pas. Et bien, changeons cela !
Dans la fonction Game:update, en dessous du calcul du délai écoulé, on va créer une variable gameover que l'on mettra à false. Elle nous servira plus tard.
local gameover = false
On va en profiter pour récupérer les dimensions du vaisseau :
-- Récupération de la position et de la dimension du vaisseau
local sx, sy = Ship:getPosition()
local sw, sh = Ship:getDimensions()
Ensuite, on va parcourir nos astéroïdes pour leur changer leur position et leur rotation.
for i = #Asteroids, 1, -1 do
local asteroid = Asteroids[i]
-- Traitement de la rotation
asteroid:setRotation(asteroid:getRotation() + asteroid.rspeed)
-- Calcul de la nouvelle position
local ax, ay = asteroid:getPosition()
ay = ay + asteroid.vy
asteroid:setPosition(ax, ay)
end
Vous l'avez peut-être remarqué mais nous parcourons la liste à l'envers.
La raison est simple : c'est pour pouvoir supprimer les astéroïdes qui ne doivent plus être affichés à l'écran.
Donc, toujours dans la boucle for et en-dessous du changement de position, juste avant le end, nous allons les supprimer :
local aw, ah = asteroid:getDimensions()
-- Si l'astéroïde est en dessous de l'écran, on le supprime
if (ay - ah) > Dina.height then
table.remove(Asteroids, i)
end
Maintenant, en lançant le jeu, vous voyez les astéroïdes se diriger vers vous.
Mais on va rajouter un petit quelque chose qui fera que ça devienne vraiment un jeu : les collisions.
On va tester si le vaisseau entre en collision avec un astéroïde à l'aide de la fonction CollideAABB. Si il y a collision, on affichera l'écran de fin.
Pour cela, nous allons rajouter les lignes ci-dessous juste après la récupération des dimensions de l'astéroïde :
if CollideAABB(ax - aw/2, ay - ah/2, aw, ah, sx - sw/2, sy - sh/2, sw, sh) then
gameover = true
end
Comme nous avons positionné l'origine des images au centre de celles-ci, on doit décaler leur position en X de la moitié de leur largeur et de la moitié de leur hauteur pour Y.
Enfin, juste après la boucle for, on va supprimer les astéroïdes restants et on changera l'état pour gameover.
if gameover then
-- Suppression des astéroïdes restants
for i = #Asteroids, 1, -1 do
Dina:removeComponent(Asteroids[i])
table.remove(Asteroids, i)
end
-- Changement de l'état
Dina:setState("gameover")
end
Vous devriez obtenir ceci pour la fonction Game:update :
function Game:update(dt)
-- on remet la rotation à 0 du vaisseau
Ship:setRotation(0)
local gameover = false
asteroidTimer = asteroidTimer + dt
if asteroidTimer > asteroidDelay then
asteroidTimer = 0
-- Création de l'astéroïde
self:createAsteroid()
end
-- Récupération de la position et de la dimension du vaisseau
local sx, sy = Ship:getPosition()
local sw, sh = Ship:getDimensions()
for i = #Asteroids, 1, -1 do
local asteroid = Asteroids[i]
-- Traitement de la rotation
asteroid:setRotation(asteroid:getRotation() + asteroid.rspeed)
-- Calcul de la nouvelle position
local ax, ay = asteroid:getPosition()
ay = ay + asteroid.vy
asteroid:setPosition(ax, ay)
-- Récupération des dimensions de l'asteroide
local aw, ah = asteroid:getDimensions()
-- Vérification de la collision entre le vaisseau et l'astéroïde
if CollideAABB(ax - aw/2, ay - ah/2, aw, ah, sx - sw/2, sy - sh/2, sw, sh) then
gameover = true
end
-- Si l'astéroïde est en dessous de l'écran, on le supprime
if (ay - ah) > Dina.height then
table.remove(Asteroids, i)
end
end
if gameover then
-- Suppression des astéroïdes restants
for i = #Asteroids, 1, -1 do
Dina:removeComponent(Asteroids[i])
table.remove(Asteroids, i)
end
-- Changement de l'état
Dina:setState("gameover")
end
Dina:update(dt, false)
end
Ecran de fin
Pour simplifier la création de cet écran de fin, on va détourner un peu le MenuManager.
Comme toujours, on reprend la structure du Logo pour la nommer GameOver.
On va rajouter une fonction local pour retourner au logo (à créer juste avant la fonction GameOver.load) :
local function ReturnToMenu()
Dina:setState("logo")
end
On crée ensuite notre MenuManager avec son titre dans la fonction GameOver.load :
local menu = Dina("MenuManager", 75)
menu:setTitle("Jeu terminé", Dina.height/4, "datas/font/Righteous-Regular.ttf", 60, Colors.WHITE, true, Colors.SILVER, 5, 5)
On ajoute ensuite une image de fond parmi 2 que j'ai trouvé :
local rnd = love.math.random(2)
menu:addImage("datas/images/gameover/blackhole_"..rnd..".jpg", Dina.width/2, Dina.height/2, true)
C"est ici qu'on va faire notre "détournement" :
menu:addItem("La civilisation s'est éteinte avec vous...", "datas/font/Exo2-Regular.ttf", 40, ReturnToMenu)
menu:addItem("Appuyer sur 'A' pour revenir au menu principal", "datas/font/Exo2-Regular.ttf", 16)
Nos items de menus ne serviront qu'à afficher les textes et seul le premier item fait appel à la fonction ReturnToMenu.
Comme cela, il nous suffit de rajouter la ligne ci-dessous pour qu'on passe à l'écran suivant avec simplicité.
menu:setNextKeys({"Keyboard", "all"}, {"Gamepad", "a"})
Il est fortement déconseillé de mettre
allpour le gamepad. Privilégiez un bouton plutôt qu'un axe ou que les triggers.
A ce stade, nous avons déjà un petit jeu d'exploration spatiale mais il manque une simple ligne pour afficher notre nouvel écran de fin de jeu.
Il faut donc rajouter le ligne suivante dans la fonction love.load du fichier main.lua :
Dina:addState("gameover", "menus/gameover")
Et vous obtiendrez ceci :

Mais on peut et on va l'améliorer !
Dans le tutoriel suivant, nous allons rajouter les éléments suivants :
- l'apparition d'une planète au bout de X secondes
- une introduction pour mettre le joueur dans l'ambiance du jeu
- et d'autres éléments rapides à mettre en place
Le code de ce tutoriel se trouve ici.
Me contacter