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 :
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"
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
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 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
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
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.
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.
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
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
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