When I use property.Next instead, it works. BUT, this results in iterating over a bunch of extra properties like “m_CorrespondingSourceObject”, “m_Father” and “m_PrefabAsset” (these are Unity’s own native properties).
Is there a way to iterate over the SerializedPropery objects returned by property.NextVisible AND the SerializedProperty objects corresponding to [HideInInspector] fields but NOT the SerializedProperty objects for “m_Father” like native properties?
In the end, I had to use reflection as you’ve suggested and it worked. In my benchmarks, it was up to ~10 times slower but it should be fine as long as this function isn’t called thousands of times per second:
delegate FieldInfo FieldInfoGetter( SerializedProperty p, out Type t );
public static void IterateProperties( SerializedObject serializedObject )
{
#if UNITY_2019_3_OR_NEWER
MethodInfo fieldInfoGetterMethod = typeof( Editor ).Assembly.GetType( "UnityEditor.ScriptAttributeUtility" ).GetMethod( "GetFieldInfoAndStaticTypeFromProperty", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static );
#else
MethodInfo fieldInfoGetterMethod = typeof( Editor ).Assembly.GetType( "UnityEditor.ScriptAttributeUtility" ).GetMethod( "GetFieldInfoFromProperty", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static );
#endif
FieldInfoGetter fieldInfoGetter = (FieldInfoGetter) Delegate.CreateDelegate( typeof( FieldInfoGetter ), fieldInfoGetterMethod );
SerializedProperty propertyAll = serializedObject.GetIterator();
SerializedProperty propertyVisible = serializedObject.GetIterator();
if( propertyAll.Next( true ) )
{
bool iteratingVisible = propertyVisible.NextVisible( true );
do
{
bool isVisible = iteratingVisible && SerializedProperty.EqualContents( propertyAll, propertyVisible );
if( isVisible )
iteratingVisible = propertyVisible.NextVisible( false ); // Change it to true if you want to enter child properties, as well
else
{
Type propFieldType;
// Internal Unity variables don't seem to have a FieldInfo but when SerializedProperty.type is "Array", we must consider it
// visible to avoid false negatives because even though "Array" type doesn't have a FieldInfo, it can be a visible array property
isVisible = propertyAll.type == "Array" || fieldInfoGetter( propertyAll, out propFieldType ) != null;
}
if( isVisible )
Debug.Log( propertyAll.name );
} while( propertyAll.Next( false ) ); // Change it to true if you want to enter child properties, as well
}
}
But I’ll be calling this function thousands of times in a couple of my plugins, so I’ll be caching the HideInInspector properties for maximum performance.
My hats off to you! Well done. I think I would have been lazy and just lived with all the extra fields and ignored them. Good on you for making it the way you want it, and thanks for posting the code for all of us to enjoy.
Prefab objects’ iterator yields a bunch of other stuff that must be included in “excludes”, as well. If I remember correctly, one of them was named something like m_PrefabAsset. This can be a legit variable name in users’ codebases so I wouldn’t rely on variable name checking for this task.