!!!FIXED!!! - A lot of problems :/ (not anymore :D)

Discussion and help for Easy Save 3
Paradox
Posts: 12
Joined: Wed Mar 29, 2023 11:59 am

!!!FIXED!!! - A lot of problems :/ (not anymore :D)

Post by Paradox »

Hey there,

As you can probably guess from the title, I have a couple of errors with Easy Save 3. Im using this asset for almost a year now in my game and it wasnt too bad. One 'error' I constantly have is that every time I save a variable (doesnt matter if it is a float or a class with a couple of variables) my game will freeze for a milli second and the "more values" a class has the longer it takes. That forced me to save only when I change scenes but it takes really long if for example I swap items in my inventory and need to save their slots. That itself is not a huge deal but today something really weird happened. I have a class that loads some enums but for some reason it gives an error that it cannot convert a random mesh in my assets into this enum.
Another thing that happened related to the first one is that if I try to save a lot of things, it will give me an error after my game is frozen for 2 minutes in the editor:
IOException: Stream was too long.
So I'd like to know how I can save a simple variable withour my game having lags every time I do so and what this error is about :)
Cheers
Last edited by Paradox on Mon Oct 09, 2023 1:27 pm, edited 1 time in total.
User avatar
Joel
Moodkie Staff
Posts: 4678
Joined: Wed Nov 07, 2012 10:32 pm

Re: A lot of problems :/

Post by Joel »

Hi there,
Hey there,

As you can probably guess from the title, I have a couple of errors with Easy Save 3. Im using this asset for almost a year now in my game and it wasnt too bad. One 'error' I constantly have is that every time I save a variable (doesnt matter if it is a float or a class with a couple of variables) my game will freeze for a milli second and the "more values" a class has the longer it takes.
This usually means that you have a lot of keys in your file, in which case using caching is usually the best way to reduce the performance issues:

https://docs.moodkie.com/easy-save-3/es ... rformance/

Also deleting your save file to ensure that you don't have any left over keys in there which aren't being used is also recommended (Tools > Easy Save 3 > Clear Persistent Data Path).
I have a class that loads some enums but for some reason it gives an error that it cannot convert a random mesh in my assets into this enum.
This usually means that you've changed a field in a class from an enum to a Mesh. However, it wouldn't be possible to say without seeing the actual error message.
Another thing that happened related to the first one is that if I try to save a lot of things, it will give me an error after my game is frozen for 2 minutes in the editor:
IOException: Stream was too long.
This means your file has become larger than the maximum size that C# can handle. This would indicate that you're saving a very large amount of data, or you're saving many redundant keys to the file. You should check that your file isn't writing a large number of unnecessary keys to the file, and that your file doesn't contain many keys which are no longer being used. You should also ensure that you're only saving data if it's necessary to be saved.

All the best,
Joel
Joel @ Moodkie Interactive
Purchase Easy Save | Contact | Guides | Docs | Getting started
Paradox
Posts: 12
Joined: Wed Mar 29, 2023 11:59 am

Re: A lot of problems :/

Post by Paradox »

Thank you very much for the fast reply!

I dont really understand how to use the Caching and what the benefits really are. Is it also saving the keys or is it something else? And what causes the game/editor to lag when I only save 1 variable?

I deleted all save files via (Tools > Easy Save 3 > Clear Persistent Data Path), but the error still remains.

On initialize I equip all my players with their default items and save these so its 6 items per player times 8 players.
Thats 48 items... Seeing the number now made me realize that it might be a lot.
Items are Scriptable Objects and one item contains a sprite, name, stats etc. Are Scriptable Objects efficent to save?

Cheers :)
User avatar
Joel
Moodkie Staff
Posts: 4678
Joined: Wed Nov 07, 2012 10:32 pm

Re: A lot of problems :/

Post by Joel »

