Page 5 of 7

Re: Save System Question ;)

Posted: Mon May 16, 2022 7:39 pm
by HawkX
I made it work!!! :)

In case you are curious, here are my scripts...

InventorySaver :

Code: Select all

using System;
using UnityEngine;
using PixelCrushers;
using GameDevTV.Inventories;
using System.Collections.Generic;

namespace PixelCrushers
{
    public class ShumaInventorySaver : Saver
    {
        [System.Serializable]
        public struct InventorySlotRecord
        {
            public string itemID;
            public int number;
        }

        [System.Serializable]
        public class Data
        {
            public InventorySlotRecord[] slots;
        }

        public override string RecordData()
        {
            var data = new Data();
            Inventory inventory = FindObjectOfType<Inventory>();
            int size = inventory.inventorySize;
            var slotStrings = new InventorySlotRecord[size];
            data.slots = new InventorySlotRecord[size];
            var invSlots = inventory.slots;

            for (int i = 0; i < size; i++)
            {
                if (invSlots[i].item != null)
                {
                    slotStrings[i].itemID = invSlots[i].item.GetItemID();
                    slotStrings[i].number = invSlots[i].number;
                }
            }

            data.slots = slotStrings;
            return SaveSystem.Serialize(data);
        }

        public override void ApplyData(string s)
        {
            if (string.IsNullOrEmpty(s)) return; // No data to apply.
            Data data = SaveSystem.Deserialize<Data>(s);
            if (data == null) return; // Serialized string isn't valid.
            int size = data.slots.Length;
            var slotStrings = data.slots;
            Inventory inventory = FindObjectOfType<Inventory>();
            inventory.CreateNewInventory(size);

            for (int i = 0; i < size; i++)
            {
                var slot = inventory.slots[i];
                slot.item = InventoryItem.GetFromID(slotStrings[i].itemID);
                slot.number = slotStrings[i].number;
                inventory.slots[i] = slot;
            }

            inventory.UpdateInventory();
        }
    }
}
/**/
then I added both of these in my "inventory" script to "update it" after reloading, and to set the right size as well :

Code: Select all

public void UpdateInventory()
        {
            if (inventoryUpdated != null)
            {
                inventoryUpdated();
            }
        }

        public void CreateNewInventory(int size)
        {
            inventorySize = size;
            slots = new List<InventorySlot>(new InventorySlot[size]);
        }
Case Closed! :) finally...

Re: Save System Question ;)

Posted: Mon May 16, 2022 8:17 pm
by Tony Li
Awesome! That's the way you do it. :-)

Re: Save System Question ;)

Posted: Tue May 17, 2022 12:08 pm
by HawkX
I'm unsure if i should make a new thread or keep going in this one... since the question "still" regards the saving system somewhat... ;)

I'm back to one of the original question... is there a way for a prefab with a simple "active/enabled" saver to always get a unique UID/hash-key auto generated?

I dont want our designers to have to manually remember to add a unique key to every items they put in the scenes... or to have them remember to always go to "Tools\pixelcrusher\common\savesystem\new key for everything" every single time they want to add or remove a prefab from a scene...

I'd like to have a simple checkbox like the "append saver type to key" that would make a unique key automatically...
kinda like what the "spawned object" does for the instantiated object manager...

Any suggestions? :)

EDIT : since you havent seen/replied to this msg yet, i'm adding a super quick followup... I have a "Auto Save Load" setup... is there a way to manually call it up so that it auto saves before loading a new scene?

Re: Save System Question ;)

Posted: Tue May 17, 2022 1:11 pm
by Tony Li
I'll see if I can add a feature that auto-assigns a unique ID. I'll add that to the roadmap.

To save before leaving a scene, you'll need to invoke the save yourself before leaving the scene. For example, if you're using a ScenePortal component, you can configure the ScenePortal's OnUsePortal() UnityEvent to call a C# method that calls SaveSystem.SaveToSlotImmediate(#).


To save after entering a scene, you can set up a Dialogue System Trigger in the new scene. Set the trigger to OnSaveDataApplied. Add a SaveSysteMethods component, select Add Action > OnExecute() UnityEvent, and configure the OnExecute() event to call SaveSystemMethods.SaveToSlot(#).

Re: Save System Question ;)

Posted: Tue May 17, 2022 1:19 pm
by HawkX
I'm getting better :)

I had actually started investigating your SaveSystem.cs and found that "SaveToSlotImmediate(#)" which I literally just added to my SceneLoader script seconds before seeing your post :)

