How to save and load custom meta data info?

Hello there! I am running into an issue of loading in custom meta data. I see the BasicSaveMetaDataCreator and tried to extend it to a custom class that saves a few custom variables. Would it be possible to see an example of how to integrate saving a float value to a game manager component?

C#:
 /// <summary>
    /// The save meta data which can be serialized.
    /// </summary>
    [Serializable]
    public class BasicSaveMetaData : SaveMetaData
    {
        [Tooltip("The date and time in ticks.")]
        [SerializeField] protected long m_DateTimeTicks;

        public long DateTimeTicks => m_DateTimeTicks;

        private float m_TimePlayed;
        public float TimePlayed => m_TimePlayed;

        /// <summary>
        /// Default constructor.
        /// </summary>
        public BasicSaveMetaData() : base()
        {
            m_DateTimeTicks = new DateTime().Ticks;
            m_TimePlayed = GameManager.Instance.TimePlayed;
        }

        /// <summary>
        /// Overloaded constructor.
        /// </summary>
        /// <param name="saveSystemManager">The save system manager.</param>
        /// <param name="saveDataInfo">The save data info linked to that meta data.</param>
        public BasicSaveMetaData(SaveSystemManager saveSystemManager, SaveDataInfo saveDataInfo) : base(saveSystemManager, saveDataInfo)
        {
            m_DateTimeTicks = DateTime.Now.Ticks;
            m_TimePlayed = GameManager.Instance.TimePlayed;
        }

        /// <summary>
        /// Set the date of the save data.
        /// </summary>
        /// <param name="newDateTime">The new date.</param>
        public void SetDateTime(DateTime newDateTime)
        {
            m_DateTimeTicks = newDateTime.Ticks;
        }
    }

For an example, I added the m_TimePlayed variable here. I then also have to add a line into the SaveViewModule to get the saved value.

Code:
m_SaveContentText.text = basicSaveMetaData.TimePlayed;

My problem is that it is always displaying the GameManager value, and not the value that is saved into the meta data file. I'd love to see an example of the proper way to save/recall a simple value!

Thanks!
 