Hi there,
I dont really understand how to use the Caching and what the benefits really are. Is it also saving the keys or is it something else?
Caching stores all of the keys in memory so that it can perform operations in memory, rather than having to perform expensive File IO everytime you save or load to/from a file. The guide I linked shows how you would use caching.
And what causes the game/editor to lag when I only save 1 variable?
This is because the file you're saving to contains many keys. When saving it has to parse the entire file to check whether the key you're saving already exists, and then divide the file, remove the existing key and append the new key to the end. This is a very expensive operation, but becomes a very simple operation when using caching.
On initialize I equip all my players with their default items and save these so its 6 items per player times 8 players.
Thats 48 items... Seeing the number now made me realize that it might be a lot.
Items are Scriptable Objects and one item contains a sprite, name, stats etc. Are Scriptable Objects efficent to save?
ScriptableObjects are saved like any other data, so efficiency really depends on the fields you're saving. For example if your ScriptableObjects are saving meshes or Textures then these are extremely performance intensive and require a lot of space.

Also saving 48 keys to a file without caching will impact performance. Saving and loading gets exponentially slower with each key you add to the file when you're not using caching.

Please could you post the code you're using to save and load, and the ScriptableObjects you are saving, and any settings you've changed in the Settings window.

If you could also go to Tools > Easy Save 3 > Open Persistent Data and let me know the size of your save file in there that would also be helpful.

All the best,
Joel
Joel @ Moodkie Interactive
Purchase Easy Save | Contact | Guides | Docs | Getting started
Paradox
Posts: 12
Joined: Wed Mar 29, 2023 11:59 am

Re: A lot of problems :/

Post by Paradox »

Hey again,
Caching stores all of the keys in memory so that it can perform operations in memory, rather than having to perform expensive File IO everytime you save or load to/from a file. The guide I linked shows how you would use caching.
ok Ill read it throughly thanks :)).
Please could you post the code you're using to save and load

Code: Select all

using System;
using System.Collections.Generic;

using UnityEngine;
using RL.StartMenu;

namespace RL.Core
{
    public class SaveManager : MonoBehaviour
    {
        #region --- Singleton ---

        public static SaveManager Instance;
        private void Awake()
        {
            if (Instance != null && Instance != this)
            {
                Destroy(this.gameObject);
                return;
            }

            Instance = this;
            AfterSingletonAwake();
        }

        #endregion

        public readonly int SaveSlotCount = 3;

        public string Path { get; private set; }

        public event Action OnSetupSaveSlots;

        public event Action OnSave;
        public event Action OnLoad;
        public event Action OnDelete;

        private const string DEFAULT_PATH_STRING = "_defaultPath";

        private void AfterSingletonAwake()
        {
            //InvokeRepeating(nameof(AutoSave), 1f, 60f);

            //DeleteFile(DEFAULT_PATH_STRING);
        }

        private void Start()
        {
            if (StartMenuManager.Instance != null)
                StartMenuManager.Instance.OnLoadIntoGameEvent += OnLoadIntoGame;

            SceneLoader.i.OnTransitionSceneEvent += Save;

            Invoke(nameof(SetSaveSlotData), 0.1f);
            Invoke(nameof(Load), 0.2f);
        }

        public void OnLoadIntoGame()
        {
            foreach (var slot in SaveSlotDatas)
            {
                slot.RemoveOnDeleteEvent();
            }
        }

        public void AutoSave()
        {
            if (SceneLoader.i.IsStartMenuScene) return;

            Save();
        }

        public void Setup(string path)
        {
            this.Path = path;
            ES3Settings.defaultSettings.path = Path;
        }

        public List<SaveSlotData> SaveSlotDatas { get; private set; } = new();

        public void SetSaveSlotData()
        {
            for (int i = 0; i < SaveSlotCount; i++)
            {
                SaveSlotData s = new(i);

                if (s.HasSaveSlot())
                {
                    s.Load();
                }

                SaveSlotDatas.Add(s);
            }

            OnSetupSaveSlots?.Invoke();
        }

        public SaveSlotData GetSaveSlot(int j) => SaveSlotDatas[j];
        public SaveSlotData CurrentSaveSlot { get; private set; }

