Bootstrap

Unity3d C# 摄像头检测敌方单位(目标层级)并在画面中标注(含源码)

前言

需要实现的功能是通过一个专门的检测摄像头将出现在摄像头画面内的敌方单位检测出来,并通过框选的UI框在画面中标记出来。检测摄像头支持自动检测和手动控制检测,同时需要实现锁定模式,检测到一个敌方单位直接锁定到对象上等功能。

效果

自动检测并锁定:
在这里插入图片描述

自动检测并标注:
在这里插入图片描述

实现

主要的实现思路通过检测摄像头中调用 Physics.OverlapSphere函数,处理检测的层级存在的敌方单位后根据扫描角度筛选出画面中的敌方单位,然后通过 lookCam.WorldToScreenPoint和RectTransformUtility.ScreenPointToLocalPointInRectangle函数计算出敌方单位在摄像头画面中的位置,在对应位置上添加选框UI即可。

UI搭建

敌方单位的标注就用一个Image对象来处理,所以UI搭建比较简单如下图:
在这里插入图片描述

不过MarkerCanvas的Render Mode就将其设置为Screen Space-Camera模式,RenderCamera就是检测摄像头。

检测配置

    [Header("检测的层级")]
    public LayerMask scanLayer = 1;

    [Header("检测半径(远近)")]
    public float viewRadius = 10000;

    [Header("检测角度范围(扇形)")]
    public float detectionAngle = 90;

    [Header("检测旋转速度")]
    public float rotationSpeed = 5.0f; // 旋转速度


    [Header("是否自动旋转检测")]
    public bool isAutoScan = true;
    bool AutoDoing = false;
    float resumeDura = 5; //无目标时,无手动操作5秒后自动扫描。
    Vector3  AutoDir = Vector3.up;

    [Header("是否自动锁定")]
    [Tooltip("检测到后会锁定到对象,只会有一个锁定对象")]
    public bool isTargetLock = false;

    [Header("标记")]
    public TargetMarker marker;

这些是Inspector中暴露出来用于配置的变量,效果如下:
在这里插入图片描述

其中检测的层级可以多选:
在这里插入图片描述

检测逻辑

    float angle = Mathf.Deg2Rad * (detectionAngle * 0.5f);
    Collider[] hitColliders = Physics.OverlapSphere(transform.position, viewRadius, scanLayer);

    targets.Clear();
    foreach (var hitCol in hitColliders)
    {
        Vector3 dir = (hitCol.transform.position - transform.position).normalized;
        if (Mathf.Acos(Vector3.Dot(transform.forward, dir)) > angle)
            continue;

        targets.Add(hitCol.transform);
        // 处理检测到
        // Debug.Log(hitCol.gameObject.name);
        if (isTargetLock)
        {
            scanState = false;
            nowLock = hitCol.transform;
            DoLockAnima();
            break;
        }
  }

调用 Physics.OverlapSphere函数找到scanLayer层级的对象,然后判断画面角度找出敌方单位。

标记实现

   if (LockTran != null)
        {
            if (isInFront())
            {
                Vector2 mScreenPos = lookCam.WorldToScreenPoint(LockTran.position);
                Vector2 mRectPos;
                RectTransformUtility.ScreenPointToLocalPointInRectangle(ParentTran, mScreenPos, lookCam, out mRectPos);
                Rtran.localPosition = mRectPos + Offset;


                DisScale = ScaleRate / Vector3.Distance(LockTran.position, lookCam.transform.position);
                Rtran.localScale = Vector3.one * (DisScale < MinScale ? MinScale : DisScale > MaxScale ? MaxScale : DisScale);

                //Rtran.localScale = Vector3.one;
            }
            else
                Rtran.localScale = Vector3.zero;
        }
        else
            Rtran.localScale = Vector3.zero;

其中LockTran 就是这边标记框需要标记的对象,详情可查改该博客“Unity3d C# 实现UGUI面板跟随标注3D模型功能”。
通过localScale 同时设置了标记框的远小近大,其思路参照这篇博文“Unity3d C#实现三维场景中图标根据相机距离动态缩放功能”。

源码

https://download.csdn.net/download/qq_33789001/90056743

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;