Unity敌人Raycast穿墙检测异常问题求助
问题:敌人Raycast无视障碍物直接命中玩家
我正在制作一款FPS游戏,使用Probuilder创建了关卡。敌人会向玩家方向发射Raycast,但无论中间存在何种障碍物,都会直接命中玩家。所有Layer已配置完成,Probuilder立方体使用Mesh Collider,尝试替换为Unity默认立方体后问题仍存在(FireProjectile函数未启用)。
敌人Raycast相关代码如下:
public class Enemy : MonoBehaviour { [Header("Enemy Settings")] bool canShoot, isDead; Vector3 playerDir; [Header("Enemy Type")] [Space] NavMeshAgent agent; RaycastHit hit, shotHit; PlayerMovement playerM; public AudioSource enemyShot; public AudioClip gunS; public ParticleSystem enemyFlash; float timer; public GameObject projectilePrefab, debugSpot; public float Health, shootRange, noticeRange, bulletSpeed, shotCooldown, stopDistance, shotDamage; public LayerMask playerLayer; public Transform player, enemyBarrel; // Start is called before the first frame update void Start() { playerM = player.GetComponent<PlayerMovement>(); agent = GetComponent<NavMeshAgent>(); } // Update is called once per frame void Update() { if(Health <= 0f) { Death(); } float distance = Vector3.Distance(transform.position, player.position); playerDir = player.position - transform.position; if ((distance <= noticeRange) && !isDead) { transform.LookAt(player.position); if (distance >= stopDistance) { agent.isStopped = false; agent.SetDestination(player.position); } else { agent.isStopped = true; } } if (distance > noticeRange) { timer = 0f; } if (timer >= shotCooldown) { canShoot = true; } if(timer <= shotCooldown) { timer += Time.deltaTime * 2f; canShoot = false; } if(Physics.Raycast(transform.position, playerDir, out hit, shootRange, playerLayer) && canShoot && !isDead) { Shoot(); Debug.Log(hit.transform.name); } } // AI CODE void Shoot() { enemyFlash.Play(); enemyShot.PlayOneShot(gunS); timer = 0f; // Calculate the player's predicted position Vector3 playerVelocity = player.GetComponent<Rigidbody>().velocity; //float distanceFromPlayer = Vector3.Distance(transform.position, player.position); float timeToReachPlayer = Vector3.Distance(transform.position, player.position) / shootRange; Vector3 predictedPosition = player.position + playerVelocity/3f * timeToReachPlayer; Vector3 dir = predictedPosition - transform.position; if(Physics.Raycast(transform.position, dir, out shotHit, shootRange, playerLayer)) { shotHit.transform.gameObject.GetComponent<PlayerMovement>().Damage(shotDamage); Instantiate(debugSpot, shotHit.point, Quaternion.identity); } //FireProjectile(predictedPosition); } void FireProjectile(Vector3 targetPosition) { // Instantiate and launch the projectile towards the target position GameObject projectile = Instantiate(projectilePrefab, transform.position, Quaternion.identity); Rigidbody projectileRigidbody = projectile.GetComponent<Rigidbody>(); // Calculate the direction towards the target position Vector3 direction = (targetPosition - transform.position).normalized; // Launch the projectile towards the target position projectileRigidbody.velocity = direction * bulletSpeed; Destroy(projectile, 5f); } // AI CODE public void Damage(float dmg) { Health -= dmg; } public void Death() { isDead = true; agent.enabled = false; transform.GetComponent<Rigidbody>().freezeRotation = false; Destroy(gameObject, 3f); } void OnDrawGizmos() { Gizmos.DrawLine(transform.position, player.position); } }
问题根源
你的Raycast检测仅指定playerLayer作为目标层,这会导致Raycast完全忽略其他所有层的碰撞体——包括障碍物。Raycast的LayerMask参数是用来定义需要检测的层,而非需要忽略的层,只传玩家层的话,射线会直接穿过障碍物,只和玩家层物体交互。
修复方案
1. 调整Raycast的LayerMask逻辑
有两种可行的修改方式:
- 方式一:忽略障碍物层
新增障碍物层变量,让Raycast检测玩家层并忽略障碍物层:// 在Enemy类中新增变量 public LayerMask obstacleLayer; // 修改Update中的Raycast判断 if(Physics.Raycast(transform.position, playerDir.normalized, out hit, shootRange, playerLayer | ~obstacleLayer) && canShoot && !isDead) { Shoot(); Debug.Log(hit.transform.name); } // 同时修改Shoot函数中的Raycast if(Physics.Raycast(transform.position, dir.normalized, out shotHit, shootRange, playerLayer | ~obstacleLayer)) { shotHit.transform.gameObject.GetComponent<PlayerMovement>().Damage(shotDamage); Instantiate(debugSpot, shotHit.point, Quaternion.identity); } - 方式二:检测包含玩家和障碍物的层,再判断命中对象
创建包含玩家层和障碍物层的LayerMask,检测后判断是否命中玩家:
注意:必须将// 替换原有的playerLayer变量 public LayerMask shootableLayers; // 在Inspector中配置为包含玩家和障碍物的层 // 修改Update中的判断 if(Physics.Raycast(transform.position, playerDir.normalized, out hit, shootRange, shootableLayers) && hit.collider.gameObject.layer == LayerMask.NameToLayer("Player") && canShoot && !isDead) { Shoot(); Debug.Log(hit.transform.name); } // 修改Shoot函数中的判断 if(Physics.Raycast(transform.position, dir.normalized, out shotHit, shootRange, shootableLayers) && shotHit.collider.gameObject.layer == LayerMask.NameToLayer("Player")) { shotHit.transform.gameObject.GetComponent<PlayerMovement>().Damage(shotDamage); Instantiate(debugSpot, shotHit.point, Quaternion.identity); }playerDir和dir归一化,使用方向向量而非带长度的位移向量,这是Raycast的规范用法。
2. 检查碰撞体配置
- 确保障碍物的碰撞体处于启用状态,且Layer正确设置为障碍物层
- Probuilder创建的Mesh Collider需要勾选Convex选项,否则无法被Raycast正确检测
3. 修复计时器逻辑
当前计时器代码会导致canShoot始终为false,因为timer <= shotCooldown的判断会覆盖之前的canShoot = true赋值,修改为:
// 替换原有的计时器逻辑 if (distance > noticeRange) { timer = 0f; } else { timer += Time.deltaTime * 2f; } canShoot = timer >= shotCooldown;
额外优化
将Raycast的起点改为敌人的枪口位置(enemyBarrel.position),而非敌人Transform的原点,这样射击逻辑更符合实际场景。
内容的提问来源于stack exchange,提问作者TheBreadPerson
相关产品推荐
相关产品推荐

