Overwriting spawned gameobjects/prefabs

Discussion and help for Easy Save 3
Post Reply
User avatar
LoneDev
Posts: 9
Joined: Fri Jun 04, 2021 5:45 pm

Overwriting spawned gameobjects/prefabs

Post by LoneDev »

Hello!
Currently OnApplicationQuit I'm saving dungeon_tiles instantiated prefabs into a file and some of the dungeon rooms children gameobjects into another file (in this case the dungeon walls with Doorway script, because I need to save if the walls children are removed from the scene or not, since they hold the collision and meshrenderer).
Then on Start I load the dungeon rooms, then I load the dungeon_doorways file right after.

The problem is that seems Doorway children are still spawned despite the fact they are destroyed.
What I'd expect is that the children would get removed when I load the second file.

To clarify here is the Start()

Code: Select all

dungeonDoorwaysSave = new ES3Settings("dungeon_doorways.save") {saveChildren = true};
playerSave = new ES3Settings("players_data.save") {saveChildren = true};
worldSave = new ES3Settings("world.save");

if (load)
{
	dungeonTiles = ES3.Load("dungeon_tiles", dungeonTiles, worldSave);

	pickupables = ES3.Load("pickupables", pickupables, worldSave);
	rigidbodies = ES3.Load("rigidbodies", rigidbodies, worldSave);

	dungeonDoorways = ES3.Load("dungeon_doorways", dungeonDoorways, dungeonDoorwaysSave);

	players = ES3.Load("players", players, playerSave);
}



OnApplicationQuit

Code: Select all

if (!save) 
	return;

Debug.Log("QUIT GAME, SAVING...");

foreach (var entry in FindAllObjectsOfType<Tile>())
	dungeonTiles.Add(entry.gameObject);

foreach (var entry in FindAllObjectsOfType<PlayerStats>())
	players.Add(entry.gameObject);

foreach (var entry in FindAllObjectsOfType<Doorway>())
	dungeonDoorways.Add(entry.gameObject);

foreach (var entry in FindAllObjectsOfType<Rigidbody>())
{
	if (!PlayersManager.instance.playersTransforms.Contains(entry.transform))
		rigidbodies.Add(entry.gameObject);
}

ES3.Save("dungeon_tiles", dungeonTiles, worldSave);
ES3.Save("pickupables", pickupables, worldSave);
ES3.Save("rigidbodies", rigidbodies, worldSave);

ES3.Save("dungeon_doorways", dungeonDoorways, dungeonDoorwaysSave);


ES3.Save("players", players, playerSave);


What I even tried is this on Start: iterate the just spawned walls and remove them, then load them again from the other file dungeon_doorways with no success, no GameObject is spawned.

Code: Select all

dungeonTiles = ES3.Load("dungeon_tiles", dungeonTiles, worldSave);

pickupables = ES3.Load("pickupables", pickupables, worldSave);
rigidbodies = ES3.Load("rigidbodies", rigidbodies, worldSave);


foreach (var tile in dungeonTiles)
{
	foreach (var doorway in tile.GetComponentsInChildren<Doorway>())
	{
		Destroy(doorway.gameObject);
	}
}

dungeonDoorways = ES3.Load("dungeon_doorways", dungeonDoorways, dungeonDoorwaysSave);

players = ES3.Load("players", players, playerSave);
User avatar
LoneDev
Posts: 9
Joined: Fri Jun 04, 2021 5:45 pm

Re: Overwriting spawned gameobjects/prefabs

Post by LoneDev »

Update:
using the tricky code at the end but changing Destroy to DestroyImmediate has fixed the issue, but introduced another where all the doorways lose their parent and are spawned at 0, 0, 0.
Adding an autosave component to the doorway prefab and specifying to save the transform fixed the parenting problem, but reintroduced the initial problem explained in the first post.

Anyway please if you have any suggestion to make my workflow better it'd be accepted since I suspect my workflow is not optimal in this case but rather hacky.

