Faisons en sorte que nous puissions enregistrer et charger nos progrès dans notre jeu. Nous le faisons en écrivant et en lisant un fichier. Mais nous devons d’abord avoir un jeu, alors faisons un petit jeu où vous pouvez ramasser des pièces.
On commence avec un joueur qui peut bouger
|
|
Et pour le plaisir, donnons un visage au joueur.
|
|
Ensuite, nous voulons ajouter des pièces. Nous les placerons au hasard sur l’écran. Et donnons-leur un petit signe dollar.
|
|
Maintenant, nous voulons pouvoir les récupérer. Pour cela, nous devons vérifier s’il y a collision. Pour ce faire, nous calculons la distance, puis vérifions si la distance est inférieure au rayon des deux cercles résumé.
|
|
Et maintenant, nous parcourons toutes les pièces et vérifions si cela touche le joueur. Faisons en sorte que le joueur grandisse en ramassant une pièce.
|
|
Maintenant, nous pouvons nous déplacer et ramasser des pièces. Agréable! Mais avant de passer à l’enregistrement et au chargement, apportons quelques modifications supplémentaires.
Si vous redémarrez le jeu plusieurs fois, vous remarquerez peut-être que même si les cercles sont positionnés de manière aléatoire, ils sont toujours au même endroit.
Pour résoudre ce problème, nous pouvons utiliser math.randomseed(). Les nombres aléatoires que vous générez sont basés sur un nombre, que nous appelons la graine. Et parce que nous ne changeons pas la graine, vous obtenez toujours les mêmes positions « aléatoires ». L’avantage des graines est qu’elles sont la clé d’un certain caractère aléatoire, que vous pouvez enregistrer et partager. Avec Minecraft par exemple, vous pouvez partager la graine qui a été utilisée pour générer un monde, et d’autres personnes peuvent l’utiliser pour obtenir le même monde généré.
Alors, quel nombre utilisons-nous comme semence ? Parce que si nous devions faire des maths au hasard (123), le jeu aurait toujours le même numéro. Nous avons besoin d’un numéro unique à chaque fois que nous commençons le jeu. Et pour cela, nous pouvons utiliser os.time(). Il s’agit d’une fonction Lua qui vous donne l’heure de votre système d’exploitation à la seconde. C’est 86400 numéros uniques par jour !
Mais le mieux pourrait être d’utiliser la bibliothèque mathématique de LÖVE. Le nombre aléatoire généré par LÖVE (rng) est automatiquement amorcé (avec os.time()) et est globalement meilleur / plus aléatoire que le rng de Lua.
|
|
Une autre chose qui me dérange est la boucle for dans laquelle nous retirons des pièces. Maintenant, ramasser des pièces fonctionne bien, mais en général lorsque vous parcourez une liste et que vous supprimez des éléments de cette liste, vous souhaitez utiliser une approche plus sûre. Parce qu’en supprimant un élément, la liste se raccourcit et gâche l’utilité de la boucle for.
Essayez ce code par exemple :
|
|
Il affiche 5, ce qui signifie qu’il n’a vidé que la moitié de la liste. Cela se produit car lorsqu’il supprime un élément de la liste, tout est déplacé sur le côté et les éléments sont ignorés.
Pour résoudre ce problème, nous devons parcourir le tableau en sens inverse. De cette façon, vous ne sauterez jamais aucun élément lorsque vous aurez supprimé quelque chose.
Cela signifie que nous n’utiliserons pas ipairs mais plutôt une boucle for normale, mais à l’envers.
|
|
Très bien, il est temps de commencer à sauvegarder le jeu.
Sauvegarde
Donc, ce que nous faisons pour sauvegarder le jeu, c’est que nous créons un tableau, et dans ce tableau, nous mettons toutes les informations que nous voulons enregistrer. Alors, que voulons-nous économiser? Que diriez-vous de la position du joueur, de sa taille et de la même chose pour les pièces qui n’ont pas encore été ramassées. Créons donc la fonction saveGame () où nous stockons les données importantes dans une table.
|
|
Alors pourquoi sauvegarder ces parties spécifiques et pas seulement la table entière? Eh bien, en général, vous ne voulez pas utiliser plus de données que nécessaire. Nous n’avons pas besoin d’enregistrer la largeur et la hauteur de l’image car elles seront toujours les mêmes. De plus, notre objet joueur a un objet image et nous ne pouvons pas enregistrer les objets LÖVE.
Alors maintenant nous avons toutes les informations dont nous avons besoin pour le sérialiser. Cela signifie que nous devons transformer le tableau en quelque chose que nous pouvons lire parce qu’en ce moment, lorsque vous imprimez un tableau, vous pourriez obtenir quelque chose comme un tableau: 0x00e4ca20, et ce ne sont pas les informations que nous voulons enregistrer.
Pour sérialiser la table, nous allons utiliser lume qui est une bibliothèque utilitaire de rxi. Vous pouvez trouver la bibliothèque sur GitHub.
Cliquez sur lume.lua puis sur Raw et copiez le code.
Accédez à votre éditeur de texte, créez un nouveau fichier appelé lume.lua et collez le code. Chargez-le avec require dans main.lua en haut de love.load().
Lume a toutes sortes de fonctions intéressantes, mais les plus importantes pour notre cas sont la sérialisation et la désérialisation. Essayons-le. Sérialisez le tableau de données, puis imprimez sa valeur.
|
|
Dans votre sortie, vous verrez le tableau imprimé de manière à ce que vous puissiez le lire. C’est ce que nous allons enregistrer dans notre fichier, ce qui est la prochaine étape.
Nous pouvons créer, éditer et lire des fichiers avec le module love.filesystem (wiki). Pour créer / écrire un fichier, nous utilisons love.filesystem.write (nom de fichier, données) (wiki). Le premier argument est le nom du fichier, le deuxième argument sont les données que nous voulons écrire dans le fichier.
|
|
Maintenant, nous devons faire en sorte que vous puissiez enregistrer la partie lorsque vous appuyez sur F1.
|
|
Exécutez le jeu, prenez des pièces et appuyez sur F1. Juste comme ça, nous avons fait notre premier fichier de sauvegarde ! Alors où est-il ? Si vous êtes sous Windows, il est enregistré dans AppData\Roaming\LOVE . Vous pouvez accéder au dossier AppData caché en appuyant sur Ctrl + R, puis en tapant « appdata » et en cliquant sur OK. Il devrait y avoir un dossier qui porte le même nom que le dossier dans lequel se trouve votre projet LÖVE. Et dans ce dossier, vous trouverez un fichier nommé savedata.txt. Si vous ouvrez le fichier, vous verrez que votre table est à l’intérieur.
Maintenant, faisons en sorte que nous puissions charger nos données.
Chargement
Pour charger nos données, nous devons :
- Vérifier s’il existe un fichier de sauvegarde
- Lire le dossier
- Transformez les données en tableau
- Appliquez les données à nos joueurs et à nos pièces
Commençons donc par vérifier si notre fichier existe, et si c’est le cas, nous lisons le fichier. Nous pouvons le faire avec love.filesystem.getInfo(nom de fichier) et love.filesystem.read(nom de fichier). Si un fichier existe, love.filesystem.getInfo(filename) renverra une table avec des informations, sinon il renverra nil. Puisque nous voulons seulement savoir si le fichier existe, nous pouvons mettre la fonction dans une instruction if, car nous n’avons pas besoin des informations fournies par le tableau.
|
|
Exécutez le jeu et il devrait imprimer nos données de sauvegarde. Maintenant, nous devons transformer cette chaîne de table en une vraie table. Nous pouvons le faire avec lume.deserialize.
|
|
Et maintenant, nous pouvons appliquer les données à notre joueur et à nos pièces. Comment nous avons mis ce code avant de remplir le tableau des pièces. C’est parce que nous ne voulons pas générer les pièces que nous avons déjà récupérées dans notre fichier de sauvegarde. Les pièces que nous ajoutons sont désormais basées sur les données.
|
|
Maintenant, lorsque vous exécutez le jeu, vous verrez qu’il charge votre fichier de sauvegarde. Vous pouvez récupérer des pièces, appuyer sur F1 pour enregistrer, et lorsque vous redémarrez le jeu, vous verrez qu’il a à nouveau enregistré et chargé votre jeu. Impressionnant! Mais que se passe-t-il si nous voulons redémarrer? Ajoutons un bouton qui supprime notre fichier de sauvegarde afin que nous puissions commencer une nouvelle partie.
Réinitialisation
Pour supprimer notre fichier de sauvegarde, nous pouvons utiliser love.filesystem.remove(nom de fichier). Faisons en sorte que lorsque nous appuyons sur F2, le fichier soit supprimé et redémarre le jeu. Nous pouvons quitter le jeu avec love.event.quit(), mais si nous passons « restart » comme premier argument, le jeu redémarrera à la place.
|
|
Et voilà, nous pouvons maintenant réinitialiser notre jeu !
Résumé
Les graines décident quelles valeurs aléatoires que vous générez, et cela peut être utilisé pour partager un niveau généré aléatoirement par exemple. Nous pouvons également utiliser le module mathématique de LÖVE pour le faire. Lorsque vous supprimez des éléments d’une liste, nous devons parcourir le tableau en sens inverse pour éviter que des éléments soient ignorés. Nous pouvons créer un fichier de sauvegarde en ajoutant des données importantes à une table, puis transformer cette table en chaîne et écrire cette chaîne dans un fichier avec love.filesystem.write(nom de fichier). Nous pouvons charger un fichier de sauvegarde en lisant le fichier avec love.filesystem.read(nom de fichier), en désérialisant les données et en appliquant les données à nos objets. Nous pouvons supprimer un fichier avec love.filesystem.remove(nom de fichier) et redémarrer le jeu avec love.event.quit(« redémarrer »).