[C#/XNA]Enregistrement des préférences

Bonjour à toutes et à tous ! :slight_smile:

Je travaille sur un projet de jeu vidéo, et celui-ci étant quasiment terminé, je souhaiterais enregistrer les préférences de l’utilisateur comme la configuration des touches, la résolution, le mode fenêtré, etc… Afin qu’il ne soit pas obligé, à chaque fois qu’il démarre le jeu, de tout reconfigurer.

J’ai découvert très récemment les « paramètres » (Propriété de la solution → Paramètres) qui permettent de faire ce que je voudrais via un fichier « .config » (dont la syntaxe est fortement basée sur XML) de faire ce que je veux. Seul hic, les variables définies dans ce fichier doivent être de type très simple comme un int, une string ou un bool (voire même un Point, mais il faut aller le chercher un peu plus loin). Or, pour la configuration des touches, comme on peut jouer à 4, j’ai fait un tableau à 2 dimensions comme ceci:

Keys[][] touches_joueurs = new Keys[][] { new Keys[]{ Keys.Up, Keys.Down, Keys.Left, Keys.Right, Keys.RightControl }, new Keys[]{ Keys.Z, Keys.S, Keys.Q, Keys.D, Keys.LeftControl }, new Keys[]{ Keys.I, Keys.K, Keys.J, Keys.L, Keys.Space }, new Keys[]{ Keys.NumPad8, Keys.NumPad5, Keys.NumPad4, Keys.NumPad6, Keys.Enter } };

Comment faire pour le stocker via les paramètres ? Je n’ai rien trouvé sur le sujet et je pense qu’il n’est pas possible d’y stocker un tableau.

Donc voilà, si jamais quelqu’un a déjà été confronté à ce problème, j’aurais voulu savoir comment il avait fait pour y rémédier.

Merci d’avance ! :slight_smile:

Un XmlSerializer, une classe [Serializable], 5 lignes de code et magie :slight_smile:
(Ca devrait suffire comme indications, le mot clé etant en gras)

Tu me recommandes donc d’abandonner les paramètres ?

Lorsque j’ai commencé mes recherches, j’ai tout de suite pensé à XML, et j’avais réussi plus ou moins à faire quelque chose de pas trop mal. Le problème, c’est que je ne savais pas comment modifier certaines valeurs sans réécrire entièrement le fichier XML…

Perso, j’ai jamais été fan des parametres, meme si ca permet de mettre en place un truc rapidos, ca manque comme t’as pu le constater de flexibilité.
Avec ca, t’es a peu pres tranquille tant que tu renommes pas tes variables tout les 2 jours.
Et sinon, oui, faut reecrire le fichier en entier a chaque fois, mais je vois 1) pas le souci 2) comment faire autrement :slight_smile:

J’ai un peu tenté la sérialisation, mais le problème est qu’il ne prend pas les classes « static ». Oui, jusqu’ici, j’utilisais une classe static nommée « Config » dans laquelle est défini toutes les variables utile à l’ensemble du projet comme le nombre de joueur, la difficulté, etc… Si j’utilisais une sauvegarde de ces valeurs avec XML, ce serait dans cette classe que je chargerais le fichier XML enregistré lors de la dernière partie pour affecter toutes les variables. �?tes-vous sûr que c’est possible avec la sérialisation ? Pouvez-vous, si c’est le cas, me donner un petit exemple ?

Merci encore !

Normal, il va pas prendre une classe static, vu qu’il a besoin d’une instance pour serializer et deserializer les infos. La solution dans ce cas precis, c’est de rajouter un niveau d’indirection. C’est a dire que sur la Classe static Config, il faut rajouter un objet instance d’une classe non static.

Petit exemple codé de tete la, pas sur que ca soit bon ni meme que ca marche :

public enum MonEnumDeKeyALaCon
{
 	CTRL,	
	SHIFT,
	ALT,
}

[Serializable]
public class KeyConfiguration
{
	public int param1 { get; set; }
	public List<string> param2 { get; set; }
	public MonEnumDeKeyALaCon param3> { get; set; }
}


public static class Config
{
	public static KeyConfiguration KeyConfig { get; set; }

 	public static void Load(string fileName)
 	{
		XmlSerializer xs = new XmlSerializer(typeof(KeyConfig));
  
		using (FileStream fs = new FileStream(fileName))
		{
			Keyconfig = xs.Deserialize(fs);
		}
	}

	public static void Save(string fileName)
	{
		XmlSerializer xs = new XmlSerializer(typeof(KeyConfig));

		using (FileStream fs = new FileStream(fileName))
		{
			xs.Serialize(fs, KeyConfig);
		}
	}
}

