Search Unity

Object created via drag&drop is deleted immediately when using RegisterCreatedObjectUndo

Discussion in 'Immediate Mode GUI (IMGUI)' started by Mindmapfreak, Sep 17, 2017.

  1. Mindmapfreak

    Mindmapfreak

    Joined:
    Mar 31, 2016
    Posts:
    3
    Hi,
    I am trying to create a script, that allows the user to add an image to the scene via drag&drop (similar to dragging a prefab into the hierarchy). I used this post as a starting point and the script worked, but the new object in the scene was not registered as a change and therefore not saved.

    I tried to use Undo.RegisterCreatedObjectUndo, but if I do that, the object appears for a split second and then gets deleted. This only happens when the function for creating the image object is called from EditorApplication.hierarchyWindowItemOnGUI. I tested calling the function from a menu entry and loading the image from the AssetDatabase and that works as intended. So I guess the problem stems from the way I process the drag&drop or something happens in/after EditorApplication.hierarchyWindowItemOnGUI.

    You can see the behaviour here (link to GIF). It looks like this (link to GIF) if I remove RegisterCreatedObjectUndo (notice how the item is deselected immediately) and like this (link to GIF) when I call the function from a menu entry.

    Thanks

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEditor;
    4. using UnityEngine;
    5.  
    6. [InitializeOnLoad]
    7. public class DragImage : Editor
    8. {
    9.  
    10.     static DragImage()
    11.     {
    12.         EditorApplication.hierarchyWindowItemOnGUI += HierarchyWindowCallback;      
    13.     }
    14.  
    15.     static void HierarchyWindowCallback(int id, Rect pos)
    16.     {
    17.         if (Event.current.type == EventType.DragExited)
    18.         {
    19.             if(DragDropCallback()) {
    20.                 Event.current.Use();
    21.             }
    22.         }
    23.     }  
    24.  
    25.     static bool DragDropCallback()
    26.     {
    27.         DragAndDrop.AcceptDrag();
    28.         List<Texture2D> images = new List<Texture2D>();
    29.         foreach (Object obj in DragAndDrop.objectReferences)
    30.         {
    31.             if (obj.GetType() == typeof(Texture2D))
    32.             {
    33.                 Texture2D tex = (Texture2D)obj;
    34.                 images.Add(tex);
    35.             }
    36.         }
    37.  
    38.         foreach(Texture2D img in images)
    39.         {
    40.             CreateImage(img);
    41.         }
    42.         return images.Count>0;
    43.     }
    44.  
    45.     [MenuItem("GameObject/3D Object/TestImage")]
    46.     static void CreateInMenu()
    47.     {
    48.         CreateImage(AssetDatabase.LoadAssetAtPath<Texture2D>("Assets/test.png"));
    49.     }
    50.  
    51.  
    52.     static void CreateImage(Texture2D tex)
    53.     {
    54.         GameObject imgObject = GameObject.CreatePrimitive(PrimitiveType.Plane);
    55.         imgObject.name = "Image";
    56.         imgObject.transform.localScale = new Vector3(0.1f * ((float)tex.width) / ((float)tex.height), 1f, 0.1f);
    57.         imgObject.transform.rotation = Quaternion.Euler(90f, 0f, 180f);
    58.         imgObject.transform.position = new Vector3(0f, 1.5f, 0f);
    59.         MeshRenderer renderer = imgObject.GetComponent<MeshRenderer>();
    60.         Material mat = new Material(Shader.Find("Standard"));
    61.         mat.SetTexture("_MainTex", tex);
    62.         mat.SetColor("_Color", Color.grey);
    63.         mat.SetFloat("_Glossiness", 0.0f);
    64.         renderer.material = mat;
    65.         Undo.RegisterCreatedObjectUndo(imgObject, "Create image");
    66.         Selection.activeObject = imgObject;
    67.     }
    68. }
    69.  
     
  2. Peter77

    Peter77

    QA Jesus

    Joined:
    Jun 12, 2013
    Posts:
    6,620
    The following code should work as expected. However, since it's a callback for "items in the hierarchy window", it only accepts the drop if the mouse is located on an item.
    Code (CSharp):
    1. static void HierarchyWindowCallback(int id, Rect pos)
    2. {
    3.     if (!pos.Contains(Event.current.mousePosition))
    4.         return;
    5.  
    6.     var eventType = Event.current.type;
    7.     if (eventType == EventType.DragUpdated || eventType == EventType.DragPerform)
    8.     {
    9.         DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
    10.  
    11.         if (eventType == EventType.DragPerform)
    12.         {
    13.             DragAndDrop.AcceptDrag();
    14.             DragDropCallback();
    15.         }
    16.  
    17.         Event.current.Use();
    18.     }
    19. }
     
  3. Mindmapfreak

    Mindmapfreak

    Joined:
    Mar 31, 2016
    Posts:
    3
    Thanks for your answer. I did not realize, that the DragPerform event is only triggered, when you drag onto an existing item. Now it works when I drag onto an existing item, but that's not very practical, so I switched to SceneView.onSceneGUIDelegate (to drag&drop into the scene view).

    My final code looks like this:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEditor;
    4. using UnityEngine;
    5.  
    6. [InitializeOnLoad]
    7. public class DragImage : Editor
    8. {
    9.  
    10.     static DragImage()
    11.     {
    12.         SceneView.onSceneGUIDelegate += SceneViewCallback;
    13.     }
    14.  
    15.     static void SceneViewCallback(SceneView view)
    16.     {
    17.         if (Event.current.type == EventType.DragUpdated || Event.current.type == EventType.DragPerform)
    18.         {
    19.             DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
    20.         }
    21.         if (Event.current.type == EventType.DragPerform)
    22.         {
    23.             DragAndDrop.AcceptDrag();
    24.             if (DragDropCallback())
    25.             {
    26.                 Event.current.Use();
    27.             }
    28.         }
    29.     }
    30.        
    31.  
    32.     static bool DragDropCallback()
    33.     {      
    34.         List<Texture2D> images = new List<Texture2D>();
    35.         foreach (Object obj in DragAndDrop.objectReferences)
    36.         {
    37.             if (obj.GetType() == typeof(Texture2D))
    38.             {
    39.                 Texture2D tex = (Texture2D)obj;
    40.                 images.Add(tex);
    41.             }
    42.         }
    43.  
    44.         foreach(Texture2D img in images)
    45.         {
    46.             CreateImage(img);
    47.         }
    48.         return images.Count>0;
    49.     }
    50.  
    51.     static void CreateImage(Texture2D tex)
    52.     {
    53.         GameObject imgObject = GameObject.CreatePrimitive(PrimitiveType.Plane);
    54.         imgObject.name = "Image";
    55.         imgObject.transform.localScale = new Vector3(0.1f * ((float)tex.width) / ((float)tex.height), 1f, 0.1f);
    56.         imgObject.transform.rotation = Quaternion.Euler(90f, 0f, 180f);
    57.         imgObject.transform.position = new Vector3(0f, 1.5f, 0f);
    58.         MeshRenderer renderer = imgObject.GetComponent<MeshRenderer>();
    59.         Material mat = new Material(Shader.Find("Standard"));
    60.         mat.SetTexture("_MainTex", tex);
    61.         mat.SetColor("_Color", Color.grey);
    62.         mat.SetFloat("_Glossiness", 0.0f);
    63.         renderer.material = mat;
    64.         Undo.RegisterCreatedObjectUndo(imgObject, "Create image");
    65.         Selection.activeObject = imgObject;
    66.     }
    67. }
    68.