【第019问 Unity中对SpherecastCommand的理解?】

一、背景

游戏优化中会对物理中的射线检测进行优化,比如你会对多个玩家进行执行相同的Physics.SphereCast() 射线检测,大多数情况下,在研发的过程中可能会直接采取循环的方式,进行多次的Physics.SphereCast() 逻辑处理;这里我们可以用SpherecastCommand 替代,结合Job进行操作;

二、动画演示请添加图片描述

上文动画中有3个需要检测的对象,这里的对象根据实际情况可以增删;这里会根据实际调节的半径、检测距离、检测方向进行检测;不同的参数会检测出其对应的结果;其中红色表示该对象是被射线检测到的点;
从上面的动画中可以动态调节半径可以但绿色部分的动态变化,同理检测距离的调节也会可视化;
具体的逻辑可参考一下源码部分;代码里面有详细的逻辑注释。

三、源码

using System;
using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using Unity.Collections;
using Unity.Mathematics;
using UnityEditor.Timeline;
using UnityEngine;


[ExecuteAlways]
public class TestCommand : MonoBehaviour
{
    public List<Transform> transforms;
    public List<Material> materials;
    public List<MeshRenderer> targetsMeshRenders;


    public enum Direction
    {
        Right,
        Left,
        Forward,
        Back,
    }


    public float distacne = 1;
    public float radius = 1;
    int count = 1;

    public Direction direction = Direction.Right;

    public Vector3 _direction = Vector3.right;

    // Update is called once per frame
    void Update()
    {
        //https://docs.unity3d.com/cn/2019.1/ScriptReference/SpherecastCommand.html 具体可以参考这里官方的说明

        count = transforms.Count;
        InitDirection();
        RestMaterials();
        //定义一个RaycastHit数组 用于存储RaycastHit的信息  这里count表示要检测的个数
        NativeArray<RaycastHit> raycastHits = new NativeArray<RaycastHit>(count, Allocator.TempJob);
        //定义SpherecastCommand数组 表示球体命令数组  也就是有多少个需要执行球体射线检测的命令
        NativeArray<SpherecastCommand>
            spherecastCommands = new NativeArray<SpherecastCommand>(count, Allocator.TempJob);

        //这里的循环表示有多少个对象需要执行球体射线检测 那么创建多少个球体射线检测的命令 这里只是把创建的命令存储起来,具体的执行在后面;
        for (int i = 0; i < count; i++)
        {
            CheckRadius(i);
            CheckDistance(i);
            CheckDirection(i);
            //创建球体射线投射指令
            /*
             * 投射的起始位置
             * 投射的球体的半径【scene中的绿色可以看出】
             * direction:检测的方向【scene中可以看出】
             * distance:检测的距离 【scene中可以看出】
             */
            spherecastCommands[i] =
                new SpherecastCommand(transforms[i].position, radius, transforms[i].rotation * _direction, distacne);
        }

        //https://docs.unity3d.com/cn/2019.1/ScriptReference/SpherecastCommand.ScheduleBatch.html

        //这里表示需要执行的球体批次投射命令 这里会返回一个job对象 并安排此处作业进入等待执行状态,
        var jobhandel = SpherecastCommand.ScheduleBatch(spherecastCommands, raycastHits, 1, default);
        //等待该批次执行完成
        jobhandel.Complete();


        for (int i = 0; i < raycastHits.Length; i++)
        {
            RaycastHit raycastHit = raycastHits[i];
            if (raycastHit.transform != null)
            {
                raycastHit.transform.GetComponent<MeshRenderer>().sharedMaterial = materials[1];
            }
        }

        //释放
        raycastHits.Dispose();
        //释放
        spherecastCommands.Dispose();
    }


    void InitDirection()
    {
        switch (direction)
        {
            case Direction.Back:
                _direction = Vector3.back;
                break;
            case Direction.Forward:
                _direction = Vector3.forward;
                break;

            case Direction.Left:
                _direction = Vector3.left;
                break;
            case Direction.Right:
                _direction = Vector3.right;
                break;
        }
    }


    /// <summary>
    /// 检测的方向
    /// </summary>
    /// <param name="index"></param>
    void CheckDirection(int index)
    {
        Debug.DrawLine(transforms[index].position,
            transforms[index].position + transforms[index].rotation * _direction * 15, Color.magenta);
    }

    /// <summary>
    /// 半径的检测范围
    /// </summary>
    /// <param name="index"></param>
    void CheckRadius(int index)
    {
        Vector3 pos = transforms[index].position;
        for (int i = 0; i < 360; i += 5)
        {
            float x = pos.x + math.cos(i) * radius;
            float z = pos.z + math.sin(i) * radius;

            Debug.DrawLine(pos, new Vector3(x, pos.y, z), Color.green);
        }
    }


    /// <summary>
    /// 距离检测 距离检测的起点是半径为起点,实际检测的距离=半径+距离
    /// </summary>
    /// <param name="index"></param>
    void CheckDistance(int index)
    {
        Vector3 pos = transforms[index].position;
        for (int i = 0; i < 360; i += 5)
        {
            float rx = math.cos(i) * radius;
            float ry = math.sin(i) * radius;

            float sx = pos.x + rx;
            float sz = pos.z + ry;

            float ex = sx + math.cos(i) * distacne;
            float ey = sz + math.sin(i) * distacne;

            Debug.DrawLine(new Vector3(sx, pos.y, sz), new Vector3(ex, pos.y, ey), Color.yellow);
        }
    }


    /// <summary>
    /// 重置Materail
    /// </summary>
    void RestMaterials()
    {
        foreach (var tmr in targetsMeshRenders)
        {
            tmr.sharedMaterial = materials[0];
        }
    }
}

四、结语

辣椒好像吃多了有点上火-【valaki】

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值