Search Unity

Simple mouse drag in 2D

Discussion in 'Scripting' started by mythicwave, Feb 23, 2009.

  1. mythicwave

    mythicwave

    Joined:
    Jul 13, 2008
    Posts:
    144
    I've modified the DragRigidBody script to drag an object in 2D. It works somewhat, but I'm having to mess with parameters like drag, mass, spring, Rigidbody, etc. Since all I really want is to drag an object, I don't need these parameters and don't even want to use Rigidbody or any physics. Are there some simple examples of how to drag an object in 2D? I just want to click down on an object and have it follow the mouse.

    I've read other threads about this but none provides a complete code example including finding the clicked object.

    Thanks,

    Brian
     
  2. Mem Dixy

    Mem Dixy

    Joined:
    Feb 26, 2008
    Posts:
    51
    Code (csharp):
    1. #pragma strict
    2. // Attach this script to an orthographic camera.
    3. private var object : Transform;     // The object we will move.
    4. private var offSet : Vector3;       // The object's position relative to the mouse position.
    5.  
    6. function Update () {
    7.     var ray = camera.ScreenPointToRay(Input.mousePosition);     // Gets the mouse position in the form of a ray.
    8.     if (Input.GetButtonDown("Fire1")) {     // If we click the mouse...
    9.         if (!object) {      // And we are not currently moving an object...
    10.             var hit : RaycastHit;
    11.             if (Physics.Raycast(ray, hit, Mathf.Infinity)) {        // Then see if an object is beneath us using raycasting.
    12.                 object = hit.transform;     // If we hit an object then hold on to the object.
    13.                 offSet = object.position-ray.origin;        // This is so when you click on an object its center does not align with mouse position.
    14.             }
    15.         }
    16.     }
    17.     else if (Input.GetButtonUp("Fire1")) {
    18.         object = null;      // Let go of the object.
    19.     }
    20.     if (object) {
    21.         object.position = Vector3(ray.origin.x+offSet.x, object.position.y, ray.origin.z+offSet.z);     // Only move the object on a 2D plane.
    22.     }
    23. }
    24.  
     
  3. mythicwave

    mythicwave

    Joined:
    Jul 13, 2008
    Posts:
    144
    Thanks for the code. I did already get this working by taking part of DragRigidBody.js and converting to C#.

    One question though - Since I don't need any physics, I'm using objects without Rigidbodies. The Raycast is detecting the mouse by hitting the collider. This works, but I'm finding that if the distance parameter is too large, the collider is being hit even with the mouse way off. I've tested it with just one collider in my scene, so I don't think it is hitting the wrong object. But it's still happening. Do you know why this would be? I'm thinking something like the ray is being reflected back and hitting the collider from the back. Is this possible? However, when I use a Rigidbody to detect the mouse hit, it doesn't hit if the mouse if way off.

    -- Brian
     
  4. Mem Dixy

    Mem Dixy

    Joined:
    Feb 26, 2008
    Posts:
    51
    It seems like your question is about your script and since I don't know how your script works I can only guess what the problem is.

    A single ray won't do that, it only goes out in a straight line. Though it is possible to make a "bouncing ray" using two or more rays but it's a bit too complicated for it to be set up accidentally. It is much more likely that the ray is continually facing the object you want to pick up so no matter where the mouse is when you click the mouse it will always hit the object (and if you are using a different ray or ray position if the object has a Rigidbody then maybe that is why it works).

    Sorry I don't think I made much sense but I really don't know what else to say without an example of some sort. Maybe you just need to make the camera orthographic? I guess you could always use my script if you can't fix yours.
     
  5. mythicwave

    mythicwave

    Joined:
    Jul 13, 2008
    Posts:
    144
    Just for kicks, here's my script. It works, but in case something catches your eye re: collider vs. rigidbody. By the way - the camera is orthographic.

    -- Brian
    Code (csharp):
    1. /*
    2.   Notes:
    3.  
    4.         This script should be attached to the camera, so we can use the "camera" member variable with needing a "FindCamera" script.
    5. */
    6.  
    7. using UnityEngine;
    8. using System.Collections;
    9.  
    10. public class ChickenMove : MonoBehaviour
    11. {
    12.   Vector3 origPos, curMousePos;
    13.   GameObject chicken;
    14.  
    15.   void Update()
    16.   {
    17.         // Make sure the user pressed the mouse down
    18.         if (!Input.GetMouseButtonDown(0))
    19.             return;
    20.            
    21.     curMousePos = camera.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, 0));
    22.  
    23.     // Position relative to the eye-point of the camera
    24.     curMousePos -= transform.position;
    25.                
    26.         // We need to actually hit an object
    27.         RaycastHit hit;
    28.  
    29.         // BJL: If we use a collider instead of a rigidbody and the distance parameter is too large, we'll get a hit all the time.
    30.         // Could this be due to reflection or because the camera is orthographic, or...?   
    31.         if (!Physics.Raycast(camera.ScreenPointToRay(Input.mousePosition), out hit, 100))
    32.             return;
    33.        
    34.         // We need to hit a rigidbody.
    35.         if (!hit.rigidbody)
    36.             return;
    37.  
    38.         chicken = hit.rigidbody.gameObject;
    39.         origPos = chicken.transform.position;
    40.        
    41.         StartCoroutine ("DragObject");
    42.   }
    43.  
    44.     IEnumerator DragObject()
    45.     {
    46.     while (Input.GetMouseButton (0))
    47.     {
    48.       // Position on the near clipping plane of the camera in world space
    49.       Vector3 newMousePos = camera.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, 0));
    50.  
    51.             // Position relative to the eye-point of the camera
    52.       newMousePos -= transform.position;
    53.  
    54.       chicken.transform.Translate(curMousePos - newMousePos);
    55.             curMousePos = newMousePos;
    56.      
    57.       yield return 0;
    58.     }
    59.    
    60.         chicken.transform.position = origPos;
    61.         chicken = null;
    62.     }
    63. }
    64.  
     
  6. Mem Dixy

    Mem Dixy

    Joined:
    Feb 26, 2008
    Posts:
    51
    Well I tried the script and could not make it do what you said it did (get clicked on at a far distance while the mouse is not over the object (or in your case a chicken)). The script was behaving weirdly, such as being dragged in the opposite direction of the mouse and returning to it's original position after you let go of it (maybe it is supposed to do this or maybe you are running an older version of Unity so the code behaves differently (doubtful that the code would actually be changed)).

    Anyways as long as your script works for you that's good but since I like the way my script works (though it may not work for you hopefully it will help someone else) I edited it so it should work with objects with or without a Rigidbody (so long as you don't try and move a kinematic Rigidbody).
    Code (csharp):
    1. #pragma strict
    2. // Attach this script to an orthographic camera.
    3. private var chicken : Transform;      // The chicken we will move.
    4. private var offSet : Vector3;      // The chicken's position relative to the mouse position.
    5.  
    6. function Update () {
    7.     var ray = camera.ScreenPointToRay(Input.mousePosition);      // Gets the mouse position in the form of a ray.
    8.     if (Input.GetMouseButtonDown(0)) {      // If we click the mouse...
    9.         var hit : RaycastHit;
    10.         if (Physics.Raycast(ray, hit, Mathf.Infinity)) {      // Then see if an chicken is beneath us using raycasting.
    11.             chicken = hit.transform;      // If we hit an chicken then hold on to the chicken.
    12.             offSet = chicken.position-ray.origin;      // This is so when you click on an chicken its center does not align with mouse position.
    13.             if (chicken.rigidbody) {
    14.                 chicken.rigidbody.isKinematic = true;
    15.             }
    16.         }
    17.     }
    18.     else if (Input.GetMouseButtonUp(0)) {
    19.         if (chicken.rigidbody) {
    20.             chicken.rigidbody.isKinematic = false;
    21.         }
    22.         chicken = null;      // Let go of the chicken.
    23.     }
    24.     if (chicken) {
    25.         chicken.position = Vector3(ray.origin.x+offSet.x, chicken.position.y, ray.origin.z+offSet.z);      // Only move the chicken on a 2D plane.
    26.     }
    27. }
    And for you C# people:
    Code (csharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class ChickenMove : MonoBehaviour {
    5.     Transform chicken;
    6.     Vector3 offSet;    
    7.     void Update() {
    8.         Ray ray = camera.ScreenPointToRay(new Vector3(Input.mousePosition.x, Input.mousePosition.y, 0));        // Gets the mouse position in the form of a ray.
    9.         if (Input.GetMouseButtonDown(0)) {      // If we click the mouse...
    10.             RaycastHit hit;
    11.             if (Physics.Raycast(ray, out hit, Mathf.Infinity)) {        // Then see if an object is beneath us using raycasting.
    12.                 chicken = hit.transform;        // If we hit an object then hold on to the object.
    13.                 offSet = chicken.position-ray.origin;       // This is so when you click on an object its center does not align with mouse position.
    14.                 if (chicken.rigidbody) {
    15.                     chicken.rigidbody.isKinematic = true;
    16.                 }
    17.             }
    18.         }
    19.         else if (Input.GetMouseButtonUp(0)) {
    20.             if (chicken.rigidbody) {
    21.                 chicken.rigidbody.isKinematic = false;
    22.             }
    23.             chicken = null;     // Let go of the object.
    24.         }
    25.         if (chicken) {
    26.             chicken.transform.position = new Vector3(ray.origin.x+offSet.x, chicken.position.y, ray.origin.z+offSet.z);     // Only move the object on a 2D plane.
    27.         }
    28.     }
    29. }
    30.  
     
    Battalia likes this.
  7. dawvee

    dawvee

    Joined:
    Nov 12, 2008
    Posts:
    276
    I did a pretty bare bones script like this a while back, I'm not sure if you get any specific advantage from using raycasts, but I've just used a collider and the OnMouseDrag override.

    Code (csharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. [RequireComponent(typeof(SphereCollider))]
    5.  
    6. public class MovePoint : MonoBehaviour
    7. {
    8.     void OnMouseDrag()
    9.     {
    10.         Vector3 point = Camera.main.ScreenToWorldPoint(Input.mousePosition);
    11.         point.z = gameObject.transform.position.z;
    12.         gameObject.transform.position = point;
    13.         Screen.showCursor = false;
    14.     }
    15.    
    16.     void OnMouseUp()
    17.     {
    18.         Screen.showCursor = true;
    19.     }
    20. }
    Edit: Just re-read the first post, and to clarify, this script just gets attached to each GameObject you want to move with the mouse. Maybe not an ideal solution for your situation, but it worked well enough in my case.
     
  8. kakubei2

    kakubei2

    Joined:
    Jun 10, 2009
    Posts:
    31
     
  9. Mem Dixy

    Mem Dixy

    Joined:
    Feb 26, 2008
    Posts:
    51
    Like this:

    Code (csharp):
    1. #pragma strict  // Attach this script to an orthographic camera.
    2.  
    3. public var object : Transform;  // The object that we want to move.
    4. public var offSet = Vector3(0, 0, 10);  // Move the center of the object away from the mouse.
    5.  
    6. function Update () {
    7.     if (object) {
    8.         object.position = camera.main.ScreenToWorldPoint(Input.mousePosition)+camera.main.transform.TransformDirection(offSet);
    9.     }
    10. }
    11.  
    What it does is it translates the mouse position into world coordinates. Then it offsets the objects position relative to the camera (so no matter which way the camera is facing the object will always be 10 units in front of the camera).
     
  10. kakubei2

    kakubei2

    Joined:
    Jun 10, 2009
    Posts:
    31
    well, I tried this code and this is what happens:

    if the camera moves, the object moves with it, but if I move the mouse, the object does not move. Not sure why. Debgging tells me that even though the mouse is moving, the object.position is staying the same.

    And yes, I did drag the object in question onto the object variable of the script attached to the camera.

    Maybe this is the intended behaviour of the script? What I was asking for was that the object actually move with the mouse.
     
  11. Mem Dixy

    Mem Dixy

    Joined:
    Feb 26, 2008
    Posts:
    51
    The problem is that the object only follows the mouse position when the camera is in Orthographic mode. When the camera is not in Orthographic mode you get the results that you just described (not entirely sure why that happens). I messed around with the script a bit and now it will move with the mouse (sort of) when the camera is not in Orthographic mode (increasing the near clip plane causes the object to move more).
    Code (csharp):
    1. #pragma strict    // Attach this script to an orthographic camera.
    2. public var object : Transform;    // The object that we want to move.
    3. public var offSet = Vector3(0, 0, 10);    // Move the center of the object away from the mouse pointer.
    4. function Update () {
    5.     if (object) {
    6.         object.position = camera.ScreenPointToRay(Input.mousePosition).origin+camera.transform.TransformDirection(offSet);
    7.     }
    8. }
     
  12. kakubei2

    kakubei2

    Joined:
    Jun 10, 2009
    Posts:
    31
    You're right, this sort of works. But it moves very slowly and I can't increase the movement speed. If I multiply it by a variable, the object leaves the scene (even if the value is 1 which is odd), and it won't accept a + after the script.
     
  13. tyy

    tyy

    Joined:
    Aug 27, 2012
    Posts:
    1
  14. NullRef_

    NullRef_

    Joined:
    Jan 12, 2014
    Posts:
    1
    This was exactly what I was looking for (and the exact situation as well). Thanks a bunch chief.
     
  15. wsessums

    wsessums

    Joined:
    Jul 21, 2014
    Posts:
    1
    If you are just working with objects in 2D. The simplest way I do it is as follows:
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class MouseMove : MonoBehaviour {
    5.  
    6.     private Vector3 handleToOriginVector;
    7.     public bool isDragging;
    8.  
    9.     void OnMouseDown () {
    10.         handleToOriginVector = transform.root.position - Camera.main.ScreenToWorldPoint (Input.mousePosition);
    11.         isDragging = true;
    12.     }
    13.  
    14.     void OnMouseDrag ()    {
    15.         transform.root.position = Camera.main.ScreenToWorldPoint (Input.mousePosition) + handleToOriginVector;
    16.     }
    17.  
    18.     void OnMouseUp () {
    19.         isDragging = false;
    20.     }
    21. }
    The isDragging is optional. I use it as a check for other things that may be running at the same time.
     
    DiegoBittencourt likes this.
  16. DiegoBittencourt

    DiegoBittencourt

    Joined:
    Aug 30, 2015
    Posts:
    1