第6章 游戏AI决策系统的核心——有限状态机(FSM)设计与实现
摘要 本章介绍了有限状态机(FSM)在游戏AI中的应用,重点分析了经典游戏《吃豆人》中红幽灵的FSM实现。FSM通过定义离散状态(如追逐、散射、恐惧和被吃)及其转换条件,有效模拟了游戏角色的智能行为。示例代码展示了完整的红幽灵AI系统,包括状态管理、行为设置、路径计算和动画控制等核心组件。该实现体现了商业游戏中FSM的设计思路,如状态计时器、目标位置计算和视觉反馈更新等关键技术点。
第六章 游戏AI决策系统的核心——有限状态机(FSM)设计与实现
6.1 有限状态机的基本概念与设计模式
有限状态机(Finite State Machine,简称FSM)是游戏人工智能中最经典、最常用的决策模型之一。它是一种抽象的数学模型,用于描述对象在其生命周期内可能处于的有限状态集合,以及在这些状态之间转换的条件和规则。在游戏开发中,FSM特别适合模拟具有明确行为模式的角色,如敌人、NPC或游戏中的各种实体。
6.1.1 经典游戏《Pac-Man》中红幽灵的FSM设计解析
在经典游戏《吃豆人》中,红幽灵(Blinky)的AI行为是通过一个精心设计的有限状态机实现的。这个FSM包含了几个关键状态,每个状态都有特定的行为模式和转换条件。
红幽灵的主要状态包括:
- 追逐状态:直接朝向吃豆人当前位置移动
- 散射状态:移动到地图的特定角落
- 恐惧状态:当吃豆人吃掉能量豆时,幽灵变得脆弱并试图逃离
- 被吃状态:当幽灵被吃豆人吃掉时,返回重生点
下面是红幽灵状态机的完整实现,展示了商业游戏中的FSM设计思路:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class PacManGhostFSM : MonoBehaviour
{
[System.Serializable]
public class GhostBehaviorSettings
{
public float normalSpeed = 3.5f;
public float frightenedSpeed = 2.5f;
public float eatenSpeed = 6.0f;
public float scatterDuration = 7.0f;
public float chaseDuration = 20.0f;
public float frightenedDuration = 10.0f;
public Vector2 scatterTargetCorner = new Vector2(25, 34);
public Color normalColor = Color.red;
public Color frightenedColor = Color.blue;
public Color eatenColor = Color.white;
}
[System.Serializable]
public class GhostStateTimers
{
public float scatterTimer;
public float chaseTimer;
public float frightenedTimer;
public float stateChangeCooldown;
}
public enum GhostState
{
Scatter, // 散射状态
Chase, // 追逐状态
Frightened, // 恐惧状态
Eaten, // 被吃状态
LeavingHouse, // 离开鬼屋状态
EnteringHouse // 进入鬼屋状态
}
public enum GhostMode
{
Normal,
Frightened,
Eaten
}
[Header("行为设置")]
[SerializeField]
private GhostBehaviorSettings behaviorSettings = new GhostBehaviorSettings();
[Header("状态管理")]
[SerializeField]
private GhostState currentState = GhostState.Scatter;
[SerializeField]
private GhostMode currentMode = GhostMode.Normal;
[SerializeField]
private GhostStateTimers stateTimers = new GhostStateTimers();
[Header("目标追踪")]
[SerializeField]
private Transform pacManTransform;
[SerializeField]
private Vector2 currentTargetPosition;
[SerializeField]
private Vector2 scatterTargetPosition;
[Header("组件引用")]
[SerializeField]
private SpriteRenderer ghostSpriteRenderer;
[SerializeField]
private Animator ghostAnimator;
[SerializeField]
private GhostMovementController movementController;
[Header("路径计算")]
[SerializeField]
private PacManGridNavigation gridNavigation;
[SerializeField]
private List<Vector2> currentPath = new List<Vector2>();
[SerializeField]
private int currentPathIndex;
private Vector2 ghostHousePosition = new Vector2(13.5f, 17);
private bool isInGhostHouse = true;
private bool isVulnerable = false;
private int dotsEatenForRelease = 0;
private int dotsEatenCounter = 0;
private void Awake()
{
InitializeComponents();
InitializeStateMachine();
}
private void InitializeComponents()
{
if (ghostSpriteRenderer == null)
{
ghostSpriteRenderer = GetComponent<SpriteRenderer>();
}
if (ghostAnimator == null)
{
ghostAnimator = GetComponent<Animator>();
}
if (movementController == null)
{
movementController = GetComponent<GhostMovementController>();
}
if (gridNavigation == null)
{
gridNavigation = FindObjectOfType<PacManGridNavigation>();
}
if (pacManTransform == null)
{
GameObject pacMan = GameObject.FindGameObjectWithTag("Player");
if (pacMan != null)
{
pacManTransform = pacMan.transform;
}
}
}
private void InitializeStateMachine()
{
// 初始化状态
currentState = GhostState.LeavingHouse;
currentMode = GhostMode.Normal;
isInGhostHouse = true;
// 设置初始目标位置
scatterTargetPosition = behaviorSettings.scatterTargetCorner;
// 初始化计时器
stateTimers.scatterTimer = behaviorSettings.scatterDuration;
stateTimers.chaseTimer = behaviorSettings.chaseDuration;
stateTimers.frightenedTimer = behaviorSettings.frightenedDuration;
stateTimers.stateChangeCooldown = 0f;
// 设置初始外观
UpdateGhostAppearance();
Debug.Log("红幽灵FSM初始化完成,初始状态: " + currentState);
}
private void Update()
{
UpdateStateTimers();
UpdateStateMachine();
UpdateMovement();
UpdateAnimations();
}
private void UpdateStateTimers()
{
// 更新所有状态计时器
switch (currentState)
{
case GhostState.Scatter:
stateTimers.scatterTimer -= Time.deltaTime;
if (stateTimers.scatterTimer <= 0)
{
ChangeState(GhostState.Chase);
}
break;
case GhostState.Chase:
stateTimers.chaseTimer -= Time.deltaTime;
if (stateTimers.chaseTimer <= 0)
{
ChangeState(GhostState.Scatter);
}
break;
case GhostState.Frightened:
stateTimers.frightenedTimer -= Time.deltaTime;
if (stateTimers.frightenedTimer <= 0)
{
ChangeToNormalMode();
}
// 闪烁警告(最后几秒)
if (stateTimers.frightenedTimer < 3.0f)
{
float blinkRate = Mathf.PingPong(Time.time * 10, 1);
ghostSpriteRenderer.color = blinkRate > 0.5f ?
behaviorSettings.frightenedColor : behaviorSettings.normalColor;
}
break;
}
// 更新状态转换冷却
if (stateTimers.stateChangeCooldown > 0)
{
stateTimers.stateChangeCooldown -= Time.deltaTime;
}
}
private void UpdateStateMachine()
{
// 处理状态特定的逻辑
switch (currentState)
{
case GhostState.LeavingHouse:
HandleLeavingHouseState();
break;
case GhostState.Scatter:
HandleScatterState();
break;
case GhostState.Chase:
HandleChaseState();
break;
case GhostState.Frightened:
HandleFrightenedState();
break;
case GhostState.Eaten:
HandleEatenState();
break;
case GhostState.EnteringHouse:
HandleEnteringHouseState();
break;
}
}
private void HandleLeavingHouseState()
{
// 离开鬼屋的逻辑
Vector2 currentPos = transform.position;
Vector2 houseExit = new Vector2(13.5f, 20);
if (Vector2.Distance(currentPos, houseExit) < 0.1f)
{
// 到达出口,切换到散射状态
ChangeState(GhostState.Scatter);
isInGhostHouse = false;
}
else
{
// 向出口移动
currentTargetPosition = houseExit;
CalculatePathToTarget(houseExit);
}
}
private void HandleScatterState()
{
// 散射状态:移动到地图角落
currentTargetPosition = scatterTargetPosition;
// 定期重新计算路径
if (ShouldRecalculatePath())
{
CalculatePathToTarget(scatterTargetPosition);
}
// 检查是否接近目标(允许一定误差)
if (Vector2.Distance(transform.position, scatterTargetPosition) < 1.0f)
{
// 到达角落,可以稍微徘徊
WanderAroundCorner();
}
}
private void HandleChaseState()
{
if (pacManTransform == null)
{
return;
}
// 追逐状态:根据红幽灵的策略直接追向吃豆人
Vector2 pacManPosition = pacManTransform.position;
currentTargetPosition = pacManPosition;
// 定期重新计算路径
if (ShouldRecalculatePath())
{
CalculatePathToTarget(pacManPosition);
}
// 红幽灵的特殊行为:当距离吃豆人很近时,会尝试预测移动方向
float distanceToPacMan = Vector2.Distance(transform.position, pacManPosition);
if (distanceToPacMan < 5.0f)
{
// 尝试预测吃豆人移动方向
Rigidbody2D pacManRb = pacManTransform.GetComponent<Rigidbody2D>();
if (pacManRb != null)
{
Vector2 predictedPosition = pacManPosition + pacManRb.velocity.normalized * 3.0f;
currentTargetPosition = predictedPosition;
}
}
}
private void HandleFrightenedState()
{
// 恐惧状态:随机移动以躲避吃豆人
if (pacManTransform == null)
{
return;
}
// 计算吃豆人位置
Vector2 pacManPosition = pacManTransform.position;
Vector2 awayFromPacMan = ((Vector2)transform.position - pacManPosition).normalized;
// 选择远离吃豆人的方向
Vector2[] possibleDirections = GetPossibleDirections();
Vector2 bestDirection = Vector2.zero;
float bestScore = -Mathf.Infinity;
foreach (Vector2 direction in possibleDirections)
{
// 避免回头(除非必须)
if (movementController.CurrentDirection == -direction)
{
continue;
}
// 计算分数:远离吃豆人 + 随机因素
float distanceScore = Vector2.Dot(direction, awayFromPacMan);
float randomScore = Random.Range(0f, 0.3f);
float totalScore = distanceScore + randomScore;
if (totalScore > bestScore)
{
bestScore = totalScore;
bestDirection = direction;
}
}
// 应用移动方向
if (bestDirection != Vector2.zero)
{
movementController.SetDirection(bestDirection);
}
// 恐惧状态下的目标位置(随机点)
if (ShouldRecalculatePath() || currentPath.Count == 0)
{
Vector2 randomTarget = GetRandomAccessiblePosition();
CalculatePathToTarget(randomTarget);
}
}
private void HandleEatenState()
{
// 被吃状态:返回鬼屋
currentTargetPosition = ghostHousePosition;
if (Vector2.Distance(transform.position, ghostHousePosition) < 0.5f)
{
// 到达鬼屋,切换到进入鬼屋状态
ChangeState(GhostState.EnteringHouse);
}
else
{
// 计算返回鬼屋的路径
if (ShouldRecalculatePath())
{
CalculatePathToTarget(ghostHousePosition);
}
}
}
private void HandleEnteringHouseState()
{
// 进入鬼屋状态:在鬼屋内重置
transform.position = ghostHousePosition;
isInGhostHouse = true;
// 短暂延迟后恢复正常
StartCoroutine(ResetFromEatenState());
}
private IEnumerator ResetFromEatenState()
{
yield return new WaitForSeconds(2.0f);
// 重置为正常模式
ChangeToNormalMode();
ChangeState(GhostState.LeavingHouse);
Debug.Log("红幽灵已重生");
}
private void ChangeState(GhostState newState)
{
if (currentState == newState || stateTimers.stateChangeCooldown > 0)
{
return;
}
GhostState previousState = currentState;
currentState = newState;
// 重置相关计时器
switch (newState)
{
case GhostState.Scatter:
stateTimers.scatterTimer = behaviorSettings.scatterDuration;
break;
case GhostState.Chase:
stateTimers.chaseTimer = behaviorSettings.chaseDuration;
break;
case GhostState.Frightened:
stateTimers.frightenedTimer = behaviorSettings.frightenedDuration;
break;
}
// 设置状态转换冷却
stateTimers.stateChangeCooldown = 0.5f;
// 清除当前路径
currentPath.Clear();
currentPathIndex = 0;
// 反转方向(经典吃豆人行为)
if (movementController != null &&
(previousState == GhostState.Scatter || previousState == GhostState.Chase) &&
(newState == GhostState.Scatter || newState == GhostState.Chase))
{
movementController.ReverseDirection();
}
Debug.Log($"红幽灵状态变化: {previousState} -> {newState}");
// 触发状态变化事件
OnGhostStateChanged(previousState, newState);
}
public void SetFrightenedMode()
{
if (currentMode == GhostMode.Eaten)
{
return; // 被吃状态下不进入恐惧模式
}
currentMode = GhostMode.Frightened;
isVulnerable = true;
// 如果当前不是被吃状态,切换到恐惧状态
if (currentState != GhostState.Eaten && currentState != GhostState.EnteringHouse)
{
ChangeState(GhostState.Frightened);
}
UpdateGhostAppearance();
Debug.Log("红幽灵进入恐惧模式");
}
public void SetEatenMode()
{
if (currentMode != GhostMode.Frightened)
{
return; // 只有恐惧模式下才能被吃
}
currentMode = GhostMode.Eaten;
ChangeState(GhostState.Eaten);
UpdateGhostAppearance();
// 增加分数
GameScoreManager.Instance.AddGhostScore(200);
Debug.Log("红幽灵被吃");
}
private void ChangeToNormalMode()
{
currentMode = GhostMode.Normal;
isVulnerable = false;
// 如果不是被吃或进入鬼屋状态,切换到散射状态
if (currentState != GhostState.Eaten && currentState != GhostState.EnteringHouse)
{
ChangeState(GhostState.Scatter);
}
UpdateGhostAppearance();
Debug.Log("红幽灵恢复正常模式");
}
private void UpdateGhostAppearance()
{
if (ghostSpriteRenderer == null)
{
return;
}
switch (currentMode)
{
case GhostMode.Normal:
ghostSpriteRenderer.color = behaviorSettings.normalColor;
break;
case GhostMode.Frightened:
ghostSpriteRenderer.color = behaviorSettings.frightenedColor;
break;
case GhostMode.Eaten:
ghostSpriteRenderer.color = behaviorSettings.eatenColor;
break;
}
// 更新动画参数
if (ghostAnimator != null)
{
ghostAnimator.SetBool("IsFrightened", currentMode == GhostMode.Frightened);
ghostAnimator.SetBool("IsEaten", currentMode == GhostMode.Eaten);
}
}
private void UpdateMovement()
{
if (movementController == null)
{
return;
}
// 根据当前模式设置速度
float currentSpeed = behaviorSettings.normalSpeed;
switch (currentMode)
{
case GhostMode.Frightened:
currentSpeed = behaviorSettings.frightenedSpeed;
break;
case GhostMode.Eaten:
currentSpeed = behaviorSettings.eatenSpeed;
break;
}
movementController.SetSpeed(currentSpeed);
// 如果有路径,沿路径移动
if (currentPath.Count > 0 && currentPathIndex < currentPath.Count)
{
Vector2 targetPosition = currentPath[currentPathIndex];
Vector2 currentPosition = transform.position;
if (Vector2.Distance(currentPosition, targetPosition) < 0.1f)
{
currentPathIndex++;
if (currentPathIndex >= currentPath.Count)
{
// 到达路径终点
currentPath.Clear();
currentPathIndex = 0;
}
}
else
{
// 向路径点移动
Vector2 direction = (targetPosition - currentPosition).normalized;
movementController.SetDirection(direction);
}
}
}
private void UpdateAnimations()
{
if (ghostAnimator == null || movementController == null)
{
return;
}
// 更新移动方向动画
Vector2 currentDirection = movementController.CurrentDirection;
if (Mathf.Abs(currentDirection.x) > Mathf.Abs(currentDirection.y))
{
// 水平移动
ghostAnimator.SetFloat("MoveX", Mathf.Sign(currentDirection.x));
ghostAnimator.SetFloat("MoveY", 0);
}
else
{
// 垂直移动
ghostAnimator.SetFloat("MoveX", 0);
ghostAnimator.SetFloat("MoveY", Mathf.Sign(currentDirection.y));
}
}
private void CalculatePathToTarget(Vector2 targetPosition)
{
if (gridNavigation == null)
{
return;
}
Vector2 startPosition = transform.position;
currentPath = gridNavigation.FindPath(startPosition, targetPosition);
currentPathIndex = 0;
if (currentPath.Count == 0)
{
Debug.LogWarning("无法找到路径到目标位置: " + targetPosition);
}
}
private bool ShouldRecalculatePath()
{
// 定期重新计算路径
return Time.frameCount % 30 == 0; // 每30帧重新计算一次
}
private Vector2[] GetPossibleDirections()
{
// 获取当前可移动的方向
// 在实际实现中,这会检查网格导航数据
return new Vector2[]
{
Vector2.up,
Vector2.down,
Vector2.left,
Vector2.right
};
}
private Vector2 GetRandomAccessiblePosition()
{
// 获取随机可到达的位置
// 简化实现:返回当前区域内的随机位置
Vector2 currentPos = transform.position;
float randomX = currentPos.x + Random.Range(-5f, 5f);
float randomY = currentPos.y + Random.Range(-5f, 5f);
return new Vector2(randomX, randomY);
}
private void WanderAroundCorner()
{
// 在角落附近徘徊
float wanderRadius = 2.0f;
float angle = Time.time * 0.5f; // 缓慢旋转
Vector2 wanderOffset = new Vector2(
Mathf.Cos(angle) * wanderRadius,
Mathf.Sin(angle) * wanderRadius
);
currentTargetPosition = scatterTargetPosition + wanderOffset;
}
public void OnPacManAtePowerPellet()
{
// 当吃豆人吃掉能量豆时的反应
SetFrightenedMode();
}
public void OnPacManAteDot()
{
// 当吃豆人吃掉豆子时的计数
dotsEatenCounter++;
// 检查是否达到释放条件(如果有)
if (isInGhostHouse && dotsEatenCounter >= dotsEatenForRelease)
{
dotsEatenCounter = 0;
// 可以触发释放逻辑
}
}
public bool IsVulnerable
{
get { return isVulnerable; }
}
public GhostState CurrentState
{
get { return currentState; }
}
public GhostMode CurrentMode
{
get { return currentMode; }
}
public event System.Action<GhostState, GhostState> OnGhostStateChanged = delegate { };
private void OnDrawGizmosSelected()
{
if (!Application.isPlaying)
{
return;
}
// 绘制当前目标位置
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(currentTargetPosition, 0.3f);
Gizmos.DrawLine(transform.position, currentTargetPosition);
// 绘制路径
Gizmos.color = Color.yellow;
for (int i = 0; i < currentPath.Count - 1; i++)
{
Gizmos.DrawLine(currentPath[i], currentPath[i + 1]);
Gizmos.DrawWireSphere(currentPath[i], 0.2f);
}
// 绘制当前状态信息
#if UNITY_EDITOR
string stateInfo = $"状态: {currentState}\n模式: {currentMode}";
if (isVulnerable) stateInfo += "\n(脆弱)";
UnityEditor.Handles.Label(
transform.position + Vector3.up * 0.5f,
stateInfo
);
#endif
}
}
public class GhostMovementController : MonoBehaviour
{
[SerializeField]
private Vector2 currentDirection = Vector2.right;
[SerializeField]
private float currentSpeed = 3.5f;
private Rigidbody2D rb2D;
private void Awake()
{
rb2D = GetComponent<Rigidbody2D>();
if (rb2D == null)
{
rb2D = gameObject.AddComponent<Rigidbody2D>();
rb2D.gravityScale = 0;
rb2D.constraints = RigidbodyConstraints2D.FreezeRotation;
}
}
private void FixedUpdate()
{
UpdateMovement();
}
private void UpdateMovement()
{
if (rb2D == null)
{
return;
}
Vector2 velocity = currentDirection * currentSpeed;
rb2D.velocity = velocity;
}
public void SetDirection(Vector2 newDirection)
{
if (newDirection.magnitude > 0.1f)
{
currentDirection = newDirection.normalized;
}
}
public void SetSpeed(float newSpeed)
{
currentSpeed = Mathf.Max(0, newSpeed);
}
public void ReverseDirection()
{
currentDirection = -currentDirection;
}
public Vector2 CurrentDirection
{
get { return currentDirection; }
}
}
public class PacManGridNavigation : MonoBehaviour
{
// 简化的网格导航系统
public List<Vector2> FindPath(Vector2 start, Vector2 end)
{
// 简化实现:返回直接路径
// 在实际吃豆人游戏中,这会使用更复杂的网格导航算法
List<Vector2> path = new List<Vector2>();
// 添加起点
path.Add(start);
// 添加几个中间点(简化)
Vector2 midPoint = (start + end) / 2;
path.Add(midPoint);
// 添加终点
path.Add(end);
return path;
}
}
public class GameScoreManager : MonoBehaviour
{
private static GameScoreManager instance;
private int currentScore = 0;
public static GameScoreManager Instance
{
get
{
if (instance == null)
{
instance = FindObjectOfType<GameScoreManager>();
if (instance == null)
{
GameObject go = new GameObject("GameScoreManager");
instance = go.AddComponent<GameScoreManager>();
DontDestroyOnLoad(go);
}
}
return instance;
}
}
public void AddGhostScore(int points)
{
currentScore += points;
Debug.Log($"得分: {points},总分: {currentScore}");
// 更新UI等
}
}
6.1.2 《Quake II》中怪物AI的有限状态机案例分析
在经典第一人称射击游戏《雷神之锤II》中,怪物AI使用了相对复杂但高效的有限状态机系统。每个怪物类型都有自己独特的状态机,但共享相似的基本结构。
《Quake II》怪物状态机的核心特点:
- 感知状态:怪物检测玩家存在
- 追逐状态:怪物追踪玩家位置
- 攻击状态:怪物执行攻击行为
- 受伤状态:怪物受到伤害时的反应
- 死亡状态:怪物死亡后的处理
下面是一个基于《Quake II》怪物AI的现代实现,展示了如何在3D射击游戏中应用FSM:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class QuakeMonsterFSM : MonoBehaviour
{
[System.Serializable]
public class MonsterStats
{
public int maxHealth = 100;
public float moveSpeed = 5.0f;
public float chaseSpeed = 7.0f;
public float attackRange = 10.0f;
public float detectionRange = 20.0f;
public float attackDamage = 10.0f;
public float attackCooldown = 1.0f;
public float painChance = 0.3f; // 受伤时进入疼痛状态的概率
}
[System.Serializable]
public class MonsterSounds
{
public AudioClip idleSound;
public AudioClip chaseSound;
public AudioClip attackSound;
public AudioClip painSound;
public AudioClip deathSound;
public float soundVolume = 0.7f;
}
public enum MonsterState
{
Idle, // 空闲状态
Patrol, // 巡逻状态
Chase, // 追逐状态
Attack, // 攻击状态
Pain, // 受伤疼痛状态
Dead, // 死亡状态
Ambush // 伏击状态
}
public enum MonsterType
{
Grunt, // 普通士兵
Enforcer, // 强化士兵
Berserker, // 狂战士
Flyer, // 飞行单位
Tank // 重型单位
}
[Header("怪物属性")]
[SerializeField]
private MonsterType monsterType = MonsterType.Grunt;
[SerializeField]
private MonsterStats stats = new MonsterStats();
[Header("状态管理")]
[SerializeField]
private MonsterState currentState = MonsterState.Idle;
[SerializeField]
private MonsterState previousState = MonsterState.Idle;
[SerializeField]
private int currentHealth;
[Header("目标追踪")]
[SerializeField]
private Transform playerTarget;
[SerializeField]
private Vector3 lastKnownPlayerPosition;
[SerializeField]
private float playerLastSeenTime;
[Header("攻击系统")]
[SerializeField]
private float attackTimer;
[SerializeField]
private bool canAttack = true;
[SerializeField]
private GameObject projectilePrefab;
[SerializeField]
private Transform projectileSpawnPoint;
[Header("组件引用")]
[SerializeField]
private CharacterController characterController;
[SerializeField]
private Animator monsterAnimator;
[SerializeField]
private AudioSource audioSource;
[SerializeField]
private AudioSource footstepAudioSource;
[SerializeField]
private MonsterSounds monsterSounds = new MonsterSounds();
[Header("巡逻系统")]
[SerializeField]
private List<Vector3> patrolPoints = new List<Vector3>();
[SerializeField]
private int currentPatrolIndex;
[SerializeField]
private float patrolWaitTime = 2.0f;
[SerializeField]
private float patrolTimer;
[Header("感知系统")]
[SerializeField]
private float sightCheckInterval = 0.2f;
[SerializeField]
private float lastSightCheckTime;
[SerializeField]
private bool playerInSight;
[SerializeField]
private float hearingRange = 15.0f;
private Vector3 spawnPosition;
private Quaternion spawnRotation;
private bool isActive = true;
private float stateEnterTime;
private float painDuration = 0.5f;
private float painTimer;
private void Awake()
{
InitializeMonster();
}
private void InitializeMonster()
{
// 保存初始位置和旋转
spawnPosition = transform.position;
spawnRotation = transform.rotation;
// 初始化生命值
currentHealth = stats.maxHealth;
// 获取组件引用
if (characterController == null)
{
characterController = GetComponent<CharacterController>();
}
if (monsterAnimator == null)
{
monsterAnimator = GetComponent<Animator>();
}
if (audioSource == null)
{
audioSource = GetComponent<AudioSource>();
}
if (footstepAudioSource == null)
{
footstepAudioSource = gameObject.AddComponent<AudioSource>();
footstepAudioSource.spatialBlend = 1.0f;
footstepAudioSource.volume = 0.3f;
}
// 查找玩家
if (playerTarget == null)
{
GameObject player = GameObject.FindGameObjectWithTag("Player");
if (player != null)
{
playerTarget = player.transform;
}
}
// 初始化巡逻点
InitializePatrolPoints();
// 设置初始状态
ChangeState(MonsterState.Idle);
Debug.Log($"{monsterType} 怪物初始化完成,生命值: {currentHealth}/{stats.maxHealth}");
}
private void InitializePatrolPoints()
{
// 根据怪物类型生成巡逻点
int patrolPointCount = 0;
switch (monsterType)
{
case MonsterType.Grunt:
patrolPointCount = 3;
break;
case MonsterType.Enforcer:
patrolPointCount = 4;
break;
case MonsterType.Berserker:
patrolPointCount = 2; // 狂战士更倾向于待命
break;
case MonsterType.Flyer:
patrolPointCount = 5; // 飞行单位有更大的巡逻范围
break;
case MonsterType.Tank:
patrolPointCount = 2; // 重型单位移动缓慢
break;
}
patrolPoints.Clear();
// 生成围绕生成点的巡逻点
for (int i = 0; i < patrolPointCount; i++)
{
float angle = (i * 360f / patrolPointCount) * Mathf.Deg2Rad;
float radius = Random.Range(3f, 8f);
Vector3 patrolPoint = spawnPosition + new Vector3(
Mathf.Cos(angle) * radius,
0,
Mathf.Sin(angle) * radius
);
patrolPoints.Add(patrolPoint);
}
currentPatrolIndex = 0;
patrolTimer = 0f;
}
private void Update()
{
if (!isActive || currentState == MonsterState.Dead)
{
return;
}
UpdatePerception();
UpdateStateMachine();
UpdateTimers();
UpdateAnimations();
}
private void UpdatePerception()
{
if (playerTarget == null || Time.time - lastSightCheckTime < sightCheckInterval)
{
return;
}
lastSightCheckTime = Time.time;
// 检查玩家是否在视野内
Vector3 directionToPlayer = playerTarget.position - transform.position;
float distanceToPlayer = directionToPlayer.magnitude;
// 检查是否在检测范围内
if (distanceToPlayer > stats.detectionRange)
{
playerInSight = false;
return;
}
// 检查视线是否被阻挡
RaycastHit hit;
bool hasLineOfSight = false;
if (Physics.Raycast(
transform.position + Vector3.up * 1.5f,
directionToPlayer.normalized,
out hit,
stats.detectionRange,
~LayerMask.GetMask("Monster", "Trigger"),
QueryTriggerInteraction.Ignore
))
{
if (hit.collider.CompareTag("Player"))
{
hasLineOfSight = true;
}
}
playerInSight = hasLineOfSight;
if (playerInSight)
{
lastKnownPlayerPosition = playerTarget.position;
playerLastSeenTime = Time.time;
// 如果玩家在听觉范围内,即使看不到也能听到
if (distanceToPlayer <= hearingRange)
{
OnPlayerDetected();
}
}
// 检查是否应该听到玩家声音
CheckHearing();
}
private void CheckHearing()
{
if (playerTarget == null)
{
return;
}
float distanceToPlayer = Vector3.Distance(transform.position, playerTarget.position);
// 如果玩家在听觉范围内且最近发出过声音
PlayerNoiseEmitter noiseEmitter = playerTarget.GetComponent<PlayerNoiseEmitter>();
if (noiseEmitter != null && distanceToPlayer <= hearingRange)
{
float lastNoiseTime = noiseEmitter.GetLastNoiseTime();
float noiseLevel = noiseEmitter.GetCurrentNoiseLevel();
// 根据噪音级别调整检测
float effectiveHearingRange = hearingRange * noiseLevel;
if (Time.time - lastNoiseTime < 2.0f && distanceToPlayer <= effectiveHearingRange)
{
// 听到玩家声音,前往调查
lastKnownPlayerPosition = playerTarget.position;
playerLastSeenTime = Time.time;
if (currentState == MonsterState.Idle || currentState == MonsterState.Patrol)
{
ChangeState(MonsterState.Chase);
}
}
}
}
private void UpdateStateMachine()
{
// 处理当前状态
switch (currentState)
{
case MonsterState.Idle:
HandleIdleState();
break;
case MonsterState.Patrol:
HandlePatrolState();
break;
case MonsterState.Chase:
HandleChaseState();
break;
case MonsterState.Attack:
HandleAttackState();
break;
case MonsterState.Pain:
HandlePainState();
break;
case MonsterState.Ambush:
HandleAmbushState();
break;
}
// 全局状态转换检查
CheckGlobalTransitions();
}
private void HandleIdleState()
{
// 空闲状态:播放空闲动画,偶尔环顾四周
// 一段时间后开始巡逻
float timeInState = Time.time - stateEnterTime;
if (timeInState > 5.0f)
{
ChangeState(MonsterState.Patrol);
}
// 随机播放空闲声音
if (Random.value < 0.01f && monsterSounds.idleSound != null)
{
PlaySound(monsterSounds.idleSound);
}
}
private void HandlePatrolState()
{
if (patrolPoints.Count == 0)
{
ChangeState(MonsterState.Idle);
return;
}
Vector3 targetPosition = patrolPoints[currentPatrolIndex];
Vector3 directionToTarget = (targetPosition - transform.position).normalized;
// 移动到巡逻点
MoveTowards(directionToTarget, stats.moveSpeed);
// 检查是否到达巡逻点
if (Vector3.Distance(transform.position, targetPosition) < 1.0f)
{
patrolTimer += Time.deltaTime;
if (patrolTimer >= patrolWaitTime)
{
// 前往下一个巡逻点
currentPatrolIndex = (currentPatrolIndex + 1) % patrolPoints.Count;
patrolTimer = 0f;
// 随机决定是否停留更久
if (Random.value < 0.3f)
{
patrolWaitTime = Random.Range(2.0f, 5.0f);
}
}
}
// 播放脚步声
PlayFootstepSound();
}
private void HandleChaseState()
{
if (playerTarget == null)
{
ChangeState(MonsterState.Patrol);
return;
}
// 检查玩家是否在攻击范围内
float distanceToPlayer = Vector3.Distance(transform.position, playerTarget.position);
if (distanceToPlayer <= stats.attackRange && playerInSight)
{
ChangeState(MonsterState.Attack);
return;
}
// 追逐玩家
Vector3 chaseDirection;
if (playerInSight)
{
// 直接追逐可见玩家
chaseDirection = (playerTarget.position - transform.position).normalized;
lastKnownPlayerPosition = playerTarget.position;
}
else
{
// 前往最后已知位置
chaseDirection = (lastKnownPlayerPosition - transform.position).normalized;
// 检查是否到达最后已知位置
if (Vector3.Distance(transform.position, lastKnownPlayerPosition) < 2.0f)
{
// 玩家已丢失,返回巡逻
ChangeState(MonsterState.Patrol);
return;
}
// 检查玩家丢失时间
if (Time.time - playerLastSeenTime > 10.0f)
{
ChangeState(MonsterState.Patrol);
return;
}
}
// 移动追逐
MoveTowards(chaseDirection, stats.chaseSpeed);
// 播放追逐声音
if (Random.value < 0.02f && monsterSounds.chaseSound != null)
{
PlaySound(monsterSounds.chaseSound);
}
// 播放脚步声
PlayFootstepSound(1.5f); // 追逐时脚步声更快
}
private void HandleAttackState()
{
if (playerTarget == null)
{
ChangeState(MonsterState.Patrol);
return;
}
// 面向玩家
Vector3 directionToPlayer = (playerTarget.position - transform.position).normalized;
directionToPlayer.y = 0;
if (directionToPlayer.magnitude > 0.1f)
{
Quaternion targetRotation = Quaternion.LookRotation(directionToPlayer);
transform.rotation = Quaternion.Slerp(
transform.rotation,
targetRotation,
10.0f * Time.deltaTime
);
}
// 检查玩家是否仍在攻击范围内
float distanceToPlayer = Vector3.Distance(transform.position, playerTarget.position);
if (distanceToPlayer > stats.attackRange || !playerInSight)
{
ChangeState(MonsterState.Chase);
return;
}
// 执行攻击
if (canAttack)
{
AttackPlayer();
canAttack = false;
attackTimer = stats.attackCooldown;
}
// 根据怪物类型决定攻击时的移动
switch (monsterType)
{
case MonsterType.Berserker:
// 狂战士在攻击时会继续靠近玩家
if (distanceToPlayer > 3.0f)
{
MoveTowards(directionToPlayer, stats.moveSpeed * 0.5f);
}
break;
case MonsterType.Flyer:
// 飞行单位会在攻击时保持距离
if (distanceToPlayer < stats.attackRange * 0.7f)
{
MoveTowards(-directionToPlayer, stats.moveSpeed * 0.3f);
}
break;
}
}
private void HandlePainState()
{
// 疼痛状态:短暂停顿,播放受伤动画
painTimer += Time.deltaTime;
if (painTimer >= painDuration)
{
// 返回之前的状态
ChangeState(previousState);
}
}
private void HandleAmbushState()
{
// 伏击状态:隐藏并等待玩家靠近
if (playerTarget == null)
{
ChangeState(MonsterState.Patrol);
return;
}
float distanceToPlayer = Vector3.Distance(transform.position, playerTarget.position);
// 当玩家足够近时发动攻击
if (distanceToPlayer < stats.attackRange * 0.7f && playerInSight)
{
ChangeState(MonsterState.Attack);
}
// 如果玩家离开或等待时间过长,放弃伏击
else if (Time.time - stateEnterTime > 15.0f)
{
ChangeState(MonsterState.Patrol);
}
}
private void CheckGlobalTransitions()
{
// 全局状态转换规则
// 死亡检查
if (currentHealth <= 0 && currentState != MonsterState.Dead)
{
ChangeState(MonsterState.Dead);
return;
}
// 玩家检测
if (playerInSight &&
(currentState == MonsterState.Idle || currentState == MonsterState.Patrol))
{
// 根据怪物类型决定反应
switch (monsterType)
{
case MonsterType.Grunt:
case MonsterType.Enforcer:
ChangeState(MonsterState.Chase);
break;
case MonsterType.Berserker:
ChangeState(MonsterState.Chase);
// 狂战士立即发出战吼
PlaySound(monsterSounds.chaseSound);
break;
case MonsterType.Flyer:
// 飞行单位可能选择伏击
if (Random.value < 0.5f)
{
ChangeState(MonsterState.Ambush);
}
else
{
ChangeState(MonsterState.Chase);
}
break;
case MonsterType.Tank:
// 重型单位反应较慢
if (Random.value < 0.3f)
{
ChangeState(MonsterState.Chase);
}
break;
}
}
}
private void MoveTowards(Vector3 direction, float speed)
{
if (characterController == null || !characterController.enabled)
{
return;
}
direction.y = 0;
// 应用移动
Vector3 moveVector = direction * speed * Time.deltaTime;
characterController.Move(moveVector);
// 旋转面向移动方向
if (direction.magnitude > 0.1f)
{
Quaternion targetRotation = Quaternion.LookRotation(direction);
transform.rotation = Quaternion.Slerp(
transform.rotation,
targetRotation,
5.0f * Time.deltaTime
);
}
}
private void AttackPlayer()
{
// 执行攻击
switch (monsterType)
{
case MonsterType.Grunt:
case MonsterType.Enforcer:
RangedAttack();
break;
case MonsterType.Berserker:
MeleeAttack();
break;
case MonsterType.Flyer:
FlyerAttack();
break;
case MonsterType.Tank:
TankAttack();
break;
}
// 播放攻击声音
if (monsterSounds.attackSound != null)
{
PlaySound(monsterSounds.attackSound);
}
// 触发攻击动画
if (monsterAnimator != null)
{
monsterAnimator.SetTrigger("Attack");
}
Debug.Log($"{monsterType} 发动攻击");
}
private void RangedAttack()
{
// 远程攻击:发射抛射物
if (projectilePrefab != null && projectileSpawnPoint != null)
{
GameObject projectile = Instantiate(
projectilePrefab,
projectileSpawnPoint.position,
projectileSpawnPoint.rotation
);
MonsterProjectile projScript = projectile.GetComponent<MonsterProjectile>();
if (projScript != null)
{
projScript.SetDamage(stats.attackDamage);
projScript.SetTarget(playerTarget);
}
}
else
{
// 如果没有抛射物,进行直接伤害
if (playerTarget != null)
{
PlayerHealth playerHealth = playerTarget.GetComponent<PlayerHealth>();
if (playerHealth != null)
{
playerHealth.TakeDamage(stats.attackDamage);
}
}
}
}
private void MeleeAttack()
{
// 近战攻击:检查范围内玩家
if (playerTarget != null)
{
float distance = Vector3.Distance(transform.position, playerTarget.position);
if (distance <= 3.0f)
{
PlayerHealth playerHealth = playerTarget.GetComponent<PlayerHealth>();
if (playerHealth != null)
{
playerHealth.TakeDamage(stats.attackDamage * 1.5f); // 近战伤害更高
}
}
}
}
private void FlyerAttack()
{
// 飞行单位攻击:快速俯冲或发射多个抛射物
if (projectilePrefab != null && projectileSpawnPoint != null)
{
// 发射3个散射抛射物
for (int i = 0; i < 3; i++)
{
Vector3 spread = new Vector3(
Random.Range(-0.2f, 0.2f),
Random.Range(-0.1f, 0.1f),
Random.Range(-0.2f, 0.2f)
);
Quaternion spreadRotation = projectileSpawnPoint.rotation * Quaternion.Euler(spread * 30);
GameObject projectile = Instantiate(
projectilePrefab,
projectileSpawnPoint.position,
spreadRotation
);
MonsterProjectile projScript = projectile.GetComponent<MonsterProjectile>();
if (projScript != null)
{
projScript.SetDamage(stats.attackDamage * 0.7f);
}
}
}
}
private void TankAttack()
{
// 重型单位攻击:高伤害但慢速
if (projectilePrefab != null && projectileSpawnPoint != null)
{
GameObject projectile = Instantiate(
projectilePrefab,
projectileSpawnPoint.position,
projectileSpawnPoint.rotation
);
MonsterProjectile projScript = projectile.GetComponent<MonsterProjectile>();
if (projScript != null)
{
projScript.SetDamage(stats.attackDamage * 2.0f);
projScript.SetSpeed(15.0f); // 较慢但伤害高
}
}
}
private void UpdateTimers()
{
// 更新攻击冷却
if (!canAttack)
{
attackTimer -= Time.deltaTime;
if (attackTimer <= 0)
{
canAttack = true;
}
}
}
private void UpdateAnimations()
{
if (monsterAnimator == null)
{
return;
}
// 设置状态参数
monsterAnimator.SetInteger("State", (int)currentState);
// 设置移动速度参数
float speed = 0f;
if (characterController != null)
{
speed = characterController.velocity.magnitude;
}
monsterAnimator.SetFloat("Speed", speed);
// 设置生命值参数
float healthRatio = (float)currentHealth / stats.maxHealth;
monsterAnimator.SetFloat("Health", healthRatio);
}
private void PlaySound(AudioClip clip)
{
if (audioSource != null && clip != null)
{
audioSource.PlayOneShot(clip, monsterSounds.soundVolume);
}
}
private void PlayFootstepSound(float pitchMultiplier = 1.0f)
{
if (footstepAudioSource != null && !footstepAudioSource.isPlaying)
{
// 根据移动速度调整脚步频率
float speed = characterController != null ? characterController.velocity.magnitude : 0f;
if (speed > 0.5f)
{
footstepAudioSource.pitch = Random.Range(0.8f, 1.2f) * pitchMultiplier;
footstepAudioSource.Play();
}
}
}
public void TakeDamage(float damage, Vector3 hitPoint, GameObject damageSource)
{
if (currentState == MonsterState.Dead || !isActive)
{
return;
}
// 减少生命值
currentHealth -= Mathf.RoundToInt(damage);
// 检查是否死亡
if (currentHealth <= 0)
{
ChangeState(MonsterState.Dead);
return;
}
// 根据概率进入疼痛状态
if (Random.value < stats.painChance && currentState != MonsterState.Pain)
{
previousState = currentState;
ChangeState(MonsterState.Pain);
painTimer = 0f;
// 播放疼痛声音
if (monsterSounds.painSound != null)
{
PlaySound(monsterSounds.painSound);
}
}
// 如果受到玩家攻击且不在追逐/攻击状态,开始追逐
if (damageSource != null && damageSource.CompareTag("Player") &&
(currentState == MonsterState.Idle || currentState == MonsterState.Patrol))
{
lastKnownPlayerPosition = damageSource.transform.position;
playerLastSeenTime = Time.time;
ChangeState(MonsterState.Chase);
}
Debug.Log($"{monsterType} 受到伤害: {damage},剩余生命: {currentHealth}");
}
private void ChangeState(MonsterState newState)
{
if (currentState == newState)
{
return;
}
previousState = currentState;
currentState = newState;
stateEnterTime = Time.time;
// 状态进入时的特殊处理
switch (newState)
{
case MonsterState.Chase:
OnEnterChaseState();
break;
case MonsterState.Attack:
OnEnterAttackState();
break;
case MonsterState.Dead:
OnEnterDeadState();
break;
case MonsterState.Ambush:
OnEnterAmbushState();
break;
}
Debug.Log($"{monsterType} 状态变化: {previousState} -> {newState}");
}
private void OnEnterChaseState()
{
// 追逐状态进入时的处理
if (monsterSounds.chaseSound != null)
{
PlaySound(monsterSounds.chaseSound);
}
}
private void OnEnterAttackState()
{
// 攻击状态进入时的处理
// 重置攻击计时器,可以立即攻击
canAttack = true;
attackTimer = 0f;
}
private void OnEnterDeadState()
{
// 死亡状态进入时的处理
isActive = false;
if (characterController != null)
{
characterController.enabled = false;
}
// 播放死亡声音
if (monsterSounds.deathSound != null)
{
PlaySound(monsterSounds.deathSound);
}
// 触发死亡动画
if (monsterAnimator != null)
{
monsterAnimator.SetTrigger("Die");
}
// 禁用碰撞器
Collider collider = GetComponent<Collider>();
if (collider != null)
{
collider.enabled = false;
}
// 开始消失过程
StartCoroutine(DeathSequence());
Debug.Log($"{monsterType} 已死亡");
}
private void OnEnterAmbushState()
{
// 伏击状态进入时的处理
// 尝试隐藏(如改变材质、缩小碰撞器等)
Renderer renderer = GetComponent<Renderer>();
if (renderer != null)
{
// 变暗材质以模拟隐藏
foreach (Material material in renderer.materials)
{
Color color = material.color;
color *= 0.5f;
material.color = color;
}
}
}
private IEnumerator DeathSequence()
{
// 等待死亡动画完成
yield return new WaitForSeconds(2.0f);
// 下沉效果
float sinkSpeed = 0.5f;
float sinkTime = 2.0f;
float timer = 0f;
while (timer < sinkTime)
{
transform.position += Vector3.down * sinkSpeed * Time.deltaTime;
timer += Time.deltaTime;
yield return null;
}
// 销毁对象
Destroy(gameObject);
}
private void OnPlayerDetected()
{
// 玩家被检测到时的处理
// 可以在这里触发警报、通知其他怪物等
// 通知附近的同类
float notificationRange = 15.0f;
Collider[] nearbyMonsters = Physics.OverlapSphere(
transform.position,
notificationRange,
LayerMask.GetMask("Monster")
);
foreach (Collider collider in nearbyMonsters)
{
if (collider.gameObject != gameObject)
{
QuakeMonsterFSM otherMonster = collider.GetComponent<QuakeMonsterFSM>();
if (otherMonster != null)
{
otherMonster.OnAlarmTriggered(lastKnownPlayerPosition);
}
}
}
}
public void OnAlarmTriggered(Vector3 alarmPosition)
{
// 被其他怪物警报触发
if (currentState == MonsterState.Idle || currentState == MonsterState.Patrol)
{
lastKnownPlayerPosition = alarmPosition;
playerLastSeenTime = Time.time;
ChangeState(MonsterState.Chase);
}
}
public MonsterState CurrentState
{
get { return currentState; }
}
public int CurrentHealth
{
get { return currentHealth; }
}
public bool IsActive
{
get { return isActive; }
}
private void OnDrawGizmosSelected()
{
if (!Application.isPlaying)
{
return;
}
// 绘制检测范围
Gizmos.color = playerInSight ? Color.red : Color.yellow;
Gizmos.DrawWireSphere(transform.position, stats.detectionRange);
// 绘制攻击范围
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(transform.position, stats.attackRange);
// 绘制听觉范围
Gizmos.color = new Color(0, 1, 1, 0.3f);
Gizmos.DrawWireSphere(transform.position, hearingRange);
// 绘制巡逻点
Gizmos.color = Color.green;
foreach (Vector3 point in patrolPoints)
{
Gizmos.DrawWireSphere(point, 0.3f);
}
// 绘制当前目标
if (playerTarget != null && playerInSight)
{
Gizmos.color = Color.red;
Gizmos.DrawLine(transform.position, playerTarget.position);
}
else if (lastKnownPlayerPosition != Vector3.zero)
{
Gizmos.color = Color.yellow;
Gizmos.DrawLine(transform.position, lastKnownPlayerPosition);
Gizmos.DrawWireSphere(lastKnownPlayerPosition, 0.5f);
}
// 绘制状态信息
#if UNITY_EDITOR
string stateInfo = $"{monsterType}\n";
stateInfo += $"状态: {currentState}\n";
stateInfo += $"生命: {currentHealth}/{stats.maxHealth}\n";
stateInfo += $"玩家视野: {playerInSight}";
UnityEditor.Handles.Label(
transform.position + Vector3.up * 2.5f,
stateInfo
);
#endif
}
}
public class MonsterProjectile : MonoBehaviour
{
[SerializeField]
private float speed = 20.0f;
[SerializeField]
private float damage = 10.0f;
[SerializeField]
private float lifetime = 5.0f;
[SerializeField]
private GameObject impactEffect;
private Transform target;
private Vector3 targetDirection;
private float spawnTime;
private void Start()
{
spawnTime = Time.time;
Destroy(gameObject, lifetime);
}
private void Update()
{
// 移动逻辑
if (target != null)
{
// 追踪目标
targetDirection = (target.position - transform.position).normalized;
}
// 应用移动
transform.position += targetDirection * speed * Time.deltaTime;
// 旋转面向移动方向
if (targetDirection.magnitude > 0.1f)
{
transform.rotation = Quaternion.LookRotation(targetDirection);
}
// 检查生命周期
if (Time.time - spawnTime > lifetime)
{
Destroy(gameObject);
}
}
public void SetDamage(float newDamage)
{
damage = newDamage;
}
public void SetSpeed(float newSpeed)
{
speed = newSpeed;
}
public void SetTarget(Transform newTarget)
{
target = newTarget;
if (target != null)
{
targetDirection = (target.position - transform.position).normalized;
}
}
private void OnTriggerEnter(Collider other)
{
if (other.CompareTag("Monster") || other.CompareTag("Projectile"))
{
return; // 忽略友军和别的抛射物
}
// 对玩家造成伤害
if (other.CompareTag("Player"))
{
PlayerHealth playerHealth = other.GetComponent<PlayerHealth>();
if (playerHealth != null)
{
playerHealth.TakeDamage(damage);
}
}
// 创建击中效果
if (impactEffect != null)
{
Instantiate(impactEffect, transform.position, Quaternion.identity);
}
// 销毁抛射物
Destroy(gameObject);
}
}
public class PlayerNoiseEmitter : MonoBehaviour
{
[SerializeField]
private float baseNoiseLevel = 1.0f;
[SerializeField]
private float runningNoiseMultiplier = 2.0f;
[SerializeField]
private float shootingNoiseLevel = 5.0f;
[SerializeField]
private float noiseDecayRate = 0.5f;
private float currentNoiseLevel = 0f;
private float lastNoiseTime = 0f;
private CharacterController characterController;
private void Awake()
{
characterController = GetComponent<CharacterController>();
}
private void Update()
{
UpdateNoise();
}
private void UpdateNoise()
{
// 基础噪音(移动产生)
float movementNoise = 0f;
if (characterController != null)
{
float speed = characterController.velocity.magnitude;
if (speed > 0.1f)
{
movementNoise = baseNoiseLevel;
// 跑步产生更多噪音
if (speed > 5.0f)
{
movementNoise *= runningNoiseMultiplier;
}
}
}
// 噪音衰减
currentNoiseLevel = Mathf.Max(movementNoise, currentNoiseLevel - noiseDecayRate * Time.deltaTime);
// 更新最后噪音时间
if (currentNoiseLevel > 0.1f)
{
lastNoiseTime = Time.time;
}
}
public void EmitNoise(float noiseAmount)
{
currentNoiseLevel = Mathf.Max(currentNoiseLevel, noiseAmount);
lastNoiseTime = Time.time;
}
public float GetCurrentNoiseLevel()
{
return currentNoiseLevel;
}
public float GetLastNoiseTime()
{
return lastNoiseTime;
}
}
public class PlayerHealth : MonoBehaviour
{
[SerializeField]
private float maxHealth = 100f;
[SerializeField]
private float currentHealth;
private void Awake()
{
currentHealth = maxHealth;
}
public void TakeDamage(float damage)
{
currentHealth -= damage;
currentHealth = Mathf.Max(0, currentHealth);
Debug.Log($"玩家受到伤害: {damage},剩余生命: {currentHealth}");
if (currentHealth <= 0)
{
Die();
}
}
private void Die()
{
Debug.Log("玩家死亡");
// 处理玩家死亡逻辑
}
}
6.2 有限状态机的基础实现方法
6.2.1 使用Switch语句实现简单状态机
最简单的有限状态机实现方式是使用switch语句。这种方法直观易懂,适合状态数量较少、逻辑简单的AI系统。尽管在大型项目中可能不够灵活,但对于小型游戏或原型开发来说,switch语句状态机是一个很好的起点。
下面是一个完整的switch语句状态机实现,展示了如何创建一个基础的敌人AI:
using UnityEngine;
using System.Collections;
public class SwitchStatementFSM : MonoBehaviour
{
public enum EnemyState
{
Idle,
Patrol,
Chase,
Attack,
Retreat,
Dead
}
[Header("敌人属性")]
[SerializeField]
private float moveSpeed = 3.5f;
[SerializeField]
private float chaseSpeed = 5.0f;
[SerializeField]
private float attackRange = 2.0f;
[SerializeField]
private float detectionRange = 10.0f;
[SerializeField]
private float health = 100f;
[SerializeField]
private float attackDamage = 10f;
[SerializeField]
private float attackCooldown = 1.0f;
[Header("状态管理")]
[SerializeField]
private EnemyState currentState = EnemyState.Idle;
[SerializeField]
private float stateTimer = 0f;
[SerializeField]
private float attackTimer = 0f;
[Header("目标与导航")]
[SerializeField]
private Transform playerTarget;
[SerializeField]
private Vector3 patrolTarget;
[SerializeField]
private Vector3 retreatPosition;
[SerializeField]
private float patrolRadius = 5f;
[Header("组件引用")]
[SerializeField]
private CharacterController characterController;
[SerializeField]
private Animator enemyAnimator;
[SerializeField]
private GameObject projectilePrefab;
[SerializeField]
private Transform weaponMuzzle;
private Vector3 spawnPosition;
private bool canAttack = true;
private float idleDuration = 3f;
private float patrolPointWaitTime = 2f;
private void Awake()
{
InitializeEnemy();
}
private void InitializeEnemy()
{
spawnPosition = transform.position;
if (characterController == null)
{
characterController = GetComponent<CharacterController>();
}
if (enemyAnimator == null)
{
enemyAnimator = GetComponent<Animator>();
}
if (playerTarget == null)
{
GameObject player = GameObject.FindGameObjectWithTag("Player");
if (player != null)
{
playerTarget = player.transform;
}
}
// 设置初始巡逻目标
SetNewPatrolTarget();
// 设置初始状态
ChangeState(EnemyState.Idle);
Debug.Log("敌人初始化完成,初始状态: " + currentState);
}
private void Update()
{
// 更新状态计时器
stateTimer += Time.deltaTime;
// 更新攻击冷却
if (!canAttack)
{
attackTimer -= Time.deltaTime;
if (attackTimer <= 0)
{
canAttack = true;
}
}
// 执行当前状态
ExecuteCurrentState();
// 更新动画
UpdateAnimations();
// 全局状态检查
CheckGlobalConditions();
}
private void ExecuteCurrentState()
{
// 使用switch语句处理不同状态
switch (currentState)
{
case EnemyState.Idle:
ExecuteIdleState();
break;
case EnemyState.Patrol:
ExecutePatrolState();
break;
case EnemyState.Chase:
ExecuteChaseState();
break;
case EnemyState.Attack:
ExecuteAttackState();
break;
case EnemyState.Retreat:
ExecuteRetreatState();
break;
case EnemyState.Dead:
ExecuteDeadState();
break;
}
}
private void ExecuteIdleState()
{
// 空闲状态:站立不动,偶尔环顾四周
// 一段时间后开始巡逻
if (stateTimer >= idleDuration)
{
ChangeState(EnemyState.Patrol);
return;
}
// 随机旋转以模拟环顾四周
if (Random.value < 0.01f)
{
float randomRotation = Random.Range(-45f, 45f);
transform.Rotate(0, randomRotation * Time.deltaTime * 10f, 0);
}
// 播放空闲动画
enemyAnimator.SetBool("IsMoving", false);
}
private void ExecutePatrolState()
{
// 巡逻状态:在区域内移动
// 计算到巡逻目标的距离
float distanceToTarget = Vector3.Distance(transform.position, patrolTarget);
if (distanceToTarget < 0.5f)
{
// 到达巡逻点,等待一段时间
if (stateTimer >= patrolPointWaitTime)
{
SetNewPatrolTarget();
stateTimer = 0f;
}
enemyAnimator.SetBool("IsMoving", false);
}
else
{
// 向巡逻目标移动
Vector3 direction = (patrolTarget - transform.position).normalized;
MoveInDirection(direction, moveSpeed);
enemyAnimator.SetBool("IsMoving", true);
}
// 检查是否发现玩家
if (PlayerInSight())
{
ChangeState(EnemyState.Chase);
}
}
private void ExecuteChaseState()
{
if (playerTarget == null)
{
ChangeState(EnemyState.Patrol);
return;
}
// 计算到玩家的距离
float distanceToPlayer = Vector3.Distance(transform.position, playerTarget.position);
// 检查是否进入攻击范围
if (distanceToPlayer <= attackRange)
{
ChangeState(EnemyState.Attack);
return;
}
// 检查是否丢失玩家
if (distanceToPlayer > detectionRange * 1.5f)
{
ChangeState(EnemyState.Patrol);
return;
}
// 追逐玩家
Vector3 directionToPlayer = (playerTarget.position - transform.position).normalized;
MoveInDirection(directionToPlayer, chaseSpeed);
enemyAnimator.SetBool("IsMoving", true);
// 播放追逐声音(如果有)
if (Random.value < 0.02f)
{
// PlayChaseSound();
}
}
private void ExecuteAttackState()
{
if (playerTarget == null)
{
ChangeState(EnemyState.Patrol);
return;
}
// 面向玩家
Vector3 directionToPlayer = (playerTarget.position - transform.position).normalized;
directionToPlayer.y = 0;
if (directionToPlayer.magnitude > 0.1f)
{
Quaternion targetRotation = Quaternion.LookRotation(directionToPlayer);
transform.rotation = Quaternion.Slerp(
transform.rotation,
targetRotation,
10f * Time.deltaTime
);
}
// 检查玩家是否仍在攻击范围内
float distanceToPlayer = Vector3.Distance(transform.position, playerTarget.position);
if (distanceToPlayer > attackRange * 1.2f)
{
ChangeState(EnemyState.Chase);
return;
}
// 执行攻击
if (canAttack)
{
PerformAttack();
canAttack = false;
attackTimer = attackCooldown;
}
enemyAnimator.SetBool("IsMoving", false);
enemyAnimator.SetBool("IsAttacking", !canAttack);
}
private void ExecuteRetreatState()
{
// 撤退状态:低生命值时返回安全位置
float distanceToRetreatPoint = Vector3.Distance(transform.position, retreatPosition);
if (distanceToRetreatPoint < 1f)
{
// 到达撤退点,进入空闲状态
ChangeState(EnemyState.Idle);
return;
}
// 向撤退点移动
Vector3 direction = (retreatPosition - transform.position).normalized;
MoveInDirection(direction, moveSpeed);
enemyAnimator.SetBool("IsMoving", true);
// 检查生命值是否恢复
if (health > 50f)
{
ChangeState(EnemyState.Patrol);
}
}
private void ExecuteDeadState()
{
// 死亡状态:播放死亡动画,然后销毁
enemyAnimator.SetBool("IsDead", true);
// 禁用控制器
if (characterController != null)
{
characterController.enabled = false;
}
// 禁用碰撞器
Collider collider = GetComponent<Collider>();
if (collider != null)
{
collider.enabled = false;
}
// 一段时间后销毁
if (stateTimer > 3f)
{
Destroy(gameObject);
}
}
private void ChangeState(EnemyState newState)
{
if (currentState == newState)
{
return;
}
// 退出当前状态
OnStateExit(currentState);
// 更新状态
EnemyState previousState = currentState;
currentState = newState;
stateTimer = 0f;
// 进入新状态
OnStateEnter(newState, previousState);
Debug.Log($"状态变化: {previousState} -> {newState}");
}
private void OnStateEnter(EnemyState newState, EnemyState previousState)
{
// 状态进入时的处理
switch (newState)
{
case EnemyState.Patrol:
SetNewPatrolTarget();
break;
case EnemyState.Retreat:
SetRetreatPosition();
break;
case EnemyState.Attack:
canAttack = true; // 进入攻击状态时可以立即攻击
break;
case EnemyState.Dead:
// 播放死亡声音
// PlayDeathSound();
// 掉落物品
SpawnLoot();
break;
}
}
private void OnStateExit(EnemyState oldState)
{
// 状态退出时的处理
switch (oldState)
{
case EnemyState.Attack:
enemyAnimator.SetBool("IsAttacking", false);
break;
}
}
private void MoveInDirection(Vector3 direction, float speed)
{
if (characterController == null || !characterController.enabled)
{
return;
}
direction.y = 0;
// 应用移动
Vector3 moveVector = direction * speed * Time.deltaTime;
characterController.Move(moveVector);
// 旋转面向移动方向
if (direction.magnitude > 0.1f)
{
Quaternion targetRotation = Quaternion.LookRotation(direction);
transform.rotation = Quaternion.Slerp(
transform.rotation,
targetRotation,
5f * Time.deltaTime
);
}
}
private void SetNewPatrolTarget()
{
// 在巡逻半径内随机选择新目标
Vector2 randomCircle = Random.insideUnitCircle * patrolRadius;
patrolTarget = spawnPosition + new Vector3(randomCircle.x, 0, randomCircle.y);
// 确保目标在地面上
RaycastHit hit;
if (Physics.Raycast(patrolTarget + Vector3.up * 10f, Vector3.down, out hit, 20f))
{
patrolTarget = hit.point;
}
}
private void SetRetreatPosition()
{
// 设置撤退位置(返回生成点)
retreatPosition = spawnPosition;
}
private bool PlayerInSight()
{
if (playerTarget == null)
{
return false;
}
float distance = Vector3.Distance(transform.position, playerTarget.position);
if (distance > detectionRange)
{
return false;
}
// 检查视线
RaycastHit hit;
Vector3 direction = playerTarget.position - transform.position;
if (Physics.Raycast(
transform.position + Vector3.up,
direction.normalized,
out hit,
detectionRange
))
{
return hit.collider.CompareTag("Player");
}
return false;
}
private void PerformAttack()
{
// 执行攻击
if (projectilePrefab != null && weaponMuzzle != null)
{
// 远程攻击:发射抛射物
GameObject projectile = Instantiate(
projectilePrefab,
weaponMuzzle.position,
weaponMuzzle.rotation
);
EnemyProjectile projectileScript = projectile.GetComponent<EnemyProjectile>();
if (projectileScript != null)
{
projectileScript.damage = attackDamage;
}
}
else
{
// 近战攻击:直接伤害
if (playerTarget != null)
{
PlayerHealth playerHealth = playerTarget.GetComponent<PlayerHealth>();
if (playerHealth != null)
{
playerHealth.TakeDamage(attackDamage);
}
}
}
// 播放攻击动画
enemyAnimator.SetTrigger("Attack");
Debug.Log("敌人发动攻击,伤害: " + attackDamage);
}
private void UpdateAnimations()
{
if (enemyAnimator == null)
{
return;
}
// 设置状态参数
enemyAnimator.SetInteger("State", (int)currentState);
// 设置移动速度
float speed = 0f;
if (characterController != null)
{
speed = characterController.velocity.magnitude;
}
enemyAnimator.SetFloat("Speed", speed);
}
private void CheckGlobalConditions()
{
// 全局状态转换检查
// 死亡检查
if (health <= 0 && currentState != EnemyState.Dead)
{
ChangeState(EnemyState.Dead);
return;
}
// 低生命值撤退
if (health < 30f && currentState != EnemyState.Retreat && currentState != EnemyState.Dead)
{
ChangeState(EnemyState.Retreat);
return;
}
// 玩家检测(除了撤退和死亡状态)
if (currentState != EnemyState.Retreat &&
currentState != EnemyState.Dead &&
currentState != EnemyState.Chase &&
currentState != EnemyState.Attack)
{
if (PlayerInSight())
{
ChangeState(EnemyState.Chase);
}
}
}
public void TakeDamage(float damage)
{
if (currentState == EnemyState.Dead)
{
return;
}
health -= damage;
Debug.Log($"敌人受到伤害: {damage},剩余生命: {health}");
// 如果受到伤害且不在战斗状态,开始追逐玩家
if (currentState != EnemyState.Chase &&
currentState != EnemyState.Attack &&
currentState != EnemyState.Retreat)
{
ChangeState(EnemyState.Chase);
}
}
private void SpawnLoot()
{
// 生成掉落物品
// 在实际游戏中,这里会实例化掉落物预制体
Debug.Log("敌人死亡,生成掉落物");
// 示例:30%概率掉落生命值
if (Random.value < 0.3f)
{
// Instantiate(healthPickupPrefab, transform.position, Quaternion.identity);
}
}
private void OnDrawGizmosSelected()
{
if (!Application.isPlaying)
{
return;
}
// 绘制检测范围
Gizmos.color = Color.yellow;
Gizmos.DrawWireSphere(transform.position, detectionRange);
// 绘制攻击范围
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(transform.position, attackRange);
// 绘制巡逻半径
Gizmos.color = Color.green;
Gizmos.DrawWireSphere(spawnPosition, patrolRadius);
// 绘制当前目标
switch (currentState)
{
case EnemyState.Patrol:
Gizmos.color = Color.green;
Gizmos.DrawLine(transform.position, patrolTarget);
Gizmos.DrawWireSphere(patrolTarget, 0.3f);
break;
case EnemyState.Chase:
case EnemyState.Attack:
if (playerTarget != null)
{
Gizmos.color = Color.red;
Gizmos.DrawLine(transform.position, playerTarget.position);
}
break;
case EnemyState.Retreat:
Gizmos.color = Color.blue;
Gizmos.DrawLine(transform.position, retreatPosition);
Gizmos.DrawWireSphere(retreatPosition, 0.3f);
break;
}
// 绘制状态信息
#if UNITY_EDITOR
string info = $"状态: {currentState}\n";
info += $"生命: {health:F0}\n";
info += $"计时: {stateTimer:F1}s";
UnityEditor.Handles.Label(
transform.position + Vector3.up * 2f,
info
);
#endif
}
}
public class EnemyProjectile : MonoBehaviour
{
public float damage = 10f;
public float speed = 15f;
public float lifetime = 3f;
private void Start()
{
Destroy(gameObject, lifetime);
}
private void Update()
{
transform.position += transform.forward * speed * Time.deltaTime;
}
private void OnTriggerEnter(Collider other)
{
if (other.CompareTag("Player"))
{
PlayerHealth playerHealth = other.GetComponent<PlayerHealth>();
if (playerHealth != null)
{
playerHealth.TakeDamage(damage);
}
Destroy(gameObject);
}
else if (!other.CompareTag("Enemy") && !other.CompareTag("Projectile"))
{
Destroy(gameObject);
}
}
}
6.2.2 Switch语句状态机的优化与扩展
虽然switch语句状态机简单直观,但随着状态数量的增加,代码会变得难以维护。下面介绍一些优化技巧和扩展方法,使switch语句状态机更适合中型项目:
using UnityEngine;
using System;
using System.Collections.Generic;
public class EnhancedSwitchFSM : MonoBehaviour
{
public enum AIState
{
Idle,
Patrol,
Investigate,
Chase,
Attack,
TakeCover,
Reload,
Heal,
Dead
}
[System.Serializable]
public class StateConfiguration
{
public AIState state;
public float minDuration = 0f;
public float maxDuration = Mathf.Infinity;
public bool canBeInterrupted = true;
public Color debugColor = Color.white;
}
[System.Serializable]
public class TransitionCondition
{
public AIState fromState;
public AIState toState;
public Func<bool> condition;
public int priority = 0;
}
[Header("状态配置")]
[SerializeField]
private List<StateConfiguration> stateConfigurations = new List<StateConfiguration>();
[Header("当前状态")]
[SerializeField]
private AIState currentState = AIState.Idle;
[SerializeField]
private float timeInCurrentState = 0f;
[Header("目标系统")]
[SerializeField]
private Transform primaryTarget;
[SerializeField]
private Vector3 lastKnownTargetPosition;
[SerializeField]
private float targetLastSeenTime;
[Header("属性")]
[SerializeField]
private float health = 100f;
[SerializeField]
private float ammo = 30f;
[SerializeField]
private float stamina = 100f;
[Header("感知")]
[SerializeField]
private float detectionRange = 15f;
[SerializeField]
private float hearingRange = 10f;
[SerializeField]
private float fieldOfView = 90f;
private Dictionary<AIState, Action> stateEnterActions = new Dictionary<AIState, Action>();
private Dictionary<AIState, Action> stateUpdateActions = new Dictionary<AIState, Action>();
private Dictionary<AIState, Action> stateExitActions = new Dictionary<AIState, Action>();
private List<TransitionCondition> transitionConditions = new List<TransitionCondition>();
private CharacterController characterController;
private Animator animator;
private StateConfiguration currentStateConfig;
private void Awake()
{
InitializeEnhancedFSM();
}
private void InitializeEnhancedFSM()
{
characterController = GetComponent<CharacterController>();
animator = GetComponent<Animator>();
if (primaryTarget == null)
{
GameObject player = GameObject.FindGameObjectWithTag("Player");
if (player != null)
{
primaryTarget = player.transform;
}
}
// 初始化状态配置
InitializeStateConfigurations();
// 注册状态行为
RegisterStateBehaviors();
// 注册转换条件
RegisterTransitionConditions();
// 设置初始状态
ChangeState(AIState.Idle);
Debug.Log("增强型FSM初始化完成");
}
private void InitializeStateConfigurations()
{
// 清除现有配置
stateConfigurations.Clear();
// 添加默认状态配置
AddStateConfiguration(AIState.Idle, 2f, 5f, true, Color.gray);
AddStateConfiguration(AIState.Patrol, 3f, 10f, true, Color.green);
AddStateConfiguration(AIState.Investigate, 2f, 8f, true, Color.yellow);
AddStateConfiguration(AIState.Chase, 1f, 15f, true, Color.red);
AddStateConfiguration(AIState.Attack, 0.5f, 3f, false, Color.magenta);
AddStateConfiguration(AIState.TakeCover, 1f, 5f, false, Color.blue);
AddStateConfiguration(AIState.Reload, 1f, 2f, false, Color.cyan);
AddStateConfiguration(AIState.Heal, 2f, 4f, false, new Color(0, 1, 0, 1));
AddStateConfiguration(AIState.Dead, Mathf.Infinity, Mathf.Infinity, false, Color.black);
}
private void AddStateConfiguration(AIState state, float minDur, float maxDur, bool interruptible, Color color)
{
StateConfiguration config = new StateConfiguration
{
state = state,
minDuration = minDur,
maxDuration = maxDur,
canBeInterrupted = interruptible,
debugColor = color
};
stateConfigurations.Add(config);
}
private void RegisterStateBehaviors()
{
// 注册状态进入行为
stateEnterActions[AIState.Idle] = OnEnterIdle;
stateEnterActions[AIState.Patrol] = OnEnterPatrol;
stateEnterActions[AIState.Investigate] = OnEnterInvestigate;
stateEnterActions[AIState.Chase] = OnEnterChase;
stateEnterActions[AIState.Attack] = OnEnterAttack;
stateEnterActions[AIState.TakeCover] = OnEnterTakeCover;
stateEnterActions[AIState.Reload] = OnEnterReload;
stateEnterActions[AIState.Heal] = OnEnterHeal;
stateEnterActions[AIState.Dead] = OnEnterDead;
// 注册状态更新行为
stateUpdateActions[AIState.Idle] = OnUpdateIdle;
stateUpdateActions[AIState.Patrol] = OnUpdatePatrol;
stateUpdateActions[AIState.Investigate] = OnUpdateInvestigate;
stateUpdateActions[AIState.Chase] = OnUpdateChase;
stateUpdateActions[AIState.Attack] = OnUpdateAttack;
stateUpdateActions[AIState.TakeCover] = OnUpdateTakeCover;
stateUpdateActions[AIState.Reload] = OnUpdateReload;
stateUpdateActions[AIState.Heal] = OnUpdateHeal;
stateUpdateActions[AIState.Dead] = OnUpdateDead;
// 注册状态退出行为
stateExitActions[AIState.Idle] = OnExitIdle;
stateExitActions[AIState.Patrol] = OnExitPatrol;
stateExitActions[AIState.Investigate] = OnExitInvestigate;
stateExitActions[AIState.Chase] = OnExitChase;
stateExitActions[AIState.Attack] = OnExitAttack;
stateExitActions[AIState.TakeCover] = OnExitTakeCover;
stateExitActions[AIState.Reload] = OnExitReload;
stateExitActions[AIState.Heal] = OnExitHeal;
stateExitActions[AIState.Dead] = OnExitDead;
}
private void RegisterTransitionConditions()
{
// 注册状态转换条件
// 空闲 -> 巡逻
AddTransitionCondition(AIState.Idle, AIState.Patrol,
() => timeInCurrentState > currentStateConfig.minDuration, 1);
// 巡逻 -> 调查(听到声音)
AddTransitionCondition(AIState.Patrol, AIState.Investigate,
() => HeardSuspiciousSound(), 2);
// 巡逻/空闲 -> 追逐(看到目标)
AddTransitionCondition(AIState.Patrol, AIState.Chase,
() => TargetInSight(), 3);
AddTransitionCondition(AIState.Idle, AIState.Chase,
() => TargetInSight(), 3);
// 调查 -> 追逐(看到目标)
AddTransitionCondition(AIState.Investigate, AIState.Chase,
() => TargetInSight(), 3);
// 追逐 -> 攻击(在攻击范围内)
AddTransitionCondition(AIState.Chase, AIState.Attack,
() => TargetInAttackRange() && TargetInSight(), 4);
// 攻击 -> 寻找掩护(生命值低)
AddTransitionCondition(AIState.Attack, AIState.TakeCover,
() => health < 30f, 5);
// 攻击 -> 重新装弹(弹药不足)
AddTransitionCondition(AIState.Attack, AIState.Reload,
() => ammo < 5f, 5);
// 任何状态 -> 治疗(生命值很低且有治疗包)
AddTransitionCondition(AIState.Attack, AIState.Heal,
() => health < 20f && HasHealthPack(), 6);
AddTransitionCondition(AIState.Chase, AIState.Heal,
() => health < 20f && HasHealthPack(), 6);
// 任何状态 -> 死亡(生命值<=0)
foreach (AIState state in Enum.GetValues(typeof(AIState)))
{
if (state != AIState.Dead)
{
AddTransitionCondition(state, AIState.Dead,
() => health <= 0f, 10);
}
}
}
private void AddTransitionCondition(AIState fromState, AIState toState, Func<bool> condition, int priority)
{
TransitionCondition transition = new TransitionCondition
{
fromState = fromState,
toState = toState,
condition = condition,
priority = priority
};
transitionConditions.Add(transition);
}
private void Update()
{
// 更新状态计时器
timeInCurrentState += Time.deltaTime;
// 检查状态转换
CheckStateTransitions();
// 执行当前状态更新
ExecuteCurrentState();
// 更新动画
UpdateAnimations();
}
private void CheckStateTransitions()
{
if (currentStateConfig == null || !currentStateConfig.canBeInterrupted)
{
return;
}
// 检查最小持续时间
if (timeInCurrentState < currentStateConfig.minDuration)
{
return;
}
// 检查最大持续时间
if (timeInCurrentState > currentStateConfig.maxDuration)
{
// 强制转换到适当的状态
switch (currentState)
{
case AIState.Idle:
case AIState.Patrol:
ChangeState(AIState.Idle);
break;
case AIState.Investigate:
ChangeState(AIState.Patrol);
break;
case AIState.Chase:
ChangeState(AIState.Investigate);
break;
case AIState.Attack:
ChangeState(AIState.TakeCover);
break;
}
return;
}
// 查找符合条件的转换(按优先级排序)
List<TransitionCondition> validTransitions = new List<TransitionCondition>();
foreach (TransitionCondition transition in transitionConditions)
{
if (transition.fromState == currentState && transition.condition())
{
validTransitions.Add(transition);
}
}
// 选择优先级最高的转换
if (validTransitions.Count > 0)
{
validTransitions.Sort((a, b) => b.priority.CompareTo(a.priority));
ChangeState(validTransitions[0].toState);
}
}
private void ExecuteCurrentState()
{
// 执行当前状态的更新逻辑
if (stateUpdateActions.ContainsKey(currentState))
{
stateUpdateActions[currentState]();
}
}
private void ChangeState(AIState newState)
{
if (currentState == newState)
{
return;
}
// 执行退出当前状态的行为
if (stateExitActions.ContainsKey(currentState))
{
stateExitActions[currentState]();
}
AIState previousState = currentState;
currentState = newState;
timeInCurrentState = 0f;
// 获取新状态的配置
currentStateConfig = stateConfigurations.Find(c => c.state == newState);
// 执行进入新状态的行为
if (stateEnterActions.ContainsKey(newState))
{
stateEnterActions[newState]();
}
Debug.Log($"状态转换: {previousState} -> {newState}");
// 触发状态变化事件
OnStateChanged?.Invoke(previousState, newState);
}
#region 状态行为实现
private void OnEnterIdle()
{
animator.SetBool("IsMoving", false);
Debug.Log("进入空闲状态");
}
private void OnUpdateIdle()
{
// 空闲状态:缓慢旋转,偶尔移动位置
if (Random.value < 0.01f)
{
transform.Rotate(0, Random.Range(-30f, 30f), 0);
}
}
private void OnExitIdle()
{
Debug.Log("离开空闲状态");
}
private void OnEnterPatrol()
{
animator.SetBool("IsMoving", true);
Debug.Log("进入巡逻状态");
}
private void OnUpdatePatrol()
{
// 巡逻逻辑
// 这里应该实现实际的巡逻路径
if (characterController != null)
{
Vector3 moveDirection = transform.forward;
characterController.Move(moveDirection * 3f * Time.deltaTime);
}
}
private void OnExitPatrol()
{
Debug.Log("离开巡逻状态");
}
private void OnEnterInvestigate()
{
animator.SetBool("IsMoving", true);
Debug.Log("进入调查状态,前往: " + lastKnownTargetPosition);
}
private void OnUpdateInvestigate()
{
// 前往最后已知位置
if (characterController != null && lastKnownTargetPosition != Vector3.zero)
{
Vector3 direction = (lastKnownTargetPosition - transform.position).normalized;
characterController.Move(direction * 4f * Time.deltaTime);
// 面向移动方向
if (direction.magnitude > 0.1f)
{
Quaternion targetRotation = Quaternion.LookRotation(direction);
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, 5f * Time.deltaTime);
}
// 检查是否到达调查点
if (Vector3.Distance(transform.position, lastKnownTargetPosition) < 1f)
{
// 环顾四周
transform.Rotate(0, 45f * Time.deltaTime, 0);
}
}
}
private void OnExitInvestigate()
{
Debug.Log("离开调查状态");
}
private void OnEnterChase()
{
animator.SetBool("IsMoving", true);
animator.SetBool("IsRunning", true);
Debug.Log("进入追逐状态");
}
private void OnUpdateChase()
{
if (primaryTarget == null)
{
return;
}
// 追逐目标
Vector3 direction = (primaryTarget.position - transform.position).normalized;
if (characterController != null)
{
characterController.Move(direction * 6f * Time.deltaTime);
// 面向目标
if (direction.magnitude > 0.1f)
{
Quaternion targetRotation = Quaternion.LookRotation(direction);
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, 10f * Time.deltaTime);
}
}
}
private void OnExitChase()
{
animator.SetBool("IsRunning", false);
Debug.Log("离开追逐状态");
}
private void OnEnterAttack()
{
animator.SetBool("IsAttacking", true);
Debug.Log("进入攻击状态");
}
private void OnUpdateAttack()
{
if (primaryTarget == null)
{
return;
}
// 面向目标
Vector3 direction = (primaryTarget.position - transform.position).normalized;
direction.y = 0;
if (direction.magnitude > 0.1f)
{
Quaternion targetRotation = Quaternion.LookRotation(direction);
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, 15f * Time.deltaTime);
}
// 执行攻击逻辑
if (timeInCurrentState > 0.5f && ammo > 0)
{
// 模拟攻击
ammo--;
Debug.Log($"攻击!剩余弹药: {ammo}");
// 重置计时器以模拟攻击间隔
timeInCurrentState = 0f;
}
}
private void OnExitAttack()
{
animator.SetBool("IsAttacking", false);
Debug.Log("离开攻击状态");
}
private void OnEnterTakeCover()
{
animator.SetBool("IsCrouching", true);
Debug.Log("进入寻找掩护状态");
}
private void OnUpdateTakeCover()
{
// 寻找最近的掩护位置
// 简化实现:移动到固定位置
Vector3 coverPosition = transform.position - transform.forward * 2f;
if (characterController != null)
{
Vector3 direction = (coverPosition - transform.position).normalized;
characterController.Move(direction * 2f * Time.deltaTime);
}
// 蹲下
if (characterController != null)
{
characterController.height = Mathf.Lerp(characterController.height, 1f, 5f * Time.deltaTime);
}
}
private void OnExitTakeCover()
{
animator.SetBool("IsCrouching", false);
// 恢复站立高度
if (characterController != null)
{
characterController.height = 2f;
}
Debug.Log("离开寻找掩护状态");
}
private void OnEnterReload()
{
animator.SetTrigger("Reload");
Debug.Log("进入重新装弹状态");
}
private void OnUpdateReload()
{
// 装弹动画播放中...
// 完成后自动返回适当状态
}
private void OnExitReload()
{
// 完成装弹
ammo = 30f;
Debug.Log("离开重新装弹状态,弹药已装满");
}
private void OnEnterHeal()
{
animator.SetTrigger("Heal");
Debug.Log("进入治疗状态");
}
private void OnUpdateHeal()
{
// 治疗中...
health = Mathf.Min(100f, health + 20f * Time.deltaTime);
}
private void OnExitHeal()
{
Debug.Log("离开治疗状态,当前生命值: " + health);
}
private void OnEnterDead()
{
animator.SetTrigger("Die");
if (characterController != null)
{
characterController.enabled = false;
}
Debug.Log("进入死亡状态");
}
private void OnUpdateDead()
{
// 死亡状态不需要更新逻辑
}
private void OnExitDead()
{
// 死亡状态不会退出
}
#endregion
#region 条件检查方法
private bool TargetInSight()
{
if (primaryTarget == null)
{
return false;
}
float distance = Vector3.Distance(transform.position, primaryTarget.position);
if (distance > detectionRange)
{
return false;
}
// 检查是否在视野内
Vector3 directionToTarget = (primaryTarget.position - transform.position).normalized;
float angleToTarget = Vector3.Angle(transform.forward, directionToTarget);
if (angleToTarget > fieldOfView / 2)
{
return false;
}
// 检查视线遮挡
RaycastHit hit;
if (Physics.Raycast(
transform.position + Vector3.up,
directionToTarget,
out hit,
detectionRange
))
{
return hit.collider.transform == primaryTarget;
}
return false;
}
private bool TargetInAttackRange()
{
if (primaryTarget == null)
{
return false;
}
float distance = Vector3.Distance(transform.position, primaryTarget.position);
return distance < 5f; // 攻击范围
}
private bool HeardSuspiciousSound()
{
// 检查是否听到可疑声音
// 简化实现:随机决定
return Random.value < 0.05f;
}
private bool HasHealthPack()
{
// 检查是否有治疗包
// 简化实现:随机决定
return Random.value < 0.7f;
}
#endregion
private void UpdateAnimations()
{
if (animator == null)
{
return;
}
// 设置移动速度
float speed = 0f;
if (characterController != null)
{
speed = characterController.velocity.magnitude;
}
animator.SetFloat("Speed", speed);
// 设置生命值比例
animator.SetFloat("Health", health / 100f);
}
public void TakeDamage(float damage, GameObject damageSource)
{
if (currentState == AIState.Dead)
{
return;
}
health -= damage;
Debug.Log($"受到伤害: {damage},剩余生命: {health}");
// 记录伤害来源位置
if (damageSource != null)
{
lastKnownTargetPosition = damageSource.transform.position;
targetLastSeenTime = Time.time;
}
// 如果受到攻击且不在战斗状态,开始调查或追逐
if (currentState != AIState.Chase &&
currentState != AIState.Attack &&
currentState != AIState.Investigate)
{
if (damageSource != null && damageSource.CompareTag("Player"))
{
ChangeState(AIState.Chase);
}
else
{
ChangeState(AIState.Investigate);
}
}
}
public void OnSoundHeard(Vector3 soundPosition, float soundIntensity)
{
if (currentState == AIState.Dead)
{
return;
}
// 记录声音位置
lastKnownTargetPosition = soundPosition;
targetLastSeenTime = Time.time;
// 如果不是在战斗状态,前往调查
if (currentState != AIState.Chase &&
currentState != AIState.Attack &&
currentState != AIState.Investigate)
{
ChangeState(AIState.Investigate);
}
}
public AIState CurrentState
{
get { return currentState; }
}
public float Health
{
get { return health; }
}
public event Action<AIState, AIState> OnStateChanged;
private void OnDrawGizmosSelected()
{
if (!Application.isPlaying)
{
return;
}
// 绘制状态相关可视化
if (currentStateConfig != null)
{
Gizmos.color = currentStateConfig.debugColor;
// 绘制状态范围
float radius = 0f;
switch (currentState)
{
case AIState.Idle:
radius = 1f;
break;
case AIState.Patrol:
radius = 2f;
break;
case AIState.Investigate:
radius = 3f;
Gizmos.DrawWireSphere(lastKnownTargetPosition, 0.5f);
Gizmos.DrawLine(transform.position, lastKnownTargetPosition);
break;
case AIState.Chase:
case AIState.Attack:
radius = 4f;
if (primaryTarget != null)
{
Gizmos.DrawLine(transform.position, primaryTarget.position);
}
break;
}
Gizmos.DrawWireSphere(transform.position, radius);
}
// 绘制检测范围
Gizmos.color = new Color(1, 1, 0, 0.3f);
Gizmos.DrawWireSphere(transform.position, detectionRange);
// 绘制视野锥
DrawFieldOfViewGizmo();
// 绘制状态信息
#if UNITY_EDITOR
string info = $"状态: {currentState}\n";
info += $"时间: {timeInCurrentState:F1}s\n";
info += $"生命: {health:F0}\n";
info += $"弹药: {ammo:F0}";
UnityEditor.Handles.Label(
transform.position + Vector3.up * 2.5f,
info
);
#endif
}
private void DrawFieldOfViewGizmo()
{
Gizmos.color = new Color(1, 1, 0, 0.2f);
int segments = 10;
float stepAngle = fieldOfView / segments;
float halfFov = fieldOfView / 2;
float range = detectionRange;
Vector3 forward = transform.forward * range;
for (int i = 0; i <= segments; i++)
{
float angle = -halfFov + (stepAngle * i);
Vector3 direction = Quaternion.Euler(0, angle, 0) * forward;
Vector3 point = transform.position + direction;
Gizmos.DrawLine(transform.position, point);
if (i > 0)
{
Vector3 prevDirection = Quaternion.Euler(0, -halfFov + (stepAngle * (i - 1)), 0) * forward;
Vector3 prevPoint = transform.position + prevDirection;
Gizmos.DrawLine(prevPoint, point);
}
}
}
}
这个增强的switch语句状态机通过配置系统和条件注册机制,大大提高了代码的可维护性和扩展性。它适合中等复杂度的AI系统,在保持简单性的同时提供了足够的灵活性。
6.3 通用FSM框架的设计与实现
6.3.1 面向对象的状态机框架设计
对于大型商业项目,一个可扩展、可维护的通用FSM框架是必不可少的。这种框架应该支持以下特性:
- 状态和转换的模块化设计
- 状态间的数据传递
- 层级状态机(HFSM)
- 并行状态机
- 状态历史记录和回退
下面是一个完整的通用FSM框架实现:
using UnityEngine;
using System;
using System.Collections.Generic;
namespace AdvancedFSM
{
/// <summary>
/// 状态接口
/// </summary>
public interface IState
{
string StateId { get; }
void OnEnter();
void OnUpdate(float deltaTime);
void OnExit();
void OnPause();
void OnResume();
}
/// <summary>
/// 状态转换条件
/// </summary>
public interface ITransitionCondition
{
bool IsMet();
int Priority { get; }
}
/// <summary>
/// 状态转换
/// </summary>
public class StateTransition
{
public string FromStateId { get; private set; }
public string ToStateId { get; private set; }
public ITransitionCondition Condition { get; private set; }
public Action OnTransition { get; private set; }
public StateTransition(string fromStateId, string toStateId,
ITransitionCondition condition, Action onTransition = null)
{
FromStateId = fromStateId;
ToStateId = toStateId;
Condition = condition;
OnTransition = onTransition;
}
}
/// <summary>
/// 高级有限状态机
/// </summary>
public class AdvancedFiniteStateMachine
{
private Dictionary<string, IState> states = new Dictionary<string, IState>();
private List<StateTransition> transitions = new List<StateTransition>();
private List<StateTransition> anyTransitions = new List<StateTransition>();
private IState currentState;
private IState previousState;
private IState defaultState;
private Stack<IState> stateHistory = new Stack<IState>();
private int maxHistorySize = 10;
private bool isPaused = false;
private float timeInCurrentState = 0f;
public IState CurrentState
{
get { return currentState; }
}
public IState PreviousState
{
get { return previousState; }
}
public float TimeInCurrentState
{
get { return timeInCurrentState; }
}
public event Action<IState, IState> OnStateChanged;
public event Action<string> OnTransitionTriggered;
/// <summary>
/// 添加状态
/// </summary>
public void AddState(IState state, bool isDefault = false)
{
if (states.ContainsKey(state.StateId))
{
Debug.LogWarning($"状态已存在: {state.StateId}");
return;
}
states.Add(state.StateId, state);
if (isDefault)
{
defaultState = state;
}
}
/// <summary>
/// 添加状态转换
/// </summary>
public void AddTransition(string fromStateId, string toStateId,
ITransitionCondition condition, Action onTransition = null)
{
transitions.Add(new StateTransition(fromStateId, toStateId, condition, onTransition));
}
/// <summary>
/// 添加从任意状态转换
/// </summary>
public void AddAnyTransition(string toStateId, ITransitionCondition condition, Action onTransition = null)
{
anyTransitions.Add(new StateTransition(null, toStateId, condition, onTransition));
}
/// <summary>
/// 初始化状态机
/// </summary>
public void Initialize()
{
if (defaultState != null)
{
ChangeState(defaultState);
}
else if (states.Count > 0)
{
// 使用第一个状态作为默认状态
using (var enumerator = states.Values.GetEnumerator())
{
if (enumerator.MoveNext())
{
ChangeState(enumerator.Current);
}
}
}
}
/// <summary>
/// 更新状态机
/// </summary>
public void Update(float deltaTime)
{
if (isPaused || currentState == null)
{
return;
}
// 更新当前状态时间
timeInCurrentState += deltaTime;
// 更新当前状态
currentState.OnUpdate(deltaTime);
// 检查状态转换
CheckTransitions();
}
/// <summary>
/// 强制改变状态
/// </summary>
public void ChangeState(string stateId, bool recordHistory = true)
{
if (!states.ContainsKey(stateId))
{
Debug.LogError($"状态不存在: {stateId}");
return;
}
ChangeState(states[stateId], recordHistory);
}
/// <summary>
/// 强制改变状态
/// </summary>
public void ChangeState(IState newState, bool recordHistory = true)
{
if (currentState == newState)
{
return;
}
// 暂停当前状态
if (currentState != null)
{
currentState.OnPause();
currentState.OnExit();
// 记录状态历史
if (recordHistory && currentState != newState)
{
stateHistory.Push(currentState);
// 限制历史记录大小
if (stateHistory.Count > maxHistorySize)
{
// 移除最旧的状态
Stack<IState> temp = new Stack<IState>();
while (stateHistory.Count > 1)
{
temp.Push(stateHistory.Pop());
}
stateHistory = temp;
}
}
}
// 更新状态引用
previousState = currentState;
currentState = newState;
timeInCurrentState = 0f;
// 进入新状态
currentState.OnEnter();
// 触发事件
OnStateChanged?.Invoke(previousState, currentState);
Debug.Log($"状态改变: {previousState?.StateId ?? "None"} -> {currentState.StateId}");
}
/// <summary>
/// 返回上一个状态
/// </summary>
public bool RevertToPreviousState()
{
if (stateHistory.Count == 0)
{
return false;
}
IState previous = stateHistory.Pop();
ChangeState(previous, false); // 不记录历史,避免循环
return true;
}
/// <summary>
/// 暂停状态机
/// </summary>
public void Pause()
{
if (isPaused)
{
return;
}
isPaused = true;
currentState?.OnPause();
}
/// <summary>
/// 恢复状态机
/// </summary>
public void Resume()
{
if (!isPaused)
{
return;
}
isPaused = false;
currentState?.OnResume();
}
/// <summary>
/// 重置状态机
/// </summary>
public void Reset()
{
if (defaultState != null)
{
ChangeState(defaultState);
}
stateHistory.Clear();
timeInCurrentState = 0f;
isPaused = false;
}
/// <summary>
/// 获取指定状态
/// </summary>
public IState GetState(string stateId)
{
return states.ContainsKey(stateId) ? states[stateId] : null;
}
/// <summary>
/// 检查是否处于指定状态
/// </summary>
public bool IsInState(string stateId)
{
return currentState != null && currentState.StateId == stateId;
}
/// <summary>
/// 获取状态历史
/// </summary>
public List<string> GetStateHistory()
{
List<string> history = new List<string>();
foreach (IState state in stateHistory)
{
history.Add(state.StateId);
}
return history;
}
private void CheckTransitions()
{
// 首先检查从任意状态的转换
foreach (StateTransition transition in anyTransitions)
{
if (transition.Condition.IsMet())
{
ExecuteTransition(transition);
return;
}
}
// 然后检查从当前状态的转换
foreach (StateTransition transition in transitions)
{
if (transition.FromStateId == currentState.StateId && transition.Condition.IsMet())
{
ExecuteTransition(transition);
return;
}
}
}
private void ExecuteTransition(StateTransition transition)
{
if (!states.ContainsKey(transition.ToStateId))
{
Debug.LogError($"目标状态不存在: {transition.ToStateId}");
return;
}
// 执行转换回调
transition.OnTransition?.Invoke();
// 触发转换事件
OnTransitionTriggered?.Invoke($"{transition.FromStateId ?? "Any"} -> {transition.ToStateId}");
// 改变状态
ChangeState(states[transition.ToStateId]);
}
}
/// <summary>
/// 基础状态类
/// </summary>
public abstract class BaseState : IState
{
public abstract string StateId { get; }
protected GameObject owner;
protected AdvancedFiniteStateMachine fsm;
public BaseState(GameObject owner, AdvancedFiniteStateMachine fsm)
{
this.owner = owner;
this.fsm = fsm;
}
public virtual void OnEnter() { }
public virtual void OnUpdate(float deltaTime) { }
public virtual void OnExit() { }
public virtual void OnPause() { }
public virtual void OnResume() { }
}
/// <summary>
/// 基础转换条件类
/// </summary>
public abstract class BaseTransitionCondition : ITransitionCondition
{
public abstract bool IsMet();
public virtual int Priority { get { return 0; } }
}
/// <summary>
/// 延迟条件
/// </summary>
public class DelayCondition : BaseTransitionCondition
{
private float delayDuration;
private float elapsedTime;
private bool isActive;
public DelayCondition(float duration)
{
delayDuration = duration;
elapsedTime = 0f;
isActive = false;
}
public void Start()
{
isActive = true;
elapsedTime = 0f;
}
public void Reset()
{
isActive = false;
elapsedTime = 0f;
}
public override bool IsMet()
{
if (!isActive)
{
return false;
}
elapsedTime += Time.deltaTime;
return elapsedTime >= delayDuration;
}
}
/// <summary>
/// 布尔条件
/// </summary>
public class BoolCondition : BaseTransitionCondition
{
private Func<bool> conditionFunc;
private int priority;
public BoolCondition(Func<bool> condition, int priority = 0)
{
conditionFunc = condition;
this.priority = priority;
}
public override bool IsMet()
{
return conditionFunc();
}
public override int Priority
{
get { return priority; }
}
}
/// <summary>
/// 数值条件
/// </summary>
public class ValueCondition<T> : BaseTransitionCondition where T : IComparable<T>
{
public enum ComparisonType
{
LessThan,
LessThanOrEqual,
Equal,
GreaterThanOrEqual,
GreaterThan,
NotEqual
}
private Func<T> valueGetter;
private T threshold;
private ComparisonType comparison;
private int priority;
public ValueCondition(Func<T> valueGetter, T threshold,
ComparisonType comparison, int priority = 0)
{
this.valueGetter = valueGetter;
this.threshold = threshold;
this.comparison = comparison;
this.priority = priority;
}
public override bool IsMet()
{
T currentValue = valueGetter();
int comparisonResult = currentValue.CompareTo(threshold);
switch (comparison)
{
case ComparisonType.LessThan:
return comparisonResult < 0;
case ComparisonType.LessThanOrEqual:
return comparisonResult <= 0;
case ComparisonType.Equal:
return comparisonResult == 0;
case ComparisonType.GreaterThanOrEqual:
return comparisonResult >= 0;
case ComparisonType.GreaterThan:
return comparisonResult > 0;
case ComparisonType.NotEqual:
return comparisonResult != 0;
default:
return false;
}
}
public override int Priority
{
get { return priority; }
}
}
}
// 使用示例:AI巡逻状态
namespace AdvancedFSM.Example
{
/// <summary>
/// 巡逻状态
/// </summary>
public class PatrolState : AdvancedFSM.BaseState
{
private Transform transform;
private CharacterController characterController;
private Animator animator;
private Vector3[] patrolPoints;
private int currentPatrolIndex = 0;
private float moveSpeed = 3f;
private float rotationSpeed = 5f;
private float waitTimeAtPoint = 2f;
private float waitTimer = 0f;
private bool isWaiting = false;
public override string StateId
{
get { return "Patrol"; }
}
public PatrolState(GameObject owner, AdvancedFiniteStateMachine fsm)
: base(owner, fsm)
{
transform = owner.transform;
characterController = owner.GetComponent<CharacterController>();
animator = owner.GetComponent<Animator>();
// 初始化巡逻点
InitializePatrolPoints();
}
private void InitializePatrolPoints()
{
// 在实际项目中,巡逻点可能来自关卡设计或动态生成
patrolPoints = new Vector3[]
{
transform.position + new Vector3(5, 0, 0),
transform.position + new Vector3(5, 0, 5),
transform.position + new Vector3(0, 0, 5),
transform.position + new Vector3(-5, 0, 5),
transform.position + new Vector3(-5, 0, 0),
transform.position + new Vector3(-5, 0, -5),
transform.position + new Vector3(0, 0, -5),
transform.position + new Vector3(5, 0, -5)
};
}
public override void OnEnter()
{
Debug.Log("进入巡逻状态");
if (animator != null)
{
animator.SetBool("IsWalking", true);
animator.SetBool("IsRunning", false);
}
isWaiting = false;
waitTimer = 0f;
currentPatrolIndex = 0;
MoveToNextPatrolPoint();
}
public override void OnUpdate(float deltaTime)
{
if (isWaiting)
{
// 在巡逻点等待
waitTimer += deltaTime;
if (waitTimer >= waitTimeAtPoint)
{
isWaiting = false;
MoveToNextPatrolPoint();
}
// 等待时环顾四周
transform.Rotate(0, 30f * deltaTime, 0);
}
else
{
// 移动到当前巡逻点
Vector3 targetPoint = patrolPoints[currentPatrolIndex];
Vector3 direction = (targetPoint - transform.position).normalized;
// 移动
if (characterController != null)
{
characterController.Move(direction * moveSpeed * deltaTime);
}
else
{
transform.position += direction * moveSpeed * deltaTime;
}
// 旋转面向移动方向
if (direction.magnitude > 0.1f)
{
Quaternion targetRotation = Quaternion.LookRotation(direction);
transform.rotation = Quaternion.Slerp(
transform.rotation,
targetRotation,
rotationSpeed * deltaTime
);
}
// 检查是否到达巡逻点
float distanceToTarget = Vector3.Distance(transform.position, targetPoint);
if (distanceToTarget < 0.5f)
{
StartWaiting();
}
}
}
public override void OnExit()
{
Debug.Log("退出巡逻状态");
if (animator != null)
{
animator.SetBool("IsWalking", false);
}
}
private void MoveToNextPatrolPoint()
{
currentPatrolIndex = (currentPatrolIndex + 1) % patrolPoints.Length;
}
private void StartWaiting()
{
isWaiting = true;
waitTimer = 0f;
}
}
/// <summary>
/// 追逐状态
/// </summary>
public class ChaseState : AdvancedFSM.BaseState
{
private Transform transform;
private CharacterController characterController;
private Animator animator;
private Transform playerTarget;
private float chaseSpeed = 6f;
private float rotationSpeed = 10f;
private float attackRange = 3f;
private float maxChaseDistance = 20f;
public override string StateId
{
get { return "Chase"; }
}
public ChaseState(GameObject owner, AdvancedFiniteStateMachine fsm, Transform playerTarget)
: base(owner, fsm)
{
transform = owner.transform;
characterController = owner.GetComponent<CharacterController>();
animator = owner.GetComponent<Animator>();
this.playerTarget = playerTarget;
}
public override void OnEnter()
{
Debug.Log("进入追逐状态");
if (animator != null)
{
animator.SetBool("IsRunning", true);
animator.SetBool("IsWalking", false);
}
}
public override void OnUpdate(float deltaTime)
{
if (playerTarget == null)
{
// 没有目标,返回巡逻
fsm.ChangeState("Patrol");
return;
}
// 计算到玩家的距离
float distanceToPlayer = Vector3.Distance(transform.position, playerTarget.position);
// 检查是否超出最大追逐距离
if (distanceToPlayer > maxChaseDistance)
{
fsm.ChangeState("Patrol");
return;
}
// 检查是否在攻击范围内
if (distanceToPlayer <= attackRange)
{
fsm.ChangeState("Attack");
return;
}
// 追逐玩家
Vector3 directionToPlayer = (playerTarget.position - transform.position).normalized;
// 移动
if (characterController != null)
{
characterController.Move(directionToPlayer * chaseSpeed * deltaTime);
}
else
{
transform.position += directionToPlayer * chaseSpeed * deltaTime;
}
// 旋转面向玩家
if (directionToPlayer.magnitude > 0.1f)
{
Quaternion targetRotation = Quaternion.LookRotation(directionToPlayer);
transform.rotation = Quaternion.Slerp(
transform.rotation,
targetRotation,
rotationSpeed * deltaTime
);
}
}
public override void OnExit()
{
Debug.Log("退出追逐状态");
if (animator != null)
{
animator.SetBool("IsRunning", false);
}
}
}
/// <summary>
/// 攻击状态
/// </summary>
public class AttackState : AdvancedFSM.BaseState
{
private Transform transform;
private Animator animator;
private Transform playerTarget;
private float attackCooldown = 1f;
private float attackTimer = 0f;
private float attackRange = 3f;
private float attackDamage = 10f;
public override string StateId
{
get { return "Attack"; }
}
public AttackState(GameObject owner, AdvancedFiniteStateMachine fsm, Transform playerTarget)
: base(owner, fsm)
{
transform = owner.transform;
animator = owner.GetComponent<Animator>();
this.playerTarget = playerTarget;
}
public override void OnEnter()
{
Debug.Log("进入攻击状态");
attackTimer = 0f;
if (animator != null)
{
animator.SetBool("IsAttacking", true);
}
}
public override void OnUpdate(float deltaTime)
{
if (playerTarget == null)
{
fsm.ChangeState("Patrol");
return;
}
// 更新攻击计时器
attackTimer += deltaTime;
// 面向玩家
Vector3 directionToPlayer = (playerTarget.position - transform.position).normalized;
directionToPlayer.y = 0;
if (directionToPlayer.magnitude > 0.1f)
{
Quaternion targetRotation = Quaternion.LookRotation(directionToPlayer);
transform.rotation = Quaternion.Slerp(
transform.rotation,
targetRotation,
15f * deltaTime
);
}
// 检查玩家是否在攻击范围内
float distanceToPlayer = Vector3.Distance(transform.position, playerTarget.position);
if (distanceToPlayer > attackRange * 1.5f)
{
// 玩家离开攻击范围,返回追逐
fsm.ChangeState("Chase");
return;
}
// 执行攻击
if (attackTimer >= attackCooldown)
{
PerformAttack();
attackTimer = 0f;
}
}
public override void OnExit()
{
Debug.Log("退出攻击状态");
if (animator != null)
{
animator.SetBool("IsAttacking", false);
}
}
private void PerformAttack()
{
Debug.Log("执行攻击,造成伤害: " + attackDamage);
// 触发攻击动画
if (animator != null)
{
animator.SetTrigger("AttackTrigger");
}
// 在实际项目中,这里会对玩家造成伤害
if (playerTarget != null)
{
PlayerHealth playerHealth = playerTarget.GetComponent<PlayerHealth>();
if (playerHealth != null)
{
playerHealth.TakeDamage(attackDamage);
}
}
}
}
/// <summary>
/// 死亡状态
/// </summary>
public class DeadState : AdvancedFSM.BaseState
{
private Transform transform;
private Animator animator;
private CharacterController characterController;
private float deathTimer = 0f;
private float destroyDelay = 3f;
public override string StateId
{
get { return "Dead"; }
}
public DeadState(GameObject owner, AdvancedFiniteStateMachine fsm)
: base(owner, fsm)
{
transform = owner.transform;
animator = owner.GetComponent<Animator>();
characterController = owner.GetComponent<CharacterController>();
}
public override void OnEnter()
{
Debug.Log("进入死亡状态");
if (animator != null)
{
animator.SetTrigger("Die");
}
if (characterController != null)
{
characterController.enabled = false;
}
// 禁用碰撞器
Collider collider = owner.GetComponent<Collider>();
if (collider != null)
{
collider.enabled = false;
}
deathTimer = 0f;
}
public override void OnUpdate(float deltaTime)
{
deathTimer += deltaTime;
// 一段时间后销毁对象
if (deathTimer >= destroyDelay)
{
GameObject.Destroy(owner);
}
}
}
/// <summary>
/// AI控制器
/// </summary>
public class AIController : MonoBehaviour
{
[Header("目标设置")]
[SerializeField]
private Transform playerTarget;
[Header("AI属性")]
[SerializeField]
private float health = 100f;
[SerializeField]
private float detectionRange = 15f;
[SerializeField]
private float attackRange = 3f;
[Header("组件引用")]
[SerializeField]
private Animator animator;
private AdvancedFiniteStateMachine fsm;
private bool isPlayerInSight = false;
private float lastHealth;
private void Awake()
{
InitializeAI();
}
private void InitializeAI()
{
// 初始化FSM
fsm = new AdvancedFiniteStateMachine();
// 创建状态
PatrolState patrolState = new PatrolState(gameObject, fsm);
ChaseState chaseState = new ChaseState(gameObject, fsm, playerTarget);
AttackState attackState = new AttackState(gameObject, fsm, playerTarget);
DeadState deadState = new DeadState(gameObject, fsm);
// 添加状态
fsm.AddState(patrolState, true); // 默认状态
fsm.AddState(chaseState);
fsm.AddState(attackState);
fsm.AddState(deadState);
// 添加转换条件
// 巡逻 -> 追逐 (看到玩家)
fsm.AddTransition("Patrol", "Chase",
new BoolCondition(() => IsPlayerInSight() && IsPlayerInRange(), 1));
// 追逐 -> 攻击 (玩家在攻击范围内)
fsm.AddTransition("Chase", "Attack",
new BoolCondition(() => IsPlayerInAttackRange(), 2));
// 攻击 -> 追逐 (玩家离开攻击范围)
fsm.AddTransition("Attack", "Chase",
new BoolCondition(() => !IsPlayerInAttackRange() && IsPlayerInSight(), 1));
// 追逐 -> 巡逻 (丢失玩家)
fsm.AddTransition("Chase", "Patrol",
new BoolCondition(() => !IsPlayerInSight(), 1));
// 攻击 -> 巡逻 (丢失玩家)
fsm.AddTransition("Attack", "Patrol",
new BoolCondition(() => !IsPlayerInSight(), 1));
// 任意状态 -> 死亡 (生命值<=0)
fsm.AddAnyTransition("Dead",
new BoolCondition(() => health <= 0, 10));
// 注册事件
fsm.OnStateChanged += OnStateChanged;
fsm.OnTransitionTriggered += OnTransitionTriggered;
// 初始化FSM
fsm.Initialize();
lastHealth = health;
Debug.Log("AI控制器初始化完成");
}
private void Update()
{
// 更新FSM
fsm.Update(Time.deltaTime);
// 更新玩家检测
UpdatePlayerDetection();
// 检查生命值变化
if (health != lastHealth)
{
OnHealthChanged();
lastHealth = health;
}
}
private void UpdatePlayerDetection()
{
if (playerTarget == null)
{
isPlayerInSight = false;
return;
}
float distanceToPlayer = Vector3.Distance(transform.position, playerTarget.position);
isPlayerInSight = distanceToPlayer <= detectionRange;
}
private bool IsPlayerInSight()
{
return isPlayerInSight;
}
private bool IsPlayerInRange()
{
if (playerTarget == null)
{
return false;
}
float distance = Vector3.Distance(transform.position, playerTarget.position);
return distance <= detectionRange;
}
private bool IsPlayerInAttackRange()
{
if (playerTarget == null)
{
return false;
}
float distance = Vector3.Distance(transform.position, playerTarget.position);
return distance <= attackRange;
}
public void TakeDamage(float damage)
{
if (health <= 0)
{
return;
}
health -= damage;
health = Mathf.Max(0, health);
Debug.Log($"AI受到伤害: {damage},剩余生命: {health}");
// 如果受到伤害且不在战斗状态,开始追逐
if (!fsm.IsInState("Chase") && !fsm.IsInState("Attack") && !fsm.IsInState("Dead"))
{
fsm.ChangeState("Chase");
}
}
private void OnHealthChanged()
{
// 更新动画参数
if (animator != null)
{
float healthRatio = health / 100f;
animator.SetFloat("Health", healthRatio);
}
}
private void OnStateChanged(AdvancedFSM.IState previousState, AdvancedFSM.IState newState)
{
Debug.Log($"状态变化: {previousState?.StateId ?? "None"} -> {newState.StateId}");
}
private void OnTransitionTriggered(string transition)
{
Debug.Log($"触发转换: {transition}");
}
private void OnDrawGizmosSelected()
{
if (!Application.isPlaying)
{
return;
}
// 绘制检测范围
Gizmos.color = isPlayerInSight ? Color.red : Color.yellow;
Gizmos.DrawWireSphere(transform.position, detectionRange);
// 绘制攻击范围
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(transform.position, attackRange);
// 绘制当前状态
#if UNITY_EDITOR
string info = $"状态: {fsm.CurrentState?.StateId ?? "None"}\n";
info += $"生命: {health:F0}\n";
info += $"状态时间: {fsm.TimeInCurrentState:F1}s";
UnityEditor.Handles.Label(
transform.position + Vector3.up * 2f,
info
);
#endif
}
private void OnDestroy()
{
// 清理FSM事件
if (fsm != null)
{
fsm.OnStateChanged -= OnStateChanged;
fsm.OnTransitionTriggered -= OnTransitionTriggered;
}
}
}
}
6.3.2 通用FSM框架的高级特性
上述通用FSM框架已经具备了商业项目所需的核心功能。为了进一步增强其实用性,我们可以添加以下高级特性:
- 层级状态机(HFSM):支持状态的嵌套,允许状态内部包含子状态机
- 并行状态机:同时运行多个状态,用于处理独立的行为(如移动、攻击、动画)
- 状态间通信:允许状态之间传递数据
- 状态黑板:共享数据存储,供所有状态访问
- 可视化调试工具:实时显示状态机运行状态
下面是一个增强的通用FSM框架,包含这些高级特性:
using UnityEngine;
using System;
using System.Collections.Generic;
using System.Linq;
namespace AdvancedFSMPro
{
/// <summary>
/// 状态黑板 - 用于状态间数据共享
/// </summary>
public class StateBlackboard
{
private Dictionary<string, object> data = new Dictionary<string, object>();
private Dictionary<string, List<Action<object>>> listeners = new Dictionary<string, List<Action<object>>>();
public void SetValue<T>(string key, T value)
{
data[key] = value;
// 通知监听者
if (listeners.ContainsKey(key))
{
foreach (Action<object> listener in listeners[key])
{
listener(value);
}
}
}
public T GetValue<T>(string key, T defaultValue = default(T))
{
if (data.ContainsKey(key) && data[key] is T)
{
return (T)data[key];
}
return defaultValue;
}
public bool HasValue(string key)
{
return data.ContainsKey(key);
}
public void RemoveValue(string key)
{
if (data.ContainsKey(key))
{
data.Remove(key);
}
}
public void AddListener(string key, Action<object> listener)
{
if (!listeners.ContainsKey(key))
{
listeners[key] = new List<Action<object>>();
}
if (!listeners[key].Contains(listener))
{
listeners[key].Add(listener);
}
}
public void RemoveListener(string key, Action<object> listener)
{
if (listeners.ContainsKey(key))
{
listeners[key].Remove(listener);
}
}
public void Clear()
{
data.Clear();
listeners.Clear();
}
}
/// <summary>
/// 状态上下文 - 包含状态运行所需的所有信息
/// </summary>
public class StateContext
{
public GameObject Owner { get; private set; }
public StateBlackboard Blackboard { get; private set; }
public AdvancedFiniteStateMachine FSM { get; private set; }
public StateMachineController Controller { get; private set; }
public StateContext(GameObject owner, StateBlackboard blackboard,
AdvancedFiniteStateMachine fsm, StateMachineController controller)
{
Owner = owner;
Blackboard = blackboard;
FSM = fsm;
Controller = controller;
}
}
/// <summary>
/// 增强版状态接口
/// </summary>
public interface IEnhancedState
{
string StateId { get; }
int Priority { get; }
bool CanRunParallel { get; }
void Initialize(StateContext context);
void OnEnter();
void OnUpdate(float deltaTime);
void OnFixedUpdate(float fixedDeltaTime);
void OnLateUpdate(float deltaTime);
void OnExit();
void OnPause();
void OnResume();
bool CanEnter();
bool CanExit();
List<Type> RequiredComponents();
}
/// <summary>
/// 基础增强状态
/// </summary>
public abstract class BaseEnhancedState : IEnhancedState
{
public abstract string StateId { get; }
public virtual int Priority { get { return 0; } }
public virtual bool CanRunParallel { get { return false; } }
protected StateContext context;
protected GameObject owner;
protected StateBlackboard blackboard;
protected AdvancedFiniteStateMachine fsm;
private Dictionary<Type, Component> cachedComponents = new Dictionary<Type, Component>();
public virtual void Initialize(StateContext context)
{
this.context = context;
this.owner = context.Owner;
this.blackboard = context.Blackboard;
this.fsm = context.FSM;
}
public virtual void OnEnter() { }
public virtual void OnUpdate(float deltaTime) { }
public virtual void OnFixedUpdate(float fixedDeltaTime) { }
public virtual void OnLateUpdate(float deltaTime) { }
public virtual void OnExit() { }
public virtual void OnPause() { }
public virtual void OnResume() { }
public virtual bool CanEnter() { return true; }
public virtual bool CanExit() { return true; }
public virtual List<Type> RequiredComponents()
{
return new List<Type>();
}
protected T GetComponent<T>() where T : Component
{
Type type = typeof(T);
if (!cachedComponents.ContainsKey(type))
{
cachedComponents[type] = owner.GetComponent<T>();
}
return cachedComponents[type] as T;
}
protected T GetOrAddComponent<T>() where T : Component
{
T component = GetComponent<T>();
if (component == null)
{
component = owner.AddComponent<T>();
cachedComponents[typeof(T)] = component;
}
return component;
}
}
/// <summary>
/// 层级状态 - 可以包含子状态机
/// </summary>
public abstract class HierarchicalState : BaseEnhancedState
{
protected AdvancedFiniteStateMachine subFSM;
private bool isSubFSMInitialized = false;
public override void Initialize(StateContext context)
{
base.Initialize(context);
// 创建子状态机
subFSM = new AdvancedFiniteStateMachine();
InitializeSubFSM();
}
protected abstract void InitializeSubFSM();
public override void OnEnter()
{
if (!isSubFSMInitialized)
{
subFSM.Initialize();
isSubFSMInitialized = true;
}
else
{
subFSM.Resume();
}
}
public override void OnUpdate(float deltaTime)
{
subFSM.Update(deltaTime);
}
public override void OnExit()
{
subFSM.Pause();
}
public override void OnPause()
{
subFSM.Pause();
}
public override void OnResume()
{
subFSM.Resume();
}
}
/// <summary>
/// 并行状态容器 - 管理多个并行运行的状态
/// </summary>
public class ParallelStateContainer : BaseEnhancedState
{
public override string StateId { get { return "ParallelContainer"; } }
public override bool CanRunParallel { get { return true; } }
private List<IEnhancedState> parallelStates = new List<IEnhancedState>();
private Dictionary<IEnhancedState, bool> stateActiveStatus = new Dictionary<IEnhancedState, bool>();
public void AddParallelState(IEnhancedState state)
{
if (!parallelStates.Contains(state))
{
parallelStates.Add(state);
stateActiveStatus[state] = false;
// 按优先级排序
parallelStates = parallelStates.OrderByDescending(s => s.Priority).ToList();
}
}
public void RemoveParallelState(IEnhancedState state)
{
if (parallelStates.Contains(state))
{
if (stateActiveStatus[state])
{
state.OnExit();
}
parallelStates.Remove(state);
stateActiveStatus.Remove(state);
}
}
public override void Initialize(StateContext context)
{
base.Initialize(context);
foreach (IEnhancedState state in parallelStates)
{
state.Initialize(context);
}
}
public override void OnEnter()
{
foreach (IEnhancedState state in parallelStates)
{
if (state.CanEnter())
{
state.OnEnter();
stateActiveStatus[state] = true;
}
}
}
public override void OnUpdate(float deltaTime)
{
foreach (IEnhancedState state in parallelStates)
{
if (stateActiveStatus[state])
{
state.OnUpdate(deltaTime);
}
}
}
public override void OnFixedUpdate(float fixedDeltaTime)
{
foreach (IEnhancedState state in parallelStates)
{
if (stateActiveStatus[state])
{
state.OnFixedUpdate(fixedDeltaTime);
}
}
}
public override void OnLateUpdate(float deltaTime)
{
foreach (IEnhancedState state in parallelStates)
{
if (stateActiveStatus[state])
{
state.OnLateUpdate(deltaTime);
}
}
}
public override void OnExit()
{
foreach (IEnhancedState state in parallelStates)
{
if (stateActiveStatus[state])
{
state.OnExit();
stateActiveStatus[state] = false;
}
}
}
public override void OnPause()
{
foreach (IEnhancedState state in parallelStates)
{
if (stateActiveStatus[state])
{
state.OnPause();
}
}
}
public override void OnResume()
{
foreach (IEnhancedState state in parallelStates)
{
if (stateActiveStatus[state])
{
state.OnResume();
}
}
}
}
/// <summary>
/// 状态机控制器 - 管理增强版FSM
/// </summary>
public class StateMachineController : MonoBehaviour
{
[Header("调试设置")]
[SerializeField]
private bool enableDebug = true;
[SerializeField]
private bool drawGizmos = true;
[Header("状态机配置")]
[SerializeField]
private List<MonoBehaviour> stateBehaviours = new List<MonoBehaviour>();
private AdvancedFiniteStateMachine mainFSM;
private ParallelStateContainer parallelContainer;
private StateBlackboard blackboard;
private StateContext context;
private List<IEnhancedState> allStates = new List<IEnhancedState>();
private Dictionary<string, IEnhancedState> stateMap = new Dictionary<string, IEnhancedState>();
public AdvancedFiniteStateMachine MainFSM { get { return mainFSM; } }
public StateBlackboard Blackboard { get { return blackboard; } }
public StateContext Context { get { return context; } }
public event Action<string, string> OnStateChanged;
public event Action<string> OnTransitionTriggered;
public event Action<string, object> OnBlackboardValueChanged;
private void Awake()
{
InitializeStateMachine();
}
private void InitializeStateMachine()
{
// 创建黑板
blackboard = new StateBlackboard();
// 创建主FSM
mainFSM = new AdvancedFiniteStateMachine();
// 创建并行容器
parallelContainer = new ParallelStateContainer();
// 创建上下文
context = new StateContext(gameObject, blackboard, mainFSM, this);
// 初始化状态
InitializeStates();
// 注册黑板监听
RegisterBlackboardListeners();
// 初始化FSM
mainFSM.Initialize();
// 注册事件
mainFSM.OnStateChanged += OnMainFSMStateChanged;
mainFSM.OnTransitionTriggered += OnMainFSMTransitionTriggered;
Debug.Log("状态机控制器初始化完成");
}
private void InitializeStates()
{
// 从MonoBehaviour添加状态
foreach (MonoBehaviour behaviour in stateBehaviours)
{
if (behaviour is IEnhancedState enhancedState)
{
AddState(enhancedState);
}
}
// 添加并行容器到主FSM
mainFSM.AddState(parallelContainer);
}
public void AddState(IEnhancedState state)
{
if (stateMap.ContainsKey(state.StateId))
{
Debug.LogWarning($"状态已存在: {state.StateId}");
return;
}
// 初始化状态
state.Initialize(context);
// 检查所需组件
List<Type> requiredComponents = state.RequiredComponents();
foreach (Type componentType in requiredComponents)
{
if (gameObject.GetComponent(componentType) == null)
{
Debug.LogError($"状态 {state.StateId} 需要组件: {componentType.Name}");
return;
}
}
// 添加到相应集合
allStates.Add(state);
stateMap[state.StateId] = state;
if (state.CanRunParallel)
{
// 添加到并行容器
parallelContainer.AddParallelState(state);
}
else
{
// 添加到主FSM
mainFSM.AddState(state);
}
Debug.Log($"添加状态: {state.StateId} (并行: {state.CanRunParallel})");
}
public void AddTransition(string fromStateId, string toStateId,
ITransitionCondition condition, Action onTransition = null)
{
mainFSM.AddTransition(fromStateId, toStateId, condition, onTransition);
}
public void AddAnyTransition(string toStateId, ITransitionCondition condition, Action onTransition = null)
{
mainFSM.AddAnyTransition(toStateId, condition, onTransition);
}
private void RegisterBlackboardListeners()
{
// 示例:监听玩家位置变化
blackboard.AddListener("PlayerPosition", (value) =>
{
OnBlackboardValueChanged?.Invoke("PlayerPosition", value);
});
blackboard.AddListener("Health", (value) =>
{
OnBlackboardValueChanged?.Invoke("Health", value);
});
}
private void Update()
{
// 更新主FSM
mainFSM.Update(Time.deltaTime);
}
private void FixedUpdate()
{
// 固定更新(用于物理相关状态)
// 注意:基础FSM不支持FixedUpdate,需要状态自己处理
}
private void LateUpdate()
{
// 后期更新(用于相机相关状态)
// 注意:基础FSM不支持LateUpdate,需要状态自己处理
}
private void OnMainFSMStateChanged(IState previousState, IState newState)
{
string previousId = previousState?.StateId ?? "None";
string newId = newState?.StateId ?? "None";
if (enableDebug)
{
Debug.Log($"主FSM状态变化: {previousId} -> {newId}");
}
OnStateChanged?.Invoke(previousId, newId);
}
private void OnMainFSMTransitionTriggered(string transition)
{
if (enableDebug)
{
Debug.Log($"主FSM触发转换: {transition}");
}
OnTransitionTriggered?.Invoke(transition);
}
public IEnhancedState GetState(string stateId)
{
return stateMap.ContainsKey(stateId) ? stateMap[stateId] : null;
}
public void ChangeState(string stateId)
{
mainFSM.ChangeState(stateId);
}
public bool IsInState(string stateId)
{
return mainFSM.IsInState(stateId);
}
public void SetBlackboardValue<T>(string key, T value)
{
blackboard.SetValue(key, value);
}
public T GetBlackboardValue<T>(string key, T defaultValue = default(T))
{
return blackboard.GetValue(key, defaultValue);
}
private void OnDrawGizmos()
{
if (!drawGizmos || !Application.isPlaying)
{
return;
}
// 绘制状态机信息
DrawStateMachineGizmos();
}
private void DrawStateMachineGizmos()
{
#if UNITY_EDITOR
// 绘制当前状态信息
string info = $"状态机控制器\n";
info += $"当前状态: {mainFSM.CurrentState?.StateId ?? "None"}\n";
info += $"状态时间: {mainFSM.TimeInCurrentState:F1}s\n";
// 显示黑板重要值
if (blackboard.HasValue("Health"))
{
float health = blackboard.GetValue<float>("Health");
info += $"生命值: {health:F0}\n";
}
if (blackboard.HasValue("PlayerPosition"))
{
Vector3 playerPos = blackboard.GetValue<Vector3>("PlayerPosition");
info += $"玩家距离: {Vector3.Distance(transform.position, playerPos):F1}";
}
UnityEditor.Handles.Label(
transform.position + Vector3.up * 3f,
info
);
// 绘制到玩家的线(如果有玩家位置)
if (blackboard.HasValue("PlayerPosition"))
{
Vector3 playerPos = blackboard.GetValue<Vector3>("PlayerPosition");
Gizmos.color = Color.red;
Gizmos.DrawLine(transform.position, playerPos);
}
#endif
}
private void OnDestroy()
{
// 清理事件
if (mainFSM != null)
{
mainFSM.OnStateChanged -= OnMainFSMStateChanged;
mainFSM.OnTransitionTriggered -= OnMainFSMTransitionTriggered;
}
}
}
/// <summary>
/// 示例:移动状态(可作为并行状态运行)
/// </summary>
public class MovementState : BaseEnhancedState
{
public override string StateId { get { return "Movement"; } }
public override bool CanRunParallel { get { return true; } }
public override int Priority { get { return 1; } }
private CharacterController characterController;
private Animator animator;
private Vector3 moveDirection;
private float moveSpeed = 3f;
private float rotationSpeed = 5f;
public override List<Type> RequiredComponents()
{
return new List<Type> { typeof(CharacterController), typeof(Animator) };
}
public override void Initialize(StateContext context)
{
base.Initialize(context);
characterController = GetComponent<CharacterController>();
animator = GetComponent<Animator>();
}
public override void OnEnter()
{
Debug.Log("进入移动状态");
if (animator != null)
{
animator.SetBool("IsMoving", true);
}
}
public override void OnUpdate(float deltaTime)
{
// 从黑板获取移动方向
if (blackboard.HasValue("MoveDirection"))
{
moveDirection = blackboard.GetValue<Vector3>("MoveDirection");
}
// 从黑板获取移动速度
if (blackboard.HasValue("MoveSpeed"))
{
moveSpeed = blackboard.GetValue<float>("MoveSpeed");
}
// 应用移动
if (characterController != null && moveDirection.magnitude > 0.1f)
{
characterController.Move(moveDirection * moveSpeed * deltaTime);
// 旋转面向移动方向
Quaternion targetRotation = Quaternion.LookRotation(moveDirection);
owner.transform.rotation = Quaternion.Slerp(
owner.transform.rotation,
targetRotation,
rotationSpeed * deltaTime
);
}
// 更新动画速度
if (animator != null)
{
float speed = characterController != null ?
characterController.velocity.magnitude : 0f;
animator.SetFloat("Speed", speed);
}
}
public override void OnExit()
{
Debug.Log("退出移动状态");
if (animator != null)
{
animator.SetBool("IsMoving", false);
animator.SetFloat("Speed", 0f);
}
}
}
/// <summary>
/// 示例:战斗状态(层级状态,包含子状态机)
/// </summary>
public class CombatState : HierarchicalState
{
public override string StateId { get { return "Combat"; } }
private Transform playerTarget;
public override List<Type> RequiredComponents()
{
return new List<Type> { typeof(Animator) };
}
protected override void InitializeSubFSM()
{
// 创建战斗子状态
CombatEngageState engageState = new CombatEngageState();
CombatAttackState attackState = new CombatAttackState();
CombatRetreatState retreatState = new CombatRetreatState();
// 初始化状态
engageState.Initialize(context);
attackState.Initialize(context);
retreatState.Initialize(context);
// 添加到子FSM
subFSM.AddState(engageState, true); // 默认状态
subFSM.AddState(attackState);
subFSM.AddState(retreatState);
// 添加转换
subFSM.AddTransition("CombatEngage", "CombatAttack",
new BoolCondition(() => IsPlayerInAttackRange(), 1));
subFSM.AddTransition("CombatAttack", "CombatEngage",
new BoolCondition(() => !IsPlayerInAttackRange(), 1));
subFSM.AddTransition("CombatEngage", "CombatRetreat",
new BoolCondition(() => IsHealthLow(), 2));
subFSM.AddTransition("CombatAttack", "CombatRetreat",
new BoolCondition(() => IsHealthLow(), 2));
subFSM.AddTransition("CombatRetreat", "CombatEngage",
new BoolCondition(() => !IsHealthLow(), 1));
}
public override void OnEnter()
{
base.OnEnter();
Debug.Log("进入战斗状态(层级)");
// 获取玩家目标
playerTarget = blackboard.GetValue<Transform>("PlayerTarget", null);
}
private bool IsPlayerInAttackRange()
{
if (playerTarget == null)
{
return false;
}
float attackRange = blackboard.GetValue<float>("AttackRange", 3f);
float distance = Vector3.Distance(owner.transform.position, playerTarget.position);
return distance <= attackRange;
}
private bool IsHealthLow()
{
float health = blackboard.GetValue<float>("Health", 100f);
return health < 30f;
}
}
/// <summary>
/// 战斗接触状态
/// </summary>
public class CombatEngageState : BaseEnhancedState
{
public override string StateId { get { return "CombatEngage"; } }
private Transform playerTarget;
private float engageSpeed = 4f;
public override void OnEnter()
{
Debug.Log("进入战斗接触状态");
}
public override void OnUpdate(float deltaTime)
{
playerTarget = blackboard.GetValue<Transform>("PlayerTarget", null);
if (playerTarget != null)
{
// 向玩家移动
Vector3 direction = (playerTarget.position - owner.transform.position).normalized;
blackboard.SetValue("MoveDirection", direction);
blackboard.SetValue("MoveSpeed", engageSpeed);
}
}
public override void OnExit()
{
Debug.Log("退出战斗接触状态");
}
}
/// <summary>
/// 战斗攻击状态
/// </summary>
public class CombatAttackState : BaseEnhancedState
{
public override string StateId { get { return "CombatAttack"; } }
private Transform playerTarget;
private float attackCooldown = 1f;
private float attackTimer = 0f;
private float attackDamage = 10f;
public override void OnEnter()
{
Debug.Log("进入战斗攻击状态");
attackTimer = 0f;
}
public override void OnUpdate(float deltaTime)
{
playerTarget = blackboard.GetValue<Transform>("PlayerTarget", null);
if (playerTarget != null)
{
// 面向玩家
Vector3 direction = (playerTarget.position - owner.transform.position).normalized;
direction.y = 0;
if (direction.magnitude > 0.1f)
{
Quaternion targetRotation = Quaternion.LookRotation(direction);
owner.transform.rotation = Quaternion.Slerp(
owner.transform.rotation,
targetRotation,
10f * deltaTime
);
}
// 停止移动
blackboard.SetValue("MoveDirection", Vector3.zero);
// 攻击逻辑
attackTimer += deltaTime;
if (attackTimer >= attackCooldown)
{
PerformAttack();
attackTimer = 0f;
}
}
}
private void PerformAttack()
{
Debug.Log("执行攻击,伤害: " + attackDamage);
// 在实际项目中,这里会对玩家造成伤害
if (playerTarget != null)
{
PlayerHealth playerHealth = playerTarget.GetComponent<PlayerHealth>();
if (playerHealth != null)
{
playerHealth.TakeDamage(attackDamage);
}
}
}
public override void OnExit()
{
Debug.Log("退出战斗攻击状态");
}
}
/// <summary>
/// 战斗撤退状态
/// </summary>
public class CombatRetreatState : BaseEnhancedState
{
public override string StateId { get { return "CombatRetreat"; } }
private Transform playerTarget;
private float retreatSpeed = 3f;
private Vector3 retreatPosition;
public override void OnEnter()
{
Debug.Log("进入战斗撤退状态");
// 计算撤退位置(远离玩家)
playerTarget = blackboard.GetValue<Transform>("PlayerTarget", null);
if (playerTarget != null)
{
Vector3 awayFromPlayer = (owner.transform.position - playerTarget.position).normalized;
retreatPosition = owner.transform.position + awayFromPlayer * 10f;
}
else
{
retreatPosition = owner.transform.position;
}
}
public override void OnUpdate(float deltaTime)
{
// 向撤退位置移动
Vector3 direction = (retreatPosition - owner.transform.position).normalized;
blackboard.SetValue("MoveDirection", direction);
blackboard.SetValue("MoveSpeed", retreatSpeed);
// 检查是否到达撤退位置
float distance = Vector3.Distance(owner.transform.position, retreatPosition);
if (distance < 1f)
{
// 短暂休息
blackboard.SetValue("MoveDirection", Vector3.zero);
}
}
public override void OnExit()
{
Debug.Log("退出战斗撤退状态");
}
}
}
// 使用示例
public class AdvancedAIController : MonoBehaviour
{
[Header("AI设置")]
[SerializeField]
private Transform playerTarget;
[SerializeField]
private float detectionRange = 15f;
[SerializeField]
private float health = 100f;
[Header("状态机")]
[SerializeField]
private StateMachineController stateMachineController;
[SerializeField]
private List<MonoBehaviour> initialStateBehaviours;
private void Awake()
{
InitializeAI();
}
private void InitializeAI()
{
// 创建状态机控制器(如果不存在)
if (stateMachineController == null)
{
stateMachineController = gameObject.AddComponent<StateMachineController>();
}
// 添加初始状态
foreach (MonoBehaviour behaviour in initialStateBehaviours)
{
if (behaviour != null)
{
stateMachineController.AddState(behaviour as AdvancedFSMPro.IEnhancedState);
}
}
// 设置初始黑板值
stateMachineController.SetBlackboardValue("PlayerTarget", playerTarget);
stateMachineController.SetBlackboardValue("Health", health);
stateMachineController.SetBlackboardValue("DetectionRange", detectionRange);
stateMachineController.SetBlackboardValue("AttackRange", 3f);
stateMachineController.SetBlackboardValue("MoveSpeed", 3f);
// 注册事件
stateMachineController.OnStateChanged += OnAIStateChanged;
stateMachineController.OnTransitionTriggered += OnAITransitionTriggered;
stateMachineController.OnBlackboardValueChanged += OnBlackboardValueChanged;
// 添加初始状态转换
AddInitialTransitions();
Debug.Log("高级AI控制器初始化完成");
}
private void AddInitialTransitions()
{
// 空闲 -> 巡逻 (一段时间后)
stateMachineController.AddTransition("Idle", "Patrol",
new AdvancedFSM.DelayCondition(3f));
// 巡逻 -> 战斗 (看到玩家)
stateMachineController.AddTransition("Patrol", "Combat",
new AdvancedFSM.BoolCondition(() => IsPlayerInSight(), 1));
// 战斗 -> 巡逻 (丢失玩家一段时间)
stateMachineController.AddTransition("Combat", "Patrol",
new AdvancedFSM.BoolCondition(() => !IsPlayerInSight() && GetTimeSinceLastSeen() > 5f, 1));
}
private void Update()
{
// 更新玩家检测
UpdatePlayerDetection();
// 更新黑板值
UpdateBlackboardValues();
}
private void UpdatePlayerDetection()
{
if (playerTarget == null)
{
return;
}
float distance = Vector3.Distance(transform.position, playerTarget.position);
bool inSight = distance <= detectionRange;
if (inSight)
{
stateMachineController.SetBlackboardValue("PlayerPosition", playerTarget.position);
stateMachineController.SetBlackboardValue("PlayerLastSeenTime", Time.time);
}
}
private void UpdateBlackboardValues()
{
// 更新生命值到黑板
stateMachineController.SetBlackboardValue("Health", health);
}
private bool IsPlayerInSight()
{
if (playerTarget == null)
{
return false;
}
float detectionRange = stateMachineController.GetBlackboardValue<float>("DetectionRange", 15f);
float distance = Vector3.Distance(transform.position, playerTarget.position);
return distance <= detectionRange;
}
private float GetTimeSinceLastSeen()
{
float lastSeenTime = stateMachineController.GetBlackboardValue<float>("PlayerLastSeenTime", 0f);
return Time.time - lastSeenTime;
}
public void TakeDamage(float damage)
{
if (health <= 0)
{
return;
}
health -= damage;
health = Mathf.Max(0, health);
Debug.Log($"AI受到伤害: {damage},剩余生命: {health}");
// 更新黑板
stateMachineController.SetBlackboardValue("Health", health);
// 如果受到伤害且不在战斗状态,进入战斗
if (!stateMachineController.IsInState("Combat"))
{
stateMachineController.ChangeState("Combat");
}
}
private void OnAIStateChanged(string previousState, string newState)
{
Debug.Log($"AI状态变化: {previousState} -> {newState}");
}
private void OnAITransitionTriggered(string transition)
{
Debug.Log($"AI触发转换: {transition}");
}
private void OnBlackboardValueChanged(string key, object value)
{
// 处理黑板值变化
if (key == "Health")
{
float newHealth = (float)value;
if (newHealth <= 0)
{
// AI死亡
OnDeath();
}
}
}
private void OnDeath()
{
Debug.Log("AI死亡");
// 处理死亡逻辑
}
private void OnDestroy()
{
// 清理事件
if (stateMachineController != null)
{
stateMachineController.OnStateChanged -= OnAIStateChanged;
stateMachineController.OnTransitionTriggered -= OnAITransitionTriggered;
stateMachineController.OnBlackboardValueChanged -= OnBlackboardValueChanged;
}
}
}
这个增强的通用FSM框架提供了商业游戏开发所需的所有高级特性。通过状态黑板、层级状态机、并行状态和模块化设计,开发者可以创建复杂而强大的AI系统,同时保持代码的可维护性和可扩展性。
总结
有限状态机是游戏AI开发的基石技术之一。从简单的switch语句实现到复杂的通用框架,FSM可以适应各种规模和复杂度的项目需求。通过本章的学习,你应该能够:
- 理解有限状态机的基本概念和设计原则
- 实现基于switch语句的简单状态机
- 设计和实现通用的FSM框架
- 应用高级FSM特性如层级状态、并行状态和状态黑板
- 将FSM集成到实际的游戏AI系统中
在实际项目开发中,选择合适的FSM实现方式非常重要。对于小型项目或原型开发,简单的switch语句状态机可能就足够了。对于大型商业项目,使用成熟的FSM框架可以显著提高开发效率和代码质量。无论选择哪种方式,理解状态机的基本原理和设计模式都是成功实现游戏AI的关键。
更多推荐


所有评论(0)