Voila, en gros, ce que ca doit donner. Sachant que la, j’ai utilisé un certain override de Serialize & Deserialize, mais qu’il y en a d’autres, que j’ai pas forcement bien remplis les constructeurs de FileStream, et que j’ai mis 0 try/catch (et que j’ai surement mal tapé des trucs, ou oublié encore d’autres choses). Et ca, ca va normalement reussir a serializer un peu tout ce que tu vas mettre dans la class KeyConfiguration, du moment que c’est Serializable.

Donc oui, ca va necesitter un peu de refactoring, ca rend le truc un peu moins pratique, mais c’est robuste (enfin, j’utilises ca tout les jours depuis 2 ans avec 0 soucis, 0 maintenance, et une grosse facilité/flexibilité pour les autres dev d’ajouter leur parametres), testé et ca marche (tout en restant extremement light et simple). (Bon, par contre, ca devient un poil plus tricky quand tu commences a vouloir renommer des membres, ou a vouloir faire de l’heritage, mais globalement, pour ton probleme, en 5min, ca marche la)
KISS.

Youhou ! Merci beaucoup pour cette explication, après quelques modifications, j’ai réussi à obtenir un résultat tout à fait encourageant pour la sérialisation ! Voici le code:

	[Serializable]
	public class Configuration
	{
    	public Keys[][] touches_joueurs = new Keys[][]
    	{
     		new Keys[]{ Keys.Up, Keys.Down, Keys.Left, Keys.Right, Keys.RightControl }, 
     		new Keys[]{ Keys.Z, Keys.S, Keys.Q, Keys.D, Keys.LeftControl }, 
     		new Keys[]{ Keys.I, Keys.K, Keys.J, Keys.L, Keys.Space }, 
     		new Keys[]{ Keys.NumPad8, Keys.NumPad5, Keys.NumPad4, Keys.NumPad6, Keys.Enter }
    	};
	}

	static public class Config
	{
    	public static Keys[][] touches_joueurs { get; set; }

    	public static void Save(string fileName)
    	{
        	XmlSerializer xs = new XmlSerializer(typeof(Configuration));
        	TextWriter tw = new StreamWriter(fileName);
        	xs.Serialize(tw, new Configuration());
        	tw.Close();  	
    	}
}

En appelant Config.Save(“preference.xml”), un fichier “preference.xml” est créé avec ce contenu:

<?xml version="1.0" encoding="utf-8"?>
<Configuration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <touches_joueurs>
	<ArrayOfKeys>
  	<Keys>Up</Keys>
  	<Keys>Down</Keys>
  	<Keys>Left</Keys>
  	<Keys>Right</Keys>
  	<Keys>RightControl</Keys>
	</ArrayOfKeys>
	<ArrayOfKeys>
  	<Keys>Z</Keys>
  	<Keys>S</Keys>
  	<Keys>Q</Keys>
  	<Keys>D</Keys>
  	<Keys>LeftControl</Keys>
	</ArrayOfKeys>
	<ArrayOfKeys>
  	<Keys>I</Keys>
  	<Keys>K</Keys>
  	<Keys>J</Keys>
  	<Keys>L</Keys>
  	<Keys>Space</Keys>
	</ArrayOfKeys>
	<ArrayOfKeys>
  	<Keys>NumPad8</Keys>
  	<Keys>NumPad5</Keys>
  	<Keys>NumPad4</Keys>
  	<Keys>NumPad6</Keys>
  	<Keys>Enter</Keys>
	</ArrayOfKeys>
  </touches_joueurs>
</Configuration>

Seul problème maintenant => comment désérialiser ? Car je dois avouer que je n’ai pas vraiment compris ton code:

        public static KeyConfiguration KeyConfig { get; set; }
        public static void Load(string fileName)
        {
                XmlSerializer xs = new XmlSerializer(typeof(KeyConfig));
  
                using (FileStream fs = new FileStream(fileName))
                {
                        s_keyconfig = xs.Deserialize(fs);
                }
        }

Qu’est-ce que s_keyconfig ? Je ne l’ai pas vu défini dans le reste du code. De plus, comme vous avez pu le constater, les touches des joueurs sont stockées dans un tableau à 2 dimensions, or, si je fais comme vous et qu’il s’agit d’un KeyConfiguration, l’ensemble du code qui croit accéder à un tableau à 2 dimensions va poser problème.

Pourriez-vous m’en dire un peu plus une dernière fois ? Je continue mes recherches de mon côté en tout cas !

Merci d’avance !

Fixed, c’etait KeyConfig directement, tu deserializes direct ton object (mais du coup, il faut ptet faire un cast au passage).

Encore un “petit” soucis et après j’essaye d’arrêter de vous embêter !

