游戏开发37课 狙击枪 视野问题

news2024/7/4 5:11:55

 

首先说一下视野的思路:我们可视化的视野全部都是以扇形显示的,同时为了后期的方便调整我们的视野和距离都必须是动态的。那么我们是不是可以使用度数来控制视野范围,那么我们就需要画出一个扇形。那么我们可以先画出来一个圆 然后在这个圆上面来截取扇形,在画圆之前我们需要定义圆的半径(因为我们的物体在圆的中心,所以圆的半径就代表了视野的纵向长度,代表了物体可以看多远),我们还需要定义一个视野的角度(视野 的角度代表了扇形的大小,表示视野的横向大小)。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FieldOfView : MonoBehaviour
{
    /// <summary>
    /// 视野的半径
    /// </summary>
    public float viewRadius;
    /// <summary>
    /// 视野的角度
    /// </summary>
    [Range(0, 360)]//Unity特性(限制最大最小值)并以滑动条的形式显示出来
    public float viewAngle;
    /// <summary>
    /// 玩家的层级
    /// </summary>
    public LayerMask targetMask;
    /// <summary>
    /// 障碍物的层级
    /// </summary>
    public LayerMask obstacleMask;

    /// <summary>
    /// 保存射线打到敌人的位置的列表
    /// </summary>
    [HideInInspector]//特性(使其不会在检视面板显示)
    public List<Transform> visibleTargets = new List<Transform>();

    /// <summary>
    /// 每一度发射多少射线
    /// </summary>
    public float meshResolution;
    public int edgeResolverations;
    public float edgeDstThreshold;

    /// <summary>
    /// 布尔值控制是否可以进行变色
    /// </summary>
    bool isColor = false;

    /// <summary>
    /// 网格
    /// </summary>
    public MeshFilter viewMeshFilter;

    Mesh viewMesh;

    private void Start()
    {
        //viewMesh = new Mesh();
        //viewMesh.name = "View Mesh";
        //viewMeshFilter.mesh = viewMesh;
        //viewMesh.name = "111";
        viewMesh = new Mesh();
        viewMeshFilter.mesh = viewMesh;
        //开启协程
        StartCoroutine("FindTargetsWithDelay",0.2f);
    }
    IEnumerator FindTargetsWithDelay(float delay)
    {
        while (true)
        {
            yield return new WaitForSeconds(delay);
            FindVisibleTarget();
        }
    }

    void LateUpdate()
    {
        DrawFieldOfView();
    }

    /// <summary>
    /// 把打到的玩家物体保存在数组中
    /// </summary>
    void FindVisibleTarget()
    {
        //列表清空
        visibleTargets.Clear();

        //创建一个数组保存所有与重叠球接触的碰撞器 。 方法的参数数据为(球的中心,球的半径,选择投射的层级)
        Collider[] targetsInViewRadius = Physics.OverlapSphere(transform.position, viewRadius, targetMask);
        //遍历数组中所有的碰撞器
        for (int i = 0; i < targetsInViewRadius.Length; i++)
        {
            //遍历接收所有碰撞器的位置
            Transform target = targetsInViewRadius[i].transform;
            //获取接收到的碰撞器与物体之间的vector3向量(方向)
            Vector3 dirToTarget = (target.position - transform.position).normalized;
            //如果物体与碰撞器的角度小于视野角度的二分之一
            if (Vector3.Angle(transform.forward, dirToTarget) < viewAngle / 2)
            {
                //计算物体与碰撞器的距离
                float dstToTarget = Vector3.Distance(transform.position, target.position);
                //如果射线没有打到障碍物就把打到的物体保存在数组中{如果视野范围内有玩家,判断他们之间是否有障碍物}(起点,方向,射线的最大长度,射线可以照到的层级)
                if (!Physics.Raycast(transform.position, dirToTarget, dstToTarget, obstacleMask))
                {
                    //把打到的层为targetMask的物体保存在数组中
                    visibleTargets.Add(target);
                    //print("打到了" + target.tag);
                    isColor = true;
                }
                else
                {
                    isColor = false;
                }
            }
        }
    }
    //控制视野的变色
    float a = 0f;
    private void Update()
    {
        if (isColor)
        {
            a += 0.001f;
            print(a);
            if (a >= 1)
            {
                a = 1;
            }
            MeshRenderer ma = GameObject.Find("View Visualisation").GetComponent<MeshRenderer>();
            ma.material.color = Color.Lerp(new Color(1, 1, 1, .5f), new Color(1, 0, 0, 0.5f),/*Mathf.PingPong(Time.time,1)*/a);
            print(ma.material.color);
        }
        else
        {
            a = 0;
            MeshRenderer ma = GameObject.Find("View Visualisation").GetComponent<MeshRenderer>();
            ma.material.color = new Color(1, 1, 1, .5f);
        }
        print(isColor);
    }

    /// <summary>
    /// 绘制视图
    /// </summary>
    void DrawFieldOfView()
    {
        //判断发射多少条射线
        int stepCount = Mathf.RoundToInt(viewAngle * meshResolution);
        print(stepCount);
        //把视野分为以0.1度为单位
        float stepAngleSize = viewAngle / stepCount;
        //创建一个Vector3的列表
        List<Vector3> viewPoints = new List<Vector3>();

        //以一度为单位遍历所有的视野度数
        for (int i = 0; i <= stepCount; i++)
        {
            //物体的旋转跟随移动
            float angle = transform.eulerAngles.y - viewAngle / 2 + stepAngleSize * i;
            //print("欧拉角:" + angle);
            //Debug.DrawLine(transform.position, transform.position + DirFromAngle(angle, true) * viewRadius, Color.red);
            //把角度传进去判断是否达到了障碍物层级(如果达到了障碍物层级就把障碍物的位置,距离,还有传进去的角度全部返回结构体)
            ViewCastInfo newViewCast = ViewCast(angle);
            //if (i > 0)
            //{
            //    //如果i大于0返回视野打到障碍物的距离是否大于0.5
            //    bool edgeDsrThresholdExceeded = Mathf.Abs(oldViewCast.dst - newViewCast.dst) > edgeDstThreshold;
            //    print(edgeDsrThresholdExceeded);
            //    print("old" + oldViewCast.point);
            //    print(edgeDstThreshold);
            //    //如果old的布尔值不等与new的布尔值或者old为true new为true,edge 为true(判断打到了障碍物)
            //    if (oldViewCast.hit != newViewCast.hit || (oldViewCast.hit && newViewCast.hit && edgeDsrThresholdExceeded))
            //    {
            //        EdgeInfo edge = FindEdge(oldViewCast, newViewCast);
            //        if (edge.pointA != Vector3.zero)
            //        {
            //            viewPoints.Add(edge.pointA);
            //        }
            //        if (edge.pointB != Vector3.zero)
            //        {
            //            viewPoints.Add(edge.pointB);
            //        }
            //    }
            //}
            //保存所有的Vector3
            viewPoints.Add(newViewCast.point);
            //oldViewCast = newViewCast;
        }
        //顶点数
        int vertexCount = viewPoints.Count + 1;
        //保存顶点的数组
        Vector3[] vertices = new Vector3[vertexCount];
        //保存构建所有三角形的所有顶点数
        int[] triangles = new int[(vertexCount - 2) * 3];
        //第一个顶点的位置为0
        vertices[0] = Vector3.zero;
        //因为设置了第一个顶点,所以需要减1
        for (int i = 0; i < vertexCount - 1; i++)
        {
            //给保存顶点的数组赋值(世界坐标转换成局部坐标)
            vertices[i + 1] = transform.InverseTransformPoint(viewPoints[i]);

            if (i < vertexCount - 2)
            {
                //给构建所有三角形的所有数组赋值
                triangles[i * 3] = 0;
                triangles[i * 3 + 1] = i + 1;
                triangles[i * 3 + 2] = i + 2;
            }

        }
        viewMesh.Clear();
        //网格的顶点等于顶点数组
        viewMesh.vertices = vertices;
        //给构建三角形的所有顶点赋值
        viewMesh.triangles = triangles;
        //每次赋值结束重新计算法线
        viewMesh.RecalculateNormals();

    }

    //EdgeInfo FindEdge(ViewCastInfo minViewCast, ViewCastInfo maxViewVast)
    //{
    //    //最小的角度
    //    float minAngle = minViewCast.angle;
    //    //最大的角度
    //    float maxAngle = maxViewVast.angle;
    //    //最小向量
    //    Vector3 minPoint = Vector3.zero;
    //    //最大向量
    //    Vector3 maxPoint = Vector3.zero;

    //    //遍历4
    //    for (int i = 0; i < edgeResolverations; i++)
    //    {
    //        float angle = (minAngle + maxAngle) / 2;

    //        ViewCastInfo newViewCast = ViewCast(angle);

    //        bool edgeDsrThresholdExceeded = Mathf.Abs(minViewCast.dst - newViewCast.dst) > edgeDstThreshold;
    //        if (newViewCast.hit == minViewCast.hit && !edgeDsrThresholdExceeded)
    //        {
    //            minAngle = angle;
    //            minPoint = newViewCast.point;
    //        }
    //        else
    //        {
    //            maxAngle = angle;
    //            maxPoint = newViewCast.point;
    //        }
    //    }

    //    return new EdgeInfo(minPoint, maxPoint);

    //}

    /// <summary>
    /// 把传进来的角度转为向量,然后发射射线判断是否达到了障碍物层级(如果打到了障碍物返回碰撞信息以及障碍物的位置以及障碍物的距离)
    /// </summary>
    /// <param name="globalAngle"></param>
    /// <returns></returns>
    ViewCastInfo ViewCast(float globalAngle)
    {
        //把传进来的角度转为向量
        Vector3 dir = DirFromAngle(globalAngle, true);
        RaycastHit hit;
        //如果投射了射线(射线的参数为:起点的位置,射线的方向,射线碰撞信息的返回值,射线的最大长度,射线可以投射的层级为障碍物层级)
        if (Physics.Raycast(transform.position, dir, out hit, viewRadius, obstacleMask))
        {
            //返回值为bool值,射线碰撞信息的位置,射线发生碰撞的距离,传进来的角度
            return new ViewCastInfo(true, hit.point, hit.distance, globalAngle);
        }
        //如果没有打到的不是障碍物层级
        else
        {
            //返回值为bool值,玩家的位置+传进来的位置转为的Vector3,视野的半径,传进来的角度
            return new ViewCastInfo(false, transform.position + dir * viewRadius, viewRadius, globalAngle);
        }   
    }

    /// <summary>
    /// 把传进来的度数转为Vector3(给出角的方向)
    /// </summary>
    /// <param name="angleInDegrees">传进来的角度</param>
    /// <param name="angleIsGlobal">bool值判断角度是否变化</param>
    /// <returns></returns>
    public Vector3 DirFromAngle(float angleInDegrees, bool angleIsGlobal)
    {
        //如果使false
        if (!angleIsGlobal)
        {
            //角度加上自身的欧拉角的y(旋转角度的Y轴)
            angleInDegrees += transform.eulerAngles.y;
        }
        //返回(把传进来的度数转为弧度再转为正弦,0,把传进来的角度转为弧度再转为余弦)
        return new Vector3(Mathf.Sin(angleInDegrees * Mathf.Deg2Rad), 0, Mathf.Cos(angleInDegrees * Mathf.Deg2Rad));
    }

    /// <summary>
    /// 结构体用来接收数据射线的信息
    /// </summary>
    public struct ViewCastInfo
    {
        /// <summary>
        /// 射线是否打中物体
        /// </summary>
        public bool hit;
        /// <summary>
        /// 位置点(终点)
        /// </summary>
        public Vector3 point;
        /// <summary>
        /// 距离(射线的长度)
        /// </summary>
        public float dst;
        /// <summary>
        /// 角度
        /// </summary>
        public float angle;

        public ViewCastInfo(bool _hit, Vector3 _point, float _dst, float _angle)
        {
            hit = _hit;
            point = _point;
            dst = _dst;
            angle = _angle;
        }
    }

    public struct EdgeInfo
    {
        public Vector3 pointA;
        public Vector3 pointB;

        public EdgeInfo(Vector3 _pointA, Vector3 _pointB)
        {
            pointA = _pointA;
            pointB = _pointB;
        }
    }

}
————————————————
版权声明:本文为CSDN博主「你好棒梆」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Plutogd/article/details/117636942

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/66905.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux安装 vmware workstation

