- Home /
Using Undo.RecordObject() does not protect a component's variables from being reverted to the prefab on play.
I'm working on a custom editor script that takes an XML file of items ('trade items': I'm working on a game about the silk road), and populates the lists in the Market.cs components that are attached to each city in the game. I'm doing this because I want each market to have a supply/demand value for each good, e.g. Gold : 3.4, Silk: 8.6. I want to be able to add new trade items to my XML file, then load that file into my Markets.
Markets are a component of a trade node, basically a city. Naturally, these are instances of a prefab, so that I can edit all trade nodes at once. I use a custom editor window to load the XML data and use it to populate the Market lists:
using UnityEngine;
using System.Collections;
using UnityEditor;
using Trade;
public class Builder : EditorWindow
{
// Make a custom editor window with a button that says "Build Markets"
// This button calls the BuildMarkets() function.
// Initialises serialized data in market objects.
void BuildMarkets()
{
Market[] markets = FindObjectsOfType<Market>();
Debug.Log(markets.Length + " markets found. " + World.Instance.goods.goods.Count + " varieties of good found. Populating markets.");
// Add a supply value for each good, in every market.
foreach (Market m in markets)
{
// Record undo change (ensures the change will be 'set to dirty' and won't revert to the prefab on play).
Undo.RecordObject(m, "Build Market Supplies");
foreach (Good g in World.Instance.goods.goods)
{
// Note that you can't refer to the supply's dictionary, or use ContainsKey(), because
// the dictionary does not exist until runtime. Use the key list instead.
if (!m.supplies.keys.Contains(g))
{
Debug.Log(m.gameObject + "'s Market does not contain an entry for " + g.name + ".");
m.supplies.dictionary.Add(g, 0.0f);
}
}
}
}
}
It's worth mentioning that I needed my Market's supplies list to be a dictionary, so that I could easily search through it. Dictionaries can't be serialised, so I followed the typical example of creating a custom serializeable Dictionary seen here. As far as I can tell it's working fine, and is not part of the issue.
When I hit the "Build Markets" button, the supplies list is populated with my XML goods as I'd want. However, on clicking play, these lists revert to the prefab value: no members, and a length of 0. When the game is stopped, the values are still gone.
As you might imagine, when I use the "Build Markets" function, the Market data shown in the inspector does not turn bold, as other things in Unity do when they're set as something different from their prefab. The fix recommended in the documentation is to use Undo.RecordObject(). So I tried this, as seen in my code. This sets the lists I need serialised to 'dirty' bold font in the inspector, but not their elements, like so:
But what I need is for the lists components - basically anything that has been changed - to be protected from the prefab override. Why isn't Undo.RecordObject() doing this?
One thing I've noticed is that if I really fiddle around with stuff - repeatedly clearing the lists, rebuilding them, and clicking on the inspector lots of times, I do manage to get all of the list elements set to dirty as well.
Finally, I'm aware that SetDirty() sets an entire component to dirty, but this is far from convenient. It's likely that I'll need some variables in any given component to be dependant on the prefab.