Another bug with the portals' saving parts

I have found a bug in the way the portals are saving scenes.

Here’s how I reproduce the bug consistently:

I pick up an item in scene A and walk through to scene B. I then turn around and walk back to scene A. All is well. Now, if I restart the game the pickup is spawned again, and I also have it in my inventory.

I have dug around a bit and found this: When we walk through the portal it first saves the state of the current scene. This is all ok. It then loads the next scene, positions the player and then saves the scene again. This is where the problem lies.

If I recall correctly, when you call Destroy on an object, that object only gets destroyed at the end of the frame. When the portal loads the scene, the drop is spawned as expected. The portal then loads the saved data which detects that the pickup was collected, and calls Destroy on it to remove it again. However, that object still exists until the end of the frame. Once the portal has moved the player, it saves the scene again and the pickup spawner detects that the object is still there (we’re still in the same frame) and reports that it should spawn. The saving system saves this, but we’ve already loaded the scene so it stays away. When we restart, the pickup is spawned again because we saved that it is still there after we’ve loaded.

There are 2 ways to fix this, although both are a bit hacky. The one is in PickupSpawner.cs (which we probably don’t want to change, but here it is)

private void DestroyPickup()
{
    var pickup = GetPickup();
    if (pickup)
    {
        pickup.transform.SetParent(null);
        Destroy(pickup.gameObject);
    }
}

When we set the object’s parent to null before calling Destroy, the object is destroyed immediately. I can’t remember where I learned this, sorry.

The other way is far simpler and is in Portal.cs. Immediately after we’ve updated the player position and before we save the new scene, add yield return null;

wrapper.Load(); // load new scene state

var otherPortal = GetOtherPortal();
UpdatePlayer(otherPortal);
yield return null; // <- HERE

wrapper.Save(); // save new scene again to set a checkpoint

This should wait for the next frame which will ensure that the pickup is destroyed if it needs to be.
This is a part of my code from the Combat course, so it might not match the code available, but it should not be too far removed.

Hope this will help someone out

Edit: I have changed the destroy to destroy the object instead of the script. My bad. Sorry for any issues caused

4 Likes

I would go with the first solution, 100%

Thank you for this!

I do not have portal, or multi scenes yet, but I still applied the 1st fix since it seems to be fixing the “destroy pickup” method more in depth and will prevent future problems when I do get to multi scenes loading :slight_smile:

I replicated this as described and implemented the fix on the pickup spawner. thank you.

Privacy & Terms