sauvegarde / chargement de l'état du jeu?

12

quelle est la méthode ou l'algorithme le plus utilisé pour enregistrer l'état du jeu (profils), les fichiers texte des bases de données, comment le cryptage se passe et les choses liées.

J'ai vu Caesar IV utiliser mySQL.

Aucune suggestion.

NativeCoder
la source

Réponses:

20

Je suis presque sûr que cela se fait le plus souvent dans un format propriétaire (binaire), en écrivant simplement chaque variable de la structure d'état de jeu que vous utilisez dans un fichier, ou en lisant ce fichier dans une instance de la structure. En C ++, par exemple, vous pouvez traiter un objet comme un tableau d'octets et simplement l'écrire () dans un fichier, ou fread () à partir du fichier et le restituer sous sa forme d'objet.

Si vous utilisez Java ou un autre langage, vous pouvez choisir d'utiliser la sérialisation pour rendre la tâche vraiment facile. L'explication Java est au bas de cette réponse; d'autres langages (de niveau supérieur à C ++) ont sûrement leurs propres bibliothèques de sérialisation ou fonctions intégrées, que vous devriez choisir d'utiliser. Peu importe l'inefficacité de la routine de sérialisation, l'espace sur le disque dur est bon marché de nos jours, surtout lorsque l'état du jeu ne devrait pas être très grand, et cela vous empêche d'écrire et de maintenir vos propres routines de sérialisation.

Vous ne devez absolument pas utiliser MySQL (lisez simplement la première phrase de cette revue ). Si vous avez vraiment besoin d'une base de données relationnelle pour une raison quelconque, utilisez SQLite ; il s'agit d'un système de base de données léger et existe dans un seul fichier de base de données. Mais pour la plupart des jeux, les bases de données relationnelles ne sont pas la voie à suivre, et les entreprises qui essaient de les utiliser finissent généralement par les utiliser comme table de recherche de valeurs-clés plutôt que comme une véritable base de données relationnelle.

Toute sorte de cryptage des fichiers de disque locaux n'est que de l'obscurcissement ; n'importe quel pirate informatique reniflera juste les données juste après que votre programme les ait déchiffrées. Je suis personnellement contre ce genre de chose, EN PARTICULIER avec les jeux à un joueur. À mon avis, les propriétaires du jeu (clients payants, pensez-vous) devraient être autorisés à pirater le jeu s'ils le souhaitent. Dans certains cas, cela peut créer un plus grand sens de la communauté, et des "mods" peuvent être développés pour votre jeu qui attirent plus de clients vers vous. L'exemple le plus récent qui me vient à l'esprit est le mod Portal in Minecraft récemment publié. Il est publié sur les sites d'actualités des joueurs sur Internet, et vous pouvez parier que cela a fait grimper les ventes de Minecraft.


Si pour une raison quelconque, vous êtes assez fou pour utiliser Java pour le développement de jeux comme je le suis, voici une explication rapide de la sérialisation en Java:

Conservez toutes vos données d'état de jeu dans une classe, rendez la classe sérialisable en l'implémentant Serializable, utilisez le transientmot-clé si vous mélangez des variables instanciées spéciales dans la classe (les objets transitoires ne sont pas sérialisés), et utilisez simplement ObjectOutputStreampour écrire dans un fichier et ObjectInputStreampour lire à partir d'un fichier . C'est ça.

Ricket
la source
2
Hehe, The Witcher n'a pas chiffré leurs données. Très facile d'ouvrir le fichier de sauvegarde dans un éditeur hexadécimal, de jouer un peu avec lui et d'obtenir toutes les cartes de poussins nus ... oh mon dieu, je suis vraiment désolé!
Anthony
J'ai utilisé la sérialisation binaire .NET pour enregistrer des jeux, et C # est peut-être une plate-forme légèrement plus raisonnable que Java sur laquelle développer des jeux. J'aime la façon dont il enregistre automatiquement un graphique d'objet entier automatiquement. Auparavant, c'était beaucoup plus difficile. Bien sûr, il y a maintenant le défi d'exclure des pièces inutiles comme les textures, mais ce n'est pas un gros problème à accomplir.
BlueMonkMN
Je ne suis pas en désaccord que C # est légèrement plus populaire que Java pour développer des jeux. Plus raisonnable implique une certaine subjectivité, et en tant que développeur de XNA et Java, je préfère Java. N'hésitez pas à lister les instructions sur la sérialisation en C # aussi si vous le souhaitez, je ne l'ai jamais fait mais je peux le modifier dans ma réponse. La sérialisation de Java enregistre tout le graphique de l'objet et le mot-clé «transitoire» sur les variables exclut les éléments inutiles comme les textures; tous les membres non transitoires doivent être sérialisables ou vous pouvez écrire une méthode writeObject personnalisée pour contourner cela.
Ricket
Le cornichon de Python sauvera également un graphique d'objet entier pour vous et est facile à utiliser. Quand vient le temps de désérialiser, vous êtes restitué des objets entièrement hydratés, facile comme bonjour
drhayes
Lorsque vous utilisez le formateur binaire intégré, exclure certains champs de la sérialisation en C # /. NET est aussi simple que de les orner de l'attribut [NonSerialized]. Cependant, ce qui est facile à ignorer, c'est que les événements générés par le compilateur (c'est-à-dire tout événement sans opérateur d'ajout / suppression personnalisé) créent un champ de sauvegarde pour stocker leurs abonnés. Ainsi, il est facile d'inclure accidentellement (et sans le savoir) des gestionnaires d'événements dans un graphe d'objets sérialisé. Pour contourner ce problème, vous devez ajouter l'attribut NonSerialized à la déclaration d'événement, mais vous devez ajouter le qualificatif "field:":[field: NonSerialized]
Mike Strobel
3

