Search Unity

  1. Unity 2018.3 is now released.
    Dismiss Notice
  2. The Unity Pro & Visual Studio Professional Bundle gives you the tools you need to develop faster & collaborate more efficiently. Learn more.
    Dismiss Notice
  3. We've updated our Terms of Service. Please read our blog post from Unity CTO and Co-Founder Joachim Ante here
    Dismiss Notice
  4. Want to provide direct feedback to the Unity team? Join the Unity Advisory Panel.
    Dismiss Notice
  5. Improve your Unity skills with a certified instructor in a private, interactive classroom. Watch the overview now.
    Dismiss Notice

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

Discussion in 'Extensions & OnGUI' started by genaray, Dec 19, 2018.

  1. genaray

    genaray

    Joined:
    Feb 8, 2017
    Posts:
    131
    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:
    497
    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.