0
\$\begingroup\$

I made some editor window to add and edit some stuff. I decided to use Json, because I thought it would be easy.

I have some class Card in the Assets/Scripts folder. I have NewCardsEditor in the Assets/Editor folder. When I click the "Save" button, it works perfect. But when I open this window again, I receive this error message:

ArgumentException: Cannot deserialize JSON to new instances of type 'Card.'

Code is too long, I'll only add those parts which appear relevant to me. When you need more code, please comment.

Part of Card class:

public class Card : MonoBehaviour
    {
        [SerializeField] [Tooltip("Название карты")] private string _name;
        [SerializeField] [Tooltip("Название карты для игрока")] private string _inGameName;
        [SerializeField] [Tooltip("Стоимость карты")] [Range(0, 10)] private int _cost;
        [SerializeField] [Tooltip("Uncheck, если стоимость в ОД")] private bool _manaSpell;
        [Space(10)]
        [SerializeField] [Tooltip("Вызываемое существо")] private GameObject _creature;

        #region Бонус, если он есть
        [Space(10)]
        [Header("Бонус, если его нет - не заполнять")]

        [SerializeField] [Tooltip("Название заклинания")] private string _bonusName;
        [SerializeField] [Tooltip("Тип бонуса/заклинания(Для вызова в функции)")] private string _bonusType;
        [SerializeField] [Tooltip("Полное описание бонуса")] private string _bonusFullName;
        [SerializeField] [Tooltip("Цель/цели заклинания/бонуса")] private string _bonusTarget;
        [SerializeField] [Tooltip("Значение атаки. Если оно не нужно - 0")] private int _bonusAtt;
        [SerializeField] [Tooltip("Значение HP. Если оно не нужно - 0")] private int _bonusHP;
        #endregion

        /// <summary>
        /// True если карта с вызовом существа
        /// </summary>
        private bool _isCreature;
        /// <summary>
        /// true если карта на столе, false - в руке
        /// </summary>
        private bool _isActive = false;
#region Для редактора
        /// <summary>
        /// Название карты
        /// </summary>
        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }
        /// <summary>
        /// Название карты для игрока
        /// </summary>
        public string InGameName
        {
            get { return _inGameName; }
            set { _inGameName = value; }
        }
        /// <summary>
        /// Стоимость карты
        /// </summary>
        public int Cost
        {
            get { return _cost; }
            set { _cost = value; }
        }
        /// <summary>
        /// Стоимость в мане?
        /// </summary>
        public bool IsManaSpell
        {
            get { return _manaSpell; }
            set { _manaSpell = value; }
        }
        /// <summary>
        /// Существо ли это?
        /// </summary>
        public bool IsCreature
        {
            get { return _isCreature; }
            set { _isCreature = value; }
        }
        /// <summary>
        /// Существо, если карта - вызов существа
        /// </summary>
        public GameObject CardsCreature
        {
            get { return _creature; }
            set { _creature = value; }
        }

        #region Бонус и вся его инфа
        /// <summary>
        /// Имя бонуса
        /// </summary>
        public string BonusName
        {
            get { return _bonusName; }
            set { _bonusName = value; }
        }
        /// <summary>
        /// Тип бонуса
        /// </summary>
        public string BonusType
        {
            get { return _bonusType; }
            set { _bonusType = value; }
        }
        /// <summary>
        /// Полное описание бонуса
        /// </summary>
        public string BonusFullName
        {
            get { return _bonusFullName; }
            set { _bonusFullName = value; }
        }
        /// <summary>
        /// Цель бонуса
        /// </summary>
        public string BonusTarget
        {
            get { return _bonusTarget; }
            set { _bonusTarget = value; }
        }
        /// <summary>
        /// Значение Attack бонуса
        /// </summary>
        public int BonusAtt
        {
            get { return _bonusAtt; }
            set { _bonusAtt = value; }
        }
        /// <summary>
        /// Значение НР бонуса
        /// </summary>
        public int BonusHP
        {
            get { return _bonusHP; }
            set { _bonusHP = value; }
        }
        #endregion

        /// <summary>
        /// Если нужно применить карту - true
        /// </summary>
        public bool Active
        {
            get { return _isActive; }
            set { _isActive = value; }
        }
        #endregion

Here is NewCardsEditor editor window(part):

