Bonjour/Bonsoir!
J'ai un petit projet depuis la fin de l'année dernière sur lequel je rravaille très irrégulièrement, mais malgré tout, j'en suis maintenant à me préoccuper de la gestion des sauvegardes.
Est-ce que je dois tenir compte des profils? Comment faire pour détecter le profil principal, ou forcer une connexion de profil? Où est-ce que j'écris les fichiers? Comment lire ces fichiers par la suite?
Et sur Windows, où est ce que j'écris les fichiers ?
Autant de questions que je me pose, et pour lesquels je fais appel à vous

. Merci de votre aide!
Salut !
Alors pour mes jeux en XNA j'utilise EasyStorage :
http://easystorage.codeplex.com/, ça permet d'abstraire la gestion de sauvegardes quelque soit le périphérique.
Mais pour PC ce n'est pas forcément optimal, puisque ça stocke les fichiers (XML dans mon cas) dans le dossier "Parties Enregistrées" sur Windows. C'est possible je pense d'éviter ça en choisissant plutôt le dossier AppData ou Program Data (attention, Program Files est verrouillé sur Windows 7 donc pas de sauvegardes à côté de l'exe !), mais je n'ai jamais essayé.
Pour illustrer, voici un bout de code utilisé dans mon jeu "Rabbit Apocalypse" (code source complet disponible sur mon blog
http://www.valryon.fr).
---
Classe gérant les sauvegardes génériques avec EasyStorage (non testée sur WP7 et Xbox 360 mais ça doit marcher a peu de chose prêt) :
using System;
using EasyStorage;
#if WINDOWS_PHONE
using Microsoft.Xna.Framework.Input.Touch;
#endif
using System.Xml;
using System.Xml.Serialization;
using System.Threading;
using Lapins.Engine.Core;
namespace Lapins.Engine.Storage
{
/// <summary>
/// Multi-platform savegame handler (with EasyStorage Lib)
/// </summary>
public class Saver<TSave>
{
/// <summary>
/// Save file name
/// </summary>
private string _filename;
/// <summary>
/// Directory name
/// </summary>
private string _containerName;
/// <summary>
/// EasyStorage manager
/// </summary>
private IAsyncSaveDevice _saveDevice;
/// <summary>
/// Serializer
/// </summary>
private XmlSerializer _serializer;
/// <summary>
/// Current savegame
/// </summary>
private TSave _data;
/// <summary>
/// Raised when the save is completed
/// </summary>
public event Action<TSave> SaveCompleted;
/// <summary>
/// Raised when the load is completed
/// </summary>
public event Action<TSave> LoadCompleted;
public Saver(Game instance, string filename, string contrainerName)
{
_filename = filename;
_containerName = contrainerName;
_serializer = new XmlSerializer(typeof(TSave));
#region EasyStorage Init
EasyStorageSettings.SetSupportedLanguages(Language.French, Language.Spanish, Language.English, Language.German, Language.Japanese, Language.Italian);
//Windows Phone7: IsolatedStorage
#if WINDOWS_PHONE
saveDevice = new IsolatedStorageSaveDevice();
#else
//Xbox & PC: SharedDevice
//-- Create
SharedSaveDevice sharedSaveDevice = new SharedSaveDevice();
instance.Components.Add(sharedSaveDevice);
_saveDevice = sharedSaveDevice;
//-- Events on Xbox
// Force a player to choose a storage location on current storage disconnection
sharedSaveDevice.DeviceSelectorCanceled += (s, e) => e.Response = SaveDeviceEventResponse.Force;
sharedSaveDevice.DeviceDisconnected += (s, e) => e.Response = SaveDeviceEventResponse.Force;
// Ask the player to choose
sharedSaveDevice.PromptForDevice();
#endif
// End of savegame events
_saveDevice.SaveCompleted += new SaveCompletedEventHandler(saveDevice_SaveCompleted);
_saveDevice.LoadCompleted += new LoadCompletedEventHandler(saveDevice_LoadCompleted);
#endregion
}
/// <summary>
/// Register an empty save data (or reset the current one)
/// </summary>
/// <param name="emptySave"></param>
public void Initialize(TSave emptySave)
{
_data = emptySave;
ForceSaveAsync();
}
/// <summary>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
void saveDevice_SaveCompleted(object sender, FileActionCompletedEventArgs args)
{
if (SaveCompleted != null) SaveCompleted(_data);
}
/// <summary>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
void saveDevice_LoadCompleted(object sender, FileActionCompletedEventArgs args)
{
if (LoadCompleted != null) LoadCompleted(_data);
}
/// <summary>
/// Save asynchronously the data
/// </summary>
/// <returns></returns>
public bool SaveAsync()
{
if (_saveDevice.IsReady)
{
_saveDevice.SaveAsync(
_containerName,
_filename,
stream =>
{
XmlWriterSettings settings = new XmlWriterSettings()
{
Indent = true,
};
using (XmlWriter writer = XmlWriter.Create(stream, settings))
{
SerializeSave(writer);
}
});
return true;
}
return false;
}
/// <summary>
/// Load asynchronously the data
/// </summary>
/// <returns></returns>
public bool LoadAsync()
{
if (_saveDevice.IsReady)
{
_saveDevice.LoadAsync(
_containerName,
_filename,
stream =>
{
using (XmlReader reader = XmlReader.Create(stream))
{
DeserializeSave(reader);
}
});
return true;
}
return false;
}
/// <summary>
/// Try to save until it works
/// </summary>
/// <returns></returns>
public void ForceSaveAsync()
{
new Thread(new ThreadStart(
delegate()
{
while (this.SaveAsync() == false) ;
}
)
).Start();
}
/// <summary>
/// Try to load until it works
/// </summary>
/// <returns></returns>
public void ForceLoad()
{
while (this.LoadAsync() == false) ;
}
/// <summary>
/// Try to load until it works
/// </summary>
/// <returns></returns>
public void ForceLoadAsync()
{
new Thread(new ThreadStart(
delegate()
{
while (this.LoadAsync() == false) ;
}
)
).Start();
}
/// <summary>
/// Savegame serialization
/// </summary>
/// <param name="writer"></param>
private void SerializeSave(XmlWriter writer)
{
_serializer.Serialize(writer, _data);
}
/// <summary>
/// Savegame deserialization
/// </summary>
/// <param name="reader"></param>
private void DeserializeSave(XmlReader reader)
{
var data = _serializer.Deserialize(reader);
if (data != null)
{
_data = (TSave)data;
}
}
/// <summary>
/// Current Savegame
/// </summary>
public TSave Data
{
get { return _data; }
}
}
----
La classe sérializable (XML) contenant les données à sauver :
using System;
using Lapins.Engine.Core;
using Lapins.Engine.Score;
using Microsoft.Xna.Framework;
namespace Lapins.Save
{
[Serializable]
public class LapinsSaveData
{
public const int HighscoresLinesCount = 10;
// Serializable fields
// -- Savegame attributes
public string Version;
public string ApplicationName;
public DateTime Date;
// -- Parameters
public int ResolutionWidth;
public int ResolutionHeight;
public bool IsFullscreen;
// -- Score
public Highscores Highscores;
public LapinsSaveData()
{
Version = Application.Version;
ApplicationName = Application.Name;
Date = DateTime.Now;
Highscores = new Highscores(HighscoresLinesCount);
IsFullscreen = false;
ResolutionWidth = 1280;
ResolutionHeight = 768;
}
}
}
---
Utilisation.
Création :
Saver = new Saver<LapinsSaveData>(this, "lapins.lps", "Lapins");
Chargement (je n'utilise pas le côté asynchrone car c'est déjà dans mon code de chargement, Intiailize de Game) :
...
// Let's load the savegame and wait until it's ready
Saver.ForceLoadAsync();
Saver.LoadCompleted += Saver_LoadCompleted;
...
void Saver_LoadCompleted(LapinsSaveData data)
{
Saver.LoadCompleted -= Saver_LoadCompleted;
if (data == null)
{
Saver.Initialize(new LapinsSaveData());
}
}
---
Sauvegarde d'un score dans un objet Highscore prévu pour :
// Save score
Game.Saver.Data.Highscores.AddScore(new ScoreLine("playername", _score));
Game.Saver.SaveAsync();