EventType.DragPerform, DragAndDrop, placing prefabs in scene.

When placing prefabs in scene, on the DragPerform event, the DragAndDrop.objectReferences property contains the prefab that will be instantiated… not the instance that is spawned.

I want to get a reference to the gameobject being instantiated and do some stuff to it on placement.

What is the best way to go about this? Currently, I’m calling ‘Use’ on the event and I’m instantiating the gameobject myself. I see 2 major problems with this though:

  1. I don’t know how to place it in the same exact position as if it were dropped by unity itself
  2. Does this event ever happen for anything other than when dropping prefabs when done in this context?

Code so far:

using UnityEngine;
using UnityEditor;
using System.Collections;

namespace com.spacepuppy.Tools
{

    [InitializeOnLoad()]
    public class PrefabTools : Editor
    {

        static PrefabTools()
        {
            SceneView.onSceneGUIDelegate -= OnSceneGUI;
            SceneView.onSceneGUIDelegate += OnSceneGUI;
        }


        private static void OnSceneGUI(SceneView scene)
        {

            if (Event.current.type == EventType.DragPerform &&
                Event.current.shift &&
                DragAndDrop.objectReferences.Length > 0 &&
                DragAndDrop.objectReferences[0] is GameObject)
            {
                Event.current.Use();
                DragAndDrop.AcceptDrag();

                var draggedObj = DragAndDrop.objectReferences[0] as GameObject;
                var go = GameObject.Instantiate(draggedObj) as GameObject;
                go.name = draggedObj.name;
                PrefabUtility.DisconnectPrefabInstance(go);

                //position... this is slightly off from how unity usually positions it... really annoying!
                var mp = Event.current.mousePosition;
                mp.y = Screen.height - mp.y;
                var ray = scene.camera.ScreenPointToRay(mp);
                RaycastHit hit;
                if (Physics.Raycast(ray, out hit))
                {
                    go.transform.position = hit.point;
                }
                else
                {
                    go.transform.position = ray.origin + ray.direction * 15f;
                }
               
            }

        }


    }

}

I noticed I posted this at 2AM, and probably didn’t get seen by most people.

Bumping thread for exposure…

Well I guess no one could help out.

Either way, I came up with a pretty hack and slash way at getting what I wanted.

I figured that just after the ‘PerformDrag’ event, the next OnSceneGUI would be just after the actual instance was created in the scene. And I know that when placing prefabs in the scene it becomes the currently active selection. So I figured I’d just track that and react to it.

I broke such an event into a separate event tracking class. And then this utility in its own class.

The event tracking class:

using UnityEngine;
using UnityEditor;
using System.Collections;

namespace com.spacepuppy
{

    [InitializeOnLoad()]
    public class EditorSceneEvents : Editor
    {

        #region Static Fields

        //object added to scene vars
        private static bool _dragPerformedLastTime;

        #endregion

        #region STATIC CONSTRUCTOR

        static EditorSceneEvents()
        {
            SceneView.onSceneGUIDelegate -= OnSceneGUI;
            SceneView.onSceneGUIDelegate += OnSceneGUI;
        }

        #endregion

        #region Static Events

        public delegate void OnPrefabAddedToSceneHandler(GameObject objectAdded);
        public static event OnPrefabAddedToSceneHandler OnPrefabAddedToScene;

        #endregion

        #region OnSceneGUI Message

        private static void OnSceneGUI(SceneView scene)
        {
            ProcessObjectAddedToScene();
        }

        private static void ProcessObjectAddedToScene()
        {
            if (_dragPerformedLastTime && OnPrefabAddedToScene != null)
            {
                if (Selection.activeGameObject != null)
                {
                    OnPrefabAddedToScene(Selection.activeGameObject);
                }
            }

            if (Event.current.type == EventType.DragPerform &&
                DragAndDrop.objectReferences.Length > 0 &&
                DragAndDrop.objectReferences[0] is GameObject)
            {
                _dragPerformedLastTime = true;
            }
            else
            {
                _dragPerformedLastTime = false;
            }
        }

        #endregion
       
    }

}

And the disconnecting of the prefab if ‘shift’ is held.

using UnityEngine;
using UnityEditor;
using System.Collections;

namespace com.spacepuppy.Tools
{

    [InitializeOnLoad()]
    public class PrefabTools : Editor
    {

        static PrefabTools()
        {
            EditorSceneEvents.OnPrefabAddedToScene -= OnPrefabAddedToScene;
            EditorSceneEvents.OnPrefabAddedToScene += OnPrefabAddedToScene;
        }


        private static void OnPrefabAddedToScene(GameObject go)
        {

            if(Event.current.shift)
            {
                PrefabUtility.DisconnectPrefabInstance(go);
            }

        }


    }

}

Honestly though, if anyone knows of a better hook in unity to do this on. I’m all ears. I’ve noticed that documentation about the editor side of stuff, as well as the community articles on it, are very sparse. So figuring out all the hooks that exist are like searching for needles in a haystack.

I guess its a bit too late, but here you are, I found these:

Thanks,

Though I don’t even remember what I needed this for now… lol.

hey @lordofduct hello, i’m trying to implement your example but idk where to place those scripts or even how to make them run…

i’m trying to make a level editor so i would like to snap the prefabs of characters or obstacles into a grid but idk how to start.

could you tell me any idea to start?

You’d just create script files in your ‘Editor’ script folder, and put that code in it. The ‘InitializeOnLoad’ will take care of making sure it runs in the editor.

You’d have to change the implementation in my PrefabUtils to not disconnect the prefab, but instead position it on your grid.