Présentation du moteur de jeu LÖVE

 

 

 

SOMMAIRE

  • Presentation du moteur
  • Installation de LÖVE
  • Création de votre premier projet (Pong)
  •    1- Preparation
  •    2- Première classe : Entity
  •    3- Les Raquettes
  •    4- La Balle
  •    5- Le Score
  •    6- La Démarcation
  •    7- Timer

 

 

 

Presentation du moteur

 

LÖVE (ou Love2D) est un moteur de jeu 2D. Conçu en C++, il utilise Lua comme langage de programmation (déjà utilisé dans les jeux vidéo, notamment pour des addons de World of Warcraft, des mods Minecraft et dans Garrysmod).

Ce moteur de jeu est sous la licence zlib/libpng, ce qui veut dire qu’il est totalement gratuit même pour une utilisation commerciale. Une version du moteur appelée piLöve existe et comme son nom l’indique elle à été spécifiquement portée sur Raspberry Pi.

On retrouve souvent ce moteur dans les compétitions de développement de jeux vidéo comme la compétition internationale Lundum Dare.

 

 

 

 

 

Installation de Löve

 

Pour l’installation, rien de bien compliqué. Premièrement rendez-vous sur le site officiel de LÖVE « love2d.org », ensuite télécharger la version adaptée à votre système d’exploitation.

 

 

Lancez l’installer, cliquez sur suivant, acceptez les termes, choisissez l’endroit où vous voulez l’installer et enfin cliquez sur terminer.

Maintenant allez dans le dossier LÖVE là où vous avez choisi d’installer le moteur. Vous devriez avoir ces fichiers suivants.

 

 

Lancez donc l’application qui se nomme love. Si la fenêtre suivante se lance, félicitation ! Tout est fonctionnel vous pouvez créer votre premier projet Löve.

 

 

Création de votre premier projet

 

Maintenant, nous allons faire notre premier projet. Nous allons créer un Pong. Tout d’abord, Il faut que vous créiez un raccourci de l’application love que vous avez lancés juste avant sur votre bureau. Une fois ce raccourci prêt, créez un nouveau dossier (lui aussi sur votre bureau) et nommé le « Pong ». Puis, dans ce dossier, créez un nouveau fichier texte et nommé le « main.lua ». Techniquement, vous disposez actuellement d’un projet que vous pouvez lancer avec Löve pour cela, vous allez Glisser votre dossier Pong sur le raccourci love. Et voici ce qu’il va vous afficher :

 

 

Ce problème viens du fait que Löve a besoin que le « main.lua » soit vraiment un fichier de type .lua et si vous regardez le type de fichier, c’est encore un Document texte. Pour régler ce problème, rechercher dans le menu démarrer de Windows les « Options de l’Explorateur de fichiers ». Allez dans affichage et décochez dans paramètres avancés : « Masquer les extensions des fichiers dont le type est connu » (ligne bleue sur l’image suivante)

 

 

Appliquez les changements et normalement votre fichier « main.lua » c’est transformé en « main.lua.txt », supprimez le .txt pour avoir le « main.lua ». Vous pouvez maintenant glisser votre dossier Pong sur le raccourci löve.exe et désormais une fenêtre noire s’affiche. C’est normal puisque main.lua est totalement vide, nous pouvons commencer à coder.

Mais avant de coder, une dernière chose, nous allons avoir besoin d’une librairie pour ce projet. Je vous invite donc à télécharger le fichier classic.lua à l’adresse suivante :

 https://github.com/rxi/classic

Puis placer le classic.lua dans votre dossier Pong, à côté du main.

Maintenant, nous allons commencer à mettre du code dans le main.lua, pour cela nous allons avoir besoin de certains callbacks. Les callbacks sont des fonctions qui sont utilisées par le moteur pour effectuer des tâches diverses.

  • La fonction « love.load() ». Cette fonction est appelée une seul fois, lorsque le jeu est lancé, et est généralement celle ou vous chargez des ressources, initialisez des variables et des paramètres spécifiques.
  • La fonction « love.update() » . Cette fonction est appelée en permanence et sera probablement l’endroit ou la plupart de vos calculs seront fait. « dt » signifie  « Delta temps », c’est le nombre de secondes depuis la dernière fois que cette fonction a été appelée.
  • La fonction « love.draw() » . C’est l’endroit pour tout ce qui concerne l’affichage