        public void SetCurrentSaveSlot(SaveSlotData data)
        {
            if (data == null) return;

            CurrentSaveSlot = data;
            Setup(CurrentSaveSlot.GetSaveSlotName());
        }

        public void Save()
        {
            //Debug.Log("Save");

            OnSave?.Invoke();
            CurrentSaveSlot?.SetSaveSlot();

            foreach (var saveData in saveDataOnDefaultPathDictionary)
            {
                Debug.Log("Default Save: " + saveData.Key);
                SaveData(saveData.Key, saveData.Value, DEFAULT_PATH_STRING);
            }

            saveDataOnDefaultPathDictionary.Clear();

            foreach (var saveData in saveDataOnSaveSlotDictionary)
            {
                Debug.Log("Save Slot Save: " + saveData.Key);
                if (saveData.Value == null)
                    DeleteData(saveData.Key);
                else
                    SaveData(saveData.Key, saveData.Value, ES3Settings.defaultSettings.path);
            }

            saveDataOnSaveSlotDictionary.Clear();

            foreach (var deleteData in deleteDataOnSaveSlotDictionary)
            {
                Debug.Log("Delete Save: " + deleteData.Key);
                DeleteData(deleteData.Key);
            }

            deleteDataOnSaveSlotDictionary.Clear();
        }

        public void Load()
        {
            OnLoad?.Invoke();
            CurrentSaveSlot?.Load();
        }

        private Dictionary<object, object> saveDataOnDefaultPathDictionary = new();
        public void SaveDataOnDefault(object saveKey, object saveValue)
        {
            if (saveDataOnDefaultPathDictionary != null && saveDataOnDefaultPathDictionary.Count > 0)
            {
                if (saveDataOnDefaultPathDictionary.ContainsKey(saveKey))
                {
                    saveDataOnDefaultPathDictionary[saveKey] = saveValue;
                    return;
                }
            }

            saveDataOnDefaultPathDictionary.Add(saveKey, saveValue);
        }

        public object LoadDataOnDefault(object loadKey, object defaultValue)
        {
            return ES3.Load(loadKey.ToString(), DEFAULT_PATH_STRING, defaultValue);
        }

        public void DeleteKeyOnDefault(string key)
        {
            ES3.DeleteKey(key, DEFAULT_PATH_STRING);
        }

        private Dictionary<object, object> saveDataOnSaveSlotDictionary = new();
        public void AddToSaveData(object saveKey, object saveValue)
        {
            if (saveDataOnSaveSlotDictionary != null && saveDataOnSaveSlotDictionary.Count > 0)
            {
                if (saveDataOnSaveSlotDictionary.ContainsKey(saveKey))
                {
                    saveDataOnSaveSlotDictionary[saveKey] = saveValue;
                    return;
                }
            }

            saveDataOnSaveSlotDictionary.Add(saveKey, saveValue);
        }

        public void SaveData(object saveKey, object saveValue, string path)
        {
            if (saveValue == null)
            {
                Debug.LogError("Save Value is null");
                return;
            }

            ES3.Save(saveKey.ToString(), saveValue, path);
        }

        public object LoadData(object loadKey)
        {
            return LoadData(loadKey, null);
        }

        private Dictionary<object, object> deleteDataOnSaveSlotDictionary = new();
        public void AddToDeleteData(object saveKey)
        {
            AddToSaveData(saveKey, null);
            return;

            if (deleteDataOnSaveSlotDictionary != null && deleteDataOnSaveSlotDictionary.Count > 0)
            {
                if (deleteDataOnSaveSlotDictionary.ContainsKey(saveKey)) return;
            }

            deleteDataOnSaveSlotDictionary.Add(saveKey, null);
        }

        public object LoadData(object loadKey, object defaultValue)
        {
            if (ES3.KeyExists(loadKey.ToString()))
                return ES3.Load(loadKey.ToString(), ES3Settings.defaultSettings.path, defaultValue);

            return defaultValue;
        }

        public void DeleteData(object deleteKey)
        {
            ES3.DeleteKey(deleteKey.ToString(), ES3Settings.defaultSettings.path);
        }

        public void DeleteKey(string key)
        {
            ES3.DeleteKey(key);
        }

        public void DeleteFile()
        {
            DeleteFile(ES3Settings.defaultSettings.path);
        }

        public void DeleteFile(string path)
        {
            ES3.DeleteFile(path);
        }

        public string GetLastDatePlayed() => System.DateTime.Now.ToString("MM/dd/yyyy");
        public string GetLastTimePlayed() => System.DateTime.Now.ToString("hh:mm:ss");

        public void Delete() => OnDelete?.Invoke();

        private void OnApplicationQuit()
        {
            if (!Application.isEditor)
                Save();
        }
    }

    public class SaveSlotData
    {
        public int index;

        public bool hasSaveSlot;
        public float playTime;
        public string lastDatePlayed;
        public string lastTimePlayed;

        private string HasSaveSlotIndex() => GetSaveSlotName() + "_hasSaveSlot";
        private string PlayTimeIndex() => GetSaveSlotName() + "_playTime";
        private string LastDatePlayedIndex() => GetSaveSlotName() + "_lastDatePlayed";
        private string LastTimePlayedIndex() => GetSaveSlotName() + "_lastTimePlayed";

        public SaveSlotData(int i)
        {
            SaveManager.Instance.OnDelete += Delete;

            index = i;
            ResetData(false);
        }

        public string GetSaveSlotName() => "SaveSlot" + index;
        public bool HasSaveSlot() => (bool)SaveManager.Instance.LoadDataOnDefault(HasSaveSlotIndex(), false);
        public string PlayedTimeString()
        {
            System.Text.StringBuilder sb = new();

            float playedTimeHours = Mathf.Floor(playTime / 3600);
            float playTimeMinutes = Mathf.Floor((playTime / 60) % 60);
            float playTimeSeconds = Mathf.Floor(playTime % 60);

            bool hasPlayTimeHours = playedTimeHours >= 1f;

            if (hasPlayTimeHours) sb.Append($"{playedTimeHours}h ");
            if (playTimeMinutes >= 1f) sb.Append($"{playTimeMinutes} min");
            if (!hasPlayTimeHours && playTimeSeconds >= 1f) sb.Append($" {playTimeSeconds} sec");

            return sb.ToString();
        }

        public void ResetData(bool save)
        {
            hasSaveSlot = false;
            playTime = 0f;
            lastDatePlayed = "";
            lastTimePlayed = "";

            if (save) Save();
        }

        public void Load()
        {
            hasSaveSlot = HasSaveSlot();
            playTime = (float)SaveManager.Instance.LoadDataOnDefault(PlayTimeIndex(), 0f);
            lastDatePlayed = (string)SaveManager.Instance.LoadDataOnDefault(LastDatePlayedIndex(), "");
            lastTimePlayed = (string)SaveManager.Instance.LoadDataOnDefault(LastTimePlayedIndex(), "");
        }

        public void SetSaveSlot()
        {
            hasSaveSlot = true;
            playTime += GameManager.Instance.CurrentPlayTime;
            lastDatePlayed = SaveManager.Instance.GetLastDatePlayed();
            lastTimePlayed = SaveManager.Instance.GetLastTimePlayed();

            Save();
        }

        public void Save()
        {
            SaveManager.Instance.SaveDataOnDefault(HasSaveSlotIndex(), hasSaveSlot);
            SaveManager.Instance.SaveDataOnDefault(PlayTimeIndex(), playTime);
            SaveManager.Instance.SaveDataOnDefault(LastDatePlayedIndex(), lastDatePlayed);
            SaveManager.Instance.SaveDataOnDefault(LastTimePlayedIndex(), lastTimePlayed);

            GameManager.Instance.ResetCurrentPlayTime();
        }

        public void Delete()
        {
            SaveManager.Instance.LoadDataOnDefault(GetSaveSlotName());

            ResetData(true);
        }

        public void RemoveOnDeleteEvent() => SaveManager.Instance.OnDelete -= Delete;
    }
}
and the ScriptableObjects you are saving,

Code: Select all

using System;
using System.Collections.Generic;

using UnityEngine;
using Sirenix.OdinInspector;
using CodeStage.AntiCheat.ObscuredTypes;

using RL.Core;
using RL.Player;

namespace RL.Items
{
    [CreateAssetMenu(fileName = "Equip Item", menuName = "Equipment/Equip Item")]
    public class EquipItem : ScriptableObject
    {
        public string GetID() => $"{playerClassType}_{rarity}_{equipmentType}_{itemName}";

        [Space]

        [TitleGroup("General")]
        [HorizontalGroup("General/Data", 100), PreviewField(100), PropertySpace(SpaceBefore = 0, SpaceAfter = 7), HideLabel]
        public Sprite itemIcon;
        [VerticalGroup("General/Data/Right"), LabelWidth(50), LabelText("Name")]
        public string itemName;

        [VerticalGroup("General/Data/Right"), LabelWidth(80), LabelText("Class")]
        public PlayerClassType playerClassType;
        [VerticalGroup("General/Data/Right"), LabelWidth(80), LabelText("Rarity")]
        public ItemRarity rarity;
        [VerticalGroup("General/Data/Right"), LabelWidth(80), LabelText("Equipment")]
        public EquipmentType equipmentType;
        [VerticalGroup("General/Data/Right"), LabelWidth(80), LabelText("Specific Stats")]
        public bool hasSpecificStats;
        public bool hasSpecificAffix;
        public bool hasSpecificSet;
        [VerticalGroup("General/Data/Right"), LabelWidth(80), LabelText("Max Stats")]
        public bool hasMaxStats = false;

        [ShowIf("hasSpecificStats", true), LabelText("Specific Stats")]
        public List<ItemStatType> specificStatsList = new();
        [ShowIf("hasSpecificAffix", true), LabelText("Specific Affix")]
        public ItemAffix specificItemAffix;
        [ShowIf("hasSpecificSet", true), LabelText("Specific Set")]
        public ItemSetData specificItemSetData;

        [LabelWidth(50), TextArea(), LabelText("Description")]
        public string itemDescription;

        public BaseItemSlot ItemSlot { get; private set; }
        public PlayerStatsHolder PlayerStatsHolder { get; private set; }
        public List<EquipmentItemStat> AllPossibleStatList { get; private set; } = new();
        public List<EquipmentItemStat> EquipmentItemStats { get; private set; } = new();

        public ItemAffix ItemAffix { get; private set; }
        public ItemSetComponent ItemSet { get; private set; }

        public event Action<PlayerAI> OnEquipEvent;
        public event Action<PlayerAI> OnUnequipEvent;

        public void Setup() { }

        public EquipItem GetCopy() => Instantiate(this);
        public void SetSlot(BaseItemSlot baseItemSlot) => ItemSlot = baseItemSlot;

        public void ResetEquipmentItemStats() => EquipmentItemStats.Clear();
        public void AddStatToEquipmentItemStats(EquipmentItemStat equipmentItemStat)
        {
            EquipmentItemStats.Add(equipmentItemStat);
            EquipmentItemStats.Sort(SortIndex);
        }

        public int SortIndex(EquipmentItemStat x, EquipmentItemStat y)
        {
            if (x.playerStatType == PlayerStatType.MainStat) return -3;
            if (y.playerStatType == PlayerStatType.MainStat) return 3;
            if (x.playerStatType == PlayerStatType.Vitality) return -2;
            if (y.playerStatType == PlayerStatType.Vitality) return 2;

            if ((int)x.playerStatType < (int)y.playerStatType) return -1;

            return 1;
        }

        public void AddItemAffix(ItemAffix itemAffix)
        {
            if (this.ItemAffix != null) return;

            this.ItemAffix = itemAffix;
            if (ItemAffix != null) 
                ItemAffix.SetItem(this);
        }

        public void AddItemSet(ItemSetComponent itemSet)
        {
            return;
            /*
            if (this.ItemSet != null) return;

            ItemSet = itemSet;
            if (ItemSet != null)
            {
                ItemSet.SetItem(this);
            }
            */
        }

        public void Equip(PlayerStatsHolder playerStatsHolder)
        {
            if (playerStatsHolder == null) return;

            this.PlayerStatsHolder = playerStatsHolder;
            foreach (var stat in EquipmentItemStats)
            {
                playerStatsHolder.BuffStat(stat.playerStatType, stat.statValueType, stat.statValue);
            }

            OnEquipEvent?.Invoke(PlayerStatsHolder.PlayerAI);
        }

        public void Unequip()
        {
            if (PlayerStatsHolder != null)
            {
                foreach (var stat in EquipmentItemStats)
                {
                    PlayerStatsHolder.DebuffStat(stat.playerStatType, stat.statValueType, stat.statValue);
                }

                OnUnequipEvent?.Invoke(PlayerStatsHolder.PlayerAI);
            }

            this.PlayerStatsHolder = null;
        }

        public bool IsHealerItem() => GameManager.Instance.IsHealer(playerClassType); 

        public void Destroy()
        {
            Destroy(this);
        }
    }

    [System.Serializable]
    public class EquipmentItemStat
    {
        public ItemStatType itemStatType;
        public PlayerStatType playerStatType;
        public StatValueType statValueType;
        public ObscuredFloat statValue;
        public ObscuredFloat minValue;
        public ObscuredFloat maxValue;
    }


    #region --- Item Attributes---

    public enum EquipmentType
    {
        Head,
        Chest,
        Shoulder,
        Pants,
        MainHand,
        OffHand,
        ExtraSlot,
        None
    }

    public enum ItemRarity
    {
        Common,
        Normal,
        Rare,
        Epic,
        Legendary,
    }

    public enum MainHandType
    {
        Sword,
        Axe,
        Wand,
        Staff,
        Bow,
        Dagger,
        Hammer,
        Scythe
    }

    public enum OffHandType
    {
        Gloves,
        Book,
        Bracers,
        Quiver,
        Shield,
        Boots,
        Amulett,
        Belt,
    }

    #endregion
}
and any settings you've changed in the Settings window.
Image

A long comment, hopefully you can read it through and see/explain what is wrong/what I can do better.
Thank you very much!
Attachments
2023-09-20_15-36-04.png
2023-09-20_15-36-04.png (41.72 KiB) Viewed 499 times
User avatar
Joel
Moodkie Staff
Posts: 4678
Joined: Wed Nov 07, 2012 10:32 pm

Re: A lot of problems :/

Post by Joel »

Hi there, and thanks for sending that over.

You're saving quite a lot of separate data to different keys, so you will definitely want to be using caching. You're also using encryption, which will significantly affect performance.

As you're performing some quite complex and unusual logic it's not possible for me to tell exactly what is happening regarding why your file sizes are becoming so big or what you can do better. For example your SaveManager seems to be responsible for saving, but then the save manager calls a Save() method on the SaveSlotData (which then seems to be responsible for saving), but then this calls methods back on the SaveManager. This is unnecessarily complex and it's usually recommended to either use manager to save objects, or get an object to save itself (not a mix of both) because it becomes extremely difficult to debug.

I'm also not sure what part the ScriptableObject plays in this as I see no code in what you posted which saves or loading an EquipItem. All that seems to get saved is SaveSlotData, which just saves an int, bool, float and two strings. If you are saving the ScriptableObject, it does appear to be a very deep object with a lot of other classes which could potentially be deep. I would also check in Tools > Easy Save 3 > Settings > Advanced Settings that you haven't changed "Serialise Unity Object fields" to ByValue or ByRefAndValue, as this will save the entire serialization tree which can be quite large, especially as you have Sprite fields (this would cause the uncompressed pixel data of the Sprites to be saved to the file, which is extremely large).

When creating save slots generally you would simply set ES3Settings.defaultSettings.path to the name of your save slot, rather than the quite complex method you appear to be using in your case. I recommend trying to simplify the logic surrounding your saving so that it's more debuggable.