The code below is the saver meta data I made for the unreleased AdventureKit (I haven't tested it in a few months so things could be different).

Code:
namespace Opsive.AdventureKit
{
    using System;
    using System.Collections.Generic;
    using Opsive.Shared.Game;
    using Opsive.StatSystem;
    using Opsive.UltimateInventorySystem.Core;
    using Opsive.UltimateInventorySystem.Core.InventoryCollections;
    using Opsive.UltimateInventorySystem.SaveSystem;
    using UnityEngine;

    /// <summary>
    /// The save meta data for the adventure kit.
    /// </summary>
    [CreateAssetMenu(fileName = "AdventureKitSaveMetDataCreator", menuName = "Adventure Kit/Save System/Adventure Kit Save Meta Data Creator.")]
    public class AdventureKitSaveMetDataCreator : SaveMetaDataCreator
    {
        [SerializeField] protected string m_UniqueCollectablesItemCollection = "UniqueCollectables";
       
        public string UniqueCollectablesItemCollection => m_UniqueCollectablesItemCollection;

        /// <summary>
        /// Create the meta data.
        /// </summary>
        /// <param name="saveSystemManager">The save system manager.</param>
        /// <param name="saveDataInfo">The save data info.</param>
        /// <returns>The save meta data.</returns>
        public override SaveMetaData CreateMetaData(SaveSystemManager saveSystemManager, SaveDataInfo saveDataInfo)
        {
            return new AdventureKitSaveMetaData(saveSystemManager, saveDataInfo, this);
        }

        /// <summary>
        /// Create an empty save meta data.
        /// </summary>
        /// <returns></returns>
        public override SaveMetaData CreateEmpty()
        {
            return new AdventureKitSaveMetaData();
        }
    }

    /// <summary>
    /// The adventur Kit save meta data
    /// </summary>
    [Serializable]
    public class AdventureKitSaveMetaData : BasicSaveMetaData
    {
        [Tooltip("The game scene name where the save last took place.")]
        [SerializeField] protected string m_GameSceneName;
        [Tooltip("The time played.")]
        [SerializeField] protected float m_TimePlayed;
        [Tooltip("The character level.")]
        [SerializeField] protected int m_CharacterLevel;
        [Tooltip("The unique collectibles gathered as a binary mask.")]
        [SerializeField] protected string[] m_UniqueCollectiblesGathered;

     

        public string GameSceneName
        {
            get => m_GameSceneName;
            set => m_GameSceneName = value;
        }

        public float TimePlayed
        {
            get => m_TimePlayed;
            set => m_TimePlayed = value;
        }

        public int CharacterLevel
        {
            get => m_CharacterLevel;
            set => m_CharacterLevel = value;
        }

        public string[] UniqueCollectiblesGathered
        {
            get => m_UniqueCollectiblesGathered;
            set => m_UniqueCollectiblesGathered = value;
        }

        public long DateTimeTicks1
        {
            get => m_DateTimeTicks;
            set => m_DateTimeTicks = value;
        }

        public AdventureKitSaveMetaData() : base()
        {
            m_UniqueCollectiblesGathered = new string[0];
        }

        /// <summary>
        /// The constructor.
        /// </summary>
        /// <param name="saveSystemManager">The save system manager.</param>
        /// <param name="saveDataInfo">The save data info.</param>
        public AdventureKitSaveMetaData(SaveSystemManager saveSystemManager, SaveDataInfo saveDataInfo, AdventureKitSaveMetDataCreator creator) : base(saveSystemManager, saveDataInfo)
        {
            var previousMetaData = saveDataInfo.MetaData as AdventureKitSaveMetaData;

            var previousTime = previousMetaData != null ? previousMetaData.TimePlayed : 0;
            m_TimePlayed = SaveSystemManager.TimeLastSaved - Time.time + previousTime;
            m_GameSceneName = AdventureKitGameManager.GetActiveGameScene().sceneName;
            var character = AdventureKitGameManager.GetPlayerCharacter();

            if (character == null) {
                // Character not found when saving
                // This can happen when a new save file is created when pressing new game.

                m_UniqueCollectiblesGathered = new string[0];
                return;
            }

            var characterStats = character.GetCachedComponent<ObjectStats>();
            m_CharacterLevel = Mathf.CeilToInt(characterStats?.GetStat("LVL")?.Value ?? 0);
           
            var inventory = character.GetCachedComponent<Inventory>();
            var uniqueItemCollection = inventory.GetItemCollection(creator.UniqueCollectablesItemCollection);

            var list = new List<string>();
            var uniqueItemStacks = uniqueItemCollection.GetAllItemStacks();
            for (int i = uniqueItemStacks.Count - 1; i >= 0; i--) {
                var item = uniqueItemStacks[i].Item;
                if(item == null){ continue; }
               
                list.Add(item.name);
            }

            m_UniqueCollectiblesGathered = list.ToArray();
            Debug.Log("new length "+m_UniqueCollectiblesGathered.Length);
        }
    }
}

After replacing the SaveMetaDataCreator object in the SaveSystem inspector, it should be using that new save meta data.
If you don't assign anything here it won't be using your custom save meta data.
1669020567985.png
For the UI I use a SaveViewModule (just like you would for ItemViewModules).
Code:
namespace Opsive.AdventureKit.UI
{
   
    using System;
    using Opsive.Shared.Game;
    using Opsive.UltimateInventorySystem.Core.DataStructures;
    using Opsive.UltimateInventorySystem.SaveSystem;
    using Opsive.UltimateInventorySystem.UI.Item.ItemViewSlotRestrictions;
    using Opsive.UltimateInventorySystem.UI.Panels.Save;
    using Opsive.UltimateInventorySystem.UI.Views;
    using Opsive.UltimateInventorySystem.Utility;
    using UnityEngine;
    using UnityEngine.UI;
    using Text = Opsive.Shared.UI.Text;
    using Shared.Utility;

    /// <summary>
    /// A box used to display a save.
    /// </summary>
    public class AdventureKitSaveView : SaveViewModule
    {
        [Tooltip("The save content when the save is empty.")]
        [SerializeField] protected GameObject m_EmptyContent;
        [Tooltip("The save content when the save exists.")]
        [SerializeField] protected GameObject m_SaveContent;
        [Tooltip("The Image showing the level in which the game was saved.")]
        [SerializeField] protected Image m_LevelImage;
        [Tooltip("The time played in that save.")]
        [SerializeField] protected Text m_TimePlayed;
        [Tooltip("Text showing the real world time the game was saved.")]
        [SerializeField] protected Text m_LastSaveTime;
        [Tooltip("Show the character level.")]
        [SerializeField] protected Text m_CharacterLevel;
        [Tooltip("Show the unique collectables, collected in that save.")]
        [SerializeField] protected UniqueCollectibleItemViewSlotContainer m_UniqueCollectablesContainer;

        /// <summary>
        /// The the box value.
        /// </summary>
        /// <param name="info">The save data info.</param>
        public override void SetValue(SaveDataInfo info)
        {
            m_SaveDataInfo = info;
            m_FileNumberText.text = string.Format(m_FileNumberFormat, m_Index);

            var metaData = m_SaveDataInfo.MetaData as AdventureKitSaveMetaData;
            if (metaData == null || m_SaveDataInfo.MetaData.IsEmpty) {
                Clear();
                return;
            }

            m_CharacterLevel.text = "Lvl "+metaData.CharacterLevel.ToString();
            TimeSpan time = TimeSpan.FromSeconds(metaData.TimePlayed);
            m_TimePlayed.text = TimeSpan.FromSeconds(metaData.TimePlayed).ToString("hh':'mm':'ss");
            m_LastSaveTime.text = new DateTime(metaData.DateTimeTicks).ToString();

            m_LevelImage.sprite = AdventureKitGameManager.GetGameSceneWithSceneName(metaData.GameSceneName)?.icon;

            var itemNames = metaData.UniqueCollectiblesGathered;
            for (int i = 0; i < m_UniqueCollectablesContainer.SlotCount; i++) {
                var itemViewSlot = m_UniqueCollectablesContainer.GetItemViewSlot(i);
                var itemDefinitionRestriction = itemViewSlot.gameObject.GetCachedComponent<ItemViewSlotDefinitionRestriction>();

                var enable = itemNames.Contains(itemDefinitionRestriction.ItemDefinition.name);

                if (enable) {
                    itemViewSlot.ItemView.SetValue(new ItemInfo(1, itemDefinitionRestriction.ItemDefinition.DefaultItem));
                } else {
                    itemViewSlot.ItemView.Clear();
                }
               
            }
           
            m_EmptyContent.SetActive(false);
            m_SaveContent.SetActive(true);
        }

        /// <summary>
        /// Clear the box.
        /// </summary>
        public override void Clear()
        {
            m_FileNumberText.text = string.Format(m_FileNumberFormat, m_Index);
            m_EmptyContent.SetActive(true);
            m_SaveContent.SetActive(false);
        }
    }
}

I hope that helps, sorry I don't have a simpler example on my hands.
 
Top