- Home /
Suggestions for Persisting Items' and Enemies' state between Load?
Hi All
For our RPG the first level loaded contains data that must persist and is marked as DontDestroyOnLoad(). Subsequent in-game levels, in our case environments, are loaded as distinct levels, so we have a beach level and a town level and various other levels that the player can currently traverse, 'zoning' from one level to another when they hit a trigger.
One of the problems we have, is that we have objects in the world for a given level, such as Creatures and Items, and if the level is loaded, naturally the items are on the ground and can be looted quite happily, the creature(s) can be killed, but as we've done nothing about it yet, if the player zones out of the level and then back in, the creatures they just killed are back and so are all the items - this is to be expected of course.
So, I'm wondering about the best approach to ensuring that item data and the status of a creature (alive/dead, when it should respawn if it should) can be persisted across loads of the level. I'm thinking I need to do some sort of partial save and then apply that state as and when the level is re-loaded but I imagine that this is a problem that many people will need to solve and so I'm looking for a scalable solution - i.e. using a script that looks at tags or looks for particular scripts within the scene. I think I am most interested in how to persist the absence of an object in the scene?
I really apologise if this feels a little too broad a question, but in my view, this is a problem lots of games will have generally, especially where the player can pickup/drop or interact with the environment in some way.
Naturally this might be a subset of an actual save game system and on the subject of which I've posted on the EZ Game Saver forum a similar question, so anyone familiar with that framework please answer in either place! I use the EZGUI framework from ANBSoft and on the basis of how good that was and how great Brady is with support/fixes, if EZ Game Saver would work, that's very tempting!
Thanks in advance H
Answer by AaronG · Sep 03, 2011 at 09:07 PM
I guess one solution would be to keep a 'manifest' (in the form of a list) that stores information about all the enemies in the level that is persistant throughout the entire game. The list would basically contain a 'properties' class used by each enemy to determine things such as where they should spawn, what item they should drop and, most importantly, if they have been previously killed.
The idea is that when a player kills an enemy, the list will flag that particular enemy as 'has been killed previously'.
So, when the player returns to that level the same enemy is spawned again and then killed, the game will check during the item dropping code, "Has this enemy already been killed before?" and will not drop an item.
Hope that makes sense!
Sure, this is sort of how I would implement it, using DontDestoryOnLoad() but the difficulty is in placing enemies and items throughout multiple levels means that some Editor customisation is probably needed to allow placement of enemies/items, otherwise it's a few properties or the name of a spawn location for the enemy or item. Every level would need to spawn anything that is alive/hasn't been picked up.
I think I understand your issue. You want it to be super simple for the level designer don't you? Well, here is how I see it.
Let's say our enemy has 4 properties.
Position (vector3)
Has Been $$anonymous$$illed (bool)
Weapon Held (enum)
Item to Drop (enum)
The level designer places gameobjects and attaches the 'enemy' class. They can only define 2 of the 4 properties: 'weapon held' and 'item to drop'.
As for the manifest, it should be quite simple.
Have the manifest script be persistant.
You use FindObjectsOfType to find all instances of the 'enemy' script
Then loop through each object and... - Get/set the properties. - 'Weapon Held'? Already set! - 'Item to Drop'? Already set! - 'Position' - Grab the position of the gameobject (transform.position) and assign that to the 'Position' property. - 'HasBeen$$anonymous$$illed' - This is set to 'false' by default. - Add the 'enemy' object to the list.
Once you've finished looping set a bool named 'hasBuildList' to true.
When the player returns to the level, the code will check 'hasBuiltList' and will quit straight away as the list has already been built and we don't want to be overriding the properties!
Spawn all the enemies, one by one, using the now built level manifest.
Voila!
So if I have a GameObject in the level with the enemy script attached, it adds itself to the manifest which is built once. I then exit the level and then re-enter the level, the level is loaded again and still contains the GameObject for the enemy anyway, so another will be created won't it? I'm not seeing how the manifest is useful yet...
The only way I maybe see this working is if an item/enemy can be identified by a GUID, or other unique value that will persist between loads and then the enemy can check the manifest for its existence and destroy or re-position itself as necessary.
Each enemy in the list IS unique. An enemy at index 1 in the list is going to be different from an enemy at index 4 as they are separate objects.
As for the gameobjects, they will be empty aside from the attached enemy script which contains the properties. Think of them as dummy objects. By themselves they are useless without the manifest script. They will do absolutely nothing on their own.
Think of the manifest as the 'creator' or 'puppet master'. :P Without the creator, the enemies cannot become alive. This creator will use the dummy objects position and attached 'enemy' script to figure out what to spawn, where to spawn it, and what properties it should have.
Don't want enemies to respawn? Simple. When the level is loaded and the 'spawn' method in the creator is called, it will check, 'Has the level been played'? If it hasn't, spawn the enemies, if it has, don't do anything.
Okay, so I'm struggling a little still: let's say the player:
Starts the game
Enters the first area
The area hasn't been entered so items and enemies are spawned
The player leaves the first area
Surely all spawned items are destroyed and any GameObject in the list is invalid?
The player enters the first area again - the spawned objects don't respawn because the player has already been here.
You say each enemy is unique but either the manifest must know whether to re-spawn an enemy on the 2nd entry or the original spawn script - in which case the script would need to know which enemy script is which enemy in the list.
However I think I can see that if an enemy is killed or an item picked up, then the manifest can be updated to reflect this and when the level unloads the enemy can update the manifest with the enemies position/state, then on re-entry the manifest can recreate the enemy and items that should still be there.
Is that what you mean?
Answer by Zetsaika · Sep 03, 2011 at 06:45 PM
You should use a persistent object(DontDestroyOnLoad) that keeps tracks of what itens the player found an what monsters he killed.
So everytime your map loads, you make a check that object and instantiate only the itens the players has not found and mounsters he not killed.
So I could do that but how do I add monsters and items to the level in the editor? Also, I'll need to have all the mobs and items for all levels present in RA$$anonymous$$ - it doesn't feel as if it would scale well...?
You could use spawn point placed in the editor to represent the position you want then instantiate the right object to its when the map load.
Sure. Sounds like a lot of editor windows to make placement and automation less painful... Hmmm I was hoping for maybe a more elegant solution.... any other suggestions?
Hmmm... $$anonymous$$aybe an dictionary of String/NameOfOBject and Vector 3 representing the position of everything?
With my level os skill i can't think in anything else. Sorry.
Answer by by0log1c · Sep 04, 2011 at 04:23 AM
I think I've used this before but it could require some tweaking, anyway, here's an idea:
Have a script on every object that contains the DontDestroyOnLoad() and a uniqueID variable. Then on Awake(), search for a duplicate uniqueID and Destroy() itself if it finds one.
I don't think the older object will destroy itself instead of the newer one but if it does, register the DateTime of creation and make sure you keep the oldest one.
EDIT: Then again, I read your post and I really can't figure if the DontDestroyOnLoad() approach is the best one.
I cant have objects floating about that aren't in the current level - enemies would fall eternally and wouldn't be in the right place on returning to their originating level, not to mention a bloat of resources being used as the player progressed through the game. As likely would be that some entities from the preceding levels would exist in subsequent levels.
Answer by promoocodes · Oct 10, 2018 at 12:50 PM
I didn't know that RPG contains data that must persist thank you that you have aware we about this information keep posting Tuft And Needle Coupons
Answer by garytzu · Nov 22, 2019 at 09:57 AM
Well I must say that this blog is containing some fascinating stuff which was very interesting Nondual Awakening
Your answer
Follow this Question
Related Questions
How to make sure score can't be increased by picking up same item multipe times--new idea 2 Answers
Save scene state 4 Answers
Checkpoint system in platformer 1 Answer
How should I create/instantiate a skill/enemy/item 1 Answer
Save Scores In Scene 4 Answers