0

I'm new to the concept of transforming screen coordinates to world coordinates in a 3D world in general, even more so in Unity. I'm using the UnityEngine.EventSystems library, implementing the IDragHandler, as I want this to work the same no matter if a mouse or touch input is the source.

I want to drag around a 3D object in world space, locked to one z position. I found this excellent piece of code which I'm now using: http://coffeebreakcodes.com/drag-object-with-mouse-unity3d/

That same code can also be found in the answer here: Drag 3d object using fingers in unity3d

It works exactly as I want it to, apart from one thing: The transformation works fine when the drag starts, but the object makes larger movements than the pointer does, getting worse the further from the object's original position it gets, as if the movement is multiplied with something that gets larger the further away from the original position I get.

This is my code, which is basically the same as in the link above, apart from using the PointerEventData's pointer coordinates instead of Input.mousePosition (a change that did not affect the behaviour):

public class DragInput : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{
    private Vector3 screenPoint;
    private Vector3 offset;

    public void OnBeginDrag(PointerEventData eventData)
    {
        screenPoint = Camera.main.WorldToScreenPoint(transform.position);
        offset = transform.position - Camera.main.ScreenToWorldPoint(new Vector3(eventData.position.x, eventData.position.y, screenPoint.z));
    }

    public void OnDrag(PointerEventData eventData)
    {
        Vector3 cursorPoint = new Vector3(eventData.position.x, eventData.position.y, screenPoint.z);
        Vector3 cursorPosition = Camera.main.ScreenToWorldPoint(cursorPoint) + offset;
        transform.position = cursorPosition;
    }

    public void OnEndDrag(PointerEventData eventData)
    {
    }
}

EDIT: This video clip showcases the behaviour I'm describing: https://www.dropbox.com/s/qfx2p6kznizft1q/Unity%202017-01-13%2014-32-15-89.avi?dl=0

Fredrik Widerberg
  • 3,068
  • 10
  • 30
  • 42
Helena
  • 1,041
  • 2
  • 12
  • 24

3 Answers3

2

This is a perspective problem when dragging around 3D Object.

You need a make a Plane, covert the mouse position(screen pixels) to ray then check where they both intersect.

using UnityEngine;
using UnityEngine.EventSystems;

public class DragInput : MonoBehaviour, IPointerDownHandler, IDragHandler, IEndDragHandler
{
    Camera mainCamera;
    float zAxis = 0;
    Vector3 clickOffset = Vector3.zero;

    // Use this for initialization
    void Start()
    {
        mainCamera = Camera.main;
        zAxis = transform.position.z;
    }

    public void OnPointerDown(PointerEventData eventData)
    {
        clickOffset = transform.position - mainCamera.ScreenPointToWoldOnPlane(eventData.position, zAxis);
    }

    public void OnDrag(PointerEventData eventData)
    {
        transform.position = mainCamera.ScreenPointToWoldOnPlane(eventData.position, zAxis) + clickOffset;
    }

    public void OnEndDrag(PointerEventData eventData)
    {

    }
}

