Search Unity

Dragging a object on two axis regardless of camera angle?

Discussion in 'Scripting' started by Ifandbut, Dec 1, 2010.

  1. Ifandbut

    Ifandbut

    Joined:
    Dec 1, 2010
    Posts:
    19
    I am having problems with using the mouse to drag objects around. In my searching I noticed this is a fairly common issue, however the solutions that I have seen seem to be limited in some way (one solution only worked with orthographic cameras).

    Currently my scene consists of a flat cube and a sphere. I have no physics enabled. I have a camera that is controlled with WASD for movement and when you hold down the RMB and drag you rotate the view around on the camera.

    I also have some code that does a raytrace to see if the LMB clicked on an object, then to move that object with the mouse on the world X and Z axis (I plan on using shift+LMB to move the object on the world Y).
    Code (csharp):
    1.  
    2. //This is all in the Update() function with my camera movement logic
    3.  
    4. var raydist : float;
    5. var hit : RaycastHit;
    6. var ray = Camera.main.ScreenPointToRay(Input.mousePosition);
    7. if(Input.GetMouseButtonDown(0)){
    8.     if(!object){
    9.         if(Physics.Raycast(ray, hit, 100)){
    10.             object = hit.transform;
    11.             offset = object.position-ray.origin;
    12.             raydist = hit.distance;
    13.         }
    14.     }
    15. } else if(Input.GetMouseButtonUp(0)) {
    16.     object = null;
    17. }
    18.    
    19. if(object){
    20.     var mouseworld : Vector3 = camera.ScreenToWorldPoint(Vector3(Input.mousePosition.x, Input.mousePosition.y, raydist));
    21.     object.position = Vector3(mouseworld.x, object.position.y, mouseworld.z+mouseworld.y);
    22. }
    This works well enough when the camera is facing the world Z axis, or even when looking directly down on the scene. However, it completely messes up when I turn the camera 90 degrees.

    I kind of have an idea on what the issue is but I have no idea how to implement it in code.
    When the camera is looking down the world Z axis, the screen Y axis = world Z and screen X = world X. When rotating the camera 90 degrees on the Y axis (90 degrees to the right) the screen Y becomes world X and screen X becomes world Z.

    I am sure I am missing something simple. I just started learning scripting in Unity today and getting the camera movement has been simple enough, I'm surprised at how hard dragging and object around is turning out to be.

    If anyone could give me some advice on what I am missing or a link to some code that does this I would be grateful.
    -Ifandbut
     
  2. ivkoni

    ivkoni

    Joined:
    Jan 26, 2009
    Posts:
    978
    your mouse is in 2D, your object is in 3D. You need to decide how to translate the mouse movement in 2D to movement of your object in 3D.
    I would shoot a ray from the mouse position and then see where this ray intersects the plane that I want to move my object.
    You can use Plane.Raycast for this.
     
  3. Ifandbut

    Ifandbut

    Joined:
    Dec 1, 2010
    Posts:
    19
    Maybe I dont understand exactly what you mean but Plane.Raycast only returns a success/fail bool and a float distance (in my case I think it would be the distance from the camera pixel to the plane). If it returned a Vector2 then I could translate that into transform.position.

    I thought of another way I could do this. When the ray hit an object I would:
    1) Create an empty game object
    2) Rotate that object so it points the same direction as the camera and it's Y becomes the window's Y, as does X
    3) Parent that empty game object to the object I hit
    4) Translate the empty game object on it's local axis
    5) Check how far the target object has moved off the world Y axis and preform a translate in the opposite direction.
    When the mouse is released:
    6) Un-parent
    7) destroy the empty object

    However, I cant seem to find the function I need to call to create a new empty game object.

    Edit: I'm stupid. x = new GameObject(); I'm going to give my idea a try now. I'm still interested in that Plane.Raycast if you care to explain it more.
     
    Last edited: Dec 1, 2010
  4. ivkoni

    ivkoni

    Joined:
    Jan 26, 2009
    Posts:
    978
    you can use the distance with Ray.GetPoint to get the point in the plane where the ray would hit the plane. Then you would move your object to this point. (maybe with some smoothing).
     
  5. Ifandbut

    Ifandbut

    Joined:
    Dec 1, 2010
    Posts:
    19
    Ah, I see now. I'll give that a try a little later. I ended up getting what I needed with the method I described in my last post but I think this Ray method would be quicker and more efficient.
     
  6. ivkoni

    ivkoni

    Joined:
    Jan 26, 2009
    Posts:
    978
    Code (csharp):
    1. private var ray : Ray;
    2. private var hit : RaycastHit;
    3. private var objectToDrag : Transform;
    4. private var dragPlane : Plane;
    5. private var dragPlaneNormal : Vector3 = Vector3.up;
    6. private var distanceToDragPlane : float = 0;
    7.  
    8. function Update () {
    9.  
    10. if(Input.GetMouseButtonDown(0)) {
    11.      ray = Camera.main.ScreenPointToRay (Input.mousePosition);
    12.  
    13.         if (Physics.Raycast (ray, hit, 100)) {
    14.             objectToDrag = hit.transform;
    15.             dragPlane = Plane(dragPlaneNormal, objectToDrag.position);
    16.         }
    17.     }
    18.    
    19. if(Input.GetMouseButton(0)) {
    20.      ray = Camera.main.ScreenPointToRay (Input.mousePosition);
    21.         if(objectToDrag) {
    22.             if(dragPlane.Raycast(ray,distanceToDragPlane)){
    23.                 objectToDrag.position = ray.GetPoint(distanceToDragPlane);
    24.             }
    25.         }
    26.     }
    27.    
    28. if(Input.GetKeyDown(KeyCode.Z)){
    29.     dragPlaneNormal = Vector3.forward;
    30. }
    31.  
    32. if(Input.GetKeyDown(KeyCode.X)){
    33.     dragPlaneNormal = Vector3.right;
    34. }
    35.  
    36. if(Input.GetKeyDown(KeyCode.Y)){
    37.     dragPlaneNormal = Vector3.up;
    38. }
    39.    
    40. }
    here is a short and dirty script to illustrate the idea.
    I give you the honor to implement it properly (with grid snapping, axis movement, precision, smoothing, local coordinates etc.) and share it with the community.
    And don't say I never gave you anything.
     
  7. Ifandbut

    Ifandbut

    Joined:
    Dec 1, 2010
    Posts:
    19
    Ok, I got that Plain.Raycast thing working now...sorta. For some reason it does not update as I move the mouse (Raycast is returning false for some reason).

    Here is the code I have so far:
    Code (csharp):
    1.  
    2. private var object : Transform;
    3.  
    4. function Update () {
    5.     var hit : RaycastHit;
    6.     var moveAxis : Plane;
    7.     var ray = Camera.main.ScreenPointToRay(Input.mousePosition);
    8.     var intersect : float;
    9.     var movePoint : Vector3;
    10.  
    11.    
    12.     if(Input.GetMouseButtonDown(0)){
    13.         if(!object){
    14.             if(Physics.Raycast(ray, hit, 100)){
    15.                 object = hit.transform;
    16.                 moveAxis = Plane(Vector3.up, object.position);
    17.             }
    18.         }
    19.     } else if(Input.GetMouseButtonUp(0)) {
    20.         object = null;
    21.     }
    22.    
    23.     if(object){
    24.         if(moveAxis.Raycast(ray, intersect)) {
    25.             movePoint = ray.GetPoint(intersect);
    26.             Debug.Log("x "+movePoint.x+" y "+movePoint.y+" z "+movePoint.z);
    27.             object.position = movePoint;
    28.         }
    29.     }
    30. }
    31.  
    This is much simpler (and likely more reliable) then the way I came up with. I just need to get it so it will keep updating as I drag the mouse.
     
  8. Ifandbut

    Ifandbut

    Joined:
    Dec 1, 2010
    Posts:
    19
    Odd, I came up with (mostly) the same thing you posted just before me, yet yours works perfectly and mine does not.
    ...
    ...
    ...
    Never mind...another stupid mistake. I was redefining the Plane variable every time Update ran.

    Thanks a bunch for the code to compare it to.