Convertir un programme META en Löve2D sur PC

Introduction

Vous avez appris à faire quelques jeux sur la META mais vous vous dites que vous allez tout devoir réapprendre si vous passez sur un autre langage.

Et bien nom : apprendre à programmer c’est apprendre une syntaxe et une façon de procéder.

D’un langage à l’autre, en effet, vous allez devoir réapprendre le langage : la syntaxe en quelque sorte mais vous pouvez garder tous les concepts de programmation et ce sont ces concepts qui sont les plus important.

Nous allons illustrer ces propos en reprenant le développement de notre Pong tel que nous l’avons vu dans les Workshop et en le codant sur Löve2D.

Nous partirons du principe que vous avez déjà installé Lua, Löve et que vous utilisiez un éditeur tel que ZeroBrane Studio. Si ce n’est pas le cas, vous pouvez le faire en suivant les explications ici :

Je n’expliquerais pas ici comment se servir de ZerobraneStudio ou comment programmer en Lua mais juste comment faire la même chose que dans notre IDE Arduino pour programmer un Pong en c++ dans l’environnement de ZerobraneStudio en Löve. Si ce genre de chose vous intéresse nous pourrons le faire avec d’autres exemples.

La structure de notre programme

Bon tout d‘abord sur la META, vous devez appeler votre programme avec le même nom que celui que vous avez donné au répertoire dans lequel vous l’avez créer. Par exemple si vous avez créé le répertoire Pong, la partie principale de votre programme, celle que l’éditeur ouvrira en premier devra s’appeler Pong.ino

Dans Löve, la partie principale du programme s’appelle toujours main.lua. Dans le répertoire Pong qui servira pour votre programme Löve s’appellera main.lua

(Pour créer un nouveau document dans Zerobrane studio, cliquez sur le premier icone pour créer un nouveau document puis sur la disquette pour l’enregistrer en tant que main.lua. Enfin, cliquer sur la 5ème icône pour définir le répertoire du projet à partir du répertoire courant)

Dans l’étape 1 du Workshop 2, nous avions vu la structure d’un jeu pour notre META.

Elle était composée de 3 parties :

#include

Sur la première ligne, on trouve #include <Gamebuino-Meta.h> qui permet d’inclure la bibliothèque de la META nécessaire afin de gérer la console.

Les deux autres éléments essentiels à un jeu Gamebuino sont les 2 fonctions setup et loop .

Toutes les instructions situées entre les accolades { } seront exécutées quand la fonction sera appelée.

setup()

void setup(){
  gb.begin();
}

La fonction setup se situe donc entre les lignes 3 et 5. Elle est appelée automatiquement une fois au démarrage du jeu.

loop()

void loop(){
  while(!gb.update());
  gb.display.clear();

  // C'est ici que le plus gros du programme se déroule
  gb.display.print("hello, world");
}

Les instructions situées dans loop sont exécutées en boucle.

Je reprends ici juste le résumé de ce qui est dit dans la partie 1 du second Workshop mais vous pouvez le reprendre ici : Pong (C++ 2/5) : Hello, world - Gamebuino si besoin.

En Löve, nous aurons la même structure approximativement. Par défaut, il n’y a pas besoin de faire d’include si on n’a pas de besoin spécifique, ce qui est notre cas pour notre exemple.

Par contre nous allons tout de suite inclure la structure en 3 parties de la boucle principale que nous abordons en particulier dans l’étape 2 du Workshop : Tap Tap (C++ 3/5) Les 3 parties de la boucle principale: Entrées, Mise à jour et Affichage.

Notre programme Löve sera donc composé ainsi :

function love.load ()
end

function love.update (dt)
end

function love.draw ()
end

La fonction love.load() … end correspond à notre void setup() { } Cette fonction est en effet executée une seule fois au lancement du programme. On y place donc comme dans un programme de la META toutes les commandes qui seront nécessaire à l’initialisation de notre jeu.

