GERBELOTBARILLON.COM

Parce qu'il faut toujours un commencement...

Installation


Löve2D est un framework basé sur le langage LUA ainsi que sur la bibliothèque de programmation SDL (Simple Direct media Libray). Il permet la création de jeux 2D sur la majorité des plateformes informatiques actuelles (Windows, Mac, Linux) relativement simplement.

Pour l'installer, rendez-vous sur le site officiel https://love2d.org/ et téléchargez la version dont vous avez besoin pour votre système.

La documentation de Lua est ici : http://lua-users.org/wiki/TutorialDirectory et également ici https://www.lua.org/manual/5.1/

La documentation de Löve2D se trouve à cette adresse https://love2d.org/wiki/Main_Page mais je vais essayer de vous donner des informations directement dans la suite de la page que vous êtes en train de lire.

Pour développer des applications Löve et/ou Lua, un simple éditeur de code suffira. Si vous voulez un peu plus d'intégration vous pouvez utiliser l'éditeur dédié ZeroBrane Studio. Pour l'installation sous Linux, faites un clone du repository https://github.com/pkulchenko/ZeroBraneStudio puis lancez l'éditeur par sh ./zbstudio.sh. ou VisualStudio Code. Pour ce dernier vous pouvez installer les extensions :

Les variables dans Lua


Comme Lua est à la base du fonctionnement de Löve2D, nous allons passer en revue les principales caractéristiques du langage.

Dans Lua, les variables sont sensibles à la casse. C'est-à-dire que les éléments aventure, Aventure, AVENTURE sont des variables différentes. Un nom de variable ne peut pas commencer par un chiffre ni contenir un caractère spécial comme @#$%^&*. Enfin un nom de variable ne peut pas contenir un des mots-clés du langage comme and, if, while, for, repeat, false, local, function, else, break, return, or, until, while, not, true, elseif, nil.

Les opérations arithmétiques sont classiques :

a = 40 + 2 -- Addition
b = 40 - 2 -- Soustraction
c = 40 * 2 -- Multiplication
d = 40 / 2 -- Division
e = 40 ^ 2 -- Puissance
f = 20 % 2 -- Modulo (reste de la division)

Les chaînes de caractères contiennent du texte comme des phrases en texte normal. Pour associer des chaines de caractères, il faut utiliser deux points comme ..

jours = "42"
hello = "Hello"
texte = hello .. ", le nombre de la vie est " .. jours
print(texte) -- Affiche : "Hello, le nombre de la vie est 42"

Les fonctions dans Lua


Une fonction peut être déclarée de deux façons :

function exemple()
    print("Hello world!")
end

exemple = function()
    print("Hello World!")
end
Une fonction accepte 0, 1 ou plusieurs paramètres qui permettrent de conditionner le comportement de cette fonction.
function Somme(a, b)
    return a + b
end

print(Somme(40, 2)) -- Affiche 42

Lua permet la gestion d'un nombre variables d'arguments dans les fonctions en spécifiant juste ... comme paramètre :

function somme(...)
    local result = 0
    for _,v in ipairs({...}) do -- {...} = Conversion de la liste en tableau
        result = result + v
    end
    return result
end

print(somme(2,3)) -- retourne 5
print(somme(2,3,4)) -- retourne 9

Les boucles et conditions dans Lua


Une condition simple est le si..alors..sinon qui se traduit comme suit dans Lua :

if condition then
    -- traitement de la condition alors
else
    -- traitement de la condition sinon
end

if condition then
    -- traitement de la condition alors
elseif autre condition then
    -- raitement de la condition 2
end

Les opérations booléennes classiques et (and) et ou (or) sont simplement gérées comme suit :

if x < 100 or x > 200 then
    -- traitement de la condition de x dans l'intervalle ouvert [-infini,100[ et ]200, +infini]
    print("Les deux conditions sont valides")
end
La condition ET vérifie les conditions les unes à la suite des autres et arrête sa comparaison si une condition est fausse.

Le OU logique (Or) va analyser l'ensemble des conditions avant de choisir la branche à traiter :

if x < 100 or x > 200 or x == 150 then
    --traitement de la condition sur le même intervalle que le AND précédent avec un test complémentaire sur
    -- une valeur précise x == 150
    print("Une des conditions au moins est valide")
end

Les tables/listes/dictionnaires dans Lua


Les tables dans Lua sont des listes donc déclarées par fruits = {}. Ce sont des éléments de base dans Lua mais très versatiles dans leur usage. C'est même certainement le type de variables le plus utilisé.

La déclaration d'un tableau peut se faire directement dans les accolades par fruits = {"pomme", "banane"}. Ou bien en utilisant la méthode insert() de la table comme fruits.insert("orange").

ATTENTION : contrairement à un certain nombre de langages de programmation, Lua fait commencer les tableaux à l'indice 1 (et pas 0).

Pour afficher tous les éléments d'un tableau nous pouvons utiliser une boucle for..end. Pour avoir la taille de ce tableau vous pouvez utiliser la notation #table de cette façon :

for f = 1, #fruits do -- #fruits représente la longueur du tableau
    print (fruits[f])
end

Pour supprimer un élément, le plus simple est d'utiliser l'index du champ à supprimer par fruits.remove(2) -- supprime "banane"

Une autre solution de parcours de tableau utilise la méthode ipairs(table) qui renvoie l'index et la valeur de chaque champ. La méthode iparis est un raccourci pour cette fonction équivalente :

for i=1, #fruits do
    v = fruits[i]
end
Avec l'usage de ipairs nous obtenons
fruits = {"pomme", "banane", "orange"}
for i, v in ipairs(fruits) do
    print(i .. ' ' .. v)
    -- produit une sortie du genre
    -- 1 pomme
    -- 2 banane
    -- 3 orange
