Hi. I am using Scriptable Objects to create levels in my game.
Each level has several different items in it to be destroyed. I store each of these in an array.
When the player destroys an item it is also destroyed in the Scriptable Object as well. This means if they replay the level the items will no longer be there.
How can I use the Scriptable Object to be used to load the level, but when the level is being played and items are destroyed, the Scriptable Object retains its original unchanged values?
Scriptable objects, like all assets that don’t live in a scene, will persist any changes to their values between play mode sessions. Would just like to stress this isn’t unique to scriptable objects.
Important to note that this isn’t true in builds, too. Once the scriptable objects leave memory, the next time they re-enter memory it be at their original state.
The easiest solution would be to just Instantiate() the scriptable object when starting the level. It’s a Unity object, so like all Unity objects it can be duplicated on a whim. Just need to remember to Destroy() the non-asset instance so it doesn’t persist in memory when no longer needed.
Alternatively you can use the scriptable object as an outline with which to generate some transient regular C# data, and then at runtime this generated data is used. This might be useful if you want the play to be able to save mid-level, and restore that state, too.
as @spiney199 write above easiest way would be making something like this:
List<YourScriptableObjectClass > m_itemsInInventory = new List<YourScriptableObjectClass>();
public void AddItem(YourScriptableObjectClass _orginalScriptableItem)
{
// Creating copy of the item
YourScriptableObjectClass _coppyScriptableItem = Instantiate(_orginalScriptableItem);
// Add copy of the item to the list
m_itemsInInventory.Add(_coppyScriptableItem );
}
public void RemoveItem(YourScriptableObjectClass _itemToRemove)
{
if (m_itemsInInventory.Contains(_itemToRemove))
{
// Remove the item from the list
m_itemsInInventory.Remove(_itemToRemove);
// Destroy the copy to free up memory
Destroy(_itemToRemove);
}
}
private void OnDestroy()
{
// Cleanup all items when this manager is destroyed
foreach (var item in m_itemsInInventory)
{
Destroy(item);
}
m_itemsInInventory.Clear();
}
I decided to build and run on my phone and after I complete a level (or quit half way) the level reverts back to its original state. This is the behaviour I want.
I’m not sure why the changes persist when you exit Unity play mode but I’m glad that in the actual build that people will play on their phones / computers the Scriptable Object changes do not persist.
I don’t fully understand this script and will need to study it in detail.
However, it seems to me that this is to prevent changes to Scriptable Objects in Unity’s play mode?
If that is correct then this will be useful when I build my game for real and want to test its levels. (Right now I’ve got a prototype and can change the level settings back to their original settings when exiting play mode but when I develop for real I want to make sure I don’t accidentally forget to change the settings and values back to their original).
Only stuff that lives in scenes resets when exiting play mode. Because loaded scenes are just copies of a scene asset.
Otherwise any changes to assets in your project files will persist between play mode sessions, because you are modifying the actual asset and not a copy of it. Unity does not back up your assets in play mode to revert them afterwards. They are freely mutable at any point while in the editor.
The point of the example code is to make a copy of the scriptable object. Thus you aren’t editing the actual instance on disk, you are modifying a copy that lives only in memory.
It won’t have any effect on the build version will it, as the build version does not keep changes to the Scriptable Objects and instead reverts them back to their original values once a level ends or you quit the game?
It won’t change how the build works. It just ensures your editor behaviour matches your build behaviour, which is often desireable. Just something you need to do during development sometimes. The editor is a different environment to a build.
I will note that scriptable objects - and again, this is true of ALL assets - will only ‘reset’ once they leave memory when nothing references them any longer, and then reenters memory as a fresh instance of that asset. That’s the strict criteria for this behaviour.
That’s what I need and that’s what I get in the build. I noticed if I restart a level on my phone from within the level itself the Scriptable Object does not change.
Instead, I have to go to the main menu (which is a separate scene) and load the level from there. I suppose when switching scenes things leave memory and are loaded fresh when the scene is started again.
I will use the above script to get the Editor to match the build behaviour.