Search Unity

PropertyDrawer changes all lists items instead of only one... why does this happen?

Discussion in 'Immediate Mode GUI (IMGUI)' started by genaray, Dec 19, 2018.

  1. genaray

    genaray

    Joined:
    Feb 8, 2017
    Posts:
    191
    Im not that familiar with the editor extensions aswell as property drawers at all... im currently trying to rework an old asset and i noticed that once i change one entry in a list the property drawer changes all of them for some reason...

    I have no idea why that happens... and i could need everyhelp i can get ! I attached a video at the bottom of the post ! It represents the issue :)

    Code (CSharp):
    1. [CustomPropertyDrawer(typeof(ScriptReference_Base), true)]
    2.     public class ScriptReferenceDrawer : PropertyDrawer
    3.     {
    4.         private static bool monoScriptsCached;
    5.         private static Dictionary<Type, MonoScript> typeToScript;
    6.  
    7.         private bool scriptreferenceCached;
    8.         private ScriptReference_Base scriptReference;
    9.         private SerializableSystemType script;
    10.         private MonoScript monoScript;
    11.         private Type requiredReferenceType;
    12.  
    13.         public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    14.         {
    15.             if (!monoScriptsCached)
    16.             {
    17.                 CacheMonoScripts();
    18.             }
    19.  
    20.             if (!scriptreferenceCached)
    21.             {
    22.                 CacheScriptReferenceData(property);
    23.             }
    24.  
    25.             EditorGUI.BeginChangeCheck();
    26.  
    27.             var oldMonoScript = monoScript;
    28.             monoScript = (MonoScript) EditorGUI.ObjectField(position, label, monoScript, typeof(MonoScript), false);
    29.  
    30.             if (EditorGUI.EndChangeCheck())
    31.             {
    32.                 var systemType = monoScript == null ? null : monoScript.GetClass();
    33.  
    34.                 if (IsValidScriptType(systemType, monoScript))
    35.                 {
    36.                     scriptReference.script = systemType == null ? null : new SerializableSystemType(systemType);
    37.  
    38.                     var targetObj = property.serializedObject.targetObject;
    39.                     EditorUtility.SetDirty(targetObj);
    40.  
    41.                     var asGameObj = targetObj as GameObject;
    42.                     if (asGameObj != null)
    43.                         EditorSceneManager.MarkSceneDirty(asGameObj.scene);
    44.                 }
    45.                 else
    46.                 {
    47.                     monoScript = oldMonoScript;
    48.                 }
    49.             }
    50.         }
    51.  
    52.         private bool IsValidScriptType(Type attemptedType, MonoScript attemptedScript)
    53.         {
    54.             if (attemptedType == null)
    55.             {
    56.                 //user selected "None" when clicking the nipple.
    57.                 if (attemptedScript == null)
    58.                     return true;
    59.                
    60.                 Debug.LogWarning("Unity can't find the type inside the script " + attemptedScript.name + "! Can't assign it!");
    61.                 return false;
    62.             }
    63.  
    64.             if (!requiredReferenceType.IsAssignableFrom(attemptedType))
    65.             {
    66.                 Debug.LogWarningFormat("Cannot assign script {0} (containing the type {1}) to a {2}! The type of the script must derive from {3}!",
    67.                                        attemptedScript.name, attemptedType, scriptReference.GetType().Name, requiredReferenceType.Name);
    68.                 return false;
    69.             }
    70.  
    71.             if (script == null)
    72.                 return true;
    73.  
    74.             if (attemptedType != script.SystemType)
    75.                 return true;
    76.  
    77.             return false;
    78.         }
    79.  
    80.         private static void CacheMonoScripts()
    81.         {
    82.             typeToScript = new Dictionary<Type, MonoScript>();
    83.             var monoScripts = AssetDatabase.FindAssets("t:script")
    84.                                            .Select(guid => AssetDatabase.LoadAssetAtPath<MonoScript>(AssetDatabase.GUIDToAssetPath(guid)));
    85.             foreach (var monoScript in monoScripts)
    86.             {
    87.  
    88.                 var type = monoScript.GetClass();
    89.                 //Null for some types Unity can't handle for unknown reasons (like ScriptReference!)
    90.                 if (type != null)
    91.                 {
    92.                     typeToScript[type] = monoScript;
    93.                 }
    94.             }
    95.  
    96.             monoScriptsCached = true;
    97.         }
    98.  
    99.         private void CacheScriptReferenceData(SerializedProperty property)
    100.         {
    101.             scriptReference = (ScriptReference_Base) SerializedPropertyHelper.GetTargetObjectOfProperty(property);
    102.  
    103.             requiredReferenceType = GetGenericScriptRestriction(scriptReference.GetType());
    104.  
    105.             script = scriptReference.script;
    106.             monoScript = null;
    107.             if (script != null && script.SystemType != null)
    108.             {
    109.                 typeToScript.TryGetValue(script.SystemType, out monoScript);
    110.             }
    111.  
    112.             scriptreferenceCached = true;
    113.         }
    114.  
    115.         private Type GetGenericScriptRestriction(Type subclassOfScriptReference)
    116.         {
    117.             var current = subclassOfScriptReference;
    118.  
    119.             var scriptReferenceType = typeof(ScriptReference<>);
    120.             while (current != null)
    121.             {
    122.                 if (!current.IsGenericType)
    123.                 {
    124.                     current = current.BaseType;
    125.                     continue;
    126.                 }
    127.  
    128.                 var genericDef = current.GetGenericTypeDefinition();
    129.                 if (genericDef == scriptReferenceType)
    130.                 {
    131.                     return current.GetGenericArguments()[0];
    132.                 }
    133.  
    134.                 current = current.BaseType;
    135.             }
    136.  
    137.             return null;
    138.         }
    139.     }
     
  2. Madgvox

    Madgvox

    Joined:
    Apr 13, 2014
    Posts:
    1,317
    PropertyDrawers are cached, so it's using the same instance of ScriptReferenceDrawer for both fields. As such, you can't keep field-specific data on the class. You will have to grab the value from the property every time.
     
    karl_jones likes this.