简体   繁体   中英

How can I check if a scriptable object dragged onto inspector is implementing an interface or not?

for my unity project I'm trying to make a custom property drawer in order to make inspector accept objects that are only implementing the interface I pass via attribute parameter. I am glad that I achived the task satiably for MonoBehaviour objects. However I'm having trouble in applying a similar approach for scriptable objects. There I need help.

Here is my property drawer code. The Problem is C# doesnt let me do a type-check with "is" keyword. Here are few codes I tried

 if(temp_SO is requiredAttribute.requiredType) {}
 if(temp_SO is T) {}
 if(temp_SO.GetType().Equals(requiredAttribute.requiredType) {}

but all fails... However as seen in the code below, if I hardcode it like if(temp_SO is interface_C) then it works. I couldn't find a way to pass the type value dynamically. Because IDE keeps throwing "a constant value is expected" error. Any help will be appreciated. Thanks..

 [CustomPropertyDrawer(typeof(SOInterfaceExposeAttribute))]
    public class SOInterfaceExposeAttributeDrawer : PropertyDrawer
    {
        Type T;
        public override void OnGUI ( Rect position , SerializedProperty property , GUIContent label )
        {
            if ( property . propertyType == SerializedPropertyType . ObjectReference )
            {
                var requiredAttribute = this . attribute as SOInterfaceExposeAttribute;
    
                T = requiredAttribute . requiredType;
            
                var lastValidRef = property . objectReferenceValue;
    
                EditorGUI . BeginProperty(position , label , property);
    
                ScriptableObject temp_SO = EditorGUI . ObjectField(position , label , property . objectReferenceValue , typeof(ScriptableObject) , true) as ScriptableObject;
    
                // Finish drawing property field.
                if ( temp_SO != null )
                {
                    if ( temp_SO is Interface_C)
                    {
                        Debug . Log("interface component found");
                        property . objectReferenceValue = temp_SO as ScriptableObject;
                    }
                    else
                    {
                        Debug . Log("interface component NOT found");
                        property . objectReferenceValue = lastValidRef;
                    }
                }
    
                EditorGUI . EndProperty();
            }
            else
            {
                // If field is not reference, show error message.
                // Save previous color and change GUI to red.
                var previousColor = GUI . color;
                GUI . color = Color . red;
                // Display label with error message.
                EditorGUI . LabelField(position , label , new GUIContent("Property is not a reference type"));
                // Revert color change.
                GUI . color = previousColor;
            }
        }
    }

is indeed requires a constant type parameter.

The other way you tried

if(temp_SO.GetType().Equals(requiredAttribute.requiredType)

would not work since these two types are definitely not equal since the one implements the other;)


You could probably use IsInstanceOfType

var temp_SO = EditorGUI.ObjectField(position, label, property.objectReferenceValue, typeof(ScriptableObject), true);

// Finish drawing property field.
if (temp_SO != null)
{
    if (requiredAttribute.requiredType.IsInstanceOfType(temp_SO))
    {
        Debug.Log("interface component found");
        property.objectReferenceValue = temp_SO;
    }
    else
    {
        Debug.Log("interface component NOT found");
        property.objectReferenceValue = lastValidRef;
    }
}

or IsAssignableFrom like

var temp_SO = EditorGUI.ObjectField(position, label, property.objectReferenceValue, typeof(ScriptableObject), true);

// Finish drawing property field.
if (temp_SO != null)
{
    if (requiredAttribute.requiredType.IsAssignableFrom(temp_SO.GetType()))
    {
        Debug.Log("interface component found");
        property.objectReferenceValue = temp_SO;
    }
    else
    {
        Debug.Log("interface component NOT found");
        property.objectReferenceValue = lastValidRef;
    }
}

Another way could be using FindInterfaces in order to compare if any inherited type implements the interface type

var temp_SO = EditorGUI.ObjectField(position, label, property.objectReferenceValue, typeof(ScriptableObject), true);

// Finish drawing property field.
if (temp_SO != null)
{
    // Filter taken from the linked examples
    if (temp_SO.GetType().FindInterfaces(new TypeFilter((type, criteria) => type.ToString() == criteria.ToString()), requiredAttribute.requiredType.Name()).Length > 0)
    {
        Debug.Log("interface component found");
        property.objectReferenceValue = temp_SO;
    }
    else
    {
        Debug.Log("interface component NOT found");
        property.objectReferenceValue = lastValidRef;
    }
}

Btw you could make this more efficient by not constantly checking this but rather only when the reference was changed wrapping it within

EditorGUI.BeginChangeCheck(); 
var temp_SO = EditorGUI.ObjectField(position, label, property.objectReferenceValue, typeof(ScriptableObject), true); 
if(EditorGUI.EndChangeCheck()) 
{
    ...
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM