项目笔记N-208
状态间的转移
- 更新
- 内容块
- 145
- 阅读
- 约 4 分钟
- 路径
- 项目笔记 / CS / 2026.7.7 / AI 模组
行为树在选中节点时的落地(决策和执行的连接点)
不直接intent,而是调用:
setActiveObjective(
b,
ObjectiveType::PlantBomb,
BotIntent::Plant,
AIState::PlantBomb,
site,
site,
{},
nearestNode(site),
1.0f,
false,
"CarrierAtSite"
);- 1.判断新目标和旧目标是否相同。
- 2.如果旧目标不可打断,并且新目标优先级更低,则拒绝切换。
- 3.写入
BotObjective。
- 4.同步
b.intent、b.state、b.objectivePos、b.moveTarget。
- 5.如果目标节点变了,清空旧路线,准备重新寻路。
- 6.设置
lockTimer,防止目标每帧抖动。
团队战术层如何工作
回合开始:`buildRoundPlan()`
回合开始时 resetRound() 会调用 buildRoundPlan()。
buildRoundPlan() 的逻辑:
- 1.
resolveTactic()决定战术。
- 2.如果用户设置了固定战术,使用固定战术。
- 3.如果是
Auto,根据难度、金钱和随机数选择Default、RushA、RushB、FakeRotate、EcoPick。
- 4.决定目标是 A 还是 B。
- 5.选择主目标节点、集合点、支援点、狙击点、侧翼点。
这对应战术 FPS 里的“赛前战术安排”。
每帧:`updateTeamTacticalPlan()`
战术不是回合开始后就不变。每帧还会根据局势调整:
- •掉包且没人带包:T 的团队意图改成
RecoverBomb。
- •包已下:T 改成
PostPlantHold,CT 改成DefuseCover。
- •RushA/RushB:T 改成
ExecuteA/ExecuteB。
- •FakeRotate:T 改成
FakeAThenB/FakeBThenA。
- •发现敌人:CT 可能转 A 或转 B。
- •人数太少:可能
SaveWeapon。
个人战术层如何工作
selectBotIntent() 是最核心的行为树函数。下面按真实逻辑解释。
掉包和捡包优先
开头先调用 updateBombPickup(b)。如果 Bot 是 T、炸弹掉在地上、Bot 走到炸弹附近,就把炸弹转移给这个 Bot。
如果炸弹掉了且没人带包:
- •
droppedBombRecovererIndex()选择最适合捡包的 T。
- •这个 Bot 获得
RecoverBomb目标。
- •其他 T 不会都挤过去抢包。
这是一个典型团队任务分配。
CT 拆包逻辑优先级很高
如果回合阶段是 BombPlanted 且 Bot 是 CT:
- •如果看见敌人,先判断是否必须提交拆包。
- •如果不是必须拆包,且敌人在炸弹附近,可以先
Engage。
- •如果有指定 defuser,其他 CT 可能
HoldAngle掩护。
- •如果时间不够或非目标角色卡住,可能
SaveWeapon。
- •否则设置
DefuseBomb。
这相当于:
CT BombPlanted Selector
VisibleEnemyNearBomb and NotMustCommit -> Engage
IAmCover -> HoldAngle
TooLateOrStuck -> SaveWeapon
Else -> Defuse带包 T 的特殊逻辑
带包者不能像普通 Bot 一样被所有信息牵着跑。代码对 T 持包者做了特殊处理:
- •如果带包者已经在包点附近,并且敌人离包点远或不构成近距离威胁,则优先
Plant。
- •如果带包者还没到包点,即使看见远处敌人,也倾向继续走向包点。
- •没有其他高优先级事件时,带包者默认
TakeSpace到目标包点。
这修复了一类常见问题:AI 明明带包到点附近,却因为最后敌人记忆或普通交火不下包。
可见敌人
如果看见敌人:
- •调用
rememberEnemy()更新个人记忆和团队情报。
- •如果低弹量,选
TakeCover。
- •否则选
Engage。
这对应 FPS 最基础的“看到敌人先处理”的行为。
卡住恢复
如果 Bot 正在执行移动目标,但位置长时间没进展:
- •进入
Repath。
- •清理或重建路线。
- •避开
blockedNode。
这不是传统行为树里常见的战术节点,但对手写导航图非常重要。
包已下后的双方行为
包已下:
- •T:
PostPlantHold,选择守包角度。
- •CT:
Defuse,往炸弹走。
低弹量找掩体
如果弹量低且还有备弹:
- •设置
TakeCover。
- •执行层会找掩体并换弹。
T 出生点脱困
如果 T 在出生点口袋区域:
- •设置
ExitSpawn/TakeSpace。
- •强制先离开出生点。
这个规则专门解决“Bot 卡 T 家”的实局问题。
记忆和声音
如果没有直接看到敌人,但有最近敌人位置:
- •
SearchLastKnown
如果听到声音:
- •也转
SearchLastKnown,目标是声音位置。
默认战术
如果以上都没命中,就按团队战术和角色分配默认目标:
- •Rush:Entry/Objective 向主点,Support 补枪,Sniper 架点,Lurker 侧翼。
- •EcoPick:更分散,Lurker 或手枪位偏绕。
- •FakeRotate:部分人制造假打,部分人转点。
- •Default:按角色默认控图。
- •CT:默认守角或根据情报转点。
战斗行为怎么搭
对枪入口是 engageEnemy()。
它的大致流程:
目标死了 -> 回到 PlanRoute
根据距离和武器选 WeaponStyle
如果被闪严重 -> 延迟反应/后撤
转向目标
如果狙击太近或敌人太近 -> 后撤换位
如果无射线 -> Peek/找掩体
如果没子弹 -> 换弹找掩体
如果反应时间没到 -> Acquire
如果需要跳拉 -> jump peek + strafe
如果角度或距离不允许开火 -> 追击/后撤
如果需要急停 -> CounterStrafe
横移/跑打
fireBotWeapon()
达到扫射换位阈值 -> RepositionDuelState
DuelState 表示战斗微状态:
- •
Acquire:捕捉目标/反应中。
- •
CounterStrafe:急停准备开枪。
- •
BurstFire:点射/连发。
- •
Peek:探身或处理视线。
- •
Reposition:换位。
WeaponStyle
WeaponStyle 根据武器和距离决定打法:
- •
PistolLurk
- •
SmgRush
- •
RifleTap
- •
RifleBurst
- •
RifleSpray
- •
SniperHold
这些风格影响:
- •是否允许开火。
- •瞄准高度。
- •散布缩放。
- •开枪间隔。
- •是否跑打。
- •是否后撤。
- •是否换位。
武器模拟
真正开枪时会调用 weapon::WeaponSimulator::tryFire(),它会考虑:
- •当前弹匣和冷却。
- •后坐力预测。
- •AI 瞄准误差。
- •站立/空中状态。
- •是否开镜。
- •武器散布。
所以 AI 不是简单 if visible then damage,而是走一套接近真实武器规则的模拟。
炸弹逻辑如何融入行为树
炸弹是爆破模式 AI 的核心目标。项目里相关函数包括:
- •
updateBombPickup()
- •
repairBombInvariant()
- •
bombDebugState()
- •
executePlant()
- •
executeDefuse()
- •
siteSafeForPlant()
行为树里炸弹任务优先级很高:
- •
DefuseBomb = 120
- •
PlantBomb = 110
- •
RecoverBomb = 105
- •
DefendBomb = 95
这样做的原因是,爆破模式不是死亡竞赛。T 的最终目标是下包或消灭 CT;CT 的最终目标是阻止下包、拆包或消灭 T。AI 如果只按“看见敌人就打”写,很容易变成中路对枪游戏。