1 Sauvegardes de données Sauvegardes de données Sommaire Sauvegardes de données... 1 1 Introduction... 2 2 Recherche du chemin de stockage... 3 2.1 Le dossier de jeu... 3 2.2 Le dossier privé... 5 3 Gestion des fichiers... 7 3.1 Création et ouverture de fichier... 7 3.2 Copier un fichier... 8 3.3 Supprimer un fichier... 9 3.4 Déplacer ou renommer un fichier... 9 3.5 Ecrire dans un fichier... 10 3.5.1 Ecriture en clair... 10 3.5.2 Sérialisation d objets... 12 3.6 Lire à partir d un fichier... 15 3.6.1 Lire des données en clair... 15 3.6.2 Lire des données sérialisées... 16 4 Conclusion... 18
2 Sauvegardes de données 1 Introduction Vous voici arrivé dans le dernier module théorique d utilisation de XNA pou créer ses jeux. Dans ce module, nous verrons comment gérer les périphériques de stockages connectés et également comment sauvegarder les parties d un jeu. Illustration 1. Disque dur de Xbox 360 Globalement, pour conserver la portabilité entre PC et Xbox360, il s agit de rechercher à quel emplacement nous souhaitons écrire ou lire des données avant d effectuer cette opération en utilisant les éléments disponibles dans l espace de nom System.IO.
3 Sauvegardes de données 2 Recherche du chemin de stockage En pratique, XNA nous fourni deux endroits de stockages des informations : Le dossier de jeu : C est le dossier qui contient le fichier exécutable de votre jeu Un dossier personnalisé : C est un dossier de sauvegarde créé dans votre dossier personnel sous Windows ou la sélection entre la carte mémoire ou le disque dur sur Xbox360. Illustration 2. Dossiers personnalisés dans «Mes Documents» Dans tous les cas, il nous faut signaler avant, que nous souhaitons utiliser les services de jeu en entrant cette ligne : public Game1() base.components.add(new GamerServicesComponent(this)); Code 1. Ajout de la prise en charge des services de jeu. 2.1 Le dossier de jeu Pour y accéder, la classe StorageContainer contient une propriété statique nommée TitleLocation qui contient le chemin absolu vers l exécutable du jeu : private string file_path; protected override void Initialize() file_path = Path.Combine(StorageContainer.TitleLocation, "game_data.dat"); if (!File.Exists(file_path)) File.Create(file_path); Code 2. Crée un chemin vers un fichier de jeu. Le code présenté ici permet de définir un chemin absolu vers un fichier nommé «game_data.dat» contenu dans le dossier du jeu. Si le fichier n existe pas, on tente de le créer.
4 Sauvegardes de données Illustration 3. Fichier game_data.dat créé là où se trouve l exécutable du jeu.
5 Sauvegardes de données 2.2 Le dossier privé Afin de sélectionner un dossier privé, nous devons cette fois-ci utiliser une méthode de recherche asynchrone. Sous Windows, cette méthode va créer automatiquement un dossier nommé «SavedGames» dans votre répertoire de documents personnel tandis que sur Xbox360, cette StorageDevice device; StorageContainer container; protected override void Initialize() base.initialize(); if (!Guide.IsVisible) Guide.BeginShowStorageDeviceSelector(new AsyncCallback(DeviceFound), null); private void DeviceFound(IAsyncResult r) if (r.iscompleted) device = Guide.EndShowStorageDeviceSelector(r); container = device.opencontainer("storage"); méthode va afficher à l écran une fenêtre demandant si le joueur souhaite lire/écrire sur la carte mémoire de la manette ou sur le disque dur de la console. Code 3. Sélectionne le dossier privé comme endroit de stockage Le code ci-dessus va simplement vérifier si le guide est utilisé ou non. S il ne l est pas, il va (sous Windows) se charger de créer un dossier dans le répertoire des documents personnel de l utilisateur courant. Lorsque la recherche est terminée, la méthode DeviceFound est appelée. Dans cette méthode, on teste si la recherche est bien finie et si tel est le cas, on va récupérer à la fois l objet de référence au support de stockage (StorageDevice) ainsi qu un objet donnant accès à ce support (StorageContainer).
6 Sauvegardes de données Illustration 4. Dans mon dossier personnel est apparu le dossier SavedGames\Storage\Allplayers De la même façon que dans la partie 2.1, nous pouvons ensuite y créer des fichiers en utilisant les méthodes de la classe File.
7 Sauvegardes de données 3 Gestion des fichiers Maintenant que nous avons récupéré un endroit où nous pouvons enregistrer ou lire des informations, nous pouvons faire ces opérations avec les outils standards contenus dans l espace de nom System.IO. 3.1 Création et ouverture de fichier Pour créer et ouvrir un flux vers un fichier, nous pouvons utiliser les méthodes statiques de la classe File : La méthode Create : Permet de créer un fichier et ouvre un flux vers le fichier nouvellement crée en retournant un objet FileStream. La méthode CreateText : Permet de créer un fichier de texte encodé en UTF8 et ouvre un flux d écriture vers le fichier texte en retournant un objet StreamWriter. La méthode Open : Permet d ouvrir, de créer et/ou de modifier un fichier. Retourne également un flux vers le fichier désiré. La méthode OpenRead : Permet d ouvrir en lecture seule un fichier dont le nom est passé en paramètre. Retourne un flux FileStream. La méthode OpenText : Permet d ouvrir un fichier texte encodé en UTF8. Retourne un flux de lecture StreamReader. La méthode OpenWrite : Ouvre un fichier en écriture seule. Retourne un flux protected override void Initialize() string file_path = Path.Combine(container.Path, "test.sav"); if (!File.Exists(file_path) && container!= null) File.Create(file_path).Close(); FileStream stream = null; if (File.Exists(file_path)) stream = File.Open(file_path, FileMode.Open, FileAccess.ReadWrite); FileStream. La méthode Exists : Retourne un booléen indiquant si le fichier passé en paramètre existe ou non. Code 4. Code à ajouter pour créer un fichier puis l ouvrir Le code ci-dessus va d abord créer un chemin absolu vers un fichier. Il va ensuite tester si le fichier n existe pas. Si c est le cas, il crée le fichier et referme le flux aussitôt pour que l on puisse ouvrir le fichier par la suite. Enfin, il teste si le fichier existe et si c est le cas, il crée un flux en accès bidirectionnel (lecture et écriture sont possible sur le même flux).
8 Sauvegardes de données protected override void Initialize() string file_path2 = Path.Combine(container.Path, "test2.sav"); if (File.Exists(file_path)) File.Copy(file_path, file_path2, true); 3.2 Copier un fichier Pour créer une copie d un fichier, rien de plus simple. Il suffit d appeler la méthode Copy de la classe File. Code 5. Code permettant de copier un fichier vers le fichier test2.sav Ce code vérifie si le fichier qu on souhaite copier existe. Si c est le cas, on copie le fichier indiqué par file_path dans le fichier test2.sav. Si le fichier test2.sav existe, la méthode va le réinscrire. Illustration 5. J ai bien la copie du fichier test.sav dans le fichier test2.sav Pour copier un fichier, il faut y accéder. Si le fichier que l on souhaite copier est déjà ouvert dans un autre flux, une exception sera levée! Une fois vos accès aux fichiers terminés n oubliez jamais de fermer le flux avec la méthode Close().
9 Sauvegardes de données protected override void Initialize() if (File.Exists(file_path2)) File.Delete(file_path2); 3.3 Supprimer un fichier Comme pour la copie, on doit d abord fermer un éventuel flux ouvert vers le fichier à supprimer. Ensuite, un simple appel à la méthode Delete de la classe File supprimera le fichier passé en paramètre. Code 6. Code à ajouter pour supprimer la copie précédemment créée. Ce code va simplement vérifier que la copie précédente s est bien déroulée avant de la supprimer. protected override void Initialize() string new_file_path = Path.Combine(container.Path, "test_renamed.sav"); if (File.Exists(file_path)) File.Move(file_path, new_file_path); 3.4 Déplacer ou renommer un fichier Pour déplacer un fichier, il suffit d appeler la méthode Move de la classe File. Pour renommer un fichier, l astuce consiste à déplacer le fichier dans le même dossier que le fichier source mais avec un nom différent. Code 7. Code à ajouter pour renommer le fichier test.sav en test_renamed.sav Ce code va tester si le fichier test.sav est existant. S il est présent, il le renomme en test_renamed.sav. Comme de coutume vous prendrez soin de fermer un flux qui aurait pu être ouvert sur le fichier que l on souhaite renommer.
10 Sauvegardes de données 3.5 Ecrire dans un fichier Pour écrire dans un fichier, il y a deux façons de procéder. La première consiste à stocker les informations directement dans le fichier en clair. La seconde, appelée sérialisation d objet, permet de protected override void Initialize() FileStream stream = null; if (File.Exists(file_path)) stream = File.Open(file_path, FileMode.Open, FileAccess.ReadWrite); StreamWriter writer = new StreamWriter(stream); writer.writeline("bonjour"); writer.writeline(145); writer.flush(); writer.close(); stocker soit sous forme binaire, soit sous forme XML, l instance d un objet. 3.5.1 Ecriture en clair Afin d écrire en clair dans un fichier, il nous faut récupérer un flux en écriture vers le fichier dans lequel on souhaite écrire. Ensuite, il suffit d instancier un objet StreamWriter pour écrire de manière simple dans le flux. Code 8. Code permettant d écrire en clair dans le fichier test.sav défini plus haut Dans ce code, nous récupérons un flux en lecture/écriture vers le fichier test.sav (voir plus haut pour créer ce fichier). Puis, nous instancions un objet StreamWriter sur le flux récupéré et nous inscrivons deux nouvelles lignes : La première contenant la chaine de caractère «Bonjour» La seconde contenant le chiffre «145» La méthode Flush() permet ensuite de vider le tampon du flux dans le fichier et la méthode Close() ferme le flux ouvert vers le fichier test.sav.
11 Sauvegardes de données Illustration 6. Mon texte «Bonjour» et le chiffre 145 ont été sauvegardés dans le fichier
12 Sauvegardes de données 3.5.2 Sérialisation d objets Plutôt que de sauvegarder uniquement des valeurs dans un fichier (ce qui pourrait être source d erreurs lors de la lecture), nous pouvons enregistrer l état complet d un objet dans un fichier. Ceci permet, lors de la lecture, de récupérer notre objet dans l état où il était au moment de sa sérialisation. On pourrait par exemple imaginer une classe «Personnage» qui contient quelques propriétés «Points de vie», «Niveau» etc. et que l on souhaiterait pouvoir sauvegarder tel quel quand le joueur enregistre son avancement dans le jeu. Note : Pour sérialiser des objets, nous devrons utiliser l espace de nom System.Xml.Serialization si on souhaite sérialiser sous forme de document XML, ou l espace de nom System.Runtime.Serialization.Formatters.Binary pour sérialiser en binaire. Nous pouvons définir nos propres objets sérialisable en ajoutant l attribut Serializable avant chaque objet ayant besoin d être sérialisés (classe ou structure). using System; using Microsoft.Xna.Framework; namespace Storage [Serializable] public class Personnage private int niveau, point_de_vie; private Vector2 position; public Personnage() niveau = 0; point_de_vie = 0; position = new Vector2(0, 0); public Vector2 Position get return position; set position = value; public int Niveau get return niveau; set niveau = value; Code 9. Contenu du fichier Personnage.cs que l on souhaite sérialiser.
13 Sauvegardes de données protected override void Initialize() FileStream stream = null; if (File.Exists(file_path)) Personnage p = new Personnage(); p.niveau = 13; p.position = new Vector2(49, 80); stream = File.Open(file_path, FileMode.Open, FileAccess.ReadWrite); BinaryFormatter f = new BinaryFormatter(); f.serialize(stream, p); stream.close(); Code 10. Sérialisation binaire de la classe Personnage. Nous créons une nouvelle classe Personnage qui pourra être sérialisée. Dans le code principal, nous instancions la classe Personnage et nous la sérialisons sous forme binaire à l aide de la classe BinaryFormatter. Illustration 7. Ma classe personnage sérialisée dans le fichier test.sav
14 Sauvegardes de données Si nous souhaitons sérialiser de manière plus lisible sous forme XML en l occurrence nous utiliserons la classe XmlSerializer de l espace de nom System.Xml.Serialisation à la place de la classe BinaryFormatter. protected override void Initialize() FileStream stream = null; if (File.Exists(file_path)) Personnage p = new Personnage(); p.niveau = 13; p.position = new Vector2(49, 80); stream = File.Open(file_path, FileMode.Open, FileAccess.ReadWrite); XmlSerializer f = new XmlSerializer(typeof(Personnage)); f.serialize(stream, p); stream.close(); Code 11. Code sérialisant en XML la classe personnage. Illustration 8. Ma classe personnage sérialisée sous forme XML.
15 Sauvegardes de données 3.6 Lire à partir d un fichier De même que nous pouvons écrire des données en clair ou sérialisées, nous pouvons également lire des données en clair ou sérialisées. 3.6.1 Lire des données en clair protected override void Draw(GameTime gametime) if (container!= null) spritebatch.begin(); spritebatch.drawstring(font, file_path, new Vector2(0, 0), Color.Red); spritebatch.end(); FileStream fs = File.Open(file_path, FileMode.OpenOrCreate, FileAccess.Read); StreamReader sw = new StreamReader(fs); spritebatch.begin(); spritebatch.drawstring(font, sw.readline(), new Vector2(0, 30), Color.Red); spritebatch.end(); sw.close(); Code 12. Code à ajouter pour lire des données en clair. Ce code permet d afficher le chemin du fichier qui sera lu. Ensuite, pour lire le fichier, nous créons un flux en lecture puis nous créons un objet de lecture StreamReader et nous affichons ce qui a été lu sur la première ligne du fichier.
16 Sauvegardes de données Illustration 9. Le texte lu sur la 1ère ligne du fichier test.sav est Bonjour. 3.6.2 Lire des données sérialisées Pour lire des données sérialisées, aussi appelé dé-sérialisation, nous appelons cette fois-ci les méthodes Deserialize des classes adaptées (XmlSerializer pour une sérialisation en XML ou BinaryFormatter pour une sérialisation binaire). La désérialisation retourne en général un objet de type «Object». Aussi, pour récupérer notre protected override void Draw(GameTime gametime) if (container!= null) spritebatch.begin(); spritebatch.drawstring(font, file_path, new Vector2(0, 0), Color.Red); spritebatch.end(); FileStream fs = File.Open(file_path, FileMode.OpenOrCreate, FileAccess.Read); BinaryFormatter f = new BinaryFormatter(); Personnage p = (Personnage)f.Deserialize(fs); spritebatch.begin(); spritebatch.drawstring(font, p.position.x.tostring(), new Vector2(0, 30), Color.Red); spritebatch.end(); fs.close(); objet tel qu il était avant, nous devons réaliser un cast de l objet désérialisé. Code 13. Code désérialisant l objet sérialisé à la partie 3.5.2 Le code ci-dessus crée un nouveau flux en lecture et instancie un objet BinaryFormatter qui va nous permettre de désérialiser l objet Personnage sauvegardé plus haut dans le fichier test.sav. Nous en affichons ensuite sa position X :
17 Sauvegardes de données Illustration 10. J ai conservé mon objet Personnage comme il était avant! De même que précédemment, si nous souhaitons désérialiser un objet sérialisé en XML, nous devons juste remplacer l objet BinaryFormatter par un objet XmlSerializer. protected override void Draw(GameTime gametime) if (container!= null) spritebatch.begin(); spritebatch.drawstring(font, file_path, new Vector2(0, 0), Color.Red); spritebatch.end(); FileStream fs = File.Open(file_path, FileMode.OpenOrCreate, FileAccess.Read); XmlSerializer f = new XmlSerializer(typeof(Personnage)); Personnage p = (Personnage)f.Deserialize(fs); spritebatch.begin(); spritebatch.drawstring(font, p.position.x.tostring(), new Vector2(0, 30), Color.Red); spritebatch.end(); fs.close(); Code 14. Code désérialisant l objet Personnage sérialisé en XML.
18 Sauvegardes de données bleu): Nom StorageContainer StorageDevice Guide Path File FileStream StreamReader StreamWriter BinaryFormatter XmlSerializer 4 Conclusion Vous savez désormais comment manipuler les fichiers dans votre jeu XNA. Par ailleurs, en terminant ce module, vous avez maintenant suffisamment de connaissances sur le Framework XNA pour vous lancer dans votre propre jeu en 2 dimensions. Afin de vous remémorer les classes utile vues dans ce module, voici un récapitulatif de tout ce qui a été vu dans ce module (Les classes sont en rouge, les méthodes en vert et les propriétés en Méthodes et informations Donne l accès à un périphérique de stockage TitleLocation Propriété statique donnant un chemin absolu vers le dossier contenant l exécutable du jeu. Path Indique le chemin absolu vers un dossier accessible sur le périphérique de stockage. Dispose Permet de libérer le périphérique ouvert. Objet de référence au périphérique physique FreeSpace Retourne la taille libre sur le périphérique. IsConnected Indique si le périphérique est connecté ou non. TotalSpace Retourne la taille totale sur le périphérique OpenContainer Ouvre le périphérique concerné. Retourne un objet StorageContainer. Permet d afficher des écrans de guidage de l utilisateur. BeginShowStorageDeviceSelector Sur Xbox360, demande de choisir entre la carte mémoire et le disque dur. Sur Windows, crée un dossier SavedGames dans les documents personnel. EndShowStorageDeviceSelector Retourne l objet StorageDevice demandé. Regroupe un ensemble de méthode pour former des chemins vers des fichiers/dossiers. Combine Regroupe deux chaines pour former un chemin d accès. Regroupe un ensemble de méthodes pour gérer les fichiers. Open Ouvre le fichier indiqué. Create Créé un nouveau fichier. Delete Supprime le fichier indiqué. Move Déplace le fichier indiqué. Ouvre un flux de fichier en lecture et/ou en écriture. Close Ferme le flux et libère le fichier ciblé. Ouvre un flux de lecture. Utilisé avec les flux de fichiers pour lire simplement des fichiers. ReadLine Ligne les caractères jusqu'à la fin d une ligne. Close Ferme le flux et son flux sous-jacent. Ouvre un flux en écriture. Utilisé avec les flux de fichiers pour écrire simplement des fichiers. WriteLine Ecrit une ligne dans un fichier. Close Ferme le flux et son flux sous-jacent. Créé un objet de sérialisation binaire. Serialize Sérialise un objet dans un fichier ouvert par un flux. Deserialize Désérialise un objet sérialisé dans un fichier. Créé un objet de sérialisation XML. Serialize Sérialise un objet dans un fichier ouvert par un flux. Deserialize Désérialise un objet sérialisé dans un fichier.
19 Sauvegardes de données Pour terminer ces modules théoriques de l utilisation du Framework XNA, je ne peux que trop vous conseiller, une fois de plus, d aller visiter le site du MSDN MSDN Library Development Tools and languages XNA Game Studio XNA Game Studio 2.0 qui contient de nombreuses ressources sur l utilisation de XNA.