public static class extensionMethod
{
    public static Vector3 ScreenPointToWoldOnPlane(this Camera cam, Vector3 screenPosition, float zPos)
    {
        float enterDist;
        Plane plane = new Plane(Vector3.forward, new Vector3(0, 0, zPos));
        Ray rayCast = cam.ScreenPointToRay(screenPosition);
        plane.Raycast(rayCast, out enterDist);
        return rayCast.GetPoint(enterDist);
    }
}
Programmer
  • 121,791
  • 22
  • 236
  • 328
  • Thank you! This, however, causes the same behaviour as my code. Maybe I did something wrong with another component, like my canvas? – Helena Jan 13 '17 at 14:59
  • I did a test before posting this and it works. It fixed the problem...Did you copy the code as it is? If not, please do that. Make sure that the model is not under any GameObject. This should fix that problem! – Programmer Jan 13 '17 at 15:18
  • What do you mean with that "the model is not under any GameObject", what is the model you are referring to? – Helena Jan 13 '17 at 19:36
  • And yes, I copypasted your code, the only thing still my code being the namespace. – Helena Jan 13 '17 at 19:40
  • I must have confused things a lot. I have now solved it, what I THINK I did (not sure): I had a GraphicsRayCaster on a Canvas, now I removed the Canvas and added a PhysicsRayCaster on the MainCamera instead. I'm still highly unsure of what I'm doing, so I don't dare answer my question with that yet. – Helena Jan 13 '17 at 21:14
  • PhysicsRayCaster is supposed to be attached to the camera. My code should work after you do that. As for the problem you are having, the code in your question cannot solve that problem. Did you use my code or yours to solve that problem? – Programmer Jan 14 '17 at 03:26
  • As the problem was my Unity setup and not the code. both my code and yours produced the error when I had the Canvas and GraphicsRaycaster, and both my code and yours worked fine when I deleted the Canvas and GraphicsRaycaster and instead added a PhysicsRaycaster to MainCamera. However, my code is not locked to the z axis, which is a problem I think your code solves. – Helena Jan 14 '17 at 06:45
  • oK, that makes sense. FYI You can have Canvas and GraphicsRaycaster attached to the Canvas. That won't interfere with the PhysicsRaycaster as long as PhysicsRaycaster is attached to the Camera. If your problem is solved, you can close this question by accepting it. Happy coding! – Programmer Jan 14 '17 at 12:58
  • Dude! @Programmer, you saved my life! Thank you. Will you marry me? – ReGaSLZR Dec 21 '20 at 04:33
0

The cause of the problem I posted for, was due to me having forgotten a PhysicsRaycaster on the MainCamera. I had a GraphicsRaycaster on a Canvas, but no PhysicsRaycaster present - I think that might have caused odd behaviours.

However, my code as it is has problems with the other desired behaviour I described, moving the object in a fixed distance to the camera. See @Programmer's answer to see how to fix that.

Their code is however not what solved the issue that was the purpose of this post, hence their answer not being the accepted answer.

Helena
  • 1,041
  • 2
  • 12
  • 24
-1

Take a look at this snippet:

pos = Camera.main.ScreenToWorldPoint (Vector3 (Input.GetTouch(0).position.x,Input.GetTouch(0).position.y, 5));
transform.position = new Vector3(pos.x, pos.y, pos.z);

or else you can try this class, if this isn't working, I'm out of options :)

using UnityEngine;
using System.Collections;

public class DragObject : MonoBehaviour {

 private float dist;
 private Vector3 v3Offset;
 private Plane plane;
 private bool ObjectMouseDown = false;
 public GameObject linkedObject;

 void OnMouseDown() {
     plane.SetNormalAndPosition(Camera.main.transform.forward, transform.position);
     Ray ray = Camera.main.ScreenPointToRay (Input.mousePosition);
     float dist;
     plane.Raycast (ray, out dist);
     v3Offset = transform.position - ray.GetPoint (dist);     
     ObjectMouseDown = true;
 }

 void OnMouseDrag() {
     if (ObjectMouseDown == true){
         Ray ray = Camera.main.ScreenPointToRay (Input.mousePosition);
         float dist;
         plane.Raycast (ray, out dist);
         Vector3 v3Pos = ray.GetPoint (dist);
         v3Pos.z = gameObject.transform.position.z;
         v3Offset.z = 0;
         transform.position = v3Pos + v3Offset;

         if (linkedObject != null){ 
             linkedObject.transform.position = v3Pos + v3Offset;
         }
     }
 }

 void OnMouseOut() {
     ObjectMouseDown = false;
 }

 }
Svekke
  • 1,470
  • 1
  • 12
  • 20
  • Thank you! However, this code is definitely simpler than mine, but it still produces the same behaviour. – Helena Jan 13 '17 at 13:24