Thanks
Last edited by LoneDev on Thu Jun 17, 2021 12:51 pm, edited 1 time in total.
User avatar
Joel
Moodkie Staff
Posts: 4826
Joined: Wed Nov 07, 2012 10:32 pm

Re: Overwriting spawned gameobjects/prefabs

Post by Joel »

Hi there,

In this case DestroyImmediate is likely your best bet, as objects aren't destroyed until later when using Destroy, so it's not possible to tell that they have been destroyed.

Alternatively, ensuring that you don't destroy an object on the same frame as you save/load it would have the same effect.

All the best,
Joel
Joel @ Moodkie Interactive
Purchase Easy Save | Contact | Guides | Docs | Getting started
User avatar
LoneDev
Posts: 9
Joined: Fri Jun 04, 2021 5:45 pm

Re: Overwriting spawned gameobjects/prefabs

Post by LoneDev »

I think the issue is that an instance of prefab will still get its child respawned even if any of the child of it gets destroyed at runtime.
This happens when it has the ES3 Prefab script, so I think ES3 is respawning the original prefab and applying changes, but not taking into account removed gameobjects, right?


If it's the case I consider it a flaw, when respawning a prefab I'd expect removed gameobjects to be removed again or not spawned at all
User avatar
Joel
Moodkie Staff
Posts: 4826
Joined: Wed Nov 07, 2012 10:32 pm

Re: Overwriting spawned gameobjects/prefabs

Post by Joel »

Hi there,

I think I understand a bit better what you mean.

Serializers in general only store what's there, not that something has been destroyed as this isn't part of a serialization tree (it's more a meta property of an object which once existed but no longer does).

In your case you would need to store the destroyed state separately. Generally the easiest way to do this would be to add the reference ID of any objects you destroy to a List immediately before destroying them and use this List to destroy any extraneous objects upon loading.

For example, attaching the following scripts to any GameObjects you plan on destroying would do this:

Code: Select all

using UnityEngine;
using System.Collections.Generic;

public class LogDestroyed : MonoBehaviour
{
    public static List<long> destroyedIDs = new List<long>();

    void OnDestroy()
    {
    	if (gameObject.scene.isLoaded)
        	destroyedIDs.Add(ES3ReferenceMgr.Current.Get(this));
    }
}
And then when you come to save, also save this static List. I.e.

Code: Select all

ES3.Save("destroyedIDs", LogDestroyed.destroyedIDs);
And then after loading all of your data, load this List and destroy each object with the given ID in the list:

Code: Select all

LogDestroyed.destroyedIDs = ES3.Load<List<long>>("destroyedIDs");
foreach(var id in LogDestroyed.destroyedIDs)
    Destroy(ES3ReferenceMgr.Current.Get(id));
All the best,
Joel
Joel @ Moodkie Interactive
Purchase Easy Save | Contact | Guides | Docs | Getting started
User avatar
LoneDev
Posts: 9
Joined: Fri Jun 04, 2021 5:45 pm

Re: Overwriting spawned gameobjects/prefabs

Post by LoneDev »

Thanks for the tips, the solution is okay, only issue is that you have to also be sure that OnDestroy is not called because of scene unload or it will be triggered even when closing the game / switching scene.

This should work fine

Code: Select all

private void OnDestroy()
        {
            if (!gameObject.scene.isLoaded)
                return;
            ///// save code here ////
        }

Anyway the same limitation applies to the removal of Monobehaviours from prefabs which is not saved. You also need to keep track of that somehow and save, probably with an utility function called everytime you remove a mono.
User avatar
Joel
Moodkie Staff
Posts: 4826
Joined: Wed Nov 07, 2012 10:32 pm

Re: Overwriting spawned gameobjects/prefabs

Post by Joel »

Hi there,

Alternatively you could add the code to the parts of your code where you remove Components or destroy GameObjects, instead of using the OnDestroy event.

All the best,
Joel
Joel @ Moodkie Interactive
Purchase Easy Save | Contact | Guides | Docs | Getting started
Post Reply