if you think of a quick/dirty way for me to add that auto-unique-ID, i'd be greatly interested :)
cause im not sure if i'll be able to get new updates... considering all the changes i have done to the source files location/folder-structure...
I guess I could check our VersionControl and figure out which files have been edited (if any) then do a manual copy paste of the updated files...

Thanks always! :D

Re: Save System Question ;)

Posted: Tue May 17, 2022 1:39 pm
by Tony Li
If there's something in the future that you'd like to change in the source code, please let me know. I may be able to point you to a way to add it without directly modifying the source code, such as making a subclass. If not, I can probably add something so you don't have to modify source code.

If you do modify source code, I recommend adding a unique comment such as "//[HawkX-Mod]" to your change. This way you can do a global search for the comment to find all of your customizations.

I don't have an answer yet for auto-assigning unique keys when instances of prefabs are added to the scene.

Re: Save System Question ;)

Posted: Tue May 17, 2022 1:57 pm
by HawkX
VERY good suggestions!! :) thanks again so much for all of those!! :)
(I'm going to check all my commit from the past few weeks and see if any files are from PixelCrusher folder and "diff" what the changes were and add the //HawkX tag if needed!

For the "unique key"... it seems like it might not be "that" important after all... because the key is the gameobject name... if you put multiple instance, they automatically put (02, 03, 04) and that reflects in their saved data key...
"key": "Prefab_Monster_Spawner (07)EnabledSaver",
Unless the Designer MANUALLY rename the prefab to remove the (##) and make them all named identical, it shouldnt be THAT much of an issue...


Another very quick thing, when coming back to my "field" scene after a combat, it seems like the data was not reloaded properly... (or not at all)... some stuff was there, but a destructible that was "not" destroyed in the save was destroyed in the scene... and the SaveDataPrefabSpawnedObjectManager got emptied and didnt keep the saved items that were on the ground....
Should i add a manual "reload" after the scene load has finished?

Re: Save System Question ;)

Posted: Tue May 17, 2022 2:05 pm
by Tony Li
HawkX wrote: Tue May 17, 2022 1:57 pmFor the "unique key"... it seems like it might not be "that" important after all... because the key is the gameobject name... if you put multiple instance, they automatically put (02, 03, 04) and that reflects in their saved data key...
"key": "Prefab_Monster_Spawner (07)EnabledSaver",
Unless the Designer MANUALLY rename the prefab to remove the (##) and make them all named identical, it shouldnt be THAT much of an issue...
Another potential issue is if they add instances to multiple scenes. If 'Save Across Scene Changes' is ticked, keys must be unique across scenes.
HawkX wrote: Tue May 17, 2022 1:57 pmAnother very quick thing, when coming back to my "field" scene after a combat, it seems like the data was not reloaded properly... (or not at all)... some stuff was there, but a destructible that was "not" destroyed in the save was destroyed in the scene... and the SaveDataPrefabSpawnedObjectManager got emptied and didnt keep the saved items that were on the ground....
Should i add a manual "reload" after the scene load has finished?
No, don't do that. Not only will it take twice as long, but it probably won't fix anything. Are you changing scenes using any of these techniques -- or, if not, then calling SaveSystem.BeforeSceneChange()?

Re: Save System Question ;)

Posted: Tue May 17, 2022 2:33 pm
by HawkX
I had actually created my "SceneLoader" before starting to use PixelCrusher SaveSystem...

Guess I need to re-write this (or figure out how to switch to using yours)

FULL Scene Loader

Code: Select all

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using PixelCrushers;

public class SceneLoader : MonoBehaviour
{
    public GameObject goFieldCamera;
    public GameObject goCombatCamera;
    public GameObject goTransitionCamera;
    public GameObject goFollowCamera;
    public GameObject goShuma;
    public CanvasGroup canGroup;
    public GameObject goSliceTransition;
    [SerializeField] float fadeOutTime;
    [SerializeField] float fadeInTime;
    [SerializeField] float fadeWaitTime;
    [SerializeField] string AmbianceToPlay; 

    public static SceneLoader LS;

    public static bool boolPrefabAlreadyExist = false;

    private void Awake()
    {
        if (boolPrefabAlreadyExist)
        {
            DestroyImmediate(this.gameObject);
            return;
        }
        if (SceneManager.GetActiveScene().name.Contains("Combat"))
        {
            goFieldCamera.SetActive(false);
            goFollowCamera.SetActive(false);
            goShuma.SetActive(false);
            goCombatCamera.SetActive(true);
        }
        else
        {
            goCombatCamera.SetActive(false);
            goFieldCamera.SetActive(true);
            goFollowCamera.SetActive(true);
            goShuma.SetActive(true);
        }
        canGroup.alpha = 0;
        LS = this;
        boolPrefabAlreadyExist = true;
        goTransitionCamera.SetActive(false);
    }

    private void Start()
    {
        WWiseAudioPlayer.WS.PlayMusicAmbiance(AmbianceToPlay);
    }

    public void LoadBattleScene(string SceneName)
    {
        SaveSystem.SaveToSlotImmediate(0);
        goTransitionCamera.SetActive(true);
        string AmbianceToStop = AmbianceToPlay.Replace("Play", "Stop");
        WWiseAudioPlayer.WS.StopMusicAmbiance(AmbianceToStop);
        WWiseAudioPlayer.WS.PlayMusicCombat();
        StopAllCoroutines();
        StartCoroutine(TransitionToBattle(SceneName));
    }

    public void LoadFieldScene(string SceneName)
    {
        WWiseAudioPlayer.WS.StopMusicCombat();
        WWiseAudioPlayer.WS.PlayMusicAmbiance(AmbianceToPlay);
        StopAllCoroutines();
        StartCoroutine(TransitionToField(SceneName));
    }

    private IEnumerator TransitionToBattle(string SceneName)
    {
        goShuma.GetComponent<ThirdPersonController>().StopAllMovement();
        goShuma.GetComponent<ThirdPersonController>().enabled = false;
        goSliceTransition.SetActive(true);
        goSliceTransition.GetComponent<BattleSceneTransition>().Wipe();
        yield return new WaitForSeconds(fadeWaitTime);
        yield return SceneManager.LoadSceneAsync(SceneName);
        goFieldCamera.SetActive(false);
        goFollowCamera.SetActive(false);
        goShuma.SetActive(false);
        yield return Fade(1, 0.1f);
        goCombatCamera.SetActive(true);
        goSliceTransition.GetComponent<BattleSceneTransition>().Reset2Default();
        goSliceTransition.SetActive(false);
        yield return Fade(0, fadeInTime);
        goTransitionCamera.SetActive(false);
    }

    private IEnumerator TransitionToField(string SceneName)
    {
        yield return Fade(1, fadeOutTime);
        yield return SceneManager.LoadSceneAsync(SceneName);
        yield return new WaitForSeconds(fadeWaitTime);
        goCombatCamera.SetActive(false);
        goFieldCamera.SetActive(true);
        goFollowCamera.SetActive(true);
        goShuma.SetActive(true);
        goShuma.GetComponent<ThirdPersonController>().enabled = true;
        goShuma.GetComponent<ThirdPersonController>().RestoreAllMovement();
        yield return Fade(0, fadeOutTime);
    }

    private IEnumerator Fade(float target, float fadetime)
    {
        while (!Mathf.Approximately(canGroup.alpha, target))
        {
            canGroup.alpha = Mathf.MoveTowards(canGroup.alpha, target, Time.deltaTime / fadetime);
            yield return null;
        }
    }
}
I'm guessing I could replace this with the "Scene Transition Manager"... might need some tweaks so my other stuff dont break however ;)


EDIT : What does "Save Across Scene Changes" actually mean? the same item (box/crate/etc.) would show up at the same position but in another scene? in that case, i need to tick that off from all but my character/inventory saver :)
The description (hover tooltip) says saves across scene change to be able to return to it when coming back to this scene... so yeah it does need it checked if that is the case..

Re: Save System Question ;)

Posted: Tue May 17, 2022 3:05 pm
by HawkX
UPDATE (making a new post because this solves everything from the past message!)

I simply replaced the line :
//yield return SceneManager.LoadSceneAsync(SceneName);
With this one :
SaveSystem.LoadScene(SceneName);

In my own Scenemanager and it solved the loading/saving issue :)



I also JUST finished going through every single of my 80+ commits... and beside moving all the files from PixelCrusher to my own Script\Pixelcrusher and Script\Editor
folders, I only made a single change to
SpawnedObjectManager :
(not sure why, perhaps its not longer needed, but that was the only way I managed to get the spawned item to stick after a reload)

Code: Select all

public static void AddSpawnedObjectData(SpawnedObject spawnedObject)
        {
            if (m_instance == null || spawnedObject == null)
            {
                if (spawnedObject == null)
                {
                    return;
                }
                if (m_instance == null)
                {
                    m_instance = FindObjectOfType<SpawnedObjectManager>();
                }
            }
            m_instance.m_spawnedObjects.Add(spawnedObject);
        }
Also changed m_framesToWaitBeforeApplyData to public in SaveSystem.cs so that I could use it to restore something 1 frame after save got restored :)