Concevoir un jeu d'exploration dans l'espace - Partie 02

< < Concevoir un jeu d'exploration dans l'espace - Partie 01

Tutoriels

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 :

 

Pour l'instant, voici ce que nous avons :

 

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 :

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 all pour 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 :

Dina Space Explorer - Part 02

 

Mais on peut et on va l'améliorer !

Dans le tutoriel suivant, nous allons rajouter les éléments suivants :

 

Le code de ce tutoriel se trouve ici.



< < Concevoir un jeu d'exploration dans l'espace - Partie 01

Tutoriels

Concevoir un jeu d'exploration dans l'espace - Partie 03 > >