Search Unity

  1. Unity 2019.2 is now released.
    Dismiss Notice

AssetReferenceDrawer does not dirty the SerialziedProperty

Discussion in 'Addressables' started by MakinStuffLookGood, Oct 14, 2018.

  1. MakinStuffLookGood

    MakinStuffLookGood

    Joined:
    Nov 20, 2012
    Posts:
    14
    Whether you assign using drag and drop from the Addressables window or you use the inline popup, it seems the custom property drawer does not draw does not properly dirty the object or save it's serialized value. This results in a lot of goose chasing thinking that some other part of addressables has stop working when it's actually just your AssetReference is empty.

    Looking at the drawer's code, it doesn't seem to use SerializedObject.ApplyModifiedProperties() at all, and indeed does things that Unity cautions against like editing object references directly instead of always going through SerializedProperty/Object.

    I've tried a few things to get things serializing correctly, but have only managed to get the drag and drop working properly— the popup is a lot to grok. Anyone else run into this/have a local fix?
     
  2. MakinStuffLookGood

    MakinStuffLookGood

    Joined:
    Nov 20, 2012
    Posts:
    14
    Figured out the other thing that was making things very confusing in all this: the popup contains objects in the Editor Built In group, but entries in that group are not found using
    AddressableAssetSettings.FindAssetEntry()
    . These objects would not show the icon/label correctly when selected but they would change the underlying GUID (which due to the serialization bug will only update if you dirty the object/scene some other way).

    In the end, I was able to get things working as expected with a bit of hackery. I added a
    finally
    block to
    SetObject
    that just set the serialized property Guid to match and does an
    ApplyModifiedProperites
    call. I got the popup to just call this method instead of doing whatever the hell it was doing setting a public string on the drawer.

    I also just obliterated the
    set
    accessor for
    AssetReference.editorAsset
    . There were just too many things trying to set the backing Guid and I bet it was causing confusion for any engineer at Unity trying to make this editor work nicely.
     
  3. 5argon

    5argon

    Joined:
    Jun 10, 2013
    Posts:
    1,265
    It also has a problem that it does not understand any sub-asset generated by dragging a folder into the Addressables panel. When checking with Debug mode of Inspector it shows the GUID just fine but in normal mode it draws "None (AddressableAsset)". Also AddressableAssetSettings.FindAssetEntry() this method cannot find the sub-assets so that asset will always ended up being dragged out of whatever group it is currently in to the default group.

    And then the latest v0.4.2 will draw nothing at all if it is more than 1 level deep. (e.g. AssetReference in a class, which is a member of ScriptableObject that is being drawn)

    My current fix is to change AssetReferenceDrawer.cs's OnGUI to a simpler one for the time being.

    Code (CSharp):
    1. public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    2. {
    3.     EditorGUI.BeginProperty(position, label, property);
    4.     var guidProp = property.FindPropertyRelative("m_assetGUID");
    5.     var cachedProp = property.FindPropertyRelative("m_cachedAsset");
    6.     position.width *= 3.5f / 4f;
    7.     EditorGUI.BeginChangeCheck();
    8.     EditorGUI.PropertyField(position, cachedProp, label);
    9.     if (EditorGUI.EndChangeCheck())
    10.     {
    11.         var foundGUID = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(cachedProp.objectReferenceValue));
    12.         guidProp.stringValue = foundGUID;
    13.     }
    14.     position.x += position.width;
    15.     position.width /= 3.5f / 4f;
    16.     position.width *= 0.5f / 4f;
    17.     EditorGUI.BeginDisabledGroup(true);
    18.     EditorGUI.PropertyField(position, guidProp, new GUIContent());
    19.     EditorGUI.EndDisabledGroup();
    20.     EditorGUI.EndProperty();
    21. }
    Drag an asset in that box and its guid value will be serialized. It does not make any contact with the Addressables system catalog.

    Screenshot 2018-10-15 01.41.27.png
     
    Last edited: Oct 14, 2018
    MakinStuffLookGood and esg_evan like this.
  4. Rotary-Heart

    Rotary-Heart

    Joined:
    Dec 18, 2012
    Posts:
    481
    I managed to almost replicate the current editor that Unity has, it has the same dropdown that only shows addressables on it. No drag and drop to add addressables, but at least this way you are sure to only select the correct items.

    Code (CSharp):
    1. public string propPath;
    2. public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    3. {
    4.    var guidProp = property.FindPropertyRelative("m_assetGUID");
    5.    var cachedAsset = property.FindPropertyRelative("m_cachedAsset");
    6.  
    7.    string guid = guidProp.stringValue;
    8.    EditorGUI.BeginProperty(position, label, property);
    9.    string path = AssetDatabase.GUIDToAssetPath(guid);
    10.    string fileName = System.IO.Path.GetFileNameWithoutExtension(path);
    11.  
    12.    string name = cachedAsset.objectReferenceValue == null ? k_noAssetString : fileName;
    13.    smallPos = EditorGUI.PrefixLabel(position, label);
    14.  
    15.    if (EditorGUI.DropdownButton(smallPos, new GUIContent(name, AssetDatabase.GetCachedIcon(path)), FocusType.Keyboard))
    16.    {
    17.        propPath = property.propertyPath;
    18.        PopupWindow.Show(smallPos, new AssetReferencePopup(this));
    19.    }
    20.  
    21.    if (!string.IsNullOrEmpty(newGuid) && propPath.Equals(property.propertyPath))
    22.    {
    23.        propPath = null;
    24.        guidProp.stringValue = newGuid;
    25.        cachedAsset.objectReferenceValue = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(AssetDatabase.GUIDToAssetPath(newGuid));
    26.        newGuid = null;
    27.    }
    28.  
    29.    var aaSettings = AddressableAssetSettingsDefaultObject.Settings;
    30.  
    31.    if (position.Contains(Event.current.mousePosition))
    32.    {
    33.        switch (Event.current.type)
    34.        {
    35.            case EventType.DragUpdated:
    36.                {
    37.                    bool rejected = false;
    38.                    if (typeFilter != null)
    39.                    {
    40.                        UnityEngine.Object obj = null;
    41.                        var aaEntries = DragAndDrop.GetGenericData("AssetEntryTreeViewItem") as List<AssetEntryTreeViewItem>;
    42.                        if (aaEntries != null)
    43.                        {
    44.                            if (aaEntries.Count != 1)
    45.                                rejected = true;
    46.                            if (rejected && !typeFilter.Validate(AssetDatabase.GetMainAssetTypeAtPath(aaEntries[0].entry.AssetPath)))
    47.                                rejected = true;
    48.                        }
    49.                        else
    50.                        {
    51.                            if (DragAndDrop.objectReferences != null && DragAndDrop.objectReferences.Length == 1)
    52.                                obj = DragAndDrop.objectReferences[0];
    53.                            if (obj == null)
    54.                                rejected = true;
    55.  
    56.                            if (!rejected && !typeFilter.Validate(obj.GetType()))
    57.                                rejected = true;
    58.                        }
    59.                    }
    60.  
    61.                    if (!rejected && labelFilter != null)
    62.                    {
    63.                        if (aaSettings == null)
    64.                            rejected = true;
    65.                        else
    66.                        {
    67.                            var aaEntries = DragAndDrop.GetGenericData("AssetEntryTreeViewItem") as List<AssetEntryTreeViewItem>;
    68.                            if (aaEntries != null)
    69.                            {
    70.                                if (aaEntries.Count != 1)
    71.                                    rejected = true;
    72.                                if (rejected && !labelFilter.Validate(aaEntries[0].entry.labels))
    73.                                    rejected = true;
    74.                            }
    75.                            else
    76.                            {
    77.                                if (DragAndDrop.paths.Length == 1)
    78.                                {
    79.                                    var entry = aaSettings.FindAssetEntry(AssetDatabase.AssetPathToGUID(DragAndDrop.paths[0]));
    80.                                    //for now, do not allow creation of new AssetEntries when there is a label filter on the property.
    81.                                    //This could be changed in the future to be configurable via the attribute if desired (allowEntryCreate = true)
    82.                                    if (entry == null)
    83.                                        rejected = true;
    84.                                    if (!rejected && !labelFilter.Validate(entry.labels))
    85.                                        rejected = true;
    86.                                }
    87.                                else
    88.                                {
    89.                                    rejected = true;
    90.                                }
    91.                            }
    92.                        }
    93.                    }
    94.  
    95.                    DragAndDrop.visualMode = rejected ? DragAndDropVisualMode.Rejected : DragAndDropVisualMode.Copy;
    96.                }
    97.                break;
    98.  
    99.            case EventType.DragPerform:
    100.                {
    101.                    var aaEntries = DragAndDrop.GetGenericData("AssetEntryTreeViewItem") as List<AssetEntryTreeViewItem>;
    102.                    if (aaEntries != null)
    103.                    {
    104.                        if (aaEntries.Count == 1)
    105.                        {
    106.                            var item = aaEntries[0];
    107.                            if (item.entry != null)
    108.                            {
    109.                                if (item.entry.IsInResources)
    110.                                    Debug.LogWarning("Cannot use an AssetReference on an asset in Resources. Move asset out of Resources first.");
    111.                                else
    112.                                    SetObject(property, AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(item.entry.AssetPath), out guid);
    113.                            }
    114.                        }
    115.                    }
    116.                    else
    117.                    {
    118.                        if (DragAndDrop.paths != null && DragAndDrop.paths.Length == 1)
    119.                        {
    120.                            var dragPath = DragAndDrop.paths[0];
    121.                            if (AddressableAssetUtility.IsInResources(dragPath))
    122.                                Debug.LogWarning("Cannot use an AssetReference on an asset in Resources. Move asset out of Resources first.");
    123.                            else
    124.                            {
    125.                                UnityEngine.Object obj = null;
    126.                                if (DragAndDrop.objectReferences != null && DragAndDrop.objectReferences.Length == 1)
    127.                                    obj = DragAndDrop.objectReferences[0];
    128.                                else
    129.                                    obj = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(dragPath);
    130.  
    131.                                guid = AssetDatabase.AssetPathToGUID(dragPath);
    132.  
    133.                                aaSettings = AddressableAssetSettingsDefaultObject.GetSettings(true);
    134.                                var entry = aaSettings.FindAssetEntry(guid);
    135.  
    136.                                if (entry == null && !string.IsNullOrEmpty(guid))
    137.                                {
    138.                                    entry = aaSettings.CreateOrMoveEntry(guid, aaSettings.DefaultGroup);
    139.                                    Addressables.LogFormat("Created AddressableAsset {0} in group {1}.", entry.address, aaSettings.DefaultGroup.Name);
    140.                                }
    141.  
    142.                                property.serializedObject.FindProperty(property.propertyPath).FindPropertyRelative("m_cachedAsset").objectReferenceValue = obj;
    143.                                propPath = property.propertyPath;
    144.                                newGuid = guid;
    145.                            }
    146.                        }
    147.                    }
    148.                }
    149.                break;
    150.        }
    151.    }
    152.    EditorGUI.EndProperty();
    153. }
    Capture.PNG
    Capture.PNG
    Edit: The new code above provides full functionality, including the drag and drop.
     
    Last edited: Nov 3, 2018
    5argon likes this.
  5. unity_bill

    unity_bill

    Unity Technologies

    Joined:
    Apr 11, 2017
    Posts:
    954
    Thanks for all the input. We have several known bugs surrounding this drawer. Some of what was mentioned above is fixed in 0.4.6, but not all of it. We'll definitely reference this as we continue to fix bugs. Longer term we hope to eventually have the full code base in a public repo so you guys can just submit code fixes directly as PR's.

    -Bill
     
  6. Rotary-Heart

    Rotary-Heart

    Joined:
    Dec 18, 2012
    Posts:
    481
    I modified the code so that it works with an array of Addressables, had to use a small hack to make it work, but it will do its job while we wait for the proper inspector. Open AssetReferenceDrawer.cs and replace the OnGUI with the code below

    Code (CSharp):
    1. public string propPath;
    2. public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    3. {
    4.     var guidProp = property.FindPropertyRelative("m_assetGUID");
    5.     var cachedAsset = property.FindPropertyRelative("m_cachedAsset");
    6.  
    7.     string guid = guidProp.stringValue;
    8.     EditorGUI.BeginProperty(position, label, property);
    9.     string path = AssetDatabase.GUIDToAssetPath(guid);
    10.     string fileName = System.IO.Path.GetFileNameWithoutExtension(path);
    11.  
    12.     string name = cachedAsset.objectReferenceValue == null ? k_noAssetString : fileName;
    13.     smallPos = EditorGUI.PrefixLabel(position, label);
    14.  
    15.     if (EditorGUI.DropdownButton(smallPos, new GUIContent(name, AssetDatabase.GetCachedIcon(path)), FocusType.Keyboard))
    16.     {
    17.         propPath = property.propertyPath;
    18.         PopupWindow.Show(smallPos, new AssetReferencePopup(this));
    19.     }
    20.  
    21.     if (!string.IsNullOrEmpty(newGuid) && propPath.Equals(property.propertyPath))
    22.     {
    23.         propPath = null;
    24.         guidProp.stringValue = newGuid;
    25.         cachedAsset.objectReferenceValue = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(AssetDatabase.GUIDToAssetPath(newGuid));
    26.         newGuid = null;
    27.     }
    28.  
    29.     var aaSettings = AddressableAssetSettingsDefaultObject.Settings;
    30.  
    31.     if (position.Contains(Event.current.mousePosition))
    32.     {
    33.         switch (Event.current.type)
    34.         {
    35.             case EventType.DragUpdated:
    36.                 {
    37.                     bool rejected = false;
    38.                     if (typeFilter != null)
    39.                     {
    40.                         UnityEngine.Object obj = null;
    41.                         var aaEntries = DragAndDrop.GetGenericData("AssetEntryTreeViewItem") as List<AssetEntryTreeViewItem>;
    42.                         if (aaEntries != null)
    43.                         {
    44.                             if (aaEntries.Count != 1)
    45.                                 rejected = true;
    46.                             if (rejected && !typeFilter.Validate(AssetDatabase.GetMainAssetTypeAtPath(aaEntries[0].entry.AssetPath)))
    47.                                 rejected = true;
    48.                         }
    49.                         else
    50.                         {
    51.                             if (DragAndDrop.objectReferences != null && DragAndDrop.objectReferences.Length == 1)
    52.                                 obj = DragAndDrop.objectReferences[0];
    53.                             if (obj == null)
    54.                                 rejected = true;
    55.  
    56.                             if (!rejected && !typeFilter.Validate(obj.GetType()))
    57.                                 rejected = true;
    58.                         }
    59.                     }
    60.  
    61.                     if (!rejected && labelFilter != null)
    62.                     {
    63.                         if (aaSettings == null)
    64.                             rejected = true;
    65.                         else
    66.                         {
    67.                             var aaEntries = DragAndDrop.GetGenericData("AssetEntryTreeViewItem") as List<AssetEntryTreeViewItem>;
    68.                             if (aaEntries != null)
    69.                             {
    70.                                 if (aaEntries.Count != 1)
    71.                                     rejected = true;
    72.                                 if (rejected && !labelFilter.Validate(aaEntries[0].entry.labels))
    73.                                     rejected = true;
    74.                             }
    75.                             else
    76.                             {
    77.                                 if (DragAndDrop.paths.Length == 1)
    78.                                 {
    79.                                     var entry = aaSettings.FindAssetEntry(AssetDatabase.AssetPathToGUID(DragAndDrop.paths[0]));
    80.                                     //for now, do not allow creation of new AssetEntries when there is a label filter on the property.
    81.                                     //This could be changed in the future to be configurable via the attribute if desired (allowEntryCreate = true)
    82.                                     if (entry == null)
    83.                                         rejected = true;
    84.                                     if (!rejected && !labelFilter.Validate(entry.labels))
    85.                                         rejected = true;
    86.                                 }
    87.                                 else
    88.                                 {
    89.                                     rejected = true;
    90.                                 }
    91.                             }
    92.                         }
    93.                     }
    94.  
    95.                     DragAndDrop.visualMode = rejected ? DragAndDropVisualMode.Rejected : DragAndDropVisualMode.Copy;
    96.                 }
    97.                 break;
    98.  
    99.             case EventType.DragPerform:
    100.                 {
    101.                     var aaEntries = DragAndDrop.GetGenericData("AssetEntryTreeViewItem") as List<AssetEntryTreeViewItem>;
    102.                     if (aaEntries != null)
    103.                     {
    104.                         if (aaEntries.Count == 1)
    105.                         {
    106.                             var item = aaEntries[0];
    107.                             if (item.entry != null)
    108.                             {
    109.                                 if (item.entry.IsInResources)
    110.                                     Debug.LogWarning("Cannot use an AssetReference on an asset in Resources. Move asset out of Resources first.");
    111.                                 else
    112.                                     SetObject(property, AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(item.entry.AssetPath), out guid);
    113.                             }
    114.                         }
    115.                     }
    116.                     else
    117.                     {
    118.                         if (DragAndDrop.paths != null && DragAndDrop.paths.Length == 1)
    119.                         {
    120.                             var dragPath = DragAndDrop.paths[0];
    121.                             if (AddressableAssetUtility.IsInResources(dragPath))
    122.                                 Debug.LogWarning("Cannot use an AssetReference on an asset in Resources. Move asset out of Resources first.");
    123.                             else
    124.                             {
    125.                                 UnityEngine.Object obj = null;
    126.                                 if (DragAndDrop.objectReferences != null && DragAndDrop.objectReferences.Length == 1)
    127.                                     obj = DragAndDrop.objectReferences[0];
    128.                                 else
    129.                                     obj = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(dragPath);
    130.  
    131.                                 guid = AssetDatabase.AssetPathToGUID(dragPath);
    132.  
    133.                                 aaSettings = AddressableAssetSettingsDefaultObject.GetSettings(true);
    134.                                 var entry = aaSettings.FindAssetEntry(guid);
    135.  
    136.                                 if (entry == null && !string.IsNullOrEmpty(guid))
    137.                                 {
    138.                                     entry = aaSettings.CreateOrMoveEntry(guid, aaSettings.DefaultGroup);
    139.                                     Addressables.LogFormat("Created AddressableAsset {0} in group {1}.", entry.address, aaSettings.DefaultGroup.Name);
    140.                                 }
    141.  
    142.                                 property.serializedObject.FindProperty(property.propertyPath).FindPropertyRelative("m_cachedAsset").objectReferenceValue = obj;
    143.                                 propPath = property.propertyPath;
    144.                                 newGuid = guid;
    145.                             }
    146.                         }
    147.                     }
    148.                 }
    149.                 break;
    150.         }
    151.     }
    152.     EditorGUI.EndProperty();
    153. }
     
  7. Rotary-Heart

    Rotary-Heart

    Joined:
    Dec 18, 2012
    Posts:
    481
  8. unity_bill

    unity_bill

    Unity Technologies

    Joined:
    Apr 11, 2017
    Posts:
    954
    This should all be fixed in the next release (1.0.x) as well...
    - proper drawer behavior even in nested lists/classes/arrays
    - proper filtering on dropdown and drag&drop,
    - filtering using AssetReferenceT<> or the attributes (only Label attribute will remain, but it's setup easy user extension).
     
    MNNoxMortem, Rotary-Heart and 5argon like this.
  9. Deleted User

    Deleted User

    Guest

    What is the ETA on 1.0?
     
  10. unity_bill

    unity_bill

    Unity Technologies

    Joined:
    Apr 11, 2017
    Posts:
    954
    by mid Feb.
     
    Tuitive, joshcamas, EirikWahl and 4 others like this.
  11. Prodigga

    Prodigga

    Joined:
    Apr 13, 2011
    Posts:
    777
    Any news on this? :)
     
    Tuitive likes this.
  12. Dawar

    Dawar

    Joined:
    Dec 7, 2014
    Posts:
    122
    it Give me error
    /Library/Unity/cache/packages/packages.unity.com/com.unity.addressables@0.5.3-preview/Editor/GUI/AssetReferenceDrawer.cs(257,96): error CS1061: Type `System.Type' does not contain a definition for `GenericTypeArguments' and no extension method `GenericTypeArguments' of type `System.Type' could be found. Are you missing an assembly reference?
     
  13. Rotary-Heart

    Rotary-Heart

    Joined:
    Dec 18, 2012
    Posts:
    481
    How did you setup your code for the AssetReference? when did you get that error?
     
    Dawar likes this.
  14. Dawar

    Dawar

    Joined:
    Dec 7, 2014
    Posts:
    122
    i copy and paste assetreferencedrawer.cs script to Packages/Addressables System/Editor/GUI/AssetReferenceDrawer.cs
    Code (CSharp):
    1.  
    2. [CreateAssetMenu(menuName = "Sniper/Mission AI Data")]
    3. public class MissionAIData : ScriptableObject {
    4.  
    5.     public AssetReferenceGameObject Asset;
    6.     public AIData[] AIData;
    7. }
    8.  
    9. [Serializable]
    10. public class AIData
    11. {
    12.     [SerializeField]
    13.     public AssetReferenceGameObject uu;
    14.  
    15.  
    16. }
     
    Last edited: Feb 23, 2019
  15. Rotary-Heart

    Rotary-Heart

    Joined:
    Dec 18, 2012
    Posts:
    481
    It's working fine here. Perhaps is an issue with your unity version, scripting runtime version or .net version? Which ones are you using?

    GIF.gif
     
    Dawar likes this.
  16. Dawar

    Dawar

    Joined:
    Dec 7, 2014
    Posts:
    122
    i am using unity2018.2.8f1, android and .Net 3.5
    i also change .net to 4.0 but not working
     
    Last edited: Feb 23, 2019
  17. Dawar

    Dawar

    Joined:
    Dec 7, 2014
    Posts:
    122
    now its working I just change running script version to 4.0
    thanks you so much @
    Rotary-Heart
     
    Rotary-Heart likes this.
  18. Rotary-Heart

    Rotary-Heart

    Joined:
    Dec 18, 2012
    Posts:
    481
    I see, well at least now it's fixed. Good to know about that.
     
  19. Rotary-Heart

    Rotary-Heart

    Joined:
    Dec 18, 2012
    Posts:
    481
    @unity_bill The issue is still not 100% fixed. Here's a code that shows it not working on 1 more case. Using 2018.3.8 and Addressables 0.6.7

    Code (CSharp):
    1. [System.Serializable]
    2. public class Test : TestT<int>
    3. {
    4.  
    5. }
    6.  
    7. [System.Serializable]
    8. public class TestT<T>
    9. {
    10.     [SerializeField]
    11.     Music music;
    12.     [SerializeField]
    13.     int integer;
    14. }
    15.  
    16. [System.Serializable]
    17. public class Music
    18. {
    19.     [System.Serializable]
    20.     public class MusicAssetReference : AssetReferenceT<AudioClip> { }
    21.  
    22.     public MusicAssetReference clips;
    23. }
    24.  
    25. [SerializeField]
    26. Music testSimple;
    27. [SerializeField]
    28. Music[] testSimpleArray;
    29. [SerializeField]
    30. Test test;
    Capture.PNG
     
  20. unity_bill

    unity_bill

    Unity Technologies

    Joined:
    Apr 11, 2017
    Posts:
    954
    Thanks for the clear repro-code. I've added a ticket to our system.

    -Bill
     
    MNNoxMortem and Rotary-Heart like this.
  21. Rotary-Heart

    Rotary-Heart

    Joined:
    Dec 18, 2012
    Posts:
    481
    Is this issue fixed on the 0.7.4-preview version?

     
  22. unity_bill

    unity_bill

    Unity Technologies

    Joined:
    Apr 11, 2017
    Posts:
    954
    we think so. but please let us know if there's a case missed.
     
    Rotary-Heart likes this.
  23. Rotary-Heart

    Rotary-Heart

    Joined:
    Dec 18, 2012
    Posts:
    481
    I can confirm that is fixed. Haven't seen any other issue with it.
     
    5argon likes this.