Problem in assigning component to variable

There are two scripts A and B which extend C class and C extends MonoBehaviour and there is a G1 gameobject that has A and B scripts. And there is D script that has public C cVariable and is attached to G2 gameobject. In the inspector of the G2 I want to assign B component of G1 to cVariable of D script. But the A script is placed higher than B script and also can be assigned to cVariable. When I click on the little circle of the cVariable object field or drag my G1 gameobject to this field unity brings only A component. So how can I choose between A and B without moving up B script in the G1 inspector?

Of course you can do this in the editor. To me the question was quite clear. ^^

The trick is to use two inspector windows and lock one of them so you can actually inspect the two gameobjects at once. Now you can simply drag the component instance directly. I’ve made an animated GIF some time ago that should be more clear:

So you simply grab the components “header” and drag it to your variable. Ideally it would be nice if Unity would show some sort of popup / dropdown when you drop a gameobject that has multiple possible targets so you can choose one.

Of course it gets easier when the target script is on the same gameobject as you can directly drag the components. However if they are on different gameobjects you have to use two inspectors. In my gif example the variable type is actually “UnityEngine.Object” so we could assign any component, Mesh, Material or GameObject. So when just dragging a gameobject from the hierarchy it would assign the gameobject itself.

edit

Ok, big update

Since i said above that it would be awesome if Unity showed some sort of dropdown to select the object you want, i quickly implemented a PropertyDrawer for ObjectFields which does exactly that.

Note: When you use the attribute “ObjectDropdownAttribute” on a serialized reference type field (only types derived from UnityEngine.Object of course) you can now hold “SHIFT” while dropping an object on the field. This will trigger my custom dropdown logic. It will filter out all components / objects which are assignable to the field type.

Instead of the “ObjectDropdown” attribute you can also use the “ObjectDropdownFilter” attribute which allows you to specify an additional filter type on the field. This is useful for limiting components which should implement a certain interface.

First of all here’s the PropertyDrawer ([dropbox download][1]). This script need to be placed in an editor folder:

//ObjectSelectorDropdown.cs
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
using UnityEditor;

namespace B83.PropertyDrawers
{
    [CustomPropertyDrawer(typeof(ObjectDropdownAttribute))]
    public class ObjectSelectorDropdown : PropertyDrawer
    {
        List<Object> m_List = new List<Object>();
        public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
        {
            Event e = Event.current;
            if (property.propertyType == SerializedPropertyType.ObjectReference)
            {
                if ((e.type == EventType.DragPerform ||
                    e.type == EventType.DragExited ||
                    e.type == EventType.DragUpdated ||
                    e.type == EventType.Repaint) &&
                    position.Contains(e.mousePosition) && e.shift)
                {
                    if (DragAndDrop.objectReferences != null)
                    {
                        m_List.Clear();
                        foreach (var o in DragAndDrop.objectReferences)
                        {
                            m_List.Add(o);
                            var go = o as GameObject;
                            if (go == null && o is Component)
                            {
                                go = ((Component)o).gameObject;
                                m_List.Add(go);
                            }
                            if (go != null)
                                foreach (var c in go.GetComponents<Component>())
                                    if (c != o)
                                        m_List.Add(c);
                        }
                        var fieldInfo = property.GetPropertyReferenceType();
                        if (fieldInfo != null)
                        {
                            var type = fieldInfo.FieldType;
                            for (int i = m_List.Count - 1; i >= 0; i--)
                            {
                                if (m_List <em>== null || !type.IsAssignableFrom(m_List*.GetType()))*</em>

m_List.RemoveAt(i);
}
}
var att = attribute as ObjectDropdownFilterAttribute;
if (att != null)
{
var type = att.filterType;
for (int i = m_List.Count - 1; i >= 0; i–)
{
if (!type.IsAssignableFrom(m_List*.GetType()))
m_List.RemoveAt(i);
_}
}
if (m_List.Count == 0)
DragAndDrop.visualMode = DragAndDropVisualMode.Rejected;
else
{
DragAndDrop.visualMode = DragAndDropVisualMode.Link;
if (e.type == EventType.DragPerform)
{
GenericMenu gm = new GenericMenu();
GenericMenu.MenuFunction2 func = (o) => {
property.objectReferenceValue = (Object)o;
property.serializedObject.ApplyModifiedProperties();
};
foreach (var item in m_List)
gm.AddItem(new GUIContent(item.name+“(”+item.GetType().Name+“)”), false, func, item);
gm.ShowAsContext();
e.Use();
}
}
m_List.Clear();
}
}
EditorGUI.ObjectField(position, property, label);
}
else
EditorGUI.PropertyField(position, property, label);
}
}*_

public static class SerializedPropertyExt
{
public static FieldInfo GetPropertyReferenceType(this SerializedProperty aProperty)
{
var currentType = aProperty.serializedObject.targetObject.GetType();
FieldInfo fi = null;
var parts = aProperty.propertyPath.Split(‘.’);
foreach (string fieldName in parts)
{
fi = currentType.GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (fi == null)
return null;
currentType = fi.FieldType;
}
return fi;
}
}
}
In addition you need this script as well which defines the attributes ([dropbox download][2]). This script must not be in an editor folder but simply part of your normal runtime scripts
// ObjectDropdownAttribute.cs
using UnityEngine;

public class ObjectDropdownAttribute : PropertyAttribute
{
}
public class ObjectDropdownFilterAttribute : PropertyAttribute
{
public System.Type filterType;
public ObjectDropdownFilterAttribute(System.Type aType)
{
filterType = aType;
}
}

Usage


public class SomeScript : MonoBehaviour
{
[ObjectDropdown]
public SomeBaseClass objRef;
}
If you attach the ObjectDropdown attribute like this you can still use the object field as usual. However if you hold down SHIFT before you drop an object on the field you will actually get a dropdown menu which lets you select one of the compatible objects / components available.
*[1]: Dropbox - ObjectSelectorDropdown.cs - Simplify your life
*[2]: Dropbox - ObjectDropdownAttribute.cs - Simplify your life

Hi

Wow - with respect, that was the most confusingly asked question I’ve ever heard :slight_smile: For future reference, feel free to use actual names for examples, and a screen shot speaks a 1000 words!

Anyway, I think what you’re asking is:

  • you have a base class, Animal, derived from Monobehaviour
  • you have 2 derived classes, Dog and Cat
  • you have a single game object (animals) that has both Dog and Cat components attached
  • you have another component on another game object (animalref) that has a field of type Animal
  • how can you make sure that your Animal field on animalref points to the Dog, not the Cat or visa versa

Unfortunately, the answer is I believe that you can’t in the UI. In thoery if you knew exactly what you wanted you could add an ‘OnValidate’ function to your ‘animalref’ that looked up the correct thing in code. That said, I would recommend just having a bit of a rethink about how you structure your scene in this case - best to work with unity, not against it!

-Chris