【AI&游戏】Unity AI Perception感知系统
【AI&游戏】专栏-直达
Unity AI Perception 感知系统完全指南
一、引言
在游戏开发中,AI感知系统是构建智能游戏角色的核心组成部分。无论是让敌人能够发现玩家、让NPC能够响应环境变化,还是实现复杂的潜行游戏机制,都离不开一个强大而灵活的感知系统。Unity的AI Perception系统正是为此而设计的,它为开发者提供了一套完整的工具,用于实现游戏中各种智能体的感知能力。
AI Perception系统是Unity官方提供的AI框架的重要组成部分,它与Behavior Designer、NodeCanvas等行为树工具深度集成,使得开发者能够轻松实现复杂的感知-决策-行为链条。这个系统不仅仅能够检测目标是否在视野范围内,还支持多种感知方式,包括视觉、听觉、触觉等,能够满足各种类型游戏的感知需求。
本文将全面深入地介绍Unity AI Perception系统的各个方面,从基础概念到高级应用,从技术原理到实战项目。我们将一起探索如何在Unity项目中有效地利用感知系统创建引人入胜的智能游戏角色。
二、感知系统基础理论
2.1 什么是AI感知系统
AI感知系统是游戏中模拟智能体对外界环境感知能力的技术系统。在现实世界中,人类和动物通过视觉、听觉、嗅觉、触觉等感知方式来获取周围环境的信息,并据此做出决策。在游戏中,我们需要通过技术手段来模拟这种感知能力,让AI角色能够"看到"玩家、"听到"声音、"感觉到"碰撞。
感知系统的核心功能
一个完整的AI感知系统需要实现以下核心功能:
- 信息采集:从游戏世界获取各种感知信息,如对象位置、声音、碰撞等。
- 信号处理:对采集到的原始信息进行处理,转换为AI可以理解的语义信息。
- 记忆存储:存储感知到的信息,形成感知记忆,支持时间衰减和遗忘机制。
- 事件触发:当感知信息满足特定条件时,触发相应的响应事件。
感知系统的层次
AI感知系统通常包含以下几个层次:
- 物理层:最基础的感知实现,如射线检测、碰撞检测等。
- 语义层:对物理层检测结果进行解释,识别"看到玩家"这样的语义信息。
- 记忆层:对感知信息进行存储和管理,形成持续的环境认知。
- 决策层:根据感知信息做出决策,触发相应的AI行为。
2.2 感知系统类型
根据实现方式和检测对象的不同,感知系统可以分为以下几种类型:
视觉感知
视觉感知是游戏中最重要的感知方式,AI通过"眼睛"来检测目标的存在。
- 锥形视野:最常用的方式,AI能够看到前方锥形范围内的对象。
- 球形检测:检测周围球形范围内的所有对象。
- 射线检测:通过发射射线来检测直线上的对象,支持遮挡检测。
听觉感知
听觉感知让AI能够检测到声音的存在和来源。
- 声音位置检测:检测声音发生的位置。
- 声音强度检测:根据距离衰减原理,检测近处的声音更清晰。
- 声音类型识别:识别不同类型的声音,如脚步声、枪声、对话等。
触觉感知
触觉感知通过物理接触来获取信息。
- 碰撞检测:检测与其他对象的物理接触。
- 触发器检测:检测进入触发器区域的事件。
- 力反馈检测:检测受到的力和冲击。
2.3 感知系统在游戏中的应用
感知系统是游戏AI的基础组件,在各种类型的游戏中都有广泛应用:
潜行游戏
潜行游戏是感知系统最典型的应用场景:
- 敌人需要检测玩家的可见性。
- 需要实现视野检测和遮挡判断。
- 需要实现声音检测,让AI能够听到玩家的脚步声。
- 需要实现感知记忆,敌人需要记住短暂的发现。
战斗AI
战斗游戏中的敌人AI需要精确的感知能力:
- 检测玩家位置和距离。
- 检测玩家是否在攻击范围内。
- 检测友军位置,避免误伤。
- 检测环境中的掩体和遮挡物。
NPC交互
RPG和冒险游戏中的NPC需要与环境和玩家交互:
- 检测玩家是否在对话范围内。
- 检测玩家的接近和离开。
- 检测其他NPC的位置,实现社交行为。
三、Unity AI Perception 核心组件
3.1 AI Perception组件
Unity的AI Perception系统主要通过AI Perception组件来实现,这是系统的核心入口点。
组件概述
AI Perception组件通常挂载在需要感知能力的AI角色上,负责管理和协调该角色的所有感知模块。
主要属性
- Update Frequency:感知更新的频率,决定AI多久"看"一次周围环境。
- Perception Modes:启用的感知模式,如视觉、听觉等。
- Debug Draw:是否在Editor中绘制感知范围的调试信息。
核心方法
using UnityEngine.AI Perception;
// 获取感知组件
var perception = GetComponent<AIPerceptionModule_Unity>();
// 检测特定感知类型的激发源
public Transform[] GetCurrentlyPerceived(PerceptionType type)
// 检测特定类型的最近激发源
public Transform GetClosest(PerceptionType type)
// 检测特定类型的所有可见目标
public bool TryGetPerceptionImage(PerceptionType type, PerceptionImage image)
3.2 感知模块
感知模块是实现具体感知功能的组件,Unity提供了多种内置感知模块:
视觉感知模块
// 配置视觉感知
AIPerceptionModule_Visual visual = GetComponent<AIPerceptionModule_Visual>();
// 设置感知参数
visual感知范围.radius = 20f; // 感知半径
visual感知范围.height = 5f; // 感知高度
visual感知范围.fov = 90f; // 视野角度
visual感知范围.minRange = 0f; // 最小感知距离
// 设置检测层级
visual感知范围.detectableTags.Add("Player");
visual感知范围.detectableTags.Add("Enemy");
// 设置遮挡检测
visual感知范围.obstructionByObstacles = true;
visual感知范围.obstacleLayerMask = LayerMask.GetMask("Default");
听觉感知模块
// 配置听觉感知
AIPerceptionModule_Audio audio = GetComponent<AIPerceptionModule_Audio>();
// 设置感知参数
audio感知范围.radius = 30f;
audio感知范围.height = 10f;
// 配置声音感知
audio感知范围.states = new PerceptionState[2];
audio感知范围.states[0] = PerceptionState.PlayersStep; // 玩家脚步声
audio感知范围.states[1] = PerceptionState.LoudNoise; // 大声响
3.3 感知源
感知源是被感知对象所携带的组件,用于标识对象的身份和状态。
配置感知源
// 在被感知对象上添加感知源组件
var perceivable = gameObject.AddComponent<PerceptionSink>();
// 配置感知源参数
perceivable.SetIdentityForTags(new []{ "Player" }, "Player_001");
// 设置感知状态
perceivable.SetPerceptionState("Footstep", 1f); // 设置脚步声状态
perceivable.SetPerceptionState("Weapon", 1f); // 设置武器状态
3.4 感知配置器
感知配置器用于在全局层面配置感知系统的行为。
全局配置
// 配置全局感知设置
AIKit.PerceptionConfiguration perceptionConfig = new AIKit.PerceptionConfiguration();
// 设置默认感知范围
perceptionConfig.defaultRadius = 15f;
perceptionConfig.defaultFOV = 120f;
// 设置感知更新间隔
perceptionConfig.minUpdateInterval = 0.1f;
perceptionConfig.maxUpdateInterval = 0.5f;
// 应用配置
AIKit.ApplyConfiguration(perceptionConfig);
四、感知系统实现原理
4.1 视觉感知实现原理
视觉感知是AI感知系统中最复杂也是最重要的部分。其实现涉及多个技术点:
锥形视野检测
锥形视野检测确定目标是否在AI的视野范围内:
// 锥形视野检测原理
public bool IsInFOV(Transform target, Vector3 eyePosition, Vector3 forward, float fovAngle, float distance)
{
// 计算方向向量
Vector3 directionToTarget = (target.position - eyePosition).normalized;
// 计算角度
float angle = Vector3.Angle(forward, directionToTarget);
// 检查是否在角度范围内
if (angle > fovAngle * 0.5f)
return false;
// 检查距离
float distanceToTarget = Vector3.Distance(eyePosition, target.position);
if (distanceToTarget > distance)
return false;
return true;
}
遮挡检测
遮挡检测确保AI无法看到被障碍物遮挡的目标:
// 射线遮挡检测
public bool HasLineOfSight(Vector3 from, Vector3 to, LayerMask obstacleMask)
{
// 发射射线
RaycastHit hit;
if (Physics.Linecast(from, to, out hit, obstacleMask))
{
// 检查是否被障碍物遮挡
return false;
}
return true;
}
多层感知融合
高级感知系统可以融合多种检测方式:
// 综合视觉检测
public float GetVisualPerceptionScore(Transform target)
{
float score = 0f;
// 距离评分
float distance = Vector3.Distance(aiPosition, target.position);
score += Mathf.Max(0, 1f - distance / maxRange);
// 角度评分
float angle = Vector3.Angle(forward, (target.position - aiPosition).normalized);
score += Mathf.Max(0, 1f - angle / (fov * 0.5f));
// 可见性评分(考虑遮挡)
if (HasLineOfSight(eyePosition, target.position, obstacleMask))
{
score *= 2f; // 完全可见时加分
}
return score;
}
4.2 听觉感知实现原理
听觉感知基于声音传播的物理原理:
声音传播模型
声音在传播过程中会衰减,遵循平方反比定律:
// 声音衰减计算
public float CalculateAudibleDistance(Vector3 soundPosition, Vector3 listenerPosition, float soundRadius)
{
float distance = Vector3.Distance(soundPosition, listenerPosition);
// 声音强度随距离平方反比衰减
float intensity = 1f / (distance * distance);
// 转换为可听距离
return soundRadius * Mathf.Sqrt(intensity);
}
声音定位
通过双耳效应和时间差来定位声音:
// 声音方向检测
public Vector3 GetSoundDirection(Vector3 soundPosition, Vector3 listenerPosition, Vector3 listenerForward)
{
Vector3 direction = (soundPosition - listenerPosition).normalized;
// 计算左右声道音量差异来估计水平角度
float horizontalAngle = Vector3.SignedAngle(listenerPosition, soundPosition, Vector3.up);
// 估计垂直角度
float verticalAngle = Vector3.SignedAngle(listenerPosition, soundPosition, listenerPosition.right);
return new Vector3(horizontalAngle, verticalAngle, 0);
}
4.3 感知记忆机制
感知记忆允许AI记住曾经感知到但现在已经看不到的目标:
记忆数据结构
// 感知记忆条目
[System.Serializable]
public class PerceptionMemory
{
public Transform target; // 记忆的目标
public Vector3 lastKnownPosition; // 最后已知位置
public float confidence; // 置信度 (0-1)
public float timeSinceLastSeen; // 上次感知到的时间
public PerceptionType type; // 感知类型
}
// 记忆管理器
public class PerceptionMemoryManager
{
private List<PerceptionMemory> memories = new List<PerceptionMemory>();
private float memoryDuration = 30f; // 记忆持续时间
private float confidenceDecayRate = 0.1f; // 置信度衰减率
// 更新记忆
public void UpdateMemories()
{
foreach (var memory in memories)
{
memory.timeSinceLastSeen += Time.deltaTime;
// 置信度随时间衰减
memory.confidence = Mathf.Max(0, memory.confidence - confidenceDecayRate * Time.deltaTime);
// 位置更新(目标移动则更新)
if (memory.target != null)
{
memory.lastKnownPosition = memory.target.position;
}
}
// 移除过期的记忆
memories.RemoveAll(m => m.timeSinceLastSeen > memoryDuration);
}
// 添加或更新记忆
public void AddOrUpdateMemory(Transform target, Vector3 position, float confidence, PerceptionType type)
{
var existing = memories.Find(m => m.target == target);
if (existing != null)
{
// 更新现有记忆
existing.lastKnownPosition = position;
existing.confidence = confidence;
existing.timeSinceLastSeen = 0;
}
else
{
// 添加新记忆
memories.Add(new PerceptionMemory
{
target = target,
lastKnownPosition = position,
confidence = confidence,
timeSinceLastSeen = 0,
type = type
});
}
}
}
4.4 感知融合策略
在复杂的AI系统中,可能需要融合多种感知信息:
加权融合
// 多感知源融合
public float GetFusedPerception(Transform target)
{
float totalScore = 0f;
float totalWeight = 0f;
// 视觉感知权重
if (visualModule.IsTargetPerceived(target))
{
float visualScore = visualModule.GetPerceptionConfidence(target);
totalScore += visualScore * visualWeight;
totalWeight += visualWeight;
}
// 听觉感知权重
if (audioModule.IsTargetPerceived(target))
{
float audioScore = audioModule.GetPerceptionConfidence(target);
totalScore += audioScore * audioWeight;
totalWeight += audioWeight;
}
// 触觉感知权重
if (tactileModule.IsTargetPerceived(target))
{
float tactileScore = tactileModule.GetPerceptionConfidence(target);
totalScore += tactileScore * tactileWeight;
totalWeight += tactileWeight;
}
return totalWeight > 0 ? totalScore / totalWeight : 0;
}
五、快速入门指南
5.1 基础设置
创建AI角色
- 创建一个Capsule或Character模型作为AI角色。
- 添加AI Perception组件。
- 添加NavMeshAgent或自定义移动组件。
配置视觉感知
// 添加视觉感知模块
AIPerceptionModule_Visual visual = aiGameObject.AddComponent<AIPerceptionModule_Visual>();
// 基本配置
visual感知范围.fov = 90f; // 90度视野
visual感知范围.radius = 20f; // 20米感知范围
visual感知范围.height = 5f; // 5米感知高度
// 配置检测目标
visual感知范围.detectableTags.Add("Player");
visual感知范围.detectableTags.Add("Companion");
// 配置检测层级
visual感知范围.detectableLayerMask = LayerMask.GetMask("Player", "Default");
5.2 检测玩家实现
基本检测逻辑
// 检测玩家
public class SimpleDetection : MonoBehaviour
{
public AIPerceptionModule_Unity perception;
public string playerTag = "Player";
void Update()
{
// 获取所有感知到的目标
Transform[] perceived = perception.GetCurrentlyPerceived(PerceptionType.Vision);
foreach (Transform target in perceived)
{
if (target.CompareTag(playerTag))
{
Debug.Log($"Detected player: {target.name}");
OnPlayerDetected(target);
}
}
}
void OnPlayerDetected(Transform player)
{
// 触发AI响应
}
}
5.3 敌人AI示例
完整敌人检测AI
public class EnemyDetection : MonoBehaviour
{
[Header("Perception Settings")]
public float detectionRange = 20f;
public float fov = 90f;
public LayerMask playerLayer;
public LayerMask obstacleLayer;
[Header("References")]
public Transform eyesPosition;
public EnemyAI enemyAI;
[Header("State")]
public bool playerDetected = false;
public Transform currentTarget;
void Update()
{
if (!playerDetected)
{
// 持续检测
CheckForPlayer();
}
}
void CheckForPlayer()
{
// 找到最近的玩家
Transform closestPlayer = FindClosestPlayer();
if (closestPlayer != null)
{
// 检查是否在视野内
if (IsInVision(closestPlayer))
{
playerDetected = true;
currentTarget = closestPlayer;
enemyAI.OnTargetDetected(closestPlayer);
}
}
}
Transform FindClosestPlayer()
{
Collider[] colliders = Physics.OverlapSphere(transform.position, detectionRange, playerLayer);
Transform closest = null;
float closestDist = float.MaxValue;
foreach (var collider in colliders)
{
float dist = Vector3.Distance(transform.position, collider.transform.position);
if (dist < closestDist)
{
closestDist = dist;
closest = collider.transform;
}
}
return closest;
}
bool IsInVision(Transform target)
{
Vector3 direction = (target.position - eyesPosition.position).normalized;
float angle = Vector3.Angle(transform.forward, direction);
// 检查角度
if (angle > fov * 0.5f)
return false;
// 检查遮挡
if (Physics.Linecast(eyesPosition.position, target.position, obstacleLayer))
return false;
return true;
}
}
5.4 调试工具
感知范围可视化
// 在Editor中绘制感知范围
void OnDrawGizmos()
{
// 绘制感知范围
Gizmos.color = Color.yellow;
Gizmos.DrawWireSphere(transform.position, detectionRange);
// 绘制FOV
Vector3 leftBoundary = Quaternion.Euler(0, -fov * 0.5f, 0) * transform.forward * detectionRange;
Vector3 rightBoundary = Quaternion.Euler(0, fov * 0.5f, 0) * transform.forward * detectionRange;
Gizmos.color = Color.red;
Gizmos.DrawLine(transform.position, transform.position + leftBoundary);
Gizmos.DrawLine(transform.position, transform.position + rightBoundary);
// 绘制到最近目标的连线
if (currentTarget != null && playerDetected)
{
Gizmos.color = Color.green;
Gizmos.DrawLine(transform.position, currentTarget.position);
}
}
六、高级功能应用
6.1 分级感知系统
在实际游戏中,往往需要实现分级感知,让不同级别的AI有不同的感知能力:
感知等级配置
public enum PerceptionLevel
{
Normal, // 普通感知
Alert, // 警觉状态
Combat // 战斗状态
}
public class分级感知System : MonoBehaviour
{
public PerceptionLevel currentLevel = PerceptionLevel.Normal;
[Header("Perception Settings")]
public float normalRange = 20f;
public float normalFOV = 90f;
public float alertRange = 30f;
public float alertFOV = 120f;
public float combatRange = 50f;
public float combatFOV = 180f;
public void SetPerceptionLevel(PerceptionLevel level)
{
currentLevel = level;
switch (level)
{
case PerceptionLevel.Normal:
perceptionModule.radius = normalRange;
perceptionModule.fov = normalFOV;
break;
case PerceptionLevel.Alert:
perceptionModule.radius = alertRange;
perceptionModule.fov = alertFOV;
break;
case PerceptionLevel.Combat:
perceptionModule.radius = combatRange;
perceptionModule.fov = combatFOV;
break;
}
}
}
6.2 感知噪声系统
实现真实的噪声感知系统:
噪声事件
public class NoiseEvent
{
public Vector3 position;
public float loudness; // 噪声强度 (0-1)
public float detectionRadius; // 可检测距离
public float duration; // 持续时间
public string sourceTag; // 声源标签
public float timeSinceCreated;
}
噪声管理器
public class NoiseManager : MonoBehaviour
{
public List<NoiseEvent> activeNoises = new List<NoiseEvent>();
// 产生噪声
public void ProduceNoise(Vector3 position, float loudness, float duration, string sourceTag = "")
{
NoiseEvent noise = new NoiseEvent
{
position = position,
loudness = loudness,
detectionRadius = loudness * 30f, // 根据响度计算检测距离
duration = duration,
sourceTag = sourceTag,
timeSinceCreated = 0
};
activeNoises.Add(noise);
}
// 更新噪声
void Update()
{
float deltaTime = Time.deltaTime;
for (int i = activeNoises.Count - 1; i >= 0; i--)
{
activeNoises[i].timeSinceCreated += deltaTime;
if (activeNoises[i].timeSinceCreated > activeNoises[i].duration)
{
activeNoises.RemoveAt(i);
}
}
}
// AI检测噪声
public bool CanHearNoise(AICharacter ai, out Vector3 noisePosition)
{
noisePosition = Vector3.zero;
foreach (var noise in activeNoises)
{
float distance = Vector3.Distance(ai.Position, noise.position);
if (distance < noise.detectionRadius)
{
noisePosition = noise.position;
return true;
}
}
return false;
}
}
6.3 条件感知触发
根据游戏状态动态调整感知行为:
感知状态机
public class感知StateMachine : MonoBehaviour
{
public enum State
{
Idle, // 空闲 - 感知范围最小
Patrol, // 巡逻 - 感知范围中等
Alert, // 警觉 - 感知范围扩大
Combat // 战斗 - 感知范围最大
}
public State currentState = State.Idle;
// 状态转换
public void OnStateChanged(State newState)
{
currentState = newState;
switch (newState)
{
case State.Idle:
SetPerception(10f, 60f, 1f);
break;
case State.Patrol:
SetPerception(15f, 90f, 0.5f);
break;
case State.Alert:
SetPerception(25f, 120f, 0.2f);
break;
case State.Combat:
SetPerception(40f, 180f, 0f);
break;
}
}
void SetPerception(float range, float fov, float updateInterval)
{
perceptionModule.radius = range;
perceptionModule.fov = fov;
perceptionModule.updateInterval = updateInterval;
}
}
6.4 群体感知
实现多个AI之间的信息共享:
群体感知网络
public class感知Network : MonoBehaviour
{
public List<AICharacter> teamMembers = new List<AICharacter>();
public float communicationRange = 15f;
// 广播感知信息
public void BroadcastDetection(Transform target, Vector3 lastKnownPosition)
{
foreach (var member in teamMembers)
{
if (member == null) continue;
float distance = Vector3.Distance(transform.position, member.Position);
if (distance < communicationRange)
{
// 共享目标信息
member.OnTeamMemberDetected(target, lastKnownPosition);
}
}
}
// 添加团队成员
public void AddTeamMember(AICharacter character)
{
if (!teamMembers.Contains(character))
{
teamMembers.Add(character);
}
}
}
七、与行为系统集成
7.1 与行为树集成
将感知系统与Behavior Designer等行为树工具集成:
感知条件节点
using BehaviorDesigner.Runtime;
using BehaviorDesigner.Runtime.Tasks;
[TaskCategory("Perception")]
[TaskDescription("检测目标是否在视野范围内")]
public class CanSeeTarget : Conditional
{
public SharedTransform target;
public SharedFloat viewDistance = 20f;
public SharedFloat viewAngle = 90f;
private AICharacter aiCharacter;
public override void OnStart()
{
aiCharacter = GetComponent<AICharacter>();
}
public override TaskStatus OnUpdate()
{
if (target.Value == null)
return TaskStatus.Failure;
if (aiCharacter.Perception.CanSee(target.Value))
return TaskStatus.Success;
return TaskStatus.Failure;
}
}
感知动作节点
[TaskCategory("Perception")]
[TaskDescription("设置感知范围")]
public class SetPerceptionRange : Action
{
public SharedFloat range;
public SharedFloat fov;
private AICharacter aiCharacter;
public override void OnStart()
{
aiCharacter = GetComponent<AICharacter>();
}
public override TaskStatus OnUpdate()
{
aiCharacter.Perception.SetRange(range.Value);
aiCharacter.Perception.SetFOV(fov.Value);
return TaskStatus.Success;
}
}
7.2 状态转换集成
感知触发状态转换:
public class感知StateTransitioner : MonoBehaviour
{
public EnemyStateMachine stateMachine;
public AICharacter aiCharacter;
void Update()
{
// 检测目标
if (aiCharacter.Perception.CanSeePlayer())
{
stateMachine.TransitionTo(EnemyState.Chase);
}
else if (aiCharacter.Perception.CanHearThreat())
{
stateMachine.TransitionTo(EnemyState.Investigate);
}
}
}
7.3 决策系统集成
感知信息用于AI决策:
public class感知BasedDecision : MonoBehaviour
{
public AICharacter aiCharacter;
public DecisionResult MakeDecision()
{
// 获取感知信息
var perception = aiCharacter.Perception;
// 检查各种条件
if (perception.CanSeePlayer())
{
// 看到玩家
if (perception.GetDistanceToPlayer() < attackRange)
{
return DecisionResult.Attack;
}
else
{
return DecisionResult.Chase;
}
}
else if (perception.HasMemoryOfPlayer())
{
// 记得玩家最后位置
return DecisionResult.Investigate;
}
else if (perception.CanHearNoise())
{
// 听到声音
return DecisionResult.Investigate;
}
// 默认巡逻
return DecisionResult.Patrol;
}
}
八、性能优化与最佳实践
8.1 感知频率优化
不是所有AI都需要每帧感知更新:
分级更新策略
public class Optimized感知Update : MonoBehaviour
{
// 根据AI状态设置更新间隔
public float patrolUpdateInterval = 0.5f;
public float alertUpdateInterval = 0.2f;
public float combatUpdateInterval = 0.1f;
private float currentInterval;
private float timer;
void Update()
{
timer += Time.deltaTime;
if (timer >= currentInterval)
{
timer = 0;
UpdatePerception();
}
}
public void SetAIState(AIState state)
{
switch (state)
{
case AIState.Patrol:
currentInterval = patrolUpdateInterval;
break;
case AIState.Alert:
currentInterval = alertUpdateInterval;
break;
case AIState.Combat:
currentInterval = combatUpdateInterval;
break;
}
}
}
8.2 空间分区优化
大规模场景中使用空间分区优化感知检测:
public class Spatial感知Optimization : MonoBehaviour
{
// 八叉树或其他空间分区结构
private Octree<Transform> perceptionOctree;
// 只检测附近的潜在目标
public List<Transform> GetNearbyTargets(Vector3 position, float range)
{
return perceptionOctree.Query(position, range);
}
}
8.3 感知缓存
避免重复计算感知信息:
public class感知Cache : MonoBehaviour
{
private class CachedPerception
{
public Transform target;
public bool isVisible;
public float distance;
public Vector3 lastPosition;
public float cacheTime;
}
private Dictionary<Transform, CachedPerception> perceptionCache = new Dictionary<Transform, CachedPerception>();
private float cacheDuration = 0.1f;
// 获取缓存的感知结果
public bool GetCachedPerception(Transform target, out CachedPerception cached)
{
if (perceptionCache.TryGetValue(target, out cached))
{
if (Time.time - cached.cacheTime < cacheDuration)
{
return true;
}
}
cached = null;
return false;
}
}
8.4 最佳实践建议
设计建议
- 根据游戏类型选择合适的感知范围。
- 使用分级感知系统适应不同游戏状态。
- 实现感知记忆增加AI智能感。
- 考虑性能平衡感知精度和频率。
性能建议
- 感知更新使用适当间隔而非每帧。
- 使用LayerMask减少不必要的检测。
- 实现感知结果缓存避免重复计算。
- 对于大量AI使用空间分区优化。
调试建议
- 使用Gizmos绘制感知范围辅助调试。
- 添加日志输出感知事件。
- 创建测试场景验证感知逻辑。
- 使用Profiler监测感知系统性能。
九、实战案例:潜行游戏敌人AI
9.1 项目概述
创建一个完整的潜行游戏敌人AI系统,包含视觉感知、听觉感知和感知记忆功能。
9.2 系统架构
感知系统组件
EnemyPerceptionSystem
├── VisualPerception (视觉感知)
│ ├── FOV检测
│ ├── 遮挡检测
│ └── 距离计算
├── AudioPerception (听觉感知)
│ ├── 声音检测
│ └── 方向识别
├── PerceptionMemory (感知记忆)
│ ├── 记忆存储
│ └── 置信度衰减
└── StateIntegration (状态集成)
└── 与AI状态机连接
9.3 完整实现
敌人感知控制器
public class EnemyPerceptionController : MonoBehaviour
{
[Header("Visual Perception")]
public float visualRange = 25f;
public float visualAngle = 90f;
public LayerMask playerLayer;
public LayerMask obstacleLayer;
public Transform eyesPosition;
[Header("Audio Perception")]
public float audioRange = 20f;
[Header("Memory")]
public float memoryDuration = 30f;
public float suspicionTime = 10f;
[Header("State")]
public Transform currentTarget;
public Vector3 lastKnownPosition;
public PerceptionState state = PerceptionState.Patrol;
private PerceptionMemory memory;
private EnemyStateMachine stateMachine;
void Start()
{
memory = new PerceptionMemory(memoryDuration);
stateMachine = GetComponent<EnemyStateMachine>();
}
void Update()
{
// 根据状态更新感知行为
switch (state)
{
case PerceptionState.Patrol:
UpdatePatrolPerception();
break;
case PerceptionState.Suspicious:
UpdateSuspiciousPerception();
break;
case PerceptionState.Combat:
UpdateCombatPerception();
break;
}
// 更新记忆
memory.UpdateMemories();
}
void UpdatePatrolPerception()
{
// 检测视觉目标
Transform visibleTarget = DetectVisiblePlayer();
if (visibleTarget != null)
{
// 玩家被发现
memory.AddMemory(visibleTarget, visibleTarget.position, 1f);
currentTarget = visibleTarget;
lastKnownPosition = visibleTarget.position;
stateMachine.OnPlayerDetected();
state = PerceptionState.Combat;
}
// 检测声音
if (DetectSound(out Vector3 soundPos))
{
memory.AddSoundMemory(soundPos, 0.8f);
lastKnownPosition = soundPos;
stateMachine.OnSoundHeard(soundPos);
state = PerceptionState.Suspicious;
}
}
void UpdateSuspiciousPerception()
{
// 检查是否重新发现玩家
Transform visibleTarget = DetectVisiblePlayer();
if (visibleTarget != null)
{
currentTarget = visibleTarget;
lastKnownPosition = visibleTarget.position;
stateMachine.OnPlayerDetected();
state = PerceptionState.Combat;
}
// 检查记忆是否过期
if (memory.GetMemoryConfidence() < 0.1f)
{
stateMachine.ReturnToPatrol();
state = PerceptionState.Patrol;
}
}
void UpdateCombatPerception()
{
// 持续追踪玩家
Transform visibleTarget = DetectVisiblePlayer();
if (visibleTarget != null)
{
currentTarget = visibleTarget;
lastKnownPosition = visibleTarget.position;
memory.AddMemory(visibleTarget, visibleTarget.position, 1f);
}
else if (currentTarget != null)
{
// 玩家丢失,更新最后已知位置
memory.AddMemory(currentTarget, lastKnownPosition, memory.GetMemoryConfidence() * 0.95f);
// 转向最后已知位置搜索
stateMachine.OnPlayerLost(lastKnownPosition);
}
}
Transform DetectVisiblePlayer()
{
Collider[] players = Physics.OverlapSphere(transform.position, visualRange, playerLayer);
foreach (var player in players)
{
if (IsInFieldOfView(player.transform) && HasLineOfSight(player.transform))
{
return player.transform;
}
}
return null;
}
bool IsInFieldOfView(Transform target)
{
Vector3 direction = (target.position - eyesPosition.position).normalized;
float angle = Vector3.Angle(transform.forward, direction);
return angle < visualAngle * 0.5f;
}
bool HasLineOfSight(Transform target)
{
RaycastHit hit;
if (Physics.Linecast(eyesPosition.position, target.position, out hit, obstacleLayer))
{
return hit.transform == target;
}
return true;
}
bool DetectSound(out Vector3 soundPosition)
{
// 实现声音检测逻辑
soundPosition = Vector3.zero;
return false;
}
}
感知记忆类
public class PerceptionMemory
{
private class MemoryEntry
{
public Transform target;
public Vector3 position;
public float confidence;
public float timeSinceCreation;
}
private List<MemoryEntry> memories = new List<MemoryEntry>();
private float memoryDuration;
private float decayRate = 0.05f;
public PerceptionMemory(float duration)
{
memoryDuration = duration;
}
public void AddMemory(Transform target, Vector3 position, float confidence)
{
var existing = memories.Find(m => m.target == target);
if (existing != null)
{
existing.position = position;
existing.confidence = confidence;
existing.timeSinceCreation = 0;
}
else
{
memories.Add(new MemoryEntry
{
target = target,
position = position,
confidence = confidence,
timeSinceCreation = 0
});
}
}
public void UpdateMemories()
{
for (int i = memories.Count - 1; i >= 0; i--)
{
memories[i].timeSinceCreation += Time.deltaTime;
memories[i].confidence = Mathf.Max(0, memories[i].confidence - decayRate * Time.deltaTime);
if (memories[i].timeSinceCreation > memoryDuration || memories[i].confidence <= 0)
{
memories.RemoveAt(i);
}
}
}
public float GetMemoryConfidence()
{
if (memories.Count == 0) return 0;
float highest = 0;
foreach (var memory in memories)
{
highest = Mathf.Max(highest, memory.confidence);
}
return highest;
}
}
十、总结与展望
10.1 核心要点回顾
通过本文的详细介绍,读者应该对Unity AI Perception系统有了全面的了解:
- 感知系统基础:理解AI感知的基本概念和类型。
- 核心组件:掌握AI Perception组件和感知模块的使用方法。
- 实现原理:理解视觉、听觉感知和记忆系统的实现原理。
- 高级应用:掌握分级感知、噪声系统等高级功能。
- 集成方法:了解与行为树和决策系统的集成方式。
- 性能优化:掌握感知系统的性能优化技巧。
10.2 适用场景
AI Perception系统特别适合以下游戏类型:
- 潜行游戏
- 战术射击游戏
- RPG游戏中的敌人AI
- 开放世界游戏中的NPC
- 任何需要智能感知的游戏
10.3 学习资源
进一步学习AI Perception的资源:
- Unity官方文档
- AI导航系统文档
- 行为树集成文档
10.4 未来发展
Unity的AI感知系统将持续发展:
- 更精确的感知算法
- 更好的性能优化
- 更灵活的集成方式
- 与机器学习系统的结合
(欢迎点赞留言探讨,更多人加入进来能更加完善这个探索的过程,🙏)
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)