Serialisation et arrays

Questions à propos du scripting. Hors Shader, GUI, Audio et Mobile.
Répondre
Osmorhum
Messages : 5
Inscription : 26 Fév 2021 13:51

Serialisation et arrays

Message par Osmorhum » 26 Fév 2021 21:03

Bonjour à tous !

Je suis actuellement en train de concevoir un petit programme qui doit me permettre de gérer un inventaire et des configurations de vaisseaux spatiaux pour un JDR (irl).
Ca tourne bien, j'ai mes différents équipements dans l'inventaire que je peux attribuer à mon vaisseau tout ça, et actuellement j'en suis à l'étape de mettre en place un système de sauvegarde pour enregistrer l'inventaire des joueur et ce qu'il contient (en gros mon inventaire est composé de 8 arrays (1 pour chaque classe d'objets à enregistrer).

J'ai un objet Player qui contient un int et un string pour l'ID et le nom du groupe de joueur afin de pouvoir gérer différents groupes.

Jme suis donc attelé à la sérialisation pour sauvegarder tout ça dans un fichier. Lorsque je clique sur mon bouton de sauvegarde avec seulement Player à serialiser, aucun pb, je retrouve mon fichier et je vois même dedans qu'il m'a pris l'ID et le Nom du groupe.
En revanche, dès que j'ajoute l'inventaire dans l'histoire, j'ai un message d'erreur dans Unity...
SerializationException: Type 'Inventory' in Assembly 'Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' is not marked as serializable.
En voyant ça jme suis dit "bon ben jvais rendre Inventory serialisable" mais du coup il me fait le même coup, disant que MonoBehaviour n'est pas serialisable. Donc je retire le MonoBehaviour et... ben rien ne va plus ! (je précise que les arrays que je souhaite enregistrer peuvent contenir des lignes "vides", je sais pas si ça a une influence...)

Alors vous l'aurez surement deviné mais je tatonne sur Unity depuis environ 1 an (à un rythme tranquille)et en général je fini toujours par aboutir à ce que je cherchais mais du coup y a certainement des notions qui m'échappent :

Est-ce que c'est possible de serialiser de multiples arrays d'objets (j'ai vu que c'était pas possible de le faire avec des éléments propres à Unity, genre Vector3, mais pour des class customs ça me semblerait un peu abusé)?

Si non (ce qui me semblerait quand même vachement étrange) comment s'y prendre, est-ce qu'un array d'integer retenant les ID des objets contenus dans l'inventaire passerait (mais dans ce cas enregistrer un objet qui contient lui-même des objets me semble être une vraie galère)?

Si oui, est-ce que vous pourriez m'aider à comprendre ce que je fais de travers ?

le code :

inventory

Code : Tout sélectionner

[System.Serializable]
public class Inventory : MonoBehaviour
{

    public Chassis[] hulls;
    public Generateur[] generators;
    public Computer[] computers;
    public Propulseur[] engines;
    public Equipement[] equips;
    public Composant[] comps;
    public SystemExploit[] systems;

    public Vaisseau[] hangar;   // enregistre toutes les configurations de vaisseau de l'équipage
MyData

Code : Tout sélectionner

[System.Serializable]
public class MyData
{
    public int profilID;
    public string groupName;
    public Inventory playerInventory;

    public MyData(Player Joueur, Inventory inventaire)
    {
        groupName = Joueur.name;
        profilID = Joueur.playerID;
        playerInventory = inventaire;
    }

}
SaveScript

Code : Tout sélectionner

using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

public static class SaveScript 
{
    public static void SavePlayer(Player joueur, Inventory inventaire)
    {
        BinaryFormatter formatter = new BinaryFormatter();
        string path = Application.persistentDataPath + "/player.strshp";
        Debug.Log(path);
        FileStream stream = new FileStream(path, FileMode.OpenOrCreate);

        MyData data = new MyData(joueur, inventaire);

        formatter.Serialize(stream, data);
        stream.Close();
    }

    public static MyData LoadFile()
    {
        string path = Application.persistentDataPath + "/player.strshp";

        if(File.Exists(path))
        {

            BinaryFormatter formatter = new BinaryFormatter();
            FileStream stream = new FileStream(path, FileMode.Open);

            MyData data = formatter.Deserialize(stream) as MyData;
            stream.Close();

            return data;

        }
        else
        {
            Debug.LogError("fichier introuvable");
            return null;
        }

    }
}
Dernière édition par Osmorhum le 27 Fév 2021 02:35, édité 1 fois.

Avatar de l’utilisateur
Max
Messages : 8239
Inscription : 30 Juil 2011 13:57
Contact :

Re: Serialisation et arrays

Message par Max » 26 Fév 2021 22:07

Bonsoir,

peux-tu poster ton code svp, c'est souvent plus simple pour comprendre ce qui se passe ;)
Image
Pas d'aide par MP, le forum est là pour ça.
En cas de doute sur les bonnes pratiques à adopter sur le forum, consulter la Charte et sa FAQ

Osmorhum
Messages : 5
Inscription : 26 Fév 2021 13:51

Re: Serialisation et arrays

Message par Osmorhum » 27 Fév 2021 02:36

Oui pardon, j'ai édité le message d'au-dessus ;)

youtpout
Messages : 65
Inscription : 15 Nov 2020 15:54

Re: Serialisation et arrays

Message par youtpout » 27 Fév 2021 16:11

Il y a une utilité à hérité de monobehaviour ?

Sinon retire le et pour chaque classe que tu as dans ton Inventory il faut qu'elle soit serialisable donc chassis, hull , etc...

Ces classes sont bien des classes simple et n'hérite pas de monobehaviour aussi ?

Osmorhum
Messages : 5
Inscription : 26 Fév 2021 13:51

Re: Serialisation et arrays

Message par Osmorhum » 28 Fév 2021 15:43

Ces classes sont bien des classes simple et n'hérite pas de monobehaviour aussi ?
Pour les classes contenues dans les arrays de l'inventaire, ce sont toutes des Scriptable Objects puisque je veux pouvoir par exemple créer différent modèles de chassis, d'armes etc...
Il y a une utilité à hérité de monobehaviour ?
Et ben en vrai je sais pas vraiment... L'utilité de MonoBehaviour c'est uniquement de pouvoir coller le script à un objet sur Unity ? Tout ce que je sais c'est que quand jenlève le "Monobehaviour" du script d'inventaire, ça me sort ça :
GameObject (named 'Inventory') references runtime script in scene file. Fixing!
UnityEngine.GUIUtility:ProcessEvent(Int32, IntPtr)
'Inventory' is missing the class attribute 'ExtensionOfNativeClass'!
'Inventory' is missing the class attribute 'ExtensionOfNativeClass'!
UnityEngine.GUIUtility:ProcessEvent(Int32, IntPtr)
Si je comprends bien, c'est parce que mon script Inventory est attaché à un objet Inventaire mais je trouve ça plutot pratique vu que je peux le remplir manuellement et le refiler aux scripts qui s'en servent avec un simple Drag-n-Drop dans l'Inspector.

Et du coup lorsque inventory n'est plus MonoBehaviour, étant donné qu'il est utilisé par d'autres scripts, il semble que ça me "créer" un nouvel inventaire propre à ce script dans l'Inspector :

Image

Or j'ai besoin que l'Inventaire utilisé par CreateShip soit celui des joueurs (qui a terme devra être initialisé à partir d'un fichier de sauvegarde mais que je remplis manuellement via l'Inspector pour le moment). Est-ce que je devrais plutot instancier un inventaire (à partir d'un modèle, par exemple le fichier de sauvegarde) dans le premier script qui s'exécute puis envoyer ses infos pour créer une nouvelle instance dans chaque script qui en a besoin ?

Merci pour votre aide en tout cas !

Avatar de l’utilisateur
jmhoubre
Messages : 406
Inscription : 05 Oct 2019 22:05

Re: Serialisation et arrays

Message par jmhoubre » 28 Fév 2021 22:03

Bonsoir,

les informations données sont parcellaires. Difficile d'y voir clair.
  • A quoi sert la classe MyData ? Avec un nom pareil, ce n'est pas évident à deviner, et comme les commentaires sont aux abonnés absents... C'est peut-être la classe dont héritent Chassis, Computer et autres ? Ou le modèle ?
  • Toujours dans la classe MyData, groupName prend le nom du joueur. Bien sur, ce n'est pas la cause du problème, mais ce genre de subtilité n'éclaircit pas le code...
  • Dans SaveScript, il ne manquerait pas using UnityEngine ?
  • Dans SaveScript, comment est appelée la méthode SavePlayer ? Et comment est constitué le paramètre inventaire ? Dans l'inspecteur ?
  • Dans la documentation de BinaryFormatter.Serialize, il est écrit sur fond orange : "Attention, BinaryFormatter serialization is obsolete and should not be used."
  • Dans le guide de sécurité BinaryFormatter, il est écrit "Le BinaryFormatter type est dangereux et n’est pas recommandé pour le traitement des données. Les applications doivent cesser BinaryFormatter d’utiliser dès que possible, même si elles estiment que les données qu’elles traitent sont dignes de confiance. BinaryFormatter n’est pas sécurisé et ne peut pas être sécurisé."
  • Les classes Chassis et autres sont bien marquées sérialisables ?
  • Dans Inventory, il manque l'accolade fermante }, et peut-être d'autres choses ?
Je creuse la sérialisation d'array, je sais qu'avec le Json d'Unity elle ne fonctionne pas, mais pour un formatage binaire je n'en sais rien.

Avatar de l’utilisateur
jmhoubre
Messages : 406
Inscription : 05 Oct 2019 22:05

Re: Serialisation et arrays

Message par jmhoubre » 28 Fév 2021 23:11

J'ai trouvé peu d'infos.
  • Sérialisation d'un tableau d'objets (en XML). Cela utilise XmlSerializer, mais résout le problème de le sérialisation d'un tableau d'objets.
  • Pour être sérialisé, un champ doit être public (ou avoir un attribut [SerializeField]), ne pas être static, ne pas être une constante ni readonly, et être d'un type pouvant être sérialisé (par exemple Vector3 ne peut pas être sérialisé directement). Les types de champs sérialisables sont :
    • les classes non abstraites avec un attribut [Serializable] ;
    • les structures avec un attribut [Serializable] ;
    • les références d'objets dérivés de UnityEngine.Object ;
    • les types de données primitifs (int, float, double, bool, string …) ;
    • un tableau de type sérialisable ;
    • une List<T> de type sérialisable.
Ce qui ne permet pas de conclure : les objets Chassis et autres sont-ils sérialisables ?

Quelques points à vérifier ou tester :
  • Le prérequis pour rendre une classe sérialisable est de la marquer avec l’attribut Serializable. Tenter de sérialiser un objet qui ne serait pas marqué de cet attribut entraînerait la levée d’une exception de type SerializationException.
  • Essayer de passer par XmlSerializer. Il y a des restrictions sur les données private par exemple, mais les classes n'ont pas besoin d'être marquées avec l'attribut.
  • Avec Json, cela fonctionne en utilisant un package gratuit disponible sur GitHub.
  • Pourquoi ne pas utiliser les SO ? Voir cet excellent article sur esprit-unity.fr.

youtpout
Messages : 65
Inscription : 15 Nov 2020 15:54

Re: Serialisation et arrays

Message par youtpout » 01 Mars 2021 11:36

Oula attention Unity c'est pas la même chose que le Net Framework, même si tu peux utiliser le framework .net avec Unity.
Unity semble avoir ses propres attribut serialisable et ils peuvent s'appliquer sur des champs...

Il semble que tu peux utiliser JsonUtility
https://docs.unity3d.com/ScriptReferenc ... oJson.html

tu peux même sauvegarder l'état de tes scriptableobject
https://forum.unity.com/threads/saving- ... ed.551866/

Osmorhum
Messages : 5
Inscription : 26 Fév 2021 13:51

Re: Serialisation et arrays

Message par Osmorhum » 01 Mars 2021 14:14

Déjà, merci pour votre aide ;)
Dans Inventory, il manque l'accolade fermante }, et peut-être d'autres choses ?
Dans SaveScript, il ne manquerait pas using UnityEngine ?
Il s'agit ici simplement d'erreur de copier-coller, ces scripts n'ont aucun problème de fonctionnement à la base (les problèmes n'arrivent que lorsque je passe Inventory en Serializable et/ou non MonoBehaviour
A quoi sert la classe MyData ?
MyDate permet simplement de mettre en forme les données qui seront envoyées dans le fichier de sauvegarde par l'intermédiaire de SavePlayer.
Dans SaveScript, comment est appelée la méthode SavePlayer ? Et comment est constitué le paramètre inventaire ? Dans l'inspecteur ?
SavePlayer est appelé par l'intermédiaire d'un UI Button. Le paramètre inventaire est effectivement géré via l'inspector et justement depuis qu'Inventory n'est plus MonoBehaviour j'ai un inventaire "neuf" à la place de l'emplacement dans lequel je link normalement l'Inventaire en question (si j'ai bien compris c'est plutot normal puisque n'étant pas MonoBehaviour, je ne peux plus attacher le script à l'objet Inventaire de ma scène).
Les classes Chassis et autres sont bien marquées sérialisables ?
Alors effectivement, les classes utilisées par l'inventaire ne l'étaient pas... J'ai donc changé tout ça et... ben le problème reste le même !
Toujours dans la classe MyData, groupName prend le nom du joueur. Bien sur, ce n'est pas la cause du problème, mais ce genre de subtilité n'éclaircit pas le code..
Oui effectivement l'ergonomie est pas optimisée mais je suis seul à bosser sur le programme et tant que ça tourne et que je m'y retrouve...

De même pour l'obsolescence du BinaryFormatter; j'ai fait ça à partir d'un tuto qui effectivement n'est pas forcément très récent mais si ce n'est qu'un soucis de sécurité, sachant que le programme sera uniquement pour mon usage personnel et en local, tant que ça fonctionne je m'en contenterai pour le moment.
Le prérequis pour rendre une classe sérialisable est de la marquer avec l’attribut Serializable. Tenter de sérialiser un objet qui ne serait pas marqué de cet attribut entraînerait la levée d’une exception de type SerializationException.
A priori ça c'est bon, tout est passé en Serializable mais ça ne règlait pas le soucis avec Inventory...
Pourquoi ne pas utiliser les SO ? Voir cet excellent article sur esprit-unity.fr.
J'ai regardé rapidement l'article (je relirais plus posément ce soir), ça m'a l'air tout à fait intéressant, en particulier dans mon cas où je voudrais avoir d'un coté une sauvegarde un peu "base de données" pour tous les objets que je veux utiliser dans le programme et "collection" si j'ai suivi pour enregistrer l'inventaire des joueurs ;)

Au final je me demande si je ferais pas mieux de pas me prendre la tête et faire un fichier de sauvegarde non-serialisé (parce qu'a priori je m'en fou qu'on puisse aller modifier manuellement le fichier vu que je serais le seul à l'utiliser)... Même si en vrai le problème n'est plus tant la sérialisation que le statut de Inventory qui lorsqu'il est compatible avec la serialisation ne peut plus fonctionner comme je l'avais conçu dans le programme...
Oula attention Unity c'est pas la même chose que le Net Framework, même si tu peux utiliser le framework .net avec Unity.
Alors là, très honnêtement, le Net Framework je n'en connais que le nom ^^

PS: j'irais voir aussi pour le JSON et XML la marche à suivre ;)

Merci encore !

Osmorhum
Messages : 5
Inscription : 26 Fév 2021 13:51

Re: Serialisation et arrays

Message par Osmorhum » 23 Mars 2021 14:21

Bon, j'ai un peu contourné mon problème mais du coup tout fonctionne :

j'ai modifié mon code pour n'enregistrer au final que des tableau d'Int au lieu de tableaux d'objets; du coup j'ai plus de problème de sérialisation, je charge des int qui permettent de retrouver l'objet correspondant dans une "database"... réglé !

Je me reprendrais la tête plus précisément sur la serialisation, les objets et le type de script que j'emploie quand ça me sera indispensable.. merci quand même pour votre aide ;) !

Répondre

Revenir vers « Scripting »