接着上一篇来
Unity UI上的物体跟随场景物体位置变化而变化(人物血条/称号)
上一篇写了血条跟随角色变化 写完之后 我需要用相机的 field Of View 功能做一个远近景切换的功能 然后就发现了下边的问题:
当我的field Of View 值为60时一切正常
当我把field Of View 值调到20时:
我看着小小的血条和大大的模型陷入了沉思
这是一种视觉上的错位 血条并没有变小 只是模型变大了 然后显得血条变小了 有没有一种不用把血条挂到模型上 还能让它和模型大小同步的方法呢?
然后我试着求出了人物在相机照射位置 相机视口的横截面的宽 根据field Of View 变化前后的宽的比例 得到血条的缩放比例 以达到目的
求人物所在相机横截面的宽的步骤如下:
1.先从相机的位置往相机的最大横截面的中心点的向量(相机视锥体的高)
//获取相机方向
Vector3 cameraDir = mainCamera.transform.forward;
//获取相机最远横截面的中心点
Vector3 cameraSight = mainCamera.transform.position + cameraDir * mainCamera.farClipPlane;
Debug.DrawLine(mainCamera.transform.position, cameraSight, Color.green);
2.求出相机到角色位置的向量在相机视锥体的高上做投影 求出角色所在的横截面的高
Vector3 cam2Target = mainCamera.transform.position - tf.position;
//获取相机到目标的向量在相机方向上的投影 即 相机 到 物品所在相机的横截面的中心点 的向量
Vector3 v1 = Vector3.Project(cam2Target, cameraDir);
Debug.DrawLine(mainCamera.transform.position, target.position, Color.red);
Debug.DrawLine(mainCamera.transform.position, mainCamera.transform.position - v1, Color.yellow);
图中红线为相机到角色的向量(c2p) 黄线为 c2p在视锥体的高上的投影
3.根据顶角(field Of View 的一半)和临边(第二步求出来的值)求出底边(即为视锥体横截面的宽的一半)
0是相机的位置 A是上边 B是下边
图中角AOB的值为相机的field Of View
0是相机的位置 A是上边 B是下边
图中角AOC或者角BOC为相机的field Of View的一半
根据勾股定理可以求出AC或者BC的长度 此长度为角色所在相机横截面的宽的一半
//相机 到 物品所在相机的横截面的中心点 的距离 即是 相机视锥体的高
float length = v1.magnitude;
//视锥的角度的一半
float halfAngle = mainCamera.fieldOfView / 2f;
float halfDi = length * Mathf.Tan(Mathf.Deg2Rad * halfAngle);
//给定距离的视锥体的横截面高度
float InitFrustumHeight = 2 * halfDi;
拓展:
根据横截面的宽可以求出横截面的长
代码如下:
float aspect = mainCamera.aspect;
//给定距离视锥体的横截面长度
float InitFrustumWidth = InitFrustumHeight * aspect;
完整代码是:
Vector2 GetViewWidthAndHeight(Transform tf) {
//获取相机方向
Vector3 cameraDir = mainCamera.transform.forward;
//获取相机最远横截面的中心点
Vector3 cameraSight = mainCamera.transform.position + cameraDir * mainCamera.farClipPlane;
Debug.DrawLine(mainCamera.transform.position, cameraSight, Color.green);
//获取相机到目标tf的向量
Vector3 cam2Target = mainCamera.transform.position - tf.position;
//获取相机到目标的向量在相机方向上的投影 即 相机 到 物品所在相机的横截面的中心点 的向量
Vector3 v1 = Vector3.Project(cam2Target, cameraDir);
Debug.DrawLine(mainCamera.transform.position, target.position, Color.red);
Debug.DrawLine(mainCamera.transform.position, mainCamera.transform.position - v1, Color.yellow);
//相机 到 物品所在相机的横截面的中心点 的距离 即是 相机视锥体的高
float length = v1.magnitude;
//视锥的角度的一半
float halfAngle = mainCamera.fieldOfView / 2f;
//视锥的底边长
float halfDi = length * Mathf.Tan(Mathf.Deg2Rad * halfAngle);
//给定距离的视锥体的横截面高度
float InitFrustumHeight = 2 * halfDi;
///视口的长宽比
float aspect = mainCamera.aspect;
//给定距离视锥体的横截面长度
float InitFrustumWidth = InitFrustumHeight * aspect;
//heightRote = hightRate.y / InitFrustumHeight;
return new Vector2(InitFrustumWidth, InitFrustumHeight);
}
返回是物体所在视口的长和宽
然后把方法带入到我们刚开始的问题中
完整代码如下:
public class test5 : MonoBehaviour {
public Transform target;
public Transform hpSp;
public Camera mainCamera;
public Vector3 offsetPos;
Vector2 v1;
Vector3 hpSca;
private void Start() {
v1 = GetViewWidthAndHeight(target);
hpSca = hpSp.localScale;
}
void Update() {
Vector3 pos = mainCamera.WorldToScreenPoint(target.position + offsetPos);
//Screen.width和Screen.height分别为屏幕的宽的像素和屏幕的高的像素
Vector3 pos1 = new Vector3(pos.x - Screen.width / 2, pos.y - Screen.height / 2, pos.z);
float scale = 1280f / Screen.width;
Vector3 screenPos = new Vector3(pos1.x * scale, pos1.y * scale, 0.0f);
hpSp.transform.localPosition = screenPos;
Vector2 v2 = GetViewWidthAndHeight(target);
float rate = v1.x / v2.x;
hpSp.localScale = hpSca * rate;
}
Vector2 GetViewWidthAndHeight(Transform tf) {
//获取相机方向
Vector3 cameraDir = mainCamera.transform.forward;
//获取相机最远横截面的中心点
Vector3 cameraSight = mainCamera.transform.position + cameraDir * mainCamera.farClipPlane;
Debug.DrawLine(mainCamera.transform.position, cameraSight, Color.green);
//获取相机到目标tf的向量
Vector3 cam2Target = mainCamera.transform.position - tf.position;
//获取相机到目标的向量在相机方向上的投影 即 相机 到 物品所在相机的横截面的中心点 的向量
Vector3 v1 = Vector3.Project(cam2Target, cameraDir);
Debug.DrawLine(mainCamera.transform.position, target.position, Color.red);
Debug.DrawLine(mainCamera.transform.position, mainCamera.transform.position - v1, Color.yellow);
//相机 到 物品所在相机的横截面的中心点 的距离 即是 相机视锥体的高
float length = v1.magnitude;
//视锥的角度的一半
float halfAngle = mainCamera.fieldOfView / 2f;
//视锥的底边长
float halfDi = length * Mathf.Tan(Mathf.Deg2Rad * halfAngle);
//给定距离的视锥体的横截面高度
float InitFrustumHeight = 2 * halfDi;
///视口的长宽比
float aspect = mainCamera.aspect;
//给定距离视锥体的横截面长度
float InitFrustumWidth = InitFrustumHeight * aspect;
//heightRote = hightRate.y / InitFrustumHeight;
return new Vector2(InitFrustumWidth, InitFrustumHeight);
}
}
效果:
field Of View = 60
field Of View = 20
field Of View = 80