Forum rules - please read before posting.

Component variables not loading fast enough

edited May 2023 in Technical Q&A

I've just found a bug with AC's saving system:

(1) I have lots of component variable attached to the player prefab. To make this simple, let's assume it's just a simple integer though, like "NPC1 location".

(2) The scene has an actionlist that is assigned both to OnStart and OnLoad. This actionlist runs several actions to set up the scene and, before fading the camera in, it runs an actionlist asset called "NPC1 position".

(3) The "NPC1 position" actionlist checks for the "NPC1 location" integer. If it is 1, it teleports NPC1 to marker1. If the check returns false, it teleports NPC1 to marker0.

During normal gameplay, once the "NPC1 location" integer has been changed to 1, NPC1 is always found at marker1 when the player enters the scene. Also, if the player saves the game in the scene, goes to a different scene, and then loads that save, NPC1 will still be found at the correct location.

HOWEVER:

If the player quits the game and loads that save from the main menu (where that player prefab is not present) -- or if during gameplay the "NPC1 location" integer has been changed back to 0 -- the bug happens. On load, NPC1 will be sent to marker0 even if the component variable in the save says that "NPC1 location" == 1.

In other words, if the current scene either doesn't have the component variable, or if it does have it but the value is different from the one in the save, the check will return false and NPC1 will be teleported to the wrong location.

To make sure this was indeed happening, I enabled "Toggle breakpoint" on the teleport action that takes NPC1 to marker0. After loading, with the game paused because of the breakpoint, I found that:

(1) All the Global Variables have already been loaded correctly from the save.

(2) The player prefab is present in the scene, but the "NPC1 location" component variable is set to 0.

(3) NPC1 is at marker0.

As soon as I unpause the game, the "NPC1 location" component variable changes to the value in the save: 1.

Comments

  • Thanks for the details.

    Issues related to this were addressed in v1.77.2. Can you confirm this is occuring in this release?

    Is the Player prefab being spawned in by Addressables, and/or AC's "Player-switching" feature?

  • edited May 2023

    I was using v1.77.1 (I didn't realise .2 was out), but I've just updated, and I'm afraid the issue is still there.

    (As a side note, thanks for the destinationThresholdOverride in PlayerMovement! But I think it needs to be a public float rather than protected? I've made the change locally and now I can use the override without issues via script.)

    But I do use the Player-switching feature. The player set as default is "Player (main menu)", and we switch it to "Player (gameplay)" when the player clicks the "Start" button on the main menu.

    So it's easier to test the game in the editor from different scenes, every scene's OnStart/OnLoad cutscene (except for the main menu scene) has an action that switches the player to "Player (gameplay)" right at the beginning of the cutscene. But the issue I'm reporting happens even when I remove that action from the OnStart cutscene and rely only on the character change that happens when the player clicks "Start" on the main menu. Meaning, if I remove that action from the OnStart cutscenes, use the switch player action only once, play the game as normal, and then load a save with a different "NPC1 location" value, the value from the save will still lag to be loaded.

    I've tried playing around with a "Wait" action in the beginning of the OnStart/OnLoad cutscene. Started out with a 0.1s wait, which didn't work, and NPC1 only started being teleported to the correct marker when I increased the wait to 0.7s (0.6 still breaks). So it's not just a frame or two: the component variable load is lagging by a considerable amount of time, and I don't know if it's always the same amount or if this depends on the machine's performance. [Edit: It's just worked with a 0.6s wait when I tried again, so it does appear to depend on machine performance]

  • I'm having trouble recreating the issue.

    Are you using Addressables to spawn the Player in? If not, I'd appreciate you PMing me a repro package to test with.

    (As a side note, thanks for the destinationThresholdOverride in PlayerMovement! But I think it needs to be a public float rather than protected? I've made the change locally and now I can use the override without issues via script.)

    Use the DestinationThreshold property, which is public.

  • edited May 2023

    Use the DestinationThreshold property, which is public.

    Duh, thanks! I'd typed "KickStarter.playerMovement.D" and Visual Studio didn't autocomplete it, and instead of assuming it was bugging out, I went looking for a problem that didn't exist. Now it works, sorry!

    Are you using Addressables to spawn the Player in? If not, I'd appreciate you PMing me a repro package to test with.

    I'm not. I've DM'd you a repro project.

  • Thanks.

    Open up AC's RememberVariables script and look for the line:

    VariablesData data = new VariablesData ();
    

    Immediately underneath, add:

    data.objectID = constantID;
    data.savePrevented = savePrevented;
    

    Does that resolve it?

  • Hm, line 35, right? Just under public override string SaveData ()? Like this:

            #region PublicFunctions
    
            public override string SaveData ()
            {
                VariablesData data = new VariablesData ();
                data.objectID = constantID;
                data.savePrevented = savePrevented;
    
                foreach (GVar var in Variables.vars)
                {
                    var.Download (VariableLocation.Component);
                }
    
                data.variablesData = SaveSystem.CreateVariablesData (Variables.vars, false, VariableLocation.Component);
    
                return Serializer.SaveScriptData <VariablesData> (data);
            }
    

    It didn't resolve it. I tried it both in my main project and in the test project I sent you.

  • I've done some more experimenting here, and the issue really is specific to the fact that the component variable is a child of the player. The remember components in other scene objects don't have this issue. Does this have anything to do with the way LoadPersistentData and LoadPlayerData retrieve the Remember components differently? The former uses HashSet<Remember> persistentSaveScripts = KickStarter.stateHandler.ConstantIDManager.GetPersistentButNotPlayerComponents <Remember>();, and the latter uses a simple Remember[] playerSaveScripts = player.gameObject.GetComponentsInChildren<Remember> ();

  • Actually, it probably doesn't have anything to do with those lines. I suspect this is the culprit (under Player.cs):

                if (!IsLocalPlayer ())
                {
                    StartCoroutine (KickStarter.levelStorage.LoadPlayerData (this, playerData));                
                }
    

    I'm assuming this coroutine is taking its time to run, and the OnStart/OnLoad cutscenes are given the go ahead to run before the coroutine is finished?

  • I should think you're quite right.

    Oddly I wasn't able to recreate the issue following my change above - but it may be machine-dependent, and I can see how the culprit you point to could feasibly cause an issue.

    I will continue to look into this, thanks for the feedback.

Sign In or Register to comment.

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Welcome to the official forum for Adventure Creator.