Unity Raycast行为不一致:有时碰撞起始碰撞器有时无碰撞
Unity Raycast行为不一致问题:射线偶尔与起始碰撞器触发碰撞
我在Unity开发中遇到Raycast行为不一致的问题:射线有时会与它起始所在的碰撞器发生碰撞,有时则不会。

图片文字已完整说明问题
核心代码片段
以下是生成射线的核心代码(可忽略Debug.DrawRay()):
RaycastHit hitInfo; RaycastHit wall; RaycastHit backFace; if(Physics.Raycast(barrelEnd.transform.position, barrelEnd.transform.forward, out hitInfo)) { Debug.DrawRay(barrelEnd.transform.position, barrelEnd.transform.forward*5, Color.green, 5f, false); Instantiate(rayOneHit, hitInfo.point, Quaternion.identity); Enemy target = hitInfo.transform.GetComponent<Enemy>(); if(target != null) { if(Physics.Raycast(hitInfo.point, barrelEnd.transform.forward, out wall)) { Debug.DrawRay(hitInfo.point, (hitInfo.point - barrelEnd.transform.position)*2, Color.blue, 5f, false); Instantiate(rayTwoHit, wall.point, Quaternion.identity); if(Physics.Raycast(wall.point, ((hitInfo.point - wall.point)/Vector3.Distance(hitInfo.point,wall.point)), out backFace)) { Debug.DrawRay(wall.point, (hitInfo.point - wall.point)*10, Color.red, 5f, false); Vector3 hitLocation = new Vector3(backFace.point.x, backFace.point.y, backFace.point.z); Instantiate(hitPoint, hitLocation, Quaternion.identity); } } target.dealDamage(damage); } lineRenderer.SetPosition(0, aim.transform.position); lineRenderer.SetPosition(1, hitInfo.point); }
调用逻辑:
void Update() { if(Input.GetButtonDown("Fire1")) { Shoot(); } }
完整代码
using System.Collections; using UnityEngine; public class Shooting : MonoBehaviour { [SerializeField] Transform aim; [SerializeField] LineRenderer lineRenderer; public float damage = 30f; public Transform barrelEnd; public Transform crosshair; public Light muzzleFlash; public Object hitPoint; public Object rayOneHit; public Object rayTwoHit; public LayerMask enemyLayer; //public Light muzzleFlashSelf; // public GameObject projectile; public float force = 100f; void Start() { Cursor.visible = false; muzzleFlash.enabled = false; //muzzleFlashSelf.enabled = false; } void Update() { if(Input.GetButtonDown("Fire1")) { Shoot(); } } void Shoot() { RaycastHit hitInfo; RaycastHit wall; RaycastHit backFace; StartCoroutine(DrawLine()); StartCoroutine(Flash()); if(Physics.Raycast(barrelEnd.transform.position, barrelEnd.transform.forward, out hitInfo)) { Debug.DrawRay(barrelEnd.transform.position, barrelEnd.transform.forward*5, Color.green, 5f, false); Instantiate(rayOneHit, hitInfo.point, Quaternion.identity); Enemy target = hitInfo.transform.GetComponent<Enemy>(); if(target != null) { if(Physics.Raycast(hitInfo.point, barrelEnd.transform.forward, out wall)) { Debug.DrawRay(hitInfo.point, (hitInfo.point - barrelEnd.transform.position)*2, Color.blue, 5f, false); Instantiate(rayTwoHit, wall.point, Quaternion.identity); if(Physics.Raycast(wall.point, ((hitInfo.point - wall.point)/Vector3.Distance(hitInfo.point,wall.point)), out backFace)) { Debug.DrawRay(wall.point, (hitInfo.point - wall.point)*10, Color.red, 5f, false); Vector3 hitLocation = new Vector3(backFace.point.x, backFace.point.y, backFace.point.z); Instantiate(hitPoint, hitLocation, Quaternion.identity); } } target.dealDamage(damage); } lineRenderer.SetPosition(0, aim.transform.position); lineRenderer.SetPosition(1, hitInfo.point); } } IEnumerator DrawLine() { lineRenderer.enabled = true; float t = 0; float time = 0.01f; Vector3 orig = lineRenderer.GetPosition(0); Vector3 orig2 = lineRenderer.GetPosition(1); lineRenderer.SetPosition(1, orig); for (; t < time; t += Time.deltaTime) { yield return null; } lineRenderer.SetPosition(1, orig2); lineRenderer.enabled = false; } IEnumerator Flash() { float t = 0; float time = 0.1f; muzzleFlash.enabled = true; //muzzleFlashSelf.enabled = true; for (; t < time; t += Time.deltaTime) { yield return null; } muzzleFlash.enabled = false; //muzzleFlashSelf.enabled = false; } }
已尝试的无效方案
- 让第二个射线从敌人内部稍许位置起始,无效果
- 使用
~enemyMask排除敌人层,导致第二个射线完全不触发
可行解决方案
1. 精准控制射线检测的碰撞体/层
当从敌人碰撞点发射射线时,默认可能因浮点精度问题检测到敌人自身,可通过以下方式规避:
- 忽略当前击中的碰撞体:使用
Physics.Raycast的重载参数,直接排除第一个射线击中的敌人碰撞器:// 修改第二个Raycast,忽略hitInfo对应的碰撞体 if(Physics.Raycast(hitInfo.point, barrelEnd.transform.forward, out wall, Mathf.Infinity, -1, QueryTriggerInteraction.Ignore)) { // 确保检测到的不是同一碰撞体 if(wall.collider != hitInfo.collider) { // 执行后续逻辑 } } - 修正层掩码设置:如果使用
~enemyLayer无效,检查敌人层是否仅包含敌人,墙壁是否在其他层。正确设置后,用层掩码只检测墙壁所在层:// 假设wallLayer是你定义的墙壁层 if(Physics.Raycast(hitInfo.point, barrelEnd.transform.forward, out wall, Mathf.Infinity, wallLayer)) { // 后续逻辑 }
2. 合理偏移射线起始点
将第二个射线的起始点沿前进方向偏移一小段距离,确保离开敌人碰撞器表面:
// 偏移量根据碰撞器大小调整,建议0.01f~0.1f Vector3 offsetStart = hitInfo.point + barrelEnd.transform.forward * 0.05f; if(Physics.Raycast(offsetStart, barrelEnd.transform.forward, out wall)) { // 后续逻辑 }
3. 优化碰撞体设置
- 如果敌人使用Mesh Collider,勾选
convex选项,提升射线检测稳定性(注意复杂Mesh不适合勾选) - 确保敌人碰撞体的
isTrigger设置符合需求,避免触发器干扰射线检测
4. 调整射线检测精度
在Unity编辑器的Edit > Project Settings > Physics中,调整Raycast Hit Margin参数,适当增大数值可以缓解浮点精度导致的检测异常。
内容的提问来源于stack exchange,提问作者beholdthespoon
相关产品推荐
相关产品推荐

