Odin Inspector prevents EditorUtility.SetDirty( ... ) in PropertyDrawer to work with ScriptableObjects

Issue #811 open
Patrick Küster created an issue

If a CustomPropertyDrawer is directly changing a property of a ScriptableObject - the recommended way would be to set the targetObject dirty via

EditorUtility.SetDirty(property.serializedObject.targetObject);

If the ScriptableObject is not using OdinInspector this works flawlessly.
It is flagged dirty and on scene / project save the changes are saved into the asset, or can be saved directly by calling

AssetDatabase.SaveAssetIfDirty(property.serializedObject.targetObject);

However if any of the involved classes is using Sirenix.OdinInspector; the targetObject is not any longer the ScriptableObject but in our case Sirenix.OdinInspector.EmittedUnityProperties.EmittedSOProperty_LocaKey_System.int

And now the changes aren’t saved into the .asset file.

This is only happening if the value - here an int - is assigned to the property via Code :

property.intValue = locaId;

I’m sorry that I cannot provide the full example code → the locaId is set by an EditorWindow which gets the Loca Data from a database …

I try to provide an simple standalone example later this week - but maybe you know a workaround / trick how I can get the actual targetObject even if the Odin Inspector is used.

Wie only use Odin Inspector - with the standard Unity (2021.2.1f) Serialization.

Comments (5)

  1. Tor Esa Vestergaard
    • changed status to open

    This is a difficult problem to resolve - it is related to certain limitations on how Unity's property drawers work, and I am not sure there is a good solution to the problem that does not require Odin to be referenced by the PropertyDrawer in question, in which case it would likely be better to write an OdinDrawer instead.

    In short, any value in an object which Odin displays and which is not serialized by Unity has no associated SerializedProperty at all. Sadly, SerializedProperties are needed for Unity's PropertyDrawers to work. Odin tries to backwards compatibility nevertheless, such that PropertyDrawers written for Unity's very limited system will still work when drawing data that Unity is not serializing.

    For this purpose, Odin emits custom ScriptableObject and Component types to order and creates dummy instances of these in order to get a SerializedProperty which can be drawn by a PropertyDrawer. This dummy object is obviously not the true containing asset, though, nor can it be.

    We have considered a few different approaches to help with this, but have never committed to a decision, as there seem to be no great solutions. Our current best bet is to provide a member on the emitted SO/Component which contains a reference to the containing target Unity object.

    In such a case, the PropertyDrawer in question can have some Odin-specific code wrapped in #if ODIN_INSPECTOR, which gets the containing object out of the dummy object. Alternatively, the PropertyDrawer could use reflection more robustly fetch this value if the target happens to be emitted by Odin.

    The approach does however have the weakness that the PropertyDrawer needs to know about Odin and adapt to use it. We do not currently see a way around that, though.

  2. Patrick Küster reporter

    Wow - that was a quick response…
    yeah - I feared as much - that’s one reason why I personally loathe ScriptableObjects as much as my daughter loathes broccoli … 😆
    I guess my best bet would be using an OdinDrawer. This project is using Odin Inspector all over the place, anyway.
    Will the OdinPropertyDrawer set the ScriptableObject dirty if I assign int locaId to its this.ValueEntry.SmartValue?

  3. Tor Esa Vestergaard

    Will the OdinPropertyDrawer set the ScriptableObject dirty if I assign int locaId to its this.ValueEntry.SmartValue?

    It will, yes. Dirtying is handled automatically in almost all cases, so long as you use SmartValue and do not set members directly. If you do set members manually, all you need to do is call Property.RecordForUndo() just before you change said members (but not every frame!), and both dirtying and Undo support will be handled.

  4. Patrick Küster reporter

    It worked like a charm.
    I spent almost a day trying to figure out how it could be done within Unity → to no avail.
    The rework into an OdinAttributeDrawer was done in under half an hour …

    I appreciate your support, especially your response time is outstanding!
    Thanks for your help (next AttributeDrawer I’ve to write will be an OdinAttributeDrawer right from the beginning)

  5. Log in to comment