【Unity】判断 物体/坐标 是否在相机范围内

一、当物体有渲染(Renderer)组件时:

        这种情况比较简单,监听两个Unity事件即可:

    #region 可见性判断

    public bool IsVisableInCamera { get; private set; }

    private void OnBecameVisible() { IsVisableInCamera = true; }

    private void OnBecameInvisible() { IsVisableInCamera = false; }

    #endregion

        当然,这种判断是有条件的:

        1、必须是个MonoBehaviour;

        2、必须挂载渲染组件;

        两种条件不满足的话,这两个Unity消息函数是不会调用的。

二、直接用坐标判断:

        这种情况就需要借用相机来判断,上代码:

    #region 可见性判断

    public bool IsVisableInCamera
    {
        get
        {
            Camera mCamera = Camera.main;
            Vector3 pos = transform.position;
            //转化为视角坐标
            Vector3 viewPos = mCamera.WorldToViewportPoint(pos);
            // z<0代表在相机背后
            if (viewPos.z < 0) return false;
            //太远了!看不到了!
            if (viewPos.z > mCamera.farClipPlane)
                return false;
            // x,y取值在 0~1之外时代表在视角范围外;
            if (viewPos.x < 0 || viewPos.y < 0 || viewPos.x > 1 || viewPos.y > 1) return false;
            return true;
        }
    }

    #endregion

        这里的调用的API:WorldToViewportPoint ,用来转化成视角的坐标,返回一个Vector3。

        这里可以参考Unity的API:Unity - Scripting API: Camera.WorldToViewportPoint

        其中Z值代表在屏幕前或者后,为正代表在屏幕前方,为负代表在屏幕后方。

        X、Y值代表在视角内的坐标,在屏幕内的话取值为0~1 。

        这下判定就简单了,如代码所示即可。

        注意:

        正式代码不要直接调用 Camera.main ,性能不好。

三、使用相机视野包围盒判定

        上面调用相机的API是有缺陷的:他只能判定一个点的情况。然而对于大部分3D模型,都不会只判定一个点,而是需要判定包围盒。

        所以还可以使用相机视野包围盒来判定,这样判定的结果就是非常精确的了。判定方式可以直接调用Unity的API即可,有现成的:

private Plane[] mTempCameraPlanes = new Plane[6];//相机包围盒
public Plane[] CalculateFrustumPlanes() { return mTempCameraPlanes; }

//通过相机包围盒来判定物体是否在视野中。
public bool Check_WoldPointIsVisableInMainCamera_ByBonds(Vector3 center, Vector3 size)
{
    var planes = CalculateFrustumPlanes();
    Bounds bound = new Bounds(center, size);//这里的Size是半径
    return GeometryUtility.TestPlanesAABB(planes, bound);
}

        这个原理就非常简单了,相机的视野由六个平面组成:近裁剪面、远裁剪面,再加上上下左右4个平面。然后3D模型,简化成一个方形包围盒(中心点+半径),然后与相机的包围盒进行碰撞检测(Test AABB),据此来判定是否在相机视野内。

        当然,相机的六个平面也是需要实时读取的,不过只需要在LateUpdate里缓存一次即可:

private void LateUpdate()
{
    //调用Unity的API,获取相机包围盒
    if (mainCamera)
        GeometryUtility.CalculateFrustumPlanes(mainCamera, mTempCameraPlanes);
}

        这个性能消耗显然是比判断单个点要高的,但是结果也更为精准。在性能压力不大的情况下,建议使用这个方式来判定,能够获得更好的显示效果。

你可能感兴趣的:(游戏开发,Unity,unity,游戏开发,c#)