Necrown - Devlog #04 [Engine Dev] : RequireAll et Sérialisation
Splix Il y a 12 mois Premium Pro - Adhésion à vie0

Salut les gamecodeurs!!!

Aujourd’hui nous allons commencer à vraiment travailler sur le moteur. On va donc retourner dans le dossier SplixEng où l’on va créer un fichier engine.lua. C’est ce fichier qui gérera notre moteur. Et comme mes tocs ne sont toujours pas guéris, c’est ici que l’on va placer les fonctions de base de love, comme ça pour l’instant dans le fichier main nous n’aurons qu’un require pour importer le moteur XD

J’aime dispatcher mon code dans une multitude de fichiers. Avant, je passais mon temps à écrire des require, et il fallait évidemment modifier les chemins à chaque fois que je décidai de changer l’emplacement d’un fichier sur un coup de tête… Mais il y a toujours un moyen de simplifier les choses. Maintenant vous connaissez la chanson: L’au-to-ma-ti-sa-tion!!!

J’ai donc écrit une fonction requireAll, qui se chargera d’importer tous les fichiers lua d’une arborescence…

function requireAll(p_dir)
  if love.filesystem.getInfo(p_dir , "directory") then
    for _, v in ipairs(love.filesystem.getDirectoryItems(p_dir)) do
      requireAll(p_dir.."/"..v)
    end
  elseif love.filesystem.getInfo(p_dir) and string.sub(p_dir, -4) == ".lua" then
    require(string.sub(p_dir, 1, string.len(p_dir) - 4))
  end
end

Cette fonction est plutôt simple: C’est une fonction récursive qui commence par tester si le chemin qu’elle reçoit en paramètre est celui d’un dossier. Si c’est le cas, elle parcourt  son contenu en s’appelant elle même pour chaque élément qu’elle trouve. Sinon, elle vérifie si le fichier finit par « .lua »  et, si c’est le cas, elle le charge. Cette fonction nous simplifiera la vie, mais il faut tout de même garder à l’esprit l’ordre dans lequel les fichiers seront chargés pour ne pas avoir de mauvaises surprises.

Pour terminer ce devlog, et tester l’import avec requireAll, on va créer un dossier Core dans SplixEng, qui gérera vraiment le cœur du moteur, et on y ajoute un fichier dataStorage.lua, qui se chargera de la sauvegarde et du chargement des données du jeu. Dans ce fichier, on va créer un objet local nommé serial, qui nous permettra de sérialiser et déserialiser des tables lua, c’està dire les transformer en texte pour les enregistrer dans un fichier et vice versa. Je ne m’y attarderais pas car cet objet, bien que remanié, est basé sur Tserial, une lib lua créée par Taehl (toutes les infos sont dans le wiki de love: https://love2d.org/wiki/Tserial, ou sur la page github du projet: https://github.com/zhsso/Tserial.lua).

Je passerai plus de temps sur le second objet de ce fichier, fileIO, qui lui sera un objet public qui se chargera d’enregistrer / charger  les données dans / depuis des fichiers de sauvegarde, à l’aide des fonctions de love.filesystem. Notons au passage que le chemin de sauvegarde par défaut de love.filesystem sur windows est dans Appdata/Roaming (cf wiki de love: https://love2d.org/wiki/love.filesystem).

fileIO = {}

function fileIO.exists(p_path)
  return (love.filesystem.getInfo(p_path) ~= nil)
end

function fileIO.save(p_path, p_table)
  return love.filesystem.write(p_path, serial.pack(p_table, nil, game.inDev and 0 or 1))
end

function fileIO.load(p_path)
  return serial.unpack(love.filesystem.read(p_path))
end

Nous allons rajouter quelques fonctions intéressantes ici: Tout d’abord la fonction createDir qui sera locale. Cette fonction sera appelée par la fonction fileIO.save lorsqu’elle détecte des slashs dans le chemin qui lui est envoyé. Elle se charge de découper le chemin à l’aide de la fonction split (juste au dessus) et, via la fonction love.filesystem.createDirectory, va créer chaque dossiers de l’arborescence (lorsque ceux-ci n’existent pas). On peut donc maintenant envoyer tout un chemin lors de la sauvegarde d’un fichier sans se soucier de vérifier si les dossiers existent ou pas.

function split(p_str, p_separator)
  local array = {}
  local reg = string.format("([^%s]+)", p_separator)
  
  for mem in string.gmatch(p_str, reg) do
    table.insert(array, mem)
  end
  
  return array
end

local function createDir(p_path)
  local items = split(p_path, "/")

  local dir = ""
  for k, v in ipairs(items) do
    if k < #items then
      dir = dir..v
      love.filesystem.createDirectory(dir)
      dir = dir.."/"
    end
  end
end

function fileIO.save(p_path, p_table)
  if string.find(p_path, "/") ~= nil then createDir(p_path) end
  return love.filesystem.write(p_path, serial.pack(p_table, nil, game.inDev and 0 or 1))
end

L’autre fonction intéressante ici est fileIO.remove. C’est une fonction récursive qui fonctionne un peu comme requireAll, à ceci prêt qu’elle va parcourir une arborescence et supprimer tous les fichiers contenu dans chaque dossiers, avant de supprimer les dossiers eux-mêmes. On pourra donc maintenant supprimer toute une arborescence en un appel de fonction, simplement en indiquant son chemin dans le dossier de sauvegarde.

function fileIO.remove(p_item)
  if love.filesystem.getInfo(p_item , "directory") then
    for _, v in pairs(love.filesystem.getDirectoryItems(p_item)) do
      fileIO.remove(p_item.."/"..v)
      love.filesystem.remove(p_item.."/"..v)
    end
  elseif love.filesystem.getInfo(p_item) then 
    love.filesystem.remove(p_item) 
  end
  
  return love.filesystem.remove(p_item)
end

Voilà pour aujourd’hui, j’éspère que ce devlog vous aura plu. Dans le prochain nous élaborerons un petit système de programmation orienté objet maison.

En attendant, bon code à tous!!!

Laisser un commentaire

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.