Raycast射线检测

最后更新于 2023-01-07 364 次阅读


方法解析

原函数

public static bool Raycast (
Vector3 origin, //射线在世界坐标系中的起点。
Vector3 direction, //射线的方向
float maxDistance= Mathf.Infinity,  //射线应检查碰撞的最大距离
int layerMask= DefaultRaycastLayers,  //只检测选定层的碰撞
QueryTriggerInteraction queryTriggerInteraction=QueryTriggerInteraction.UseGlobal
//指定该查询是否应该命中碰撞体(参数QueryTriggerInteraction.Ignore指isTrigger打开时忽略物体)
);

因为origin和direction可以用Ray这种储存两个vector的变量,RaycastHit 用于从射线投射获取信息的结构,所以一般的写法为

private void FixedUpdate()
    {
        Ray ray = new Ray(transform.position, transform.forward);

        RaycastHit hitInfo;

        if(Physics.Raycast(ray, out hitInfo,10))
        {
            Debug.Log(hitInfo.collider.name);
            Debug.DrawLine(transform.position,hitInfo.point,Color.green);
        }
        else
        {
            Debug.DrawLine(ray.origin,ray.origin + ray.direction * 100,Color.red);
        }
    }
public static RaycastHit[] RaycastAll (Ray ray, float maxDistance= Mathf.Infinity, int layerMask= DefaultRaycastLayers, QueryTriggerInteraction queryTriggerInteraction= QueryTriggerInteraction.UseGlobal);

RaycastAll会检测所有射线检测到的结果并且返回一个结果的数组

ray = new Ray(transform.position, transform.forward);

        RaycastHit[] hitsInfo;

        hitsInfo = Physics.RaycastAll(ray, maxSize, layerMask);

        if (hitsInfo.Length != 0)
        {
            Debug.DrawLine(ray.origin,ray.origin + ray.direction * 10,Color.red);
        }
        else
        {
            Debug.DrawLine(ray.origin,ray.origin + ray.direction * 10,Color.green);
        }

        foreach (var info in hitsInfo)
        {
            Debug.Log(info.collider.gameObject.name);
            info.collider.gameObject.GetComponent<Renderer>().material = highlightM;
        }

方法作用

向场景中的所有碰撞体投射一条射线,该射线起点为 /origin/,朝向 /direction/,长度为 /maxDistance/。您可以选择提供一个 LayerMask,以过滤掉不想生成与其碰撞的碰撞体。
您可以通过指定 queryTriggerInteraction 来控制是让触发碰撞体生成命中效果,还是使用全局 Physics.queriesHitTriggers 设置。

注意事项

  • 对于射线投射起点位于碰撞体内的情况,Raycast 不会检测到碰撞体。
  • 对于射线检测官方建议在Fixed Update中使用
  • 射线检测的对象必须要有Collider组件,且isTrigger的开关不影响射线检测的结果(参数QueryTriggerInteraction与之相关)

案例

Camera.main.ScreenPointToRay(Input.mousePosition)

将屏幕上的像素坐标(即鼠标在屏幕坐标上的具体位置),通过ScreenPointToRay()方法,传入并返回一条在世界坐标下从相机Camera的近裁面,出发穿过屏幕上的像素点的射线,即将鼠标的位置信息从屏幕坐标系转换到Ray射线类型

鼠标位置在平面上生成物体

public class PlaceMement : MonoBehaviour
{
    public LayerMask planemask; //layer标签用于射线只检测plane
    public GameObject tree;     //生成的物体
    private void FixedUpdate()
    {
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);

        //调整在平面上的位置
        if (Physics.Raycast(ray, out RaycastHit hitInfo, Mathf.Infinity,planemask))
        {
            tree.transform.position = hitInfo.point;
            tree.transform.rotation = Quaternion.FromToRotation(Vector3.up, hitInfo.normal);//依靠平面的法线做变换角度
        }

        if (Input.GetMouseButtonDown(0))
        {
            Instantiate(tree, hitInfo.point, Quaternion.FromToRotation(Vector3.up, hitInfo.normal));
        }
    }
}

移动发射光束

using UnityEngine;

public class Enemy : MonoBehaviour
{
    [SerializeField] private float moveSpeed;
    public Transform target;
    [SerializeField] private Transform A, B;

    public Transform firePoint;
    [SerializeField] private float maxDist;
    public LayerMask layer;

    [Header("Laser")] private LineRenderer lineRender;
    [SerializeField] private Gradient redColor, greenColor;
    private void Start()
    {
        target = B;
        Physics2D.queriesStartInColliders = false;
        lineRender = GetComponentInChildren<LineRenderer>();
    }

    void Update()
    {
        Move();
        Detect();
    }

    void Move()
    {
        if (Vector2.Distance(transform.position, A.position) <= 0.1)
        {
            target = B;
        }

        if (Vector2.Distance(transform.position, B.position) <= 0.1)
        {
            target = A;
        }

        transform.position = Vector2.MoveTowards(transform.position, target.position, moveSpeed * Time.deltaTime);
    }

    void Detect()
    {
        RaycastHit2D hitInfo;
        hitInfo = Physics2D.Raycast(firePoint.position, -transform.right * 5, maxDist,layer);

        if (hitInfo.collider != null)
        {
            if (hitInfo.collider.tag == "Block")
            {
                Debug.Log(hitInfo.collider.gameObject.name);
                Debug.DrawLine(firePoint.position,hitInfo.point,Color.red);
                lineRender.SetPosition(1,hitInfo.point);
                lineRender.colorGradient = greenColor;
            }
            lineRender.SetPosition(0,firePoint.position);//linerender初始点
        }
    }
}