You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity Line Renderer 误命中Cinemachine相机碰撞体的绕过方案咨询

2D激光射线绕过Cinemachine相机碰撞体的稳定实现方案

之前实现效果出bug的核心原因并非LayerMask配置错误,而是代码逻辑重复发起了两次规则完全不一致的Raycast检测:第一次调用没有加层过滤,只要射线扫到包括相机碰撞体在内的任意对象就会进入分支;第二次调用虽然加了层过滤,但如果没有命中有效目标,返回的RaycastHit2D是空值,直接访问_hit.point会触发空引用异常,最终导致激光渲染错乱。

方案1:单步带过滤的Raycast检测(官方推荐,稳定性最高)

直接将射线最大检测距离、目标检测层级作为参数传入,单次调用完成所有检测逻辑,避免重复计算和空引用风险。
正确实现的完整代码:

[SerializeField] private float defDistanceRay = 10f;
// 仅勾选激光需要命中的目标层级(如墙体、敌人),排除Cinemachine碰撞体所在层
public LayerMask laserHitLayer;
public Transform laserFirePoint;
public LineRenderer m_linerenderer;
Transform m_transform;

private void Awake()
{
    m_transform = GetComponent<Transform>();
}

private void Update()
{
    ShootLaser();
}

void ShootLaser()
{
    // 单次调用完成检测,固定最大检测长度为激光默认射程
    RaycastHit2D _hit = Physics2D.Raycast(
        origin: m_transform.position,
        direction: transform.right,
        distance: defDistanceRay,
        layerMask: laserHitLayer
    );

    if (_hit.collider != null)
    {
        Draw2DRay(laserFirePoint.position, _hit.point);
        Debug.Log(_hit.collider.name);
    }
    else
    {
        // 修正原代码的坐标计算错误:基于发射点位置叠加方向偏移
        Vector2 endPos = (Vector2)laserFirePoint.position + (Vector2)laserFirePoint.right * defDistanceRay;
        Draw2DRay(laserFirePoint.position, endPos);
    }
}

void Draw2DRay(Vector2 startPos, Vector2 endPos)
{
    m_linerenderer.SetPosition(0, startPos);
    m_linerenderer.SetPosition(1, endPos);
}

方案落地的注意事项:

  • *层过滤规则在Inspector的LayerMask组件中配置即可,只要不勾选Cinemachine相机碰撞体所在的层级,物理检测从根源上就不会和该碰撞体产生交互
  • 原代码else分支的终点计算存在逻辑错误:直接把方向向量作为世界坐标传入Line Renderer,会导致激光终点错位到坐标原点附近,这也是之前渲染效果异常的核心原因之一
  • 单次Raycast调用即可完成所有检测逻辑,相比两次调用减少一半的物理计算开销,性能更优

方案2:全局层碰撞忽略(适合多脚本复用的场景)

如果项目中存在大量激光相关的检测逻辑,不需要每次写Raycast都挂LayerMask参数,可以在游戏初始化阶段配置全局层交互规则,自动跳过目标碰撞体:
在任意游戏启动脚本的Start方法中加入配置:

private void Start()
{
    // 假设第6层存放Cinemachine的边界碰撞体,第7层存放所有激光相关对象
    // 配置后两个层之间的所有2D物理交互完全忽略
    Physics2D.IgnoreLayerCollision(6, 7, true);
}

配置完成后,所有挂在激光层上的对象发起的2D射线检测,都会自动跳过边界层的碰撞体,不需要逐脚本修改检测逻辑。

方案3:碰撞结果手动过滤(临时兜底用,不推荐长期使用)

如果暂时无法调整项目的层配置,可以在拿到射线的所有碰撞结果后,手动跳过相机碰撞体的命中项,继续寻找有效目标:

void ShootLaser()
{
    // 获取射线路径上的所有碰撞结果
    RaycastHit2D[] _allHits = Physics2D.RaycastAll(m_transform.position, transform.right, defDistanceRay);
    RaycastHit2D _validHit = default;

    foreach (var hit in _allHits)
    {
        // 跳过标签为"CameraConfiner"的相机边界碰撞体
        if (hit.collider.CompareTag("CameraConfiner"))
            continue;
        _validHit = hit;
        break;
    }

    if (_validHit.collider != null)
    {
        Draw2DRay(laserFirePoint.position, _validHit.point);
    }
    else
    {
        Vector2 endPos = (Vector2)laserFirePoint.position + (Vector2)laserFirePoint.right * defDistanceRay;
        Draw2DRay(laserFirePoint.position, endPos);
    }
}

这种方案会一次性返回路径上的所有碰撞结果,在长射线、多碰撞体的场景下性能开销更高,仅适合临时调试使用。


内容的提问来源于stack exchange,提问作者Sam Andersson

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.26 04:45:05