Si vous écrivez votre propre code, disons en C ++, vous pouvez utiliser des fichiers binaires. Les fichiers binaires vous offrent une forme de cryptage par obscurcissement. Mais comme documenté partout sur les internets, c'est une forme de sécurité très faible.

OU, vous pouvez utiliser quelque chose comme RapidXML si vous voulez une solution lisible par l'homme.

Si vous utilisez une sorte de framework, examinez ses fonctions de support de fichiers. HTH

JustBoo
la source
0

La plupart des jeux que j'ai vus utilisent simplement du code manuscrit pour lire / écrire à partir de fichiers binaires. Souvent, le format sur disque sera une réplique exacte de la disposition de la mémoire d'un objet, le chargement est donc juste:

  1. Lire le fichier en mémoire.
  2. Cast pointeur vers le tampon en type d'objet.

C'est rapide, mais c'est une corvée à maintenir, spécifique à la plate-forme et inflexible.

Dernièrement, j'ai vu quelques jeux utiliser SQLite. Les gens à qui j'ai parlé semblent l'aimer.

munificent
la source
0

La sérialisation est mentionnée dans certaines des autres réponses, et je conviens que c'est une solution raisonnable. Cependant, il échoue dans le versioning.

Supposons que vous libériez votre jeu et que les gens y jouent et créent des sauvegardes. Ensuite, dans un patch ultérieur, vous souhaitez corriger un bug ou ajouter des fonctionnalités. Si vous utilisez une méthode de sérialisation binaire simple et que vous ajoutez un membre à vos classes, votre sérialisation peut ne pas être compatible avec les anciennes sauvegardes lorsque les clients installent votre correctif.

Quelle que soit l'approche que vous utilisez, assurez-vous de penser à ce problème avant de lancer le jeu la première fois! Les clients ne seront pas satisfaits s'ils doivent recommencer après avoir appliqué un patch.

Une façon d'éviter cela consiste à faire en sorte que chaque type de données de base implémente des méthodes Save (version) et Load (version) suffisamment intelligentes pour savoir quelles données enregistrer et charger pour chaque version du jeu. De cette façon, vous pouvez prendre en charge la compatibilité descendante de vos sauvegardes et échouer avec élégance si un utilisateur essaie de charger une sauvegarde depuis une version plus récente du jeu que celle qu'il exécute.

kevin42
la source
1
En bref: l'implémentation des fonctions "enregistrer" et "charger" pour chaque version du jeu se transforme rapidement en cauchemar de maintenance. J'ai trouvé qu'une meilleure solution consiste à incorporer un numéro de version, à écrire des fonctions "enregistrer / charger" pour la version actuelle du jeu et à écrire une fonction de "conversion" de la version la plus récente non actuelle à la version actuelle. Ensuite, conservez toutes vos fonctions de conversion. Essayer de charger un ancien jeu à dix versions exécuterait dix fonctions de conversion en série, puis la fonction native "charger la version actuelle du jeu".
ZorbaTHut
Beaucoup plus facile à maintenir à long terme - le fait de conserver une série de fonctions de "chargement de chaque fichier de jeu précédent" se transforme rapidement en une opération en temps polynomial.
ZorbaTHut
Dans les jeux sur lesquels j'ai travaillé, ce n'est pas si complexe - plutôt que de conserver plusieurs copies des méthodes de chargement / sauvegarde, la seule méthode utilise le numéro de version pour déterminer les octets à lire et à écrire. Cela est encore plus facile si vous n'écrivez pas seulement des structures, mais implémentez le chargement et l'enregistrement en tant qu'opérations au niveau du type de données primitif (par exemple, ReadByte / ReadFload / etc). Ensuite, si vous ajoutez un nouveau membre, vous pouvez faire quelque chose comme if (version> 10) {someNewByte = ReadByte (); } L'implémenter de cette façon facilite également la prise en charge de différentes plateformes endiennes.
kevin42
Une autre façon d'éviter d'améliorer la prise en charge des versions consiste à éviter que les méthodes de sérialisation / désérialisation de vos objets écrivent / lisent des valeurs individuelles directement à partir d'un flux. Au lieu de cela, les objets pouvaient écrire leurs données possédées dans un sac de propriétés à l'aide de paires clé / valeur, puis écrire le sac dans le flux. Pendant la désérialisation, les objets pouvaient lire les valeurs du sac par leur nom. Mais au lieu d'avoir des vérifications de numéro de version spécifiques comme suggéré par kevin42, vous pouvez simplement avoir des règles pour gérer les valeurs manquantes (c'est-à-dire utiliser une valeur par défaut standard lorsque la valeur requise n'est pas dans le sac).
Mike Strobel
0

Si vous pouvez utiliser le même format que vous utilisez pour stocker vos niveaux, c'est un bonus.

Par exemple, un jeu comme Peggle peut avoir une disposition initiale du plateau, le nombre de balles, le score actuel (0 pour le plateau initial), etc. Une partie enregistrée peut alors utiliser exactement le même format.

Si vous utilisez le même format, le jeu de sauvegarde et la charge de niveau peuvent partager du code, ce qui facilite votre travail!

Pour un jeu avec de très gros fichiers de carte, le jeu de sauvegarde peut inclure le fichier de carte par référence. Les fichiers de niveau initial peuvent même faire la même chose, ce qui facilite également le retour sur cette carte si, pour une raison quelconque, l'histoire a fait revenir le personnage au même endroit, sauf que certains PNJ sont morts et qu'il y a des cadavres allongés tous plus de.

Zan Lynx
la source