Unity 2D敌人巡逻功能异常:Patrol方法未调用问题求助
Unity 2D敌人巡逻功能未触发问题排查
问题概述
开发Unity 2D敌人系统时,巡逻功能异常,核心表现为Patrol()方法未被调用。首次使用继承机制,推测问题出在CurrentState的设置逻辑上。设计逻辑如下:
- 基类
EnemyBase实现Patrol方法,并在自身Update中根据CurrentState调用对应状态方法 - 子类
LunaticCultistBase在Start中设置CurrentState = EnemyState.Patrol,但该状态未触发基类的巡逻逻辑
相关代码
基类 EnemyBase
using Assets.Code.Enemy.Enemies; using System.Collections; using System.Collections.Generic; using Unity.VisualScripting; using UnityEngine; public class EnemyBase : MonoBehaviour { [Header("Base values")] private int _health = 0; private int _damage = 0; private int _speed = 0; private EnemyState _currentState; [Header("Movement elements")] protected bool playerInRange = false; [SerializeField] protected CapsuleCollider2D SpotRange; [SerializeField] protected Transform PlayerTransform; [SerializeField] protected Transform EnemyFirstPosition, EnemySecondPosition; protected GameObject CurrentEnemy; virtual protected int Speed { get => _speed; set => _speed = value; } virtual protected EnemyState CurrentState { get => _currentState; set => _currentState = value; } virtual protected int Health { get => _health; set => _health = value; } virtual protected int Damage { get => _damage; set => _damage = value; } void Update() { switch (CurrentState) { case EnemyState.Patrol: StartCoroutine(Patrol()); break; case EnemyState.Chase: MoveTowardsPlayer(); break; case EnemyState.Attack: Attack(); break; } } public virtual void TakeDamage(int damageTotake) { Health -= damageTotake; } protected virtual void MoveTowardsPlayer() { } protected virtual void Attack() { } protected virtual IEnumerator Patrol() { Transform nextTargetPoint = EnemyFirstPosition; while (true) { if (nextTargetPoint != null) { Debug.Log("move enemy"); CurrentEnemy.transform.position = Vector2.MoveTowards( CurrentEnemy.transform.position, nextTargetPoint.transform.position, Speed * Time.deltaTime ); if ((CurrentEnemy.transform.position - nextTargetPoint.transform.position).magnitude < 0.1f) { yield return new WaitForSeconds(1); nextTargetPoint = (nextTargetPoint == EnemyFirstPosition) ? EnemySecondPosition : EnemyFirstPosition; } } else if (nextTargetPoint == null) { nextTargetPoint = EnemyFirstPosition.transform; } } } protected virtual void Rest() { } protected virtual void Die() { Destroy(this.gameObject); } protected virtual void OnTriggerEnter2D(Collider2D collision) { if (collision.CompareTag("Player")) { playerInRange = true; } } protected virtual void OnTriggerExit2D(Collider2D collision) { if (collision.CompareTag("Player")) { playerInRange = false; } } }
子类 LunaticCultistBase
using Assets.Code.Enemy.Enemies; using System.Collections; using System.Collections.Generic; using UnityEngine; public class LunaticCultistBase : EnemyBase { private Animator _enemyAnimator; private Rigidbody2D _enemyRigidbody; private SpriteRenderer _enemySpriteRenderer; void Start() { _enemyAnimator = GetComponent<Animator>(); _enemyRigidbody = GetComponent<Rigidbody2D>(); _enemySpriteRenderer = GetComponent<SpriteRenderer>(); base.Health = 20; base.Damage = 10; base.Speed = 10; base.CurrentEnemy = this.gameObject; base.CurrentState = EnemyState.Patrol; CurrentEnemy = this.gameObject; } void Update() { if (base.Health <= 0) Die(); if (playerInRange) { if (PlayerTransform.position.x < this.gameObject.transform.position.x) { _enemySpriteRenderer.flipX = true; } else { _enemySpriteRenderer.flipX = false; } } } public override void TakeDamage(int damageTotake) { _enemyRigidbody.bodyType = RigidbodyType2D.Static; base.TakeDamage(damageTotake); Invoke("SetAnimationAndBodyType", 0.1f); } private void SetAnimationAndBodyType() { _enemyAnimator.SetTrigger("Hit"); _enemyRigidbody.bodyType = RigidbodyType2D.Dynamic; } protected override void Die() { _enemyAnimator.SetTrigger("Death"); Invoke("InvokeDie", 1f); } private void InvokeDie() { base.Die(); } }
EnemyState 枚举
public enum EnemyState { Patrol, Chase, Attack }
问题原因与解决方案
1. 核心问题:子类未调用基类Update方法
子类LunaticCultistBase重写了Update方法,但没有执行base.Update(),导致基类中根据CurrentState触发巡逻逻辑的代码完全没有运行。
修改子类Update方法:
void Update() { base.Update(); // 必须调用基类Update,触发状态判断逻辑 if (base.Health <= 0) Die(); if (playerInRange) { if (PlayerTransform.position.x < this.gameObject.transform.position.x) { _enemySpriteRenderer.flipX = true; } else { _enemySpriteRenderer.flipX = false; } } }
2. 次要问题:Patrol协程重复启动
基类Update每帧都会检查CurrentState,如果是Patrol就启动新的协程,会导致多个协程同时控制敌人移动,引发逻辑混乱。需要添加标记避免重复启动,并在状态切换时终止协程。
修改基类EnemyBase:
private EnemyState _currentState; private bool isPatrolling = false; // 新增巡逻状态标记 void Update() { switch (CurrentState) { case EnemyState.Patrol: if (!isPatrolling) { isPatrolling = true; StartCoroutine(Patrol()); } break; case EnemyState.Chase: isPatrolling = false; MoveTowardsPlayer(); break; case EnemyState.Attack: isPatrolling = false; Attack(); break; } } protected virtual IEnumerator Patrol() { Transform nextTargetPoint = EnemyFirstPosition; // 仅当处于Patrol状态时执行巡逻逻辑 while (CurrentState == EnemyState.Patrol) { if (nextTargetPoint != null) { Debug.Log("move enemy"); CurrentEnemy.transform.position = Vector2.MoveTowards( CurrentEnemy.transform.position, nextTargetPoint.transform.position, Speed * Time.deltaTime ); if ((CurrentEnemy.transform.position - nextTargetPoint.transform.position).magnitude < 0.1f) { yield return new WaitForSeconds(1); nextTargetPoint = (nextTargetPoint == EnemyFirstPosition) ? EnemySecondPosition : EnemyFirstPosition; } } else { // EnemyFirstPosition已经是Transform,无需再取.transform nextTargetPoint = EnemyFirstPosition; } yield return null; // 每帧等待,避免阻塞主线程 } isPatrolling = false; // 协程退出时重置标记 }
3. 细节修正
原Patrol方法中nextTargetPoint = EnemyFirstPosition.transform;存在冗余,因为EnemyFirstPosition本身就是Transform类型,直接赋值nextTargetPoint = EnemyFirstPosition;即可。
内容的提问来源于stack exchange,提问作者JULUXX JULUX
相关产品推荐
相关产品推荐

