Unity 3D:为敌人AI实现带周边视野的射线检测可见性测试
实现Unity中敌人AI的扇形周边视野检测方案
要实现敌人的扇形周边视野,需要结合角度范围判断、距离范围验证和障碍物穿透检测,同时兼顾检测效率,以下是具体实现方案:
1. 核心逻辑梳理
敌人判定玩家在视野内,需同时满足三个条件:
- 玩家处于设定的视野距离范围内
- 玩家处于敌人的扇形视野角度范围内
- 敌人与玩家之间无障碍物阻挡
2. 角度与距离范围判断
通过向量计算快速筛选出可能在视野内的玩家:
- 利用敌人Transform的
forward属性获取正前方方向 - 计算敌人到玩家的方向向量并归一化,通过
Vector3.Angle计算夹角,判断是否在视野角度的半值范围内(例如视野总角度90°,则夹角需≤45°) - 同步验证敌人与玩家的直线距离是否≤设定的视野距离
示例代码片段:
public class EnemyAI : MonoBehaviour { public float viewDistance = 12f; public float viewAngle = 90f; // 扇形视野的总覆盖角度 public LayerMask playerLayer; public LayerMask obstacleLayer; private Transform playerTrans; void Start() { playerTrans = GameObject.FindGameObjectWithTag("Player").transform; } void Update() { DetectPlayerInView(); } void DetectPlayerInView() { Vector3 dirToPlayer = playerTrans.position - transform.position; float distance = dirToPlayer.magnitude; // 1. 距离超出视野范围,直接返回 if (distance > viewDistance) return; // 2. 不在扇形角度范围内,直接返回 dirToPlayer.Normalize(); float angle = Vector3.Angle(transform.forward, dirToPlayer); if (angle > viewAngle / 2f) return; // 3. 无障碍物阻挡,触发追击逻辑 if (!HasObstacleBlocking(dirToPlayer, distance)) { ChasePlayer(); } } }
3. 障碍物穿透检测优化
单射线检测可能存在漏检(例如玩家处于扇形边缘时射线未命中),可采用以下两种方案:
方案一:多射线覆盖扇形区域
在视野扇形内均匀发射多条射线,确保覆盖整个视野范围:
bool HasObstacleBlocking(Vector3 dirToPlayer, float distance) { // 发射中心射线 if (Physics.Raycast(transform.position, dirToPlayer, distance, obstacleLayer)) return true; // 发射左右偏移射线,数量可根据视野角度调整 int rayCount = 3; float angleStep = viewAngle / (rayCount - 1); for (int i = 0; i < rayCount; i++) { float offsetAngle = -viewAngle / 2f + angleStep * i; Vector3 rayDir = Quaternion.Euler(0, offsetAngle, 0) * transform.forward; if (Physics.Raycast(transform.position, rayDir, distance, obstacleLayer)) return true; } return false; }
方案二:球体投射(SphereCast)
相比单射线,球体投射可检测更宽的区域,避免小障碍物误判,适合覆盖扇形视野:
bool HasObstacleBlocking(Vector3 dirToPlayer, float distance) { float sphereRadius = 0.4f; // 根据敌人、玩家模型大小调整 if (Physics.SphereCast(transform.position, sphereRadius, dirToPlayer, out RaycastHit hit, distance, obstacleLayer)) { // 若命中对象是玩家,则无阻挡;否则为障碍物 return !hit.collider.CompareTag("Player"); } return false; }
4. 性能优化建议
- 先用
Physics.OverlapSphere筛选出视野范围内的玩家对象,再对这些对象执行角度和障碍物检测,减少无效计算:
Collider[] inRangePlayers = Physics.OverlapSphere(transform.position, viewDistance, playerLayer); foreach (Collider col in inRangePlayers) { Transform target = col.transform; // 对每个目标执行角度与障碍物检测逻辑 }
- 给玩家、障碍物设置独立分层,通过
LayerMask精准过滤检测对象,避免检测无关物体。
内容的提问来源于stack exchange,提问作者yassin eltaramsi
相关产品推荐
相关产品推荐