官网下载地址 vmware workstation&#xff1a; Download VMware Workstation Pro 也可以下载提供的安装包。 链接&#xff1a;百度网盘 请输入提取码 提取码&#xff1a;au74 一、Ubuntu 安装 安装构建依赖项&#xff0c;打开您的系统终端并运行以下命令&#xff1a; s…

Linux操作系统中的yum命令

Linux操作系统中的yum命令是大家经常会用到的命令&#xff0c;有着非常重要的作用&#xff0c;但很多朋友依然不太清楚yum命令作用是什么&#xff1f;yum命令有哪些语法和常用命令&#xff1f;接下来我们一起来看看详细的内容介绍。 yum命令全称为Yellow dog Updater, Modified…

ChatGPT生成量化交易策略,真好玩

OK&#xff0c;还有没有更好玩的对 量化策略开发&#xff0c;高质量社群&#xff0c;交易思路分享等相关内容 『正文』 ˇ 最近比较火的OpenAI-ChatGPT&#xff0c;太有意思了。尝试让它写了几个策略&#xff0c;您别说&#xff0c;还真是有模有样。我们来看看吧。 模型一&a…

哈希(开散列、闭散列)-位图-布隆过滤器-哈希切分

文章目录1、哈希概念2、哈希表/散列表&#xff08;1&#xff09;哈希函数的设计&#xff1a;&#xff08;2&#xff09;&#xff08;最常用&#xff09;除留余数法&#xff1a;&#xff08;3&#xff09;如何解决哈希冲突&#xff1f;更加合理的设计哈希函数闭散列&#xff08;…

