GUI drag-drop
A lot of people have been asking for tips on how to implement drag-drop functionality in Unity GUI scripting, so I decided to put together a reusable script for the purpose.
Basically the solution requires that your data class derives from GUIDraggableObject and at some point in its OnGUI method call Drag( Rect ) – just like GUI.Window handles dragging.
Right. Codez. First off the GUIDraggableObject.cs file:
public class GUIDraggableObject
{
protected Vector2 m_Position;
private Vector2 m_DragStart;
private bool m_Dragging;
{
m_Position = position;
}
public bool Dragging
{
get
{
return m_Dragging;
}
}
public Vector2 Position
{
get
{
return m_Position;
}
set
{
m_Position = value;
}
}
{
if (Event.current.type == EventType.MouseUp)
{
m_Dragging = false;
}
else if (Event.current.type == EventType.MouseDown && draggingRect.Contains (Event.current.mousePosition))
{
m_Dragging = true;
m_DragStart = Event.current.mousePosition - m_Position;
Event.current.Use();
}
if (m_Dragging)
{
m_Position = Event.current.mousePosition - m_DragStart;
}
}
}
An example data class inheriting from GUIDraggableObject – DataObject.cs:
public class DataObject : GUIDraggableObject
// This class just has the capability of being dragged in GUI - it could be any type of generic data class
{
private string m_Name;
private int m_Value;
: base (position)
{
m_Name = name;
m_Value = value;
}
{
Rect drawRect = new Rect (m_Position.x, m_Position.y, 100.0f, 100.0f), dragRect;
GUILayout.BeginArea (drawRect, GUI.skin.GetStyle ("Box"));
GUILayout.Label (m_Name, GUI.skin.GetStyle ("Box"), GUILayout.ExpandWidth (true));
dragRect = GUILayoutUtility.GetLastRect ();
dragRect = new Rect (dragRect.x + m_Position.x, dragRect.y + m_Position.y, dragRect.width, dragRect.height);
if (Dragging)
{
GUILayout.Label ("Wooo...");
}
else if (GUILayout.Button ("Yes!"))
{
Debug.Log ("Yes. It is " + m_Value + "!");
}
GUILayout.EndArea ();
Drag (dragRect);
}
}
And finally, this script demonstrates how you could have your data manager class use Unity GUI for data visualisation with drag-drop enabled – MyMonoBehaviour.cs:
public class MyMonoBehaviour : MonoBehaviour
{
private List< DataObject > m_Data = new List< DataObject > ();
private Rect dropTargetRect = new Rect (10.0f, 10.0f, 30.0f, 30.0f);
{
m_Data.Add (new DataObject ("One", 1, new Vector2 (20.0f * Random.Range (1.0f, 10.0f), 20.0f * Random.Range (1.0f, 10.0f))));
m_Data.Add (new DataObject ("Two", 2, new Vector2 (20.0f * Random.Range (1.0f, 10.0f), 20.0f * Random.Range (1.0f, 10.0f))));
m_Data.Add (new DataObject ("Three", 3, new Vector2 (20.0f * Random.Range (1.0f, 10.0f), 20.0f * Random.Range (1.0f, 10.0f))));
m_Data.Add (new DataObject ("Four", 4, new Vector2 (20.0f * Random.Range (1.0f, 10.0f), 20.0f * Random.Range (1.0f, 10.0f))));
m_Data.Add (new DataObject ("Five", 5, new Vector2 (20.0f * Random.Range (1.0f, 10.0f), 20.0f * Random.Range (1.0f, 10.0f))));
}
{
DataObject toFront, dropDead;
Color color;
GUI.Box(dropTargetRect, "Die");
toFront = dropDead = null;
foreach (DataObject data in m_Data)
{
color = GUI.color;
if (data.Dragging)
{
GUI.color = dropTargetRect.Contains (Event.current.mousePosition) ? Color.red : color;
}
data.OnGUI ();
GUI.color = color;
if (data.Dragging)
{
if (m_Data.IndexOf (data) != m_Data.Count - 1)
{
toFront = data;
}
}
}
if (toFront != null)
// Move an object to front if needed
{
m_Data.Remove (toFront);
m_Data.Add (toFront);
}
}
}
Ah yea and an example of how you could do the same in an editor window – MyEditorWindow.cs:
public class MyEditorWindow : EditorWindow
{
private List< DataObject > m_Data = new List< DataObject > ();
private bool doRepaint = false;
private Rect dropTargetRect = new Rect (10.0f, 10.0f, 30.0f, 30.0f);
{
m_Data.Add (new DataObject ("One", 1, new Vector2 (20.0f * Random.Range (1.0f, 10.0f), 20.0f * Random.Range (1.0f, 10.0f))));
m_Data.Add (new DataObject ("Two", 2, new Vector2 (20.0f * Random.Range (1.0f, 10.0f), 20.0f * Random.Range (1.0f, 10.0f))));
m_Data.Add (new DataObject ("Three", 3, new Vector2 (20.0f * Random.Range (1.0f, 10.0f), 20.0f * Random.Range (1.0f, 10.0f))));
m_Data.Add (new DataObject ("Four", 4, new Vector2 (20.0f * Random.Range (1.0f, 10.0f), 20.0f * Random.Range (1.0f, 10.0f))));
m_Data.Add (new DataObject ("Five", 5, new Vector2 (20.0f * Random.Range (1.0f, 10.0f), 20.0f * Random.Range (1.0f, 10.0f))));
}
[MenuItem ("Window/MyEditorWindow")]
{
GetWindow (typeof (MyEditorWindow)).Show ();
}
{
if (doRepaint)
{
Repaint ();
}
}
{
DataObject toFront, dropDead;
bool previousState, flipRepaint;
Color color;
GUI.Box(dropTargetRect, "Die");
toFront = dropDead = null;
doRepaint = false;
flipRepaint = false;
foreach (DataObject data in m_Data)
{
previousState = data.Dragging;
color = GUI.color;
if (previousState)
{
GUI.color = dropTargetRect.Contains (Event.current.mousePosition) ? Color.red : color;
}
data.OnGUI ();
GUI.color = color;
if (data.Dragging)
{
doRepaint = true;
if (m_Data.IndexOf (data) != m_Data.Count - 1)
{
toFront = data;
}
}
else if (previousState)
{
flipRepaint = true;
if (dropTargetRect.Contains (Event.current.mousePosition))
{
dropDead = data;
}
}
}
if (toFront != null)
// Move an object to front if needed
{
m_Data.Remove (toFront);
m_Data.Add (toFront);
}
if (dropDead != null)
// Destroy an object if needed
{
m_Data.Remove (dropDead);
}
if (flipRepaint)
// If some object just stopped being dragged, we should repaing for the state change
{
Repaint ();
}
}
}
Works great!
But i was wondering.. can i also use a textured ( for example a .png ) interface?
Greets, Jorisshh
Sure. Just modify the OnGUI of your GUIDraggableObject based class to render that.
I think it’d be awesome if you’ll provide a simple gui-drag-drop project. i`d be very very appreciated. ty
Thanks a lot for the info provided. I started reading c# just to understand how you do it. I believe that in order to compile the last example, the script must be named MyEditorWindow.cs insted of EditorWindow.cs Again, thanks.
Excellent tutorial… I had a question though. How would I make the dropped item be recognized by another object. For example….
If I dragged an object into a box… Then do this animation or behavior… I understand the process but not how to do this with the GUI…
Thanks in advance!!!
@Ilan
That would increase the time it takes to get an article ready, but I’ll look into it.
@Ippokratis
Thanks for the pointer. Will correct the post
@Gibbs
If you notice in MyMonoBehaviour.cs – on the line beginning with “GUI.color = dropTargetRect.Contains (Event…”, that is one example of handling drop-targets. Try running the script and dragging over the rect.
Still looking forward to see a project based on that script. ty in advance
would also love to see an example project, time permitting of course. love your site and your work, keep it up.
Thanks for this!
Also, you guys are asking a bit too much – here you have the fundamental code snippets to provide the functionality – why do you want an entire project made and bundled up for you?
Just starting with C# and was wondering 2 things.
_1_
How would you vary the window layout. (they all use the same layout)?
_2_ How could I use a button to turn individual windows on and off?