Search Unity

Override Drag&Drop MonoScript to Inspector

Discussion in 'Editor Workflows' started by AlexanderTravkin, Aug 31, 2021.

  1. AlexanderTravkin

    AlexanderTravkin

    Joined:
    Jul 11, 2019
    Posts:
    2
    Hi Unity.

    How do I override the drag and drop MonoScript to Inspector Window action? I want to replicate the functionality of [GenerateAuthoringComponent] from Unity.Entities — where drag and drop MonoScript with IComponentData+[GenerateAuthoringComponent] on Inspector adds another script to the object, which is a wrapper over the original struct.

    Also, it's interesting how the new type generated and uploaded to AppDomain is linked to the MonoScript/struct.
     
    ThetaRabbit and AleVerDes like this.
  2. AleVerDes

    AleVerDes

    Joined:
    Jun 16, 2014
    Posts:
    40
    upload_2021-9-22_16-50-5.png

    So, I tried overriding DrawAndDrop through the Unity Editor, at least manually. And the first time I drag and drop a file named HealthComponent, it actually hinges HealthBehaviour on the GameObject, but any subsequent action will crash the Unity Editor. What am I doing wrong?

    Update is function of my test EditorWindow
     
  3. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,338
    We're doing this by reflecting into Unity's DragAndDropService, which you can see the code for here.

    Note that this works in 2020.3 LTS, I believe that they refactored that code somewhat for 2021, and that's not on GitHub so you'll have to look at a decompiler to see what happens.

    After making a wrapper for the internal code so we can call it, the code is pretty simple. Here's an example from our code base.

    Code (csharp):
    1.  
    2. public static class SpriteAnimationDragAndDrop {
    3.  
    4.     [InitializeOnLoadMethod]
    5.     public static void HookUpDragAndDropSpriteAnimationToInspector() {
    6.         var dropHandler = (DragAndDropService.InspectorDropHandler) OnInspectorDrop;
    7.  
    8.         if (!DragAndDropService.HasHandler(DragAndDropService.kHierarchyDropDstId, dropHandler))
    9.             DragAndDropService.AddDropHandler(dropHandler);
    10.     }
    11.  
    12.     private static DragAndDropVisualMode OnInspectorDrop(object[] targets, bool perform) {
    13.         // stuff here
    14.     }
    15.  
    perform will be true when the user has dropped the thing, false otherwise. Targets are all the GameObjects that the inspector is showing for, so if you have a bunch selected, then there's more in there. The dragged stuff is in
    DragAndDrop.objectReferences, as usual. The return value just indicates how to style the mouse cursor during the drag, though if you return None, then other registered drag and drop handlers gets to run (so you'd return none if DragAndDrop.objectReferences doesn't have any HealthComponents)
     
  4. richardkettlewell

    richardkettlewell

    Unity Technologies

    Joined:
    Sep 9, 2015
    Posts:
    2,285
    Baste likes this.
  5. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,338
    Yup! The entire reflection thing isn't necessary now, which is great.

    As a bonus, here's a simple one we have in our project, the SpriteDragAndDrop. It allows you to drag-and-drop sprites (and textures with sprites) onto a GameObject's inspector, and have that automatically add a SpriteRenderer with that Sprite assigned to that GameObject. It's probably a better example than the very barebones ones in the documentation:

    Unity_N4Q6arB6UV.gif

    Code (csharp):
    1. using System.Collections.Generic;
    2. using UnityEditor;
    3. using UnityEngine;
    4.  
    5. public static class SpriteDragAndDrop {
    6.     [InitializeOnLoadMethod]
    7.     public static void HookUpDragAndDropToInspector() {
    8.         DragAndDrop.AddDropHandler(OnInspectorDrop);
    9.     }
    10.  
    11.     private static List<GameObject> targetGameObjects = new List<GameObject>();
    12.     private static DragAndDropVisualMode OnInspectorDrop(Object[] targets, bool perform) {
    13.         targetGameObjects.Clear();
    14.         foreach (var target in targets)
    15.             if (target is GameObject gameObject && gameObject.GetComponent<SpriteRenderer>() == null)
    16.                 targetGameObjects.Add(gameObject);
    17.  
    18.         if (targetGameObjects.Count == 0)
    19.             return DragAndDropVisualMode.None; // this allows other drag and drop handlers to handle the drag
    20.  
    21.         Sprite sprite = null;
    22.         foreach (var obj in DragAndDrop.objectReferences) {
    23.             if (obj is Sprite s) {
    24.                 sprite = s;
    25.                 break;
    26.             }
    27.         }
    28.  
    29.         if (sprite == null) {
    30.             foreach (var obj in DragAndDrop.objectReferences) {
    31.                 if (obj is Texture t) {
    32.                     sprite = AssetDatabase.LoadAssetAtPath<Sprite>(AssetDatabase.GetAssetPath(t));
    33.                     if (sprite != null)
    34.                         break;
    35.                 }
    36.             }
    37.         }
    38.  
    39.         if (sprite == null)
    40.             return DragAndDropVisualMode.None;
    41.  
    42.         if (perform)
    43.             foreach (var gameObject in targetGameObjects)
    44.                 Undo.AddComponent<SpriteRenderer>(gameObject).sprite = sprite;
    45.  
    46.         return DragAndDropVisualMode.Link;
    47.     }
    48. }