Unity 2D Raycast检测距离失效 敌人AI超距识别玩家问题
Unity 2D敌人射线检测距离失效排查方案
问题根因
- 追击标记永久锁死:代码中
SeenPlayer变量一旦被赋值为true,没有任何逻辑将其重置为false。只要射线第一次碰撞到任意物体,敌人就会永久进入追击状态,无论玩家后续跑多远都会持续追踪,表现为距离限制完全失效。 - 射线未做碰撞过滤:当前玩家检测射线命中任意碰撞体(地面、墙体、场景道具、其他单位)都会触发追击判定,没有校验命中对象是否为玩家,容易出现隔墙误判、超远距离误触发的问题。
- 射线方向固定不跟随朝向:检测射线写死朝世界坐标左侧发射,敌人转向朝右巡逻时射线方向不会同步切换到右侧,不仅会漏检正面的玩家,转向过程中还容易误扫到其他物体提前进入追击。
- 参数默认值触发无限检测:
seeDistance是public字段,如果在Inspector面板未正确赋值、或意外清空输入框,字段默认值为0。根据Unity 2D射线的API规则,当传入的射线长度参数为0时,会沿指定方向做无限距离检测,完全忽略距离限制。 - 移动逻辑与朝向不匹配:巡逻时固定朝世界坐标右侧移动,转向后移动方向不会同步反转,会导致敌人转向后原地卡顿、移动方向错乱,间接干扰检测逻辑的表现。
修复方案
- 给公开参数加默认值,避免Inspector漏赋值导致的异常:将
seeDistance初始值设为2,同时新增玩家层、地面层的掩码参数,过滤无关碰撞体。 - 射线命中后增加校验逻辑,只有命中对象标签为Player时才触发追击标记。
- 增加丢失玩家判定:进入追击状态后持续计算和玩家的距离,超出阈值后重置
SeenPlayer标记,回到巡逻状态。 - 射线方向、巡逻移动方向随敌人朝向动态切换,不要写死固定方向。
- 调试阶段开启射线可视化,直观确认射线的长度、方向、命中对象是否符合预期。
修正后完整代码
using System.Collections; using System.Collections.Generic; using UnityEngine; public class enemyMove : MonoBehaviour { public int speed = 2; private bool movingRight = true; public bool SeenPlayer = false; public Transform target; public float seeDistance = 2f; public int health = 3; public Rigidbody2D rb; public Transform groundDetection; // 层掩码参数,在Inspector面板分别指定玩家、地面对应的层 public LayerMask playerLayer; public LayerMask groundLayer; // 丢失玩家的距离阈值,比检测距离稍大,避免状态反复切换 public float losePlayerDistance = 3f; void Start() { target = GameObject.FindGameObjectWithTag("Player").GetComponent<Transform>(); rb = GetComponent<Rigidbody2D>(); } void Update() { if (!SeenPlayer) { // 按当前朝向移动 transform.Translate((movingRight ? Vector2.right : Vector2.left) * speed * Time.deltaTime); } else { transform.position = Vector2.MoveTowards(transform.position, target.position, speed * Time.deltaTime); transform.localScale = new Vector3(0.5f, 0.5f, 0.5f); // 超出丢失距离则回到巡逻状态 if (Vector2.Distance(transform.position, target.position) > losePlayerDistance) { SeenPlayer = false; transform.localScale = Vector3.one; } } // 地面检测只碰撞地面层 RaycastHit2D groundInfo = Physics2D.Raycast(groundDetection.position, Vector2.down, 2f, groundLayer); // 玩家检测方向跟随朝向,只碰撞玩家层 Vector2 detectDir = movingRight ? Vector2.right : Vector2.left; RaycastHit2D playerHit = Physics2D.Raycast(groundDetection.position, detectDir, seeDistance, playerLayer); // 边缘转向逻辑 if (groundInfo.collider == null) { if (movingRight) { transform.eulerAngles = new Vector3(0, -180, 0); movingRight = false; } else { transform.eulerAngles = new Vector3(0, 0, 0); movingRight = true; } } // 确认命中玩家才触发追击 if (playerHit.collider != null && playerHit.collider.CompareTag("Player")) { SeenPlayer = true; } // 调试用:Scene视图中绘制射线,方便排查问题 Debug.DrawRay(groundDetection.position, detectDir * seeDistance, Color.red); Debug.DrawRay(groundDetection.position, Vector2.down * 2f, Color.green); } private void OnTriggerEnter2D(Collider2D collision) { BulletScript bullet = collision.GetComponent<BulletScript>(); if (bullet != null) { health -= 1; Destroy(bullet.gameObject); if (health == 0) { Destroy(gameObject); } else { // 受击力方向根据当前朝向调整 rb.AddForce(new Vector2(movingRight ? -4 : 4, 2), ForceMode2D.Impulse); } } } }
注意:使用层掩码前,需要在Unity的Layer设置中新建Player、Ground层,分别把玩家对象、地面平台对象挂载到对应层,再把脚本上的playerLayer、groundLayer参数指定为对应层即可。
内容的提问来源于stack exchange,提问作者RaptoRR
相关产品推荐
相关产品推荐

