- Home /
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.
Answer by hoy_smallfry · Sep 04, 2013 at 06:18 PM
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!
Fantastic! I had to make one change to get things to work: change DragEventType.DragPerform to DragEventType.DragExited
Answer by ZenithCode · Aug 16, 2013 at 07:39 AM
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.
This does give similar behavior to what I want, but I'm talking about assets that are not scripts and do not have Component representation.
Take for example a mesh. It's representation is the $$anonymous$$esh class, correct? $$anonymous$$esh class is not a Component, so it cannot be attached to GameObject. But still, when you drag it to the GameObject it adds a $$anonymous$$eshFilter, which is a Component, with the $$anonymous$$esh inside as it's content.
Those are Unity types which have underlying components.
Can you give me an example of what you want to add?
Answer by winxalex · Mar 02, 2018 at 01:12 AM
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();
}
}
Answer by MetaCitizenOfficial · Jul 30, 2020 at 02:55 AM
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;
}
}
}
Thanks for this clean and clear code. Worked great. In my case I wanted to have control over the position of any newly added gameobjects/prefabs that get dragged into the Hierarchy.
Just so its clear for anyone else:
HierarchyDragAndDrop.performed += (data) => {
//Do Stuff, like instantiate the GameObject/Prefab and position it wherever you want
};
Answer by DuskLight-Studios · Aug 14, 2020 at 10:09 PM
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.
Your answer
Follow this Question
Related Questions
How to add an asset to a script-enabled public game object? 1 Answer
GameObject permanent destruction from "Resources" in EditorWindow 0 Answers
Using Empty GameObjects to organize hierarchy? 2 Answers
Restricting "Drag GameObject" script to Z Axis? 1 Answer
Can I move a component from one GameObject to another in script? 1 Answer