Par contre la commande gb.begin(); que nous utilisons sur la META n’existe pas et nous devrons
détailler les étapes de l’initialisation : la taille de la fenêtre dans
laquelle le jeu va s’exécuter par exemple.

Sur notre console dans le programme Pong, nous utilisons une résolution de 80 pixels de large par 64
pixels de haut. Nous pourrions créer une fenêtre de la même taille sur notre PC mais cela serait minuscule. Nous utiliserons donc une constante pour grossir la taille d’affichage. Par défaut, pour un écran standard, une fenêtre de 800 par 640 me parait être une taille raisonnable, je vous propose donc d’utiliser un facteur 10 pour multiplier les dimensions de notre fenêtre et des éléments à l’intérieur.

Dans love.update, nous aurons 2 parties : la gestion des entrées et des événements et la partie
mise à jour.

Enfin dans la partie love.draw qui s’exécute à la suite de love.update, vous retrouverez toutes les commandes destinées à l’affichage des différents éléments à l’écran.

Attention toutefois, sur PC, cette fonction sera appelée 60 fois par seconde, contrairement à la META où loop est appelée 25 fois par seconde… Il faut en tenir compte dans notre code…

Bien, nous avons vu la structure de notre code, alors reprenons le tutoriel de notre Pong à partir de la fin de l’étape 2… lorsque nous faisions avancer ou reculer un petit carré à l’aide des touches
haut et bas de notre pavé directionnel et codons le sur Löve avec notre facteur grossissant de 10…

Transformons notre code du compteur de la META

Bon alors déjà pour les variables : « int counter = 0;», en Löve, pas besoin de préciser
le type pour les variables ni du ; à la fin. En löve, ça devient : « counter = 0 »

OK, donc les variables, c’est simple …

Pour le « gb.begin(); » , on verra plus tard. Pour le moment, passons le.

Pour les tests des boutons haut et bas sur la META, c’est :

if (gb.buttons.pressed(BUTTON_UP)){
    counter = counter + 1;
}

if(gb.buttons.pressed(BUTTON_DOWN)){
    counter = counter - 1;
}

En Löve, c’est :

if love.keyboard.isDown("up") then
  counter = counter + 1
end

  if love.keyboard.isDown("down") then
    counter = counter - 1
  end

Pour : gb.display.setColor(BROWN); c’est un peu plus compliqué, mais ça va : en effet, on n’a pas les nom des couleurs définies. Par conséquent, on pourrait les définir et faire correspondre des noms à leurs valeur rouge, verte et bleue.

Pour le moment, faisons simple et prenons un site qui nous donne les composantes RGB comme par exemple : Convertisseur de couleurs en HEX et RGB et visualisation. Je clique sur la couleur qui m’intéresse et je récupère les composantes R,G, B. Pour notre Brown, ce sera donc Rouge : 66, Vert : 33 et Bleu : 00

On utilise la commande : love.graphics.setColor ( Vous pouvez avoir plus d’informations sur
les instructions et des exemples sur le wiki de Löve2D : https://love2d.org/wiki/love.graphics.setColor )

Ce qui nous donnera donc:

 love.graphics.setColor ( 66, 33, 00)

Passons maintenant au tracé de notre carré que l’on fait sur la META avec : gb.display.fillRect(0, 0, counter, gb.display.height()); en Löve2D ça devient : love.graphics.rectangle( “fill”, 0, 0, counter, love.graphics.getHeight( )). C’est très proche, non ?

Pour le dernier pavé :

  gb.display.setColor(WHITE);
  gb.display.setFontSize(4);
  gb.display.setCursor(8,8);
  gb.display.print(counter);

On a pour la couleur :

love.graphics.setColor ( 255, 255, 255)

Pour la police, c’est un peu différent : on fixe la taille de notre police en pixel en créant une police à la taille désirée. Par exemple :

mainFont = love.graphics.newFont("arial.ttf", 20)

Note : mettez la police arial.ttf dans le répertoire de votre projet…

Puis on dit que l’on souhaite utiliser cette police :

love.graphics.setFont(mainFont)

On affiche ensuite notre compteur à l’aide de la ligne : love.graphics.print(counter, 80, 80)

Programme d’origine sur la META

#include <Gamebuino-Meta.h>
int counter = 0;

void setup() {
  gb.begin();
}

void loop() {
  while (!gb.update());
  gb.display.clear();

  if(gb.buttons.pressed(BUTTON_UP)){
    counter = counter + 1;
    gb.sound.playOK();
  }

  if(gb.buttons.pressed(BUTTON_DOWN)){
    counter = counter - 1;
    gb.sound.playCancel();
  }

  if(gb.buttons.pressed(BUTTON_MENU)){
    counter = 0;
    gb.sound.playTick();
  }

  gb.display.setColor(BROWN);
  gb.display.fillRect(0, 0, counter, gb.display.height());

  gb.display.setColor(WHITE);
  gb.display.setFontSize(4);
  gb.display.setCursor(8,8);
  gb.display.print(counter); 
}

Programme converti sur PC en Löve2D

zoom = 10
counter = 0

function love.load()
  mainFont = love.graphics.newFont("arial.ttf", 60)
  love.graphics.setFont(mainFont)
end

function love.update(dt)
  if love.keyboard.isDown("up") then
    counter = counter + 1
  end

  if love.keyboard.isDown("down") then
    counter = counter - 1
  end

end

function love.draw()
  love.graphics.setColor ( 66, 33, 00 )
  love.graphics.rectangle( "fill", 0, 0, counter * zoom, love.graphics.getHeight( ))

  if counter < 14 then
    love.graphics.setColor ( 255, 255, 255)
  else
    love.graphics.setColor ( 0, 0, 0)
  end

  love.graphics.print(counter, 8 * zoom, 8 * zoom)

end

Voilà nous avons fini cette première partie. Si cela vous intéresse nous continuerons ce tutoriel pour convertir des programmes de la META vers Löve2D. D’ailleurs l’inverse pourrait être vrai aussi et l’on pourrait tenir le raisonnement inverse pour convertir des programme en Love2D et les faire tourner sur la META. Alors réagissez sur cette première partie, critiquez, dites moi si ça vous intéresse et si vous voulez la suite

1 Like

Bien normalement avec ce que nous avons vu, vous pouvez reprendre la partie 3 (Pong (C++ 2/5) : Balle rebondissante - Gamebuino) jusqu’au bout en Löve2D et faire rebondir la balle dans tous les sens.

Avant de continuer, essayez. Il n’y a pas de difficulté particulière. Suivez bien les indications du Workshop et regardez comment nous avons fait les différentes opérations.

C’est important que vous essayez de trouver les solutions par vous même. Vous n’aurez pas la solution dans ce message pour le faire mais si vraiment vous n’y arrivez pas, vous pourrez regarder dans les codes suivant.

Une fois que vous y serez arrivé ou si vous êtes bloqué, continuez à lire ci dessous et ajoutons notre raquette à notre balle, puis les tests de collisions exactement de la même façon de dans le Workshop en utilisant que les éléments que l’on a déjà vu enfin codons ce qui est demandé dans le “A nous de jouer” . Je vous conseille de chercher à le faire vous même mais cette fois, je vais vous donner la solution que j’ai appliqué pour reprendre le code final fournit en C++ comme solution à la fin de cette 4ème partie du Workshop.

zoom = 10 -- facteur de grossissement pour la version PC

-- Caractéristiques de la balle
balle_posX = 30 * zoom
balle_posY = 20 * zoom
balle_speedX = 1 * zoom
balle_speedY = 1 * zoom
balle_taille = 3 * zoom

-- Caractéristiques de la raquette
raquette1_posX = 10 * zoom
raquette1_posY = 30 * zoom
raquette2_posX = love.graphics.getWidth( ) - 13 * zoom
raquette2_posY = 30 * zoom

-- Dimensions des deux raquettes
raquette_hauteur = 10 * zoom
raquette_largeur = 3 * zoom

-- Scores
score1 = 0       -- Score du joueur 1
score2 = 0       -- Score du joueur 2

function love.load()
  mainFont = love.graphics.newFont("arial.ttf", 60)
  love.graphics.setFont(mainFont)
end

function love.update(dt)
  
  -- MAJ raquette1
   -- Si la flèche "haut" est pressée on monte la raquette
  if love.keyboard.isDown("up") then
    raquette1_posY = raquette1_posY - zoom
  end
   -- Si la flèche "bas" est pressée on descend la raquette
  if love.keyboard.isDown("down") then
    raquette1_posY = raquette1_posY + zoom
  end

-- MAJ raquette2
-- Si la flèche "a" est pressée on monte la raquette
  if love.keyboard.isDown("a") then
    raquette2_posY = raquette2_posY - zoom
  end
  -- Si la flèche "q" est pressée on descend la raquette
  if love.keyboard.isDown("q") then
    raquette2_posY = raquette2_posY + zoom
  end
  
  -- MAJ balle
  balle_posX = balle_posX + balle_speedX
  balle_posY = balle_posY + balle_speedY 
  
  -- Collisions avec les murs (haut et bas)
  if (balle_posY < 0) then
    balle_speedY = zoom
  end
  
  if (balle_posY > (love.graphics.getHeight() - balle_taille) ) then
    balle_speedY = -zoom
  end

  -- Collision balle/raquette1
  if ( (balle_posX == raquette1_posX + raquette_largeur)
    and (balle_posY + balle_taille >= raquette1_posY) 
    and (balle_posY <= raquette1_posY + raquette_hauteur) ) then
    balle_speedX = zoom
  end
  -- Collision balle/raquette2
  if ( (balle_posX + balle_taille == raquette2_posX)
    and (balle_posY + balle_taille >= raquette2_posY) 
    and (balle_posY <= raquette2_posY + raquette_hauteur) ) then
    balle_speedX = -zoom
  end

  -- Vérifier si la balle est sortie de l'écran
  if (balle_posX < 0) then
    -- Replacer la balle sur l'écran
    balle_posX = 20 * zoom;
    balle_posY = 20 * zoom;
    balle_speedX = zoom;
    balle_speedY = zoom;
    -- Incrémenter le score du joueur 2
    score2 = score2 + 1;
  end
  
  if (balle_posX > love.graphics.getWidth( )) then
    -- Replacer la balle sur l'écran
    balle_posX = 20 * zoom;
    balle_posY = 20 * zoom;
    balle_speedX = zoom;
    balle_speedY = zoom;
    -- Incrémenter le score du joueur 1
    score1 = score1 + 1;
  end

end

function love.draw()
  
  -- Afficher la balle
  love.graphics.setColor ( 66, 33, 00 )
  love.graphics.rectangle( "fill", balle_posX, balle_posY, balle_taille, balle_taille)
  love.graphics.setColor ( 255, 255, 255 )
  -- Afficher la raquette1
  love.graphics.rectangle( "fill", raquette1_posX, raquette1_posY, raquette_largeur, raquette_hauteur)
  -- Afficher la raquette2
  love.graphics.rectangle( "fill", raquette2_posX, raquette2_posY, raquette_largeur, raquette_hauteur)

  -- Afficher les scores
  love.graphics.print(score1, 33 * zoom, 5 * zoom)
  love.graphics.print(score2, 44 * zoom, 5 * zoom)
 
end

J’espère que vous aviez réussit à convertir ce code, sinon ça vous fait un petit point de repère et j’espère que vous trouverez les réponses à vos problèmes dedans. Ce code n’est pas un code optimisé, c’est juste la transformation du code du Workshop en Love2D adapté à la résolution d’un PC mais vous voyez que c’est très simple à transposer… Vous n’avez besoin de rien d’autre pour faire la même chose à l’étape 5. Vous pouvez vous même ajouter l’IA à ce code.
N’hésitez pas à me dire si vous rencontrez des difficultés. Il y en a peut être que je n’ai pas vu et c’est votre expérience qui compte.

1 Like