Donc j’ai réussi à sérialiser/désérialiser un objet, et ça fonctionne plutôt bien, voici le code:

	[Serializable]
	public class Configuration
	{
    	public Keys[][] touches_joueurs = new Keys[][]
    	{
         	new Keys[]{ Keys.Up, Keys.Down, Keys.Left, Keys.Right, Keys.RightControl }, 
         	new Keys[]{ Keys.Z, Keys.S, Keys.Q, Keys.D, Keys.LeftControl }, 
         	new Keys[]{ Keys.I, Keys.K, Keys.J, Keys.L, Keys.Space }, 
         	new Keys[]{ Keys.NumPad8, Keys.NumPad5, Keys.NumPad4, Keys.NumPad6, Keys.Enter }
    	};

    	public bool[] joueurs_IA = new bool[] { true, true, true, true };
	}

	static public class Config
	{
    	public static Configuration configuration;

    	public static Configuration Load(string fileName)
    	{
        	XmlSerializer deserializer = new XmlSerializer(typeof(Configuration));
        	TextReader textReader = new StreamReader(fileName);
        	Configuration configuration;
        	configuration = (Configuration)deserializer.Deserialize(textReader);
        	textReader.Close();

        	return configuration;
    	}

    	public static void Save(string fileName)
    	{
        	XmlSerializer xs = new XmlSerializer(typeof(Configuration));
        	TextWriter tw = new StreamWriter(fileName);
        	xs.Serialize(tw, new Configuration());
        	tw.Close();  	
    	}

    	public static Keys[][] touches_joueurs;
    	static public bool[] joueurs_IA;
	}

Alors plusieurs problèmes. Dans un premier temps, je ne peut pas appeler les méthodes Load et Save à l’intérieur de la classe static donc impossible d’affecter les valeurs de toutes les variables directement à l’intérieur. Je suis obligé de le faire à l’initialisation du jeu, comme ceci:

            // Chargement du fichier XML
        	Config.configuration = Config.Load("save.xml");
            // Affectation des variables
        	Config.touches_joueurs = Config.configuration.touches_joueurs;
        	Config.joueurs_IA = Config.configuration.joueurs_IA;

Si c’est la bonne méthode, pas de soucis, mais je voudrais être sûr.

Deuxième problème, et celui-ci est plus embêtant, c’est que la sauvegarde n’enregistre rien. Là il s’agit juste d’un problème de logique.
Comme on peut s’en douter, je sauvegarde uniquement dans la méthode “UnloadContent” appellée lorsque l’on quitte le jeu. Or, lorsque j’appelle Config.Save(), qu’est-ce que ça va me faire ? Ben ça va transformer la classe “Configuration” que j’ai posté plus haut en fichier XML. Or, les attributs (ou variables) de cette classe ne changent pas, c’est ceux par défaut, et si la configuration des touches change via le menu, ce sera la variable Config.touches_joueurs qui changera. En écrivant ces lignes, je viens de penser à une solution. Serait-ce correcte de faire ça ?

    	public static void Save(string fileName)
    	{
        	Configuration nouvelle_configuration = new Configuration();

        	nouvelle_configuration.touches_joueurs = touches_joueurs;
        	nouvelle_configuration.joueurs_IA = joueurs_IA;

        	XmlSerializer xs = new XmlSerializer(typeof(Configuration));
        	TextWriter tw = new StreamWriter(fileName);
        	xs.Serialize(tw, nouvelle_configuration);
        	tw.Close();  	
    	}

(modification de la méthode Save())

Voilà, désoler de psoer plein de questions, mais comme vous avez l’air de l’avoir déjà fait, vous devez savoir ce qui est le plus pratique !

Merci encore !

Je comprends rien a ce qui te poses soucis. La t’es plus dans les subtilités (ou pas) du C#.

Bon, pour la premiere question, Je comprends meme pas pourquoi tu affectes la variable dans la fonction, que tu retournes, puis que tu la reaffectes avec la valeur de retour de la fonction. T’en fais trop la.
Et si tu veux appeler la fonction statique dans la classe, tu dois pouvoir la mettre dans un constructeur static, et zou, on en parles plus. Par contre pour la sauvegarde, il faudrat l’appeler a la main, c’est simple mais pas encore magique hein.

Pour le reste, il faut sauver ce que tu veux sauver, je vois pas le soucis, si tu sauves une instance nouvelle de ta classe, ca va sauver une instance nouvelle de ta classe. Si tu sauves une instance avec ses propres parametres, ca va la sauver. Plus bete tu meurs.
(Et du coup, je le ferais vraiment pas dans le unload content, mais plutot apres avoir validé les options, sur le boutons back par exemple).

Et si tu veux un truc compatible Xbox 360, jette un coup d’oeil du coté de EasyStorage.

Et sinon:
http://www.xna-connection.com/post/Tutoriel%3A-Composant-de-gestion-de-fichiers-de-configuration-pour-XNA-sous-Windows

/sifflote