Unity 2021.3 2D RPG 实战:基于 Ruby‘s Adventure 扩展双状态敌人 AI 系统

发布时间:2026/7/5 11:47:25

Unity 2021.3 2D RPG 实战:基于 Ruby‘s Adventure 扩展双状态敌人 AI 系统 Unity 2021.3 2D RPG 实战模块化双状态敌人 AI 系统设计与实现在 2D RPG 游戏开发中敌人 AI 的行为复杂度直接影响游戏体验。本文将基于 Unity 2021.3 引擎从零构建一个可扩展的有限状态机FSMAI 框架并以 Rubys Adventure 项目为基础实现包含巡逻、追击、警戒和返回四种行为模式的智能敌人系统。1. 有限状态机基础架构设计有限状态机是游戏 AI 的经典实现方式其核心思想是将复杂行为分解为离散的状态和转换条件。我们先创建一个基础 FSM 类作为所有 AI 行为的父类using UnityEngine; public abstract class FSMBase : MonoBehaviour { protected enum FSMState { None, Patrol, Chase, Alert, Return } protected FSMState currentState FSMState.None; protected virtual void Update() { switch(currentState) { case FSMState.Patrol: PatrolState(); break; case FSMState.Chase: ChaseState(); break; case FSMState.Alert: AlertState(); break; case FSMState.Return: ReturnState(); break; } } protected abstract void PatrolState(); protected abstract void ChaseState(); protected abstract void AlertState(); protected abstract void ReturnState(); protected void ChangeState(FSMState newState) { if(currentState ! newState) { ExitState(currentState); currentState newState; EnterState(newState); } } protected virtual void EnterState(FSMState state) {} protected virtual void ExitState(FSMState state) {} }这个基类定义了状态机的核心结构包含四个关键方法状态更新逻辑通过 Update 方法驱动状态转换状态切换机制ChangeState 方法处理状态过渡状态生命周期钩子EnterState/ExitState 用于状态初始化和清理抽象状态方法强制子类实现具体行为逻辑2. 巡逻状态实现与路径点系统巡逻是敌人 AI 的基础行为我们首先实现一个基于路径点的巡逻系统[System.Serializable] public class PatrolPath { public Transform[] waypoints; public float waitTime 1f; public float moveSpeed 2f; [Range(0.1f, 5f)] public float detectionRadius 2f; } public class PatrolState : FSMBase { [SerializeField] private PatrolPath patrolConfig; private int currentWaypoint 0; private float waitCounter 0; private bool isWaiting false; protected override void PatrolState() { if(patrolConfig.waypoints.Length 0) return; Transform target patrolConfig.waypoints[currentWaypoint]; float distance Vector2.Distance(transform.position, target.position); if(distance 0.1f) { if(!isWaiting) { waitCounter Time.time patrolConfig.waitTime; isWaiting true; } else if(Time.time waitCounter) { currentWaypoint (currentWaypoint 1) % patrolConfig.waypoints.Length; isWaiting false; } } else { Vector2 direction (target.position - transform.position).normalized; transform.Translate(direction * patrolConfig.moveSpeed * Time.deltaTime); } } protected override void EnterState(FSMState state) { if(state FSMState.Patrol) { currentWaypoint 0; isWaiting false; } } }关键参数配置建议参数推荐值作用waitTime0.5-3秒每个路径点停留时间moveSpeed1.5-3移动速度detectionRadius1.5-3玩家检测半径3. 追击与警戒状态实现当敌人发现玩家后应切换到追击状态并在丢失目标后进入警戒状态public class ChaseAndAlertState : FSMBase { [Header(Chase Settings)] [SerializeField] private float chaseSpeed 3.5f; [SerializeField] private float chaseRange 5f; [SerializeField] private float attackRange 1f; [Header(Alert Settings)] [SerializeField] private float alertDuration 3f; [SerializeField] private float alertMoveSpeed 1f; [SerializeField] private int alertSearchPoints 8; private Transform player; private float alertTimer; private Vector2 lastKnownPosition; protected override void ChaseState() { if(player null) return; float distance Vector2.Distance(transform.position, player.position); if(distance chaseRange) { lastKnownPosition player.position; ChangeState(FSMState.Alert); return; } if(distance attackRange) { // 攻击逻辑 return; } Vector2 direction (player.position - transform.position).normalized; transform.Translate(direction * chaseSpeed * Time.deltaTime); } protected override void AlertState() { alertTimer - Time.deltaTime; if(alertTimer 0) { ChangeState(FSMState.Return); return; } // 扇形搜索逻辑 float angleStep 360f / alertSearchPoints; for(int i 0; i alertSearchPoints; i) { float angle i * angleStep; Vector2 dir Quaternion.Euler(0, 0, angle) * Vector2.right; RaycastHit2D hit Physics2D.Raycast(transform.position, dir, chaseRange); if(hit.collider ! null hit.collider.CompareTag(Player)) { ChangeState(FSMState.Chase); return; } } // 向最后已知位置移动 if(Vector2.Distance(transform.position, lastKnownPosition) 0.5f) { Vector2 direction (lastKnownPosition - (Vector2)transform.position).normalized; transform.Translate(direction * alertMoveSpeed * Time.deltaTime); } } protected override void EnterState(FSMState state) { if(state FSMState.Alert) { alertTimer alertDuration; } } }4. 状态转换与视觉检测系统完整的 AI 系统需要可靠的状态转换机制。我们实现一个基于视觉锥的检测系统public class AIVision : MonoBehaviour { [Header(Detection Settings)] [SerializeField] private float viewRadius 5f; [Range(0, 360)] [SerializeField] private float viewAngle 90f; [SerializeField] private LayerMask targetMask; [SerializeField] private LayerMask obstacleMask; [Header(Debug)] [SerializeField] private bool drawGizmos true; public bool CanSeePlayer(out Transform player) { player null; Collider2D[] targetsInView Physics2D.OverlapCircleAll( transform.position, viewRadius, targetMask); foreach(var target in targetsInView) { if(!target.CompareTag(Player)) continue; Vector2 dirToTarget (target.transform.position - transform.position).normalized; float angleToTarget Vector2.Angle(transform.right, dirToTarget); if(angleToTarget viewAngle / 2f) { float dstToTarget Vector2.Distance(transform.position, target.transform.position); if(!Physics2D.Raycast(transform.position, dirToTarget, dstToTarget, obstacleMask)) { player target.transform; return true; } } } return false; } private void OnDrawGizmos() { if(!drawGizmos) return; Gizmos.color Color.white; Gizmos.DrawWireSphere(transform.position, viewRadius); Vector3 viewAngleA DirFromAngle(-viewAngle / 2, false); Vector3 viewAngleB DirFromAngle(viewAngle / 2, false); Gizmos.DrawLine(transform.position, transform.position viewAngleA * viewRadius); Gizmos.DrawLine(transform.position, transform.position viewAngleB * viewRadius); } private Vector3 DirFromAngle(float angleInDegrees, bool angleIsGlobal) { if(!angleIsGlobal) { angleInDegrees transform.eulerAngles.z; } return new Vector3( Mathf.Cos(angleInDegrees * Mathf.Deg2Rad), Mathf.Sin(angleInDegrees * Mathf.Deg2Rad), 0); } }视觉检测参数优化建议参数作用典型值viewRadius检测半径4-8viewAngle视野角度60-120targetMask目标层级PlayerobstacleMask障碍物层级Walls, Obstacles5. 完整 AI 控制器集成将各个模块整合为完整的敌人 AI 控制器[RequireComponent(typeof(AIVision))] public class EnemyAIController : FSMBase { [Header(References)] [SerializeField] private PatrolPath patrolPath; [SerializeField] private Transform returnPoint; private AIVision vision; private Transform player; private void Awake() { vision GetComponentAIVision(); currentState FSMState.Patrol; } protected override void Update() { bool canSeePlayer vision.CanSeePlayer(out player); switch(currentState) { case FSMState.Patrol: if(canSeePlayer) ChangeState(FSMState.Chase); else PatrolState(); break; case FSMState.Chase: if(!canSeePlayer) ChangeState(FSMState.Alert); else ChaseState(); break; case FSMState.Alert: if(canSeePlayer) ChangeState(FSMState.Chase); else AlertState(); break; case FSMState.Return: if(canSeePlayer) ChangeState(FSMState.Chase); else ReturnState(); break; } } protected override void ReturnState() { float distance Vector2.Distance(transform.position, returnPoint.position); if(distance 0.1f) { ChangeState(FSMState.Patrol); return; } Vector2 direction (returnPoint.position - transform.position).normalized; transform.Translate(direction * patrolPath.moveSpeed * Time.deltaTime); } // 其他状态方法已在前面实现... }6. 性能优化与调试技巧在实际项目中AI 系统性能至关重要。以下是几个关键优化策略检测频率控制[Header(Performance)] [SerializeField] private float detectionInterval 0.3f; private float detectionTimer; private void Update() { detectionTimer - Time.deltaTime; if(detectionTimer 0) { detectionTimer detectionInterval; UpdateDetection(); } // 其他更新逻辑... }状态机调试可视化private void OnGUI() { if(!debugMode) return; GUIStyle style new GUIStyle(); style.fontSize 20; style.normal.textColor Color.red; Vector3 screenPos Camera.main.WorldToScreenPoint(transform.position); GUI.Label(new Rect(screenPos.x, Screen.height - screenPos.y, 200, 50), $State: {currentState}, style); }行为参数动态调整[Header(Dynamic Difficulty)] [SerializeField] private bool adaptiveAI true; [SerializeField] private float playerSkillFactor 1f; private float GetAdaptiveSpeed(float baseSpeed) { if(!adaptiveAI) return baseSpeed; // 根据玩家表现调整难度 float healthFactor 1 - (player.CurrentHealth / player.MaxHealth); return baseSpeed * (1 healthFactor * playerSkillFactor); }7. 扩展与高级功能基础 AI 系统完成后可以考虑添加以下高级功能行为树集成// 示例简单的行为树节点 public abstract class BTNode { public enum Status { Running, Success, Failure } public abstract Status Execute(); } public class ChaseNode : BTNode { private Transform target; private EnemyAIController ai; public ChaseNode(EnemyAIController ai, Transform target) { this.ai ai; this.target target; } public override Status Execute() { // 实现追击逻辑 return Status.Running; } }机器学习行为// 示例使用ML-Agents进行简单学习 public class LearningAgent : Agent { public override void OnActionReceived(float[] vectorAction) { // 解析AI决策动作 float moveX vectorAction[0]; float moveY vectorAction[1]; // 执行动作 transform.Translate(new Vector3(moveX, moveY, 0) * Time.deltaTime); // 奖励/惩罚机制 if(FoundPlayer()) AddReward(1.0f); else AddReward(-0.01f); } }群体行为系统public class SwarmBehavior : MonoBehaviour { [SerializeField] private float separationRadius 1.5f; [SerializeField] private float alignmentWeight 0.5f; private ListEnemyAIController nearbyEnemies new ListEnemyAIController(); private void Update() { UpdateNearbyEnemies(); ApplySwarmBehavior(); } private void ApplySwarmBehavior() { Vector2 separation Vector2.zero; Vector2 alignment Vector2.zero; foreach(var enemy in nearbyEnemies) { // 分离行为 float distance Vector2.Distance(transform.position, enemy.transform.position); if(distance separationRadius) { separation (Vector2)(transform.position - enemy.transform.position) * (separationRadius - distance); } // 对齐行为 alignment enemy.GetMovementDirection(); } // 应用行为 if(nearbyEnemies.Count 0) { alignment alignment / nearbyEnemies.Count * alignmentWeight; Vector2 swarmDirection (separation alignment).normalized; transform.Translate(swarmDirection * Time.deltaTime); } } }

相关新闻