Java多线程之线程同步机制(锁,线程池等等)

Java多线程之线程同步机制一、概念1、并发2、起因3、缺点二、三大不安全案例1、样例一&#xff08;模拟买票场景&#xff09;2、样例二&#xff08;模拟取钱场景&#xff09;3、样例三&#xff08;模拟集合&#xff09;三、同步方法及同步块1、同步方法2、同步块四、JUC安全类型…

java多线程这一篇就差不多了

java多线程这一篇就差不多了 什么是多线程&#xff1f; 一般被问你对多线程了解多少的时候&#xff0c;你可能不仅仅只需要知道线程怎么创建&#xff0c;你可能需要了解线程的几种创建方式&#xff0c;线程的生命周期&#xff0c;线程池相关&#xff0c;并发安全&#xff0c;…

ADSP-21489的图形化编程详解(4:左右声道音量调节和多通道的输入输出详解)

左右声道音量调节 在直通的前提下&#xff0c;我们加入一个调音量的算法模块&#xff0c;来实现调节输出音量大小的功能。首先拖出来一个音量调节算法模块&#xff1a; 我们这里都是双通道&#xff0c;所以需要对这个音量调节模块进行配置&#xff1a; 连好程序&#xff0c;下…

acwing基础课——Dijkstra

由数据范围反推算法复杂度以及算法内容 - AcWing 常用代码模板3——搜索与图论 - AcWing 基本思想&#xff1a; 迪杰斯特拉&#xff08;dijkstra&#xff09;算法是单源最短路径问题的求解方法,它是一个按路径长度递增的次序产生最短路径的算法。单源最短路径就在给出一个固定…

