最终代码

private void OnMouseDrag()
{
Vector3 mousePos = Input.mousePosition;
mousePos.z = camera.WorldToScreenPoint(transform.position).z;
Vector3 pos = Camera.main.ScreenToWorldPoint(mousePos);
transform.position = pos;
}

问题描述

测试鼠标事件 OnMouseDrag()时,想让鼠标拖着游戏物体移动,最开始使用代码

private void OnMouseDrag()
{
transform.position = Camera.main.ScreenToWorldPoint(Input.mousePosition);
}

发现物体不见了,检查发现 Cube 位置变为了(0,1,-10)
CubeTrs

分别输出 Input.mousePosition 和转换后的 Camera.main.ScreenToWorldPoint(Input.mousePosition),发现前者正常显示指针的屏幕坐标,后者始终为(0,1,-10)

分析原因

关于 ScreenToWorldPoint 的使用说明:

  1. 如果是 2D,Camera.main.ScreenToWorldPoint(Input.mousePosition),那么转换后的坐标,直接用没有问题,原因是 2D 的 Z 轴默认是 0,所以转后没有问题

  2. 如果是 3D,Camera.main.ScreenToWorldPoint(Input.mousePosition),那么转换后的坐标是不正确的,因为此时的摄像机是投影摄像机,n 多个面,所以必须指定要转换哪个面上的坐标,即 z 距离所对应的面:Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, transform.position.z)),这样转换后的坐标才是正确的!

解决方案

  1. 修改代码为
Vector3 mousePos = Input.mousePosition;
transform.position = Camera.main.ScreenToWorldPoint(new Vector3(mousePos.x, mousePos.y, transform.position.z));
  1. 发现物体一直在移动,因为每次转换后的 z 轴坐标都在变化,所以锁一下 Z 轴
Vector3 mousePos = Input.mousePosition;
Vector3 pos = Camera.main.ScreenToWorldPoint(new Vector3(mousePos.x, mousePos.y, transform.position.z));
transform.position = new Vector3(pos.x, pos.y, transform.position.z);
  1. 运行发现物体的运动 XY 轴是反向的.

既然 Cube 要使用屏幕坐标,那就将 Cube 的屏幕坐标的 Z 轴赋值给指针屏幕坐标的 Z 轴

Vector3 mousePos = Input.mousePosition;
mousePos.z = camera.WorldToScreenPoint(transform.position).z;
Vector3 pos = Camera.main.ScreenToWorldPoint(mousePos);
transform.position = pos;

现在 Cube 就可以跟随指针移动了

参考

unity 屏幕坐标转世界坐标 ScreenToWorldPoint

从屏幕坐标系转换到世界坐标系