最近搞了个屏幕外重要单位指示的东西,效果见上图。开发过程中发现:当GameObject在Camera后面的时候,WorldToViewportPoint计算出来的坐标可能不准确。从表现上看,是错误的位置和正确的位置是关于屏幕中心对称的(对角线关系)。
问了一些人,说这种情况要自己计算。苦想半天也不知道要怎么算(数学忘光有点伤)Orz...最后找了个取巧的办法。问题产生的情况是对象在相机后面,那么计算时把对象位置拉到相机前面应该就可以了吧?
顺着这个思路,我把GameObject的坐标投影到相机的近裁切面上,然后用投影点的坐标进行计算 —— 得到了想要的结果~
下面记录几个要点备忘:
- 用Plane表示近裁切面
Plane nearPlane = new Plane();
Vector3 forward = camTrans.forward;
Vector3 center = camTrans.position + (forward * cam.nearClipPlane);
nearPlane.SetNormalAndPosition(forward, center);
- 用NearPlane判断是否需要矫正坐标
只有NearPlane后面的对象需要矫正
// NearPlane前面的一个点坐标
Vector3 forwordPoint = _nearPlane.Center + _nearPlane.Forward;
if (_nearPlane.NearPlane.SameSide(forwordPoint, target.position))
{
// 矫正
}
else
{
// 不矫正
}
- 获得目标延相机方向在NearPlane上的投影坐标(矫正后的坐标)
Unity提供了Vector3.ProjectOnPlane做Vector3向平面的投影,但在这里它不能用。从它两个形参就能看出了,它是做向量的投影,而不是点;它需要的投影平面仅用一个法向量表示,而我要的是一个过定点的平面。
没有找到投影的API,最后采用了射线的方案。
Ray projectRay = new Ray();
projectRay.direction = forward;
projectRay.origin = target.position;
float dis;
if (NearPlane.Raycast(_projectRay, out dis))
{
// 投影后的坐标!
Vector3 projPoint = _projectRay.GetPoint(dis);
}