Sqoop数据导出第2关:HDFS数据导出至Mysql内

为了完成本关任务,你需要掌握: 1、数据库( MySQL )建表。 2、HDFS 数据导出至 MySQL 中。 数据库( MySQL )建表 因为这边 Sqoop 不能够帮关系型数据库创建表,所以需要我们自己来创建表。 用命令进入 MySQL 客户端。 mysql -uroot -p123123 -h127.0.0.1 创建数据库…

备忘录模式(Memento)

参考&#xff1a; [备忘录设计模式 (refactoringguru.cn)](https://refactoringguru.cn/design-patterns/mediator) 文章目录一、什么是备忘录模式&#xff1f;二、实现三、优缺点优点缺点四、适用环境一、什么是备忘录模式&#xff1f; 在软件构建过程中&#xff0c;某些对象…

3.ORM实践

文章目录3.1 介绍Spring Data JPAJPA&#xff08;Java Persistence API&#xff09;标准HibernateSpring DataSpring Data JPA引入依赖3.2 定义JPA的实体对象常用JPA注解实体主键映射关系常用lombok注解3.3 SpringBucks线上咖啡馆实战项目&#xff08;1&#xff09;项目目标&am…

如何利用Java爬取网站数据?

1.Jsoup介绍 - 官网文档&#xff1a;https://jsoup.org - Jsoup 是一款Java 的HTML解析器&#xff0c;可直接解析某个URL地址、HTML文本内容。它提供了一套非常省力的API&#xff0c;可通过DOM&#xff0c;CSS以及类似于jQuery的操作方法来取出和操作数据。 2. Jsoup快速入门…

