- Home /
Prevent Reset() from clearing out serialized fields
I have a component which stores a unique id, once it gets created. After it was once set, the id should never change.
However, when a designer selects the cog and "Reset" function on the component, all serialized values are cleared. I know I can implement my own Reset() call, but that doesn't help, because it gets called, after the fields were already cleared.
Is there any way I can prevent this or get a callback so that I can save and restore my unique id? I'm trying to prevent my level designes from accidentally corrupting references to said ids.
EDIT: This was my old answer, I've since created a better solution: look for my icon to find my new answer.
Old question, but it is certainly possible! All you need is a NonSerialized value that you use as a dump for your serialized value. Like so:
public class MyClass : MonoBehaviour, ISerializationCallbackReceiver
{
// [HideInspector] in your case I'd suggest this as well.
[SerializeField] private int uniqueID;
private int uniqueIDDump;
public void OnAfterDeserialize()
{
if(uniqueIDDump != default) // or use a Nullable
{
uniqueID = uniqueIDDump;
}
}
public void OnBeforeSerialize()
{
uniqueIDDump = uniqueID;
}
}
Even if you call your uniqueID in Reset, it will have its old value.
Note that the "default" check is important! Since the dump is not serialized, in some cases (e.g. Editor Restart) it will reset the dump. In these cases we may assume the uniqueID has been serialized and will restore the dump on its soonest OnBeforeSerialize call, which will always be before any editing takes place.
Answer by Bunny83 · Jul 24, 2017 at 10:27 AM
No, you can't prevent that fundamental editor feature. Just like you can't prevent the designer from removing the component and re-adding a new one which would result in the same problem.
Yout only options would be setting the hideflags and prevent interaction completely. Either set the "NotEditable" flag which would display the whole component "inactive" (just like the components on an imported fbx model). Or if the component doesn't even need to be seen you can completely hide it from the inspector by setting the "HideInInspector" flag as well.
However keep in mind that there are still ways to at least mess up your IDs. If the object gets duplicated or stored as prefab it would still have the same ID. So you would have multiple objects with the same ID.
However it depends on the actual usage of the component.
Thanks, those are some helpful pointers. I think the NotEditable flag might help. Still have to think about prefabs though. $$anonymous$$aybe I can retrieve the local file identifier from the scene asset and assign it as guid to my component when it is added to the scene, or something like that.
Answer by CaseyHofland · May 11 at 09:39 PM
It is possible, and in a very intuitive way. First, copy this wrapper:
[Serializable]
public struct NonResetable<T> : ISerializationCallbackReceiver
{
public T value;
private T _dump;
[SerializeField] [HideInInspector] private bool valid;
public static implicit operator T(NonResetable<T> nonResetable) => nonResetable.value;
public static implicit operator NonResetable<T>(T value) => new NonResetable<T> { value = value };
public void OnBeforeSerialize()
{
_dump = value;
}
public void OnAfterDeserialize()
{
if (!valid)
{
value = _dump;
valid = true;
}
}
}
You don't need to know how it works in order to use it. It automatically converts to and from the type it's wrapping, so you can use a NonResetable 'mostly' like you would a float.
public NonResetable<float> f = 5f;
float SomeMethod() => 10 * f; // Returns 50.
For styling, I would also suggest you copy this script inside an Editor folder:
[CustomPropertyDrawer(typeof(NonResetable<>))]
public class NonResetableDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
EditorGUI.BeginProperty(position, label, property);
var valueProperty = property.FindPropertyRelative(nameof(NonResetable<bool>.value));
EditorGUI.PropertyField(position, valueProperty, label);
EditorGUI.EndProperty();
}
}