Unity 模拟摄像头拍摄到玩家触发检测的三种方法

2019-08-04  本文已影响0人  周末的游戏之旅

方法一 触发器

这种方法比较简单,直接给摄像机加一个四棱锥体的触发器即可。


优点
简单,直接用触发检测即可。
缺点
这种方法虽然简单,但检测效果并不真实,因为使用的是一个四棱锥触发器,而真实的摄像头检测范围应该是一个圆,如果使用圆锥体的触发器则会增加性能消耗。

方法二 范围检测

向摄像机的前方发射一条射线,以该射线和地面的交点为圆心,画一个圆。如果玩家在这个圆内,则触发警报。其实只要检测玩家和圆心点的位置是否小于半径就可以了。半径自己定义。
效果如下,为了更方便显示,我给摄像头加了一个Spotlight。


优点
在检测的精确度上比第一个方法好很多,同时性能开销也比较小,只用了一条射线。
缺点
还是不够真实,因为摄像头的角度是倾斜于地面的,而不是垂直于地面的,所以检测的范围在地面上应该是一个椭圆。
代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CCTVCircularDetection : MonoBehaviour {

    //射线检测
    RaycastHit hit;
    //玩家
    Transform player;
    //半径
    public float radius = 1f;

    private void Awake()
    {
        //获取玩家对象
        player = GameObject.FindWithTag(StealthConst.PLAYER).transform;
    }
    
    void Update () {
        //发出射线
        if (Physics.Raycast(transform.position,transform.forward,out hit))
        {
            Debug.Log("检测到");
            //辅助线,同射线
            Debug.DrawLine(transform.position,hit.point);
            //辅助线,同半径
            Debug.DrawRay(hit.point, Vector3.back *radius);
            //判断玩家是否在圆内
            if (Vector3.Distance(hit.point,player.position)<radius)
            {
                Debug.Log("检测到玩家");
            }
        }
    }
}

方法三 锚点检测法

这是我自己思考出来的办法,不使用射线和触发器,纯粹的数学算法。原理如下:
使用向量的叉乘来判断玩家是否在摄像头的前方,如下图。


如果玩家在摄像头的正前方,则计算玩家与摄像头之间的向量和摄像头正前方的向量之间的夹角是否小于阈值。如果小于阈值说明玩家被检测到。如下图:


但是只做这样的检测是不够的,因为玩家到摄像机的向量是从玩家中心点计算的,假定玩家的中心点在玩家模型的中心处,那么如果玩家只露出来头部或者腿部、手部等位置,是检测不到的,显然这是不科学的。如下图:


为了解决这种尴尬的情况,我们需要给玩家添加一些锚点来作为检测点。这样就可以避免一些尴尬的情况。如下图,给玩家添加了上、中、下 三个锚点用于检测:


优点:
没有使用物理系统,只是纯粹的数学计算,性能开销小。
检测比较精确,且可以灵活调整(锚点越多性能开销越大)。
缺点:
计算方法比上面两种方法复杂。
随着锚点的增加,性能开销也会增加。

代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class cctvCamera : MonoBehaviour {

    /// <summary>
    /// 角度范围
    /// </summary>
    public float angleRange = 30f;
    /// <summary>
    /// 距离s
    /// </summary>
    public float distance = 10f;

    private Transform player;

    //摄像机到玩家之间的向量
    private Vector3 dir;

    private void Awake()
    {
        player = GameObject.FindWithTag("Player").transform;
    }

    void Update() {
        dir = player.position - transform.position;

        //当玩家在摄像机的前方时
        if (Vector3.Dot(transform.forward, dir) > 0)
        {

            //遍历所有锚点
            for (int i = 0; i < player.childCount; i++)
            {           //计算玩家和摄像机之间的距离
                if (Vector3.Distance(transform.position, player.GetChild(i).position) < distance)
                {
                    //计算锚点与摄像机的向量和摄像机前方的向量的夹角
                    float angle = Vector3.Angle(transform.forward, player.GetChild(i).position - transform.position);
                    if (angle < angleRange)
                    {
                        Debug.DrawLine(transform.position, player.GetChild(i).position);
                    }
                }
            }
        } 
    }
}

上一篇下一篇

猜你喜欢

热点阅读