TCP--三次握手和四次挥手

原文网址&#xff1a;TCP--三次握手和四次挥手_IT利刃出鞘的博客-CSDN博客 简介 本文介绍TCP的三次握手和四次挥手。即&#xff1a;TCP建立连接和断开连接的过程。 三次握手 流程图 主机 A为客户端&#xff0c;主机B为服务端。 第一次握手 A 发送同步报文段&#xff08;SYN…

小程序初始创建

1. 注册小程序账号 官网&#xff1a; https://mp.weixin.qq.com/wxopen/waregister?actionstep1 2. 下载小程序 官网&#xff1a; https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html 百度网盘&#xff08;非最新版&#xff09; https://pan.baidu…

部署SNMP使网管与设备通信,配置关于TCP测试NQA的配置案例

一、部署SNMP 组网需求&#xff1a;通过部署RouterA由NMS网管设备管理用于监控网络是否畅通和业务是否正常。为了保证NMS和RouterA之间有一个数据传输安全、接入方式灵活、链路传输可靠的网络&#xff0c;并且可以实时监控设备的运行情况&#xff0c;网络中的RouterA通过网管实…

Databend 开源周报 #70

Databend 是一款强大的云数仓。专为弹性和高效设计&#xff0c;自由且开源。 即刻体验云服务&#xff1a;https://app.databend.com。 What’s New 探索 Databend 本周新进展&#xff0c;遇到更贴近你心意的 Databend 。 Features & Improvements Format 更好地检查格…

PCB 二:AD 原理图绘制以及PCB绘制

PCB 二&#xff1a;AD 原理图绘制以及PCB绘制前言(一)资料总结(二)PCB前言 本文简单收集了AD软件在绘制PCB电路板的一些资料&#xff0c;还有遇到的一些问题&#xff0c;并记录一些常用的操作。 (一)资料总结 1【AD】Altium Designer 原理图的绘制 2【AD】Altium Designer P…

飞利浦CT的AI重建技术

原文&#xff1a;AI for significantly lower dose and improved image quality 飞利浦医疗CT的深度学习重建技术。 人工智能可显着降低剂量并提高图像质量概述背景飞利浦CT智能工作流Precise Image 如何训练神经网络深入了解深度学习训练神经网络验证神经网络推断法可以实现快…

Android 11及以上授予文件管理权限

背景 安卓11改变了此前安卓系统对于文件管理的规则,在安卓11上,文件读写变成了特殊权限。应用默认只能读写自己的目录/android/data/包名 gradle配置 Android11系统对应用写入权限做了严格的限制。本文介绍如何获取文件读写权限。 项目中 build.gradle 的targetSdkVersion …

YOLOv5 模型结构及代码详细讲解(一)

王旭*&#xff0c;沈啸彬 *, 张钊* (淮北师范大学计算机科学与技术学院&#xff0c;淮北师范大学经济与管理学院&#xff0c;安徽 淮北) *These authors contributed to the work equllly and should be regarded as co-first authors. &#x1f31e;欢迎来到深度学习的世界 …