unity 求出物体所在相机的横截面 解决调整 field Of View后场景物体和UI不匹配

接着上一篇来

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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一梭键盘任平生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值