I also recommend taking a look at your save files (the script you've posted creates two separate save files) and check that the data in there makes sense and that you're not saving duplicates of things. Ultimately Easy Save will only save what you tell it to, so if it's saving more data than necessary (or the same thing multiple times) it indicates that your logic is causing more data to be saved than necessary.

All the best,
Joel
Joel @ Moodkie Interactive
Purchase Easy Save | Contact | Guides | Docs | Getting started
Paradox
Posts: 12
Joined: Wed Mar 29, 2023 11:59 am

Re: A lot of problems :/

Post by Paradox »

Hello again,

originally I only saved the equip item as 1 save file as one, but when I tried to do it like this, the sprite and some other components were not loaded thats why I am saving some components seperatly. And true I need to change my code a bit so that it is not as complicated as it is right now :P. Should I use a cache file for the EquipItem Script or isnt it worth using it in this example?


The reason I have different path names is so that I can have different save slots for the game (I have 3, might reconsider using only 1) but for balancing purposes it will stay like that for a while. Right now I am only saving when changing scenes. I created a Dictionary<string, object> (before the Key was also an object; The string is the Save ID, the object the Type Im saving) and add everything I want to save in there. Of course I check if any Key has the same ID as the object so I can override it so I probably am not saving duplicate things.

Code: Select all


private Dictionary<string, object> saveDataOnSaveSlotDictionary = new();

public void AddToSaveData(string saveKey, object saveValue)
{
	if (saveDataOnSaveSlotDictionary != null && saveDataOnSaveSlotDictionary.Count > 0)
	{
		if (saveDataOnSaveSlotDictionary.ContainsKey(saveKey))
		{
			saveDataOnSaveSlotDictionary[saveKey] = saveValue;
			return;
		}
	}

	saveDataOnSaveSlotDictionary.Add(saveKey, saveValue);
}

When I change scenes I save every variable in the Dictionary, thats how I solved it kind of... Any tips to improve this method?

Thanks once again!
User avatar
Joel
Moodkie Staff
Posts: 4678
Joined: Wed Nov 07, 2012 10:32 pm

Re: A lot of problems :/

Post by Joel »

Hi there,
Should I use a cache file for the EquipItem Script or isnt it worth using it in this example?
If your save file has many keys then you should use caching.
When I change scenes I save every variable in the Dictionary, thats how I solved it kind of... Any tips to improve this method?
This is more a matter of your own game logic rather than Easy Save directly, but if that works for you and you find it easier to maintain then that's fine. However, generally instead of saving to an intermediate Dictionary, I personally find it more debuggable and maintainable to save the key directly to the file (of course, using caching as you're likely to have many keys).

All the best,
Joel
Joel @ Moodkie Interactive
Purchase Easy Save | Contact | Guides | Docs | Getting started
Paradox
Posts: 12
Joined: Wed Mar 29, 2023 11:59 am

Re: A lot of problems :/

Post by Paradox »

Hey there,

So I tried to implement Caching into my save manager. Basically I just did this:

Code: Select all


public void SaveData(string saveKey, object saveValue, string path)
{
	if (saveValue == null)
	{
		return;
	}
	
	var settings = new ES3Settings(ES3.Location.Cache);		
	ES3.Save(saveKey, saveValue, path, settings);
}

If I implemented it correctly I dont really feel much of a difference in loading time. But when I try to save an EquipItem, I get the following error:

ArgumentException: Only types of UnityEngine.Object can be written with this method, but argument given is type of RaidLead.Items.EquipItem

Could you help me out yet again :D?

Cheers
User avatar
Joel
Moodkie Staff
Posts: 4678
Joined: Wed Nov 07, 2012 10:32 pm

Re: A lot of problems :/

Post by Joel »

Hi there,

Are you calling ES3.CacheFile and ES3.StoreCachedFile at appropriate times as noted in the Improving Performance guide?
Joel @ Moodkie Interactive
Purchase Easy Save | Contact | Guides | Docs | Getting started
Post Reply