end

Les objets dans Lua


Un objet dans le jargon Lua est un tableau auquel nous allons donner des valeurs et des méthodes. Le terme de valeur est souvent remplacé par celui de propriété. Au final un objet est un tableau disposant de clés et de valeurs. Par exemple :

perso = {
    name = "John",
    surname = "Doe",
    sayHello = function() {
        print("Bonjour je m'appelle " .. name)
    }
}

Pour appeler les propriétés de cet objet nous pouvons faire

p = perso()
print(perso.surname)
print(perso["name"])
p.sayHello() -- Appelle la méthode de l'objet perso

Fonctionnement de Löve2D

Le moteur du framework Löve est un moteur à états qui réalise une gestion des événements sur une boucle appelant les fonctions suivantes en permanence :


love.load() -- Chargement des différents assets du jeu

love.update() <---| -- Mise à jour des éléments du jeu
     |            |
     |            |
     \/           |
love.draw()   ----| -- Mise à jour des affichages

Löve repose sur une quinzaine de modules tels que love.graphics, love.audio, love.filesystem, love.print chacun spécialisé dans une activité précise. Par exemple pour dessiner un rectangle plein :

function love.draw()
    love.graphics.rectangle("fill", 100, 100, 30, 50)
end
Pour un rectangle avec seulement les lignes tracées, il suffit de replacer "fill" par "line" pour le premier paramètre qui représente le DrawMode de Löve. Vous retrouverez ces informations dans le Wiki.

Le squlette d'un programme Löve2D est relativement simple puisqu'il se compose des 3 fonctions qui gèrent la boucle des événements dans le framework.

function love.load()
end

function love.update(dt) -- dt est le delta time
end

function love.draw()
end

Pour synchroniser les mouvements quelle que soit la puissance et la fréquence de fonctionnement de l'ordinateur, si vous souhaitez avoir le même déplacement des objets sur chaque configuration il faut utiliser la notion de delta time.

Le Delta Time est le temps qui s'est écoulé entre deux appels à love.update(). Sur un ordinateur à 100 FPS, ce sera environ 1/100 soit 0.01. Sur un ordinateur à 200 FPS, ce sera 1/200 soit 0.005.

Donc si l'on veut conserver ce mouvement similaire, il faudra multiplier notre déplacement par cette valeur de delta time. Par exemple, si l'on souhaite déplacer de 5 pixels vers la droite par seconde un objet, alors au lieu de faire simplement x = x + 5, nous ferons x = x + 5 * dt.

Gestion des modules

Lua / Löve2D permet d'organiser son projet en découpant les activités entre plusieurs fichiers que l'on appelle modules dans Lua. Pour utiliser un de ces modules :

-- Fichier: calculs.lua
function somme(a, b)
    return a + b
end

-- Fichier: main.lua
local module = require("calculs") -- inclusion du module sans l'extension .lua
print(somme(40, 2))

Une inclusion de fichier module peut s'effecture n'importe où mais généralement elle se situe en début de fichier ou dans la fonction Löve love.load()

function love.load()
    local module = require("biblio")
end
Le module "biblio" serait composé comme suit
local monModule = {}
monModule.variableExposee = 10
local variablePrivee = 99

function monModule.maFonction()
    print("Je suis dans le module")
end

return monModule

Si un module est situé dans une sous-arborescence, il faut remplacer les '/' par des '.' comme require("path.to.file") pour un fichier situé dans path/to/file.

Les images dans Löve

Löve2D est capable de gérer des images mais uniquement si elles sont au format .png. La fonction love.graphics.newImage(path) permet de charger une image dans une variable. Pour l'afficher il suffit d'appeler ensuite la fonction love.graphics.draw(image, x, y). Mais la fonction draw() est plus complexe et accepte plus de paramètres que simplement les positions x et y. On peut disposer des paramètres suivants pour draw() :

function love.load()
    myImage = love.graphics.newImage("image.png")
    width = myImage:getWidth()
    height = myImage:getHeight()
end

function love.draw()
    -- Affiche l'image en calculant les coordonnées en fonction du centre de l'image
    -- et non du top,left (0,0) comme par défaut
    love.graphics.draw(myImage, 100, 100, 0, 2, 2, width/2, height/2)
end

Les polices de caractères dans Löve

Löve2D permet d'utiliser des polices de caractères au format .TTF. De nombreuses ressources de ce type sont disponibles sur Internet. Pour utiliser une font, procédez comme suit :

font = love.graphics.newFont("fontname.ttf", size)

function love.draw()
    love.graphics.setFont(font)
    love.graphics.print("Score = 0")
end
            

Gestion de la couleur

La gestion de la couleur s'effectue de la même façon qu'avec OpenGL à savoir, une fois qu'une information de couleur a été définie, elle est valable pour tous les affichages suivants. La couleur ne porte pas sur un objet en particulier mais sur un état dans le pipeline de rendu graphique.

A la différence des autres systèmes, Löve gère les couleurs entre 0 (noir) et 1 (blanc) au lieu des classiques couleurs RGB sur 256 valeurs. Ainsi la valeur 0.5 représente la moitié de l'intervalle de couleur (équivalent à 128/255). Donc pour faire plus simple :

function love.load()
    myImage = love.graphics.newImage("sheep.png")
    love.graphics.setBackgroundColor(1, 1, 1) -- Définit le fond blanc
end

function love.draw()
    -- Les deux lignes suivantes sont identiques
    love.graphics.setColor(255/255, 200/255, 40/255, 127/255)
    love.graphics.setColor(1, 0.78, 0.15, 0.5)
    love.graphics.draw(myImage, 100, 100)
end
Les fonctions classiques sont

Liens pour assets