#region Переменные

    List<Card> cardsList = new List<Card>();
    string savePath;

    //Для удобства в редакторе
    /// <summary>
    /// Типы стоимости карт
    /// </summary>
    string[] costTypes = { "action", "mana" };
    /// <summary>
    /// Типы карт
    /// </summary>
    string[] spellTypes = { "Creature", "Spell" };
    int indexCost, indexType;
    /// <summary>
    /// Все возможные цели заклинаний
    /// </summary>
    string[] targets = { "Summoner", "Enemy", "Summoner and enemy", "Elly creature", "Enemy creature",
        "All ally's creatures", "All enemy's creatures", "All creatures", "Everyone"};
    /// <summary>
    /// Выбранная цель(индекс). Для записи выбранной цели
    /// </summary>
    int indexTarget;

    //Переменные-составляющие карты
    string _name, _inGameName;
    int _cost;
    bool _isManaSpell;
    bool _isCreature;

    GameObject _creature;
    //Далее параметры бонуса
    string _bonusName, _bonusFullName;
    string _bonusType;
    string _bonusTarget;
    int _bonusAtt, _bonusHP;

    #endregion

private void OnEnable()
    {
        savePath = Application.dataPath + "/SaveData/Cards.txt";

        if (File.Exists(savePath))
        {
            if (File.ReadAllLines(savePath).Length != 0)
            {
                foreach (string s in File.ReadAllLines(savePath))
                {
                    cardsList.Add(JsonUtility.FromJson<Card>(s));
                }
            }
        }
        else
        {
            File.Create(savePath);
        }
    }

...

if (GUILayout.Button(new GUIContent("Save", "Сохраняет карту(-ы) в файл")))
        {
            var card = new Card();

            card.Name = _name;
            card.InGameName = _inGameName;
            card.Cost = _cost;
            card.IsManaSpell = _isManaSpell;
            card.IsCreature = _isCreature;
            card.CardsCreature = _creature;
            card.BonusName = _bonusName;
            card.BonusFullName = _bonusFullName;
            card.BonusType = _bonusType;
            card.BonusTarget = _bonusTarget;
            card.BonusAtt = _bonusAtt;
            card.BonusHP = _bonusHP;

            var str = JsonUtility.ToJson(card);
            using (StreamWriter sw = File.AppendText(savePath))
                sw.WriteLine(str);
        }

And here is saved file:

{
  "_name": "apple",
  "_inGameName": "Вкусное яблочко",
  "_cost": 1,
  "_manaSpell": false,
  "_creature": {
    "instanceID": 0
  },
  "_bonusName": "heal by 2",
  "_bonusType": "",
  "_bonusFullName": "Исцеляет вкусившего на 2",
  "_bonusTarget": "",
  "_bonusAtt": 0,
  "_bonusHP": 2
}
\$\endgroup\$
4
  • 1
    \$\begingroup\$ Generally you cannot serialize and deserialize Unity types like MonoBehaviours directly. They have additional requirements (like their association with an attached GameObject in a scene or prefab) that are visible only to Unity's built-in serialization systems and will not be correctly reconstructed from your JSON. Have you considered making a Plain Old Data Structure struct or class that holds your data, and have your MonoBehaviour hold an instance of this data container? \$\endgroup\$
    – DMGregory
    Commented Sep 4, 2020 at 13:10
  • 1
    \$\begingroup\$ This looks like something you would typically use ScriptableObjects for. They are a way to create a custom asset type which you can create and edit within Unity. \$\endgroup\$
    – Philipp
    Commented Sep 4, 2020 at 13:26
  • \$\begingroup\$ @DMGregory I don't even know what is Plain Old Data Structure. I didn't try it. How actually I can do it? \$\endgroup\$
    – Dead Lord
    Commented Sep 4, 2020 at 13:48
  • \$\begingroup\$ @DeadLord As Philipp said, you should use a ScriptableObject. \$\endgroup\$
    – Kevin
    Commented Sep 4, 2020 at 16:23

1 Answer 1

0
\$\begingroup\$

IT WORKS! @DMGregory idk if that is what you meant, but I did so and it works PERFECT! I created a simple class(It's all I have in that script) for card(and the same for Bonus):

public class CardData
{
    public string cardName;
    public string inGameName;
    public int cost;
    public bool manaSpell;
    public string creatureName;

    public BonusData cardsBonus;
}

Then I use it to save/load via Json.

\$\endgroup\$
1
  • \$\begingroup\$ Yep, that's a plain old data structure. To help others apply your solution, it might help to show how this changes your MonoBehaviour and loading code. \$\endgroup\$
    – DMGregory
    Commented Sep 6, 2020 at 10:16

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .