Search code examples
drag-and-dropunityscriptunity-game-engine

Drag and Drop between 2 gameObjects


I have 2 Spheres in my scene. I want to be able to drag and drop my mouse from one sphere to the other, and have an indicator (a straight line for example) while dragging. After releasing the mouse button, I want to store in the first sphere the other sphere (as GameObject).

I need this in UnityScript, but I can accept C# ideas.

So far I have thought about onMouseDown event on the first Sphere, and then onMouseEnter to the other sphere, I'll store the data in some global variable (which I donno how to do yet) and in case of onMouseExit I'll just put the global variable as null.

Then onMouseUp on the first Sphere I'll store the global variable in the pressed object (the sphere).

Any tips and tricks on how to do it?


Solution

  • Click and drag functionality demonstration

    Assumptions/Setup

    1. You're working in a 2D worldspace; the logic for dragging the object for this part of the solution would need changing.

    2. Setting parent after click drag, referred to setting the object you didn't click to be the child of the parent you did click on.

    3. The camera should be an orthographic camera; using a perspective camera will cause the dragging to not align with where you think it should be.

    4. In order to make the dragging work, I created a quad that was used as the 'background' to the 2D scene, and put that on a new layer called 'background'. Then setup the layermask field to only use the background layer.

    5. Create and setup a material for the line renderer, (I'm using a Particles/Additive shader for the above example), and for the parameters of the line renderer I'm using Start Width: 0.75, End Width: 0, and make sure Use World Space is ticked.

    enter image description here

    Explanation

    Firstly setup the otherSphere field to be pointing to the other sphere in the scene. OnMouseDown and OnMouseUp toggle the mouseDown bool, that is used to determine if the line renderer should be updated. These are also used to turn on/off the line renderer, and set/remove the parent transform of the two spheres.

    To get the position to be dragged to, I'm getting a ray based off of the mouse position on screen, using the method ScreenPointToRay. If you wanted to create add smoothing to this drag functionality, you should enable the if (...) else statement at the bottom, and set the lerpTime to a value (0.25 worked well for me).

    Note; when you release the mouse, the parent is immediately set, as such both objects end up getting dragged, but the child will return to it's former position anyway.

    [RequireComponent(typeof(LineRenderer))]
    public class ConnectedSphere : MonoBehaviour
    {
        [SerializeField]
        private Transform       m_otherSphere;
        [SerializeField]
        private float           m_lerpTime;
        [SerializeField]
        private LayerMask       m_layerMask;
    
        private LineRenderer    m_lineRenderer;
        private bool            m_mouseDown;
        private Vector3         m_position;
        private RaycastHit      m_hit;
    
        public void Start()
        {
            m_lineRenderer          = GetComponent<LineRenderer>();
            m_lineRenderer.enabled  = false;
            m_position              = transform.position;
        }
    
        public void OnMouseDown()
        {
            //Un-parent objects
            transform.parent        = null;
            m_otherSphere.parent    = null;
    
            m_mouseDown             = true;
            m_lineRenderer.enabled  = true;
        }
    
        public void OnMouseUp()
        {
            //Parent other object
            m_otherSphere.parent    = transform;
    
            m_mouseDown             = false;
            m_lineRenderer.enabled  = false;
        }
    
        public void Update()
        {
            //Update line renderer and target position whilst mouse down
            if (m_mouseDown)
            {
                //Set line renderer
                m_lineRenderer.SetPosition(0, transform.position);
                m_lineRenderer.SetPosition(1, m_otherSphere.position);
    
                //Get mouse world position
                if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out m_hit, m_layerMask))
                {
                    m_position.x    = m_hit.point.x;
                    m_position.y    = m_hit.point.y;
                }
            }
    
            //Set position (2D world space)
            //if (m_lerpTime == 0f)
                transform.position = m_position;
            //else
                //transform.position = Vector3.Lerp(transform.position, m_position, Time.deltaTime / m_lerpTime);
        }
    }
    

    Hope this helped someone.