Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Question Drag and Drop a sprite object in World with Unity (2019.3.0f1)

Discussion in 'Scripting' started by t1000, Nov 16, 2020.

  1. t1000

    t1000

    Joined:
    Jan 30, 2016
    Posts:
    16
    Hi there,

    So, I am struggling big time here. I used to have a Canvas on my prototype card game, where I could listen to events from Input.mouse and then use eventData and all the plethora of methods to simply drag and drop and make an UI object follow the mouse.

    Unfortunately, I decided to bring my cards outside of the UI into the 3D World using a 2D perspective (should I use an orthographic camera?).

    Can someone ELI5, how the Drag and Drop and Input.mouse events work with Sprites and Game Objects in general within Unity. Apologies if this is an extremely noob question but everything I gathered on forums seems to be either not to be working, obsolete code, or uses UI.

    So I cannot figure out what is the appropriate way to handle this.

    what I want to achieve and understand

    Let's say I have my 3D URP project, with your regular UI, my camera pointing to some plane or region in the space (either orthographically or with perspective) and then I have my hand on the 3D space which is a bunch of Sprite objects; then I want to drop these cards into another region of the screen (World space, no UI) or be able to hold the card in my hand and drag it until it collides with a region or another card in which case I run some method to either drop the card or trigger an action.

    What are my options here? are they built-it methods equivalents to those for UI? does this rely entirely on the usage of ray casting?

    I would really appreciate any insights. I have several doubts on the camera setup, perspective and other things which are not an issue when you deal with a Canvas an UI Images instead of Sprites.
     
  2. TheRaider

    TheRaider

    Joined:
    Dec 5, 2010
    Posts:
    2,245
    I would just use 2D raycast and then use screenToWorld on the camera for moving the drag.
    You could also make events as a result of the raycast so you could use a similar way to call your methods.
     
  3. t1000

    t1000

    Joined:
    Jan 30, 2016
    Posts:
    16
    My understanding is:
    1. I should make my sprite so that it can detect that I clicked on it, and that I am holding the mouse in it.
    2. Then, I ray cast from the camera into the position on the mouse and I update the transform of the sprite to that new position?
    3. Then on mouse release, I stop updating the position.
    4. (optional) trigger methods for any hits.
    So, apologies, but I am not quite following.

    Is there any code or documented example that I can refer to, so that I can have a more visual example of this? not so sure on how to operate in relation with screenToWorld.

    Thanks for your reply, much appreciated.
     
  4. eses

    eses

    Joined:
    Feb 26, 2013
    Posts:
    2,637
    Hi @t1000

    "Unfortunately, I decided to bring my cards outside of the UI into the 3D World using a 2D perspective (should I use an orthographic camera?)."


    Actually events work outside of UI - this is explained in the documentation IIRC.

    IMO there may not be need for going around existing event system, if you need some simple solution, so maybe try this first?

    Steps:
    1. Add EventSystem to your scene simply add some UI element, this will automatically add EventSystem, get rid of the Canvas afterwards, of course you can manually add EventSystem component too.

    2. Then add a Physics2D or Physics raycaster to your camera depending on what colliders you are going to use on your objects.UI doesn't need these, it has its own "raycaster" i.e. GraphicRaycaster.

    After this you can detect clicks with the same UI events (BeginDrag, Drag).

    Note - as you are not working with Canvas, you'll probably have to convert screen positions to world positions (like I did below). Drop should also work, but note / you are not going to have the luxury of having Canvas Groups and such, so you'll have to use layers to avoid hitting dragged object (for example).

    Here I'm dragging sprites using the same UI events, using IBeginDragHandler, IDragHandler, IEndDragHandler:

    blocks.gif

    Edit:
    As you are talking about "2D perspective" this should work.

    But if you are going for full 3D world, I can't recall if there was some obstacles with above method. In that case, you can create your own raycast system for dragging, like already mentioned in previous answer.
     
    Last edited: Nov 16, 2020
    t1000 likes this.
  5. t1000

    t1000

    Joined:
    Jan 30, 2016
    Posts:
    16
    Thanks for your time writing the explanation, very helpful. So, I wanted to come back to you rather soon and I decided to test a few things on 2D Space.

    I did not add the "Physics2D Raycaster" into the Camera in the end, but since it is an interesting suggestion I tested further and what seems to change here is that if I don't use a Physics2D Raycaster then events such as OnBeginDrag or OnDrag would not work; but OnMouseDrag still works without the raycaster indeed.

    Funnily, the editor returns an error when adding the Physics2D Raycaster object into the camera. Nevertheless, it´s an editor bug apparently as it does work regardless.

    Using the below code, I was able to move my cards around with only
    1. EventSystem
    2. Main Camera
    3. Two card Sprites with a BoxCollider2D with isTrigger.
    I do not know what the difference is to use the methods implemented by the interfaces IBeginDragHandler, IDragHandler, IEndDragHandler versus the below methods.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. public class DragDropCard: MonoBehaviour
    5. {
    6.     private bool isDragging;
    7.     public void OnMouseDown()  {
    8.         isDragging = true;
    9.     }
    10.     public void OnMouseUp()  {
    11.         isDragging = false;
    12.     }
    13.     public void OnMouseDrag()   {
    14.         Vector2 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition) - transform.position;
    15.         transform.Translate(mousePosition);  
    16.     }
    17.     private void OnTriggerEnter2D(Collider2D other)  {
    18.         Debug.Log("collision "+other.name);
    19.     }
    20.  
    2020-11-16_20-24-32.gif

    I imagine that to do the same into the 3D space I should adjust Vector3 mousePosition and the hits from my raycasts (if I used them in case and some other objects get in the way).

    If I am understanding better the previous answer, his suggestion is to ray cast to from screen to world, then whatever I hit with the ray is a "drag" object I can actually start moving around updating its transform to my mouse position (converted to world space every time I figure) and I think by "make events as a result of the raycast" we are talking here about the regular methods and interfaces we were discussing on the code, If I am not mistaken.

    I would like to ask here, what approach is - generally speaking - more performant or best practice? surely getting a drag and drop must be relatively easy for any experienced Unity developer but there should a way that is (arguably) considered correct.
     
    Last edited: Nov 17, 2020
  6. eses

    eses

    Joined:
    Feb 26, 2013
    Posts:
    2,637
    AFAIK OnMouseDown, OnMouseDrag and OnMouseUp are not part of the event system which IIRC was added along with UGUI but I might be wrong about this. These come from MonoBehaviour.

    But I know OnMouseUp and OnMouseDown don't require EventSystem, these only require a Collider to work.

    And for 3D I too have done drag and drop pretty much like that, using raycasting. But I have only used such thing once or twice...

    "what approach is - generally speaking - more performant or best practice"

    I have no idea. I'm an amateur programmer... but anyway you usually drag one object and a few raycasts won't usually be the most expensive thing in your game I guess.
     
    Last edited: Nov 17, 2020
    t1000 likes this.