Create GameObjects/Components by Dragging Assets to Hierarchy/Inspector

So, anyone that’s used Unity long enough knows, if you drag certain asset types to the hierarchy or inspector windows, Unity will create the appropriate objects. There is an obvious, 1-to-1 relationship with script assets and their deserialized classes - the MonoBehaviourX script asset will attach the MonoBehaviourX class it defines, right?

However, not all assets are deserialized as Components, and therefore can’t be attached to a GameObject directly. Instead, they rely on certain Components to be attached, while the asset itself becomes referenced in the Component. Take for instance sound files, which are deserialized to AudioClips. In order to be of any use in the scene, they require an AudioSource to be attached to a GameObject. Likewise, AudioSource by itself is useless, because it requires a reference to an AudioClip to have content to playback.

What’s interesting about the latter type of assets is that Unity is aware of their relationship to a component, so when a user drags these kinds of assets to the Heirarchy or Inspector windows, it knows exactly what that it should do. In the Inspector window, the component related to the asset is attached, with asset as the component’s content. In the hierarchy window, it creates a new GameObject with the appropriate attachment.

Which leads me to my question: Is there a way to define this drag and drop relationship? I’m hoping to override which Components are added from built-in assets, as well defining new relationships between custom assets and scripts.

The ultimate goal is to get a script attached along with the AudioSource when AudioClips are dragged in.

Okay so this took a bit looking into, but I found an almost perfect solution. I say nearly perfect, because this script won’t run if there are no GameObjects previously in the hierarchy. That’s because EditorApplication.hierarchyWindowItemOnGUI runs for each GameObject previously in the hierarchy. So, if there are no objects previously, then the delegate wont callback.

With a little bit of effort, this can be modified for use with custom assets as well:

using UnityEditor;
using UnityEngine;
using System.Collections.Generic;

// makes sure that the static constructor is always called in the editor.
[InitializeOnLoad]
public class ComponentXCreator : Editor
{
    static ComponentXCreator ()
    {
        // Adds a callback for when the hierarchy window processes GUI events
        // for every GameObject in the heirarchy.
        EditorApplication.hierarchyWindowItemOnGUI += HierarchyWindowItemCallback;
    }

    static void HierarchyWindowItemCallback(int pID, Rect pRect)
    {
        // happens when an acceptable item is released over the GUI window
        if (Event.current.type == EventType.DragPerform)
        {
            // get all the drag and drop information ready for processing.
            DragAndDrop.AcceptDrag();

            // used to emulate selection of new objects.
            var selectedObjects = new List<GameObject>();

            // run through each object that was dragged in.
            foreach (var objectRef in DragAndDrop.objectReferences)
            {
                // if the object is the particular asset type...
                if (objectRef is AssetX)
                {
                    // we create a new GameObject using the asset's name.
                    var gameObject = new GameObject(objectRef.name);

                    // we attach component X, associated with asset X.
                    var componentX = gameObject.AddComponent<ComponentX>();

                    // we place asset X within component X.
                    componentX.assetX = objectRef as AssetX;

                    // add to the list of selected objects.
                    selectedObjects.Add(gameObject);
                }
            }

            // we didn't drag any assets of type AssetX, so do nothing.
            if (selectedObjects.Count == 0) return;

            // emulate selection of newly created objects.
            Selection.objects = selectedObjects.ToArray();

            // make sure this call is the only one that processes the event.
            Event.current.Use();
        }
    }
}

I avoided running more than once for multiple objects by using Event.current.Use() to clean the Event value, so that it’s not detected again. This has the added benefit of preventing Unity’s built-in object creation, which effectively overrides their behavior. If you don’t intend to override built-in behavior, find another way to avoid multiple calls, such as using some sort of flag.

Since there is no similar delegate for the inspector that will encompass the entire GUI bounds, there is no way to override drag and drop attachment that I’m aware of.

Thanks to @VoxelBoy for pointing me in the right direction at this year’s Unite!

Hi,

Yes add this line of code to your MonoBehaviour.

[RequireComponent(typeof(AudioSource))]
[RequireComponent(typeof(Class2))]
public class YourClass: MonoBehaviour
{
}

This will add an AudioSource and Class2 when you drag it onto a gameobject.

Similar if you want to drop on existing GameObject

 static void HierarchyWindowItemCallback(int instanceID, Rect pRect) {

        // happens when an acceptable item is released over the GUI window
        if (Event.current.type == EventType.DragPerform && pRect.Contains(Event.current.mousePosition))
        {
            // get all the drag and drop information ready for processing.
            DragAndDrop.AcceptDrag();



            GameObject gameObject = EditorUtility.InstanceIDToObject(instanceID) as GameObject;

            if (gameObject == null) return;

            gameObject = PrefabUtility.GetPrefabType(gameObject) == PrefabType.PrefabInstance ? PrefabUtility.GetPrefabParent(gameObject) as GameObject : gameObject;


            if (gameObject != null)
                // run through each object that was dragged in.
                foreach (var objectRef in DragAndDrop.objectReferences) {
                    // if the object is the particular asset type...
                    if (objectRef is AssetX)
                    {


                        // we attach component X, associated with asset X.
                        var componentX = gameObject.AddComponent<ComponentX>();
                        // we place asset X within component X.
                        componentX.assetX = objectRef as AssetX;

                    }


                }


            Event.current.Use();


        }
    }

This works with and without a target

Usage

HierarchyDragAndDrop.performed += (data) => { };

Code

using System;
using UnityEngine;
using Object = UnityEngine.Object;
using UnityEditor;

public static class HierarchyDragAndDrop
{
    private static DragAndDropData data = null;
    public static Action<DragAndDropData> performed;

    [InitializeOnLoadMethod]
    private static void Initalize()
    {
        EditorApplication.hierarchyWindowItemOnGUI += (instanceID, selectionRect) =>
        {
            if (Event.current.type == EventType.DragPerform)
                data = new DragAndDropData();
            if (data != null && selectionRect.Contains(Event.current.mousePosition))
                data.target = EditorUtility.InstanceIDToObject(instanceID) as GameObject;
        };
    }

    public class DragAndDropData
    {
        public GameObject target;
        public Object[] objectReferences;
        public string[] paths;

        public DragAndDropData()
        {
            DragAndDrop.AcceptDrag();
            objectReferences = DragAndDrop.objectReferences;
            paths = DragAndDrop.paths;
            EditorApplication.update += Process;
            Event.current.Use();
        }
        public void Process()
        {
            performed?.Invoke(this);
            EditorApplication.update -= Process;
            data = null;
        }
    }
}

This is pretty amazing! The only thing I’m missing now is being able to drag and drop the asset into the Inspector window, but I can’t find any reference on how to detect events over the Inspector Window.

I was thinking of locating the Inspector Editor Window in some way, then on the Editor’s update, check when a drag and drop event ends on top of it. Then, somehow find what Object is selected by the Inspector and add the asset’s component to it if it’s a GameObject.

Maybe this could work? I’ll look into it.