Première classe : Entity

On va donc commencer par appeler ces 3 fonctions dans le main et assigner classic (du fichier classic.lua) à Object dans la fonction love.load ce qui vous donne ceci

 

 

Bien, pour faire un Pong de quoi avons-nous besoin ? D’une Balle et de 2 Raquettes. Créons alors une classe raquette ainsi qu’une classe balle. En y réfléchissant, les raquettes et la balle ont des points communs, ils se déplacent tous et sont tous des rectangles (oui même la balle d’une certaine manière) nous pouvons alors créer une classe mère que nous appellerons entity. Créer un nouveau fichier et nommez le entity.lua.

Cette classe entity doit hériter de la classe Object de la librairie téléchargé auparavant. Commencez donc par écrire dans cette classe : Entity = Object :extend()

Ensuite il faut créer une fonction Entity:new qui prend en paramètre une position x, une position y, une largeur et une hauteur. C’est dans cette fonction que nous allons définir nos variables nécessaires pour nos classe raquette et balle, c’est-à-dire une position (x, y), une taille (largeur, hauteur) ainsi qu’une vitesse de déplacement. Actuellement, votre classe entity devrai ressembler à ça

 

 

Maintenant passons au mouvement de nos entité et à l’affichage de nos raquettes. Toujours dans la classe entity, créer une fonction entity:update(dt) dans laquelle nous allons gérer les mouvements de nos raquettes ainsi que de notre balle. Pour cela écrivez le code suivant :


Self.x = self.x + self.xSpeed * dt

Self.y = self.y + self.ySpeed * dt


 

Pour l’affichage de nos raquettes, on ne va pas vraiment les afficher ici mais nous allons au moins les créer. Après la fonction Entity:update, créer une fonction Entity:draw() dans laquelle nous allons créer un rectangle d’une certaine taille, à une certaine position.

Love.graphics.rectangle ("fill", self.x, self.y, self.width, self.height)

Voilà Désormais votre classe Entity doit ressembler à ça :

 

 

Les Raquettes

 

Passons maintenant aux raquettes, tout d’abord, la classe Raquette hérite de Entity. On commence donc par écrire Raquette = Entity :extend(). Ensuite nous devons créer une fonction Raquette:new() dans laquelle nous donnons une position ainsi qu’une taille à une raquette :

Raquette.super.new(self, 50, 100, 10, 100)

Toujours dans la même fonction, définissons les touches du clavier avec lesquels nous allons bouger cette raquette :


self.keyUp = "up"

self.keyDown = "down"


Maintenant, pour le déplacement des raquettes nous devons créer une classe Raquette:update(dt) dans laquelle nous allons dire que si la flèche du haut de votre clavier est enfoncé, alors la raquette se déplace vers le haut et inversement avec la flèche du bas. Ce qui donne normalement quelque chose comme ça :


If love.keyboard.isDown(self.keyUp) then

self.ySpeed = -400

elseif love.keyboard.isDown(self.keyDown) then

self.ySpeed = 400

else

self.ySpeed = 0

end


Le dernier else ou l’on définit la vitesse à 0 sert simplement à stopper le déplacement de la raquette lorsque qu’on lâche la touche. Sans lui, la raquette se déplace sans arrêt, même si on appuie sur aucunes touches. Terminez cette fonction en écrivant Raquette.super.update(self, dt). Voila votre classe Raquette est terminée et devrait ressembler à ça :

 

 

Créons maintenant une classe game qui hérite de Object. Dans cette classe nous avons une fonction Game:new(), une fonction Game:update(dt) et une fonction Game:draw(). Dans Game:new(), nous allons instancier nos deux raquettes. Celle de gauche :


self.raquetteGauche = Raquette()

self.raquetteGauche.keyUp = z

self.raquetteGauche.keyDown = s


(z et s sont nécessaire sinon les 2 raquettes se contrôle avec les flèches) Et celle de droite :


self.raquetteDroite = Raquette()

self.raquetteDroite .x = 740


Dans Game:update(dt) appliquez un update sur les deux raquettes comme ceci :


self.raquetteGauche:update(dt)

self.raquetteDroite:update(dt)


Et enfin dans la fonction Game:draw(), affichez les deux raquettes :


self.raquetteGauche:draw()

self.raquetteDroite:draw()


Voici à quoi doit ressembler votre classe Game :

 

 

Avant de lancer le jeu, nous devons inclure tous les fichiers nécessaires dans le main.lua et instancier un nouveau game. Voici donc à quoi doit ressembler votre main.lua :

 

 

Vous pouvez désormais lancer votre jeu (toujours en glissant le dossier Pong sur votre raccourci de löve), si tout ce passe bien vous devriez voir apparaître votre écran de jeu tout noir avec vos 2 raquettes, une à gauche et une à droite. Et vous pouvez déplacer vos raquettes verticalement avec z et s pour la raquette de gauche et les flèches de votre clavier pour la raquette de droite.

En déplacent vos deux raquettes, vous pouvez voir qu’elles sortent de l’écran, pas de panique c’est normal mais avant de régler ce problème, nous allons créer notre balle.

 

La Balle

 

Dans votre nouveau fichier fraichement nommé balle.lua, faite hériter votre classe Balle de Entity, créez la fonction Balle:new() anisi que la fonction Balle:update(). Dans Ball:new(), nous allons définir sa position de départ ainsi que sa taille grâce à :


Balle.super.new(self, 300, 400, 15, 15)


Pour faire bouger cette balle, il faut lui ajouter une vitesse de déplacement sur l’axe x et sur l’axe y :


Self.xSpeed = 400

Self.ySpeed = -500


Pour la fonction Balle:update(), ajoutez-y la ligne suivante :


Balle.super.update(self, dt)


Voici à quoi ressemble la classe Balle :

 

 

Avant de voir si votre balle est bien là, vous devez l’instancier dans votre classe Game. Dans la fonction Game:new(), ajoutez self.balle = Balle(). Ajoutez aussi self.balle:update(dt) dans la fonction Game:update(dt) et self.balle:draw() dans la fonction Game:draw().

Vous pouvez maintenant lancer le votre jeu et vous vous rendez compte la balle par du centre de la fenêtre et sort de cette dernière.

Nous allons donc maintenant gérer les collisions des raquettes et de la balle. Pour cela, rendez vous dans la classe Entity. Pour savoir si la balle est en dehors de l’écran, on va devoir regarder si le haut du rectangle (la balle est un rectangle) est supérieur au haut de la fenêtre

Ecrivez dans la fonction Entity:update() :


if self.y <= 0 then

self.y = 0

end


(Le 0 correspond au haut de la fenêtre, pour le bas, c’est 600, qui va nous être utile juste après)

Si vous lancez le jeu, vous pouvez voir que les raquettes ne sortent plus de la fenêtre en haut, Et la balle non plus. Cependant la balle semble collée en haut de l’écran, nous allons donc la faire rebondir. Ecrivez dans le if créé juste avant, « self.ySpeed = -self.ySpeed »

Faisons la même chose pour le bas de la fenêtre en ajoutant le elseif suivant, au if que vous venez d’écrire :


elseif self.y + self.height >= 600 then

self.y = 600 – self.height

self.ySpeed = -self.ySpeed


Maintenant nous allons gérer les collisions entre la balle et les raquettes. Pour cela, allez dans la classe Balle et ajoutez-y la fonction suivante :


function Balle:rebond(e)

local left1 = self.x

local right1 = self.x +self.width

local top1 = self.y

local bottom1 = self.y + self.height

local left2 = e.x

local right2 = e.x +e.width

local top2 = e.y

local bottom2 = e.y + e.height

if left1 < right2 and right1 > left2

    and top1 < bottom2

    and bottom1 > top2 then

self.xSpeed = -self.xSpeed

end

end


Voici à quoi doit ressembler votre classe Balle :

 

 

Il ne reste plus qu’à appeler votre fonction Balle:rebond() avec les raquettes en paramètre  dans la fonction Game:update() de votre classe Game.

 

 

Lancez votre projet Pong et regardez, la balle rebondit en haut et en bas de la fenêtre ainsi que sur les 2 raquettes.

 

Le Score

 

Attardons-nous maintenant sur le système de score.

Premièrement, créez la fonction Balle:horsJeu() dans la classe Balle. Il faut y définir quand est ce qu’un joueur marque un point. Un joueur marque un point lorsque la balle passe derrière la raquette de son adversaire. Voici donc le code qui nous permet de verifier ça :


Function Balle:horsJeu()

If self.x + self.width < 0 then

return “left”

elseif self.x > 800 then

return “right”

else

return false

end

end


Maintenant rendez-vous dans la classe Game, dans la fonction Game:new(), ajoutez 2 variables (une pour le score du joueur de gauche et une pour le score du joueur de droite) initialisez les à 0.

Voici ce à quoi doit ressembler votre fonction Game:new() :

 

 

Il ne reste plus qu’à incrémenter le score du joueur gagnant lorsqu’un point est marqué et à afficher le score total. Pour cela, voici le code qu’il faut mettre dans la fonction Game:update() pour incrémenter le score.

 

 

Pour Afficher le score, ajoutez dans la fonction Game:draw() la ligne suivante :


love.graphics.print(self.scoreLeft .. “  ” .. self.scoreRight, 620, 20, 0, 4, 4)


Je vous laisse modifier les chiffres à la fin à votre guise pour que vous puissiez affiche le score comme bon vous semble. Sachez juste que 620 et 20 servent à positionner le score et les deux 4 correspondent respectivement à la largeur et la hauteur.

La Démarcation

 

Toujours dans la fonction Game:draw(), on peut ajouter un démarcation (suite de rectangle) au centre de la fenêtre (un peu comme un filet) :


for i = 0, 10 do

love.graphics.rectangle(“fill”, 390, 80 * i, 15, 60)

end


Voici à quoi doit ressembler votre Pong Maintenant

 

 

Comme avant, les deux dernier chiffres 15 et 60 correspondent à la largeur et à la hauteur de chaque rectangle. Donc si vous souhaitez les modifier, allez-y !

Vous vous en été peut-être déjà rendu compte mais à chaque démarrage, la balle par toujours du même côté, ajoutons un peu d’aléatoire à notre Pong.

Dans la classe Balle, effectuez quelques changements dans la fonction Balle:new(). Tout d’abord on va rendre le lancé de balle aléatoire, c’est-à-dire que la balle partira ou à gauche ou à droite et plus seulement à droite. Voici la fonction une fois modifié.

 

 

Le Timer

 

Pensez à écrire self.timer = 1, Il va nous servir à faire un petit temps d’attente avant que la balle parte. Pour cela, modifié la fonction Balle:update() en plus de la petite ligne qu’on vient d’ajouter dans Balle:new(dt).Voici le Balle:update() une fois modifié.

 

 

 

Enfin, pour pouvoir redémarrer une partie, définissons une touche du clavier (ici r) pour démarrer une nouvelle partie. Dans le main, ajouter la fonction love.keypressed(k) qui va donc redémarrer le jeu si le bouton r du clavier est appuyé :


function love.keypressed(k)

if k == “r” then

love.load()

end

end


Et voila votre Pong est terminé, maintenant libre à vous de le personnalisé comme bon vous semble. Par exemple pourquoi pas ajouter des effets sonores à certains moments ou simplement changer la couleur de la balle ou des raquettes.

 

 

 

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *