查看源码下载文档
CHAPTER / 09

单位任务、小队脚本与电脑战略:AI 其实分布在好几层

红警源码中的 AI() 通常只是“这个对象本轮更新逻辑”的名称,并不专指电脑对手。玩家的矿车会自动采矿,玩家坦克会自动警戒,建筑会推进状态,动画会播放,这些都可以进入各自的 AI 更新。理解这个约定,才能避免把全部游戏模拟误读为敌方决策算法。

电脑对手的实际表现,是关卡触发器、阵营级生产与战略、小队脚本、单体任务以及移动战斗机制共同合成的。本章分析真实函数体,刻意区分实现、空桩和历史注释;案例是静态流程推演。

1. 四种决策尺度,以及它们如何碰头

层次 代表类型 回答的问题 输出
关卡导演 TriggerClass/TActionClass 何时开始生产、派哪波援军 状态开关、队伍创建、胜负标记
阵营战略 HouseClass 建什么、敌人是谁、何时总攻 建造请求、任务下发
小队协调 TeamClass/TeamTypeClass 组哪些单位、先去哪再打什么 成员的 Mission、NavCom、TarCom
单体行为 MissionClass 及派生类 当前该走、打、采矿还是停 状态转换、移动与攻击要求

LogicClass::AI 先处理通用触发器,随后更新 Teams,再更新逻辑对象,之后处理地图、工厂和阵营。这个顺序解释了为何不同层能在相邻逻辑时刻观察到彼此结果,也提醒读者这不是多个独立智能体并发线程。主逻辑更新顺序对象、地图与工厂更新

一个重要的工程判断是:这种分层让任务作者无需理解坦克的每一个转弯动作,单位作者也不用知道战役第几波援军何时到。两边通过少量命令和共享状态连接;代价是排查“单位为什么不行动”时,必须沿层级往下追。

2. Mission 是有定时器的状态机,不是一次函数调用

MissionClass 有当前 Mission、待切换的 MissionQueue、内部阶段 StatusTimerAssign_Mission 通常只是写待执行槽;Commence 才真正将其变成当前任务,同时清零 StatusTimer。这里的 Queue 是一个待执行任务槽,不要想象成可无限排队的容器。排队与开始

每次 MissionClass::AI 都先做基类更新,但只有任务定时器到零、对象仍有生命值时,才分派到 Mission_Move/Attack/Guard/Harvest 等函数。任务函数返回“下次多久之后再来”,返回值直接写入 Timer。悬在空中的步兵、车辆、舰船还会暂停这一层处理,避免伞降尚未落地就开展地面行动。任务调度

这让昂贵的寻敌和决策不用每个逻辑更新都完整执行,同时底层移动、朝向、动画仍可以持续变化。很多任务使用规则中的 Normal_Delay(),再加少量随机偏移;这种错开检查时点的写法可以降低所有单位同时醒来的尖峰。后者是从结构得出的工程解释,不是源码中的性能测试结论。

三种很容易混淆的行为可直接从 FootClass 看出来:

  • Move 关心导航目标是否完成;部分非人类控制单位还会寻找射程内目标。
  • Attack 在目标合法时调用 Approach_Target,失去目标则回到空闲模式。
  • GuardTHREAT_RANGE 搜索身边威胁;Hunt 用更广的 THREAT_NORMAL 获取目标,然后接近。特殊步兵还会转为占领或破坏任务。

因此“站着不动也会打人”和“主动追着敌人跑”是不同任务策略,不是同一个布尔值“开启 AI”。移动与攻击任务警戒与猎杀

任务还能被临时覆盖。MissionClass::Override_Mission 保存原任务,TechnoClass 再保存攻击目标,FootClass 再保存导航目标;恢复时逐层取回。这不是通用调用栈,而是分别保留一份暂停状态。前一章坦克为清除路障临时开火,就是理解这一设计的好例子:仅把 Mission 改回 Move 还不够,原本要去哪里也必须恢复。多层继承在这里不仅用于分类,还分担恢复上下文的职责。任务保存攻击目标保存导航目标保存

3. 先用矿车理解“状态”与“动作”之间的差别

UnitClass::Mission_Harvest 给出了非常直观的状态机:LOOKINGHARVESTINGFINDHOMEHEADINGHOMEGOINGTOIDLE。非采矿型车辆收到该任务会长时间等待;没有矿场时则改为警戒。这些保护条件说明任务接口允许被通用地调用,但每种对象仍负责验证自身能力。采矿状态与入口保护

假设矿车正在矿区,流程可以这样跟踪:寻找状态会尝试记忆中的矿区位置并作较大范围搜索;找到可采位置后打开采矿动画,切入采集状态。采集状态检查动画阶段再实际采矿;装满则记下当前矿区为 ArchiveTarget 并转入找家。找家阶段查询可停靠矿场、发 RADIO_HELLO,收到 RADIO_ROGER 才进入返家协调;没有得到允许时可能先去矿场附近等候。寻找、采集及返航握手

所以“采矿 AI”不只是 moveTo(ore); money += amount。它还需要处理矿尽、装载比例、运输、基地接收能力、建筑忙闲与动画时序。源码变量继续叫 Tiberium 是系列代码沿用痕迹,不能据此说本作采集的是前作泰伯利亚矿。经济闭环在其他章节展开,这里把它作为任务状态机的可读样本。

4. TeamType 是配方,Team 是运行中的队伍

TeamTypeClass::Fill_In 从关卡文本读入队伍所属方、招募优先级、数量限制、出发 waypoint、成员类型与数量,以及一个按顺序执行的任务列表。新格式将绕行、自杀式、自动创建、预先生产、可补员等标志打包为位;旧格式分别占文本字段。随后按名字寻找步兵、车辆、飞机和舰船类型,最后读入 (任务编号, 参数) 对。队伍配方解析

一个 TeamType 可以有多个运行实例;Team 保存实际成员链、各类型现有数量、当前脚本位置、目标、集合状态和超时。它也不是玩家按 Ctrl+数字建立的快捷选择组;本章的 Team 主要是游戏逻辑中的编组与脚本控制对象。

招募不是随便拉一个最近单位。Can_Add 要求对象属于同一 House、可活动、不正忙于无线电协调、当前任务允许招募、已有队伍的招募优先级低于新队伍;带武器但无弹药的飞机会被拒绝,类型和剩余名额也要吻合。Recruit 再扫描对应对象集合、结合队伍中心或 Origin 评估距离。车辆分支的添加位置有自己的历史实现,不能把所有分支简化成完全相同的“一次挑最近一个”。招募资格成员搜索

TeamClass::AI 在成员改变时重新判断人数:达到配方总人数就是满员;可补员队伍通常在不足约三分之一时进入兵力不足状态,小队总编制很小时有专门条件。不可补员队伍一旦正式出发,就不会因为减员一直回头等人。严重减员时,它会尝试挑选友方建筑附近的集合点,距离结合地图威胁评价,维修设施获得偏好。兵力与重组

5. 小队脚本怎样驱动一波进攻

构造一个“3 辆坦克,移至 waypoint A,进攻建筑”的队伍作为阅读案例。这里用语义描述,不把它冒充仓库自带的某张地图或可直接粘贴的 INI。

  1. 关卡或 House 创建 Team 实例,它还没有凭空得到三辆坦克。Team 的招募逻辑寻找合格的现有车辆;缺兵可能通过阵营生产请求补足。
  2. 满员或被 Force_Active 后,IsMoving 置位,CurrentMission=-1IsNextMission=true。下一次推进会把脚本下标加一。
  3. TMISSION_MOVE 的参数是 waypoint 编号,先查 Scen.Waypoint[]。领队不能进入该格时,可改找附近位置。
  4. Coordinate_Move 对每个成员派 MISSION_MOVE 和导航目标;掉队者、仍在卸载者、未停止的导航都会影响完成判定。普通队伍允许一定距离容差,编队移动可能要求精确格位。
  5. 全部满足后设置 IsNextMission,下次推进执行攻击步骤。领队按目标类别调用 Greatest_Threat 选一个目标,小队再给成员分配攻击任务与 TarCom
  6. 每辆坦克在单体任务里接近敌人,交给移动层寻路、战斗层决定能否开火。队伍本身不直接移动坦克坐标,也不直接扣敌人生命值。

出发与脚本推进移动协调完成条件按猎物类别选择目标给成员下攻击命令

脚本还支持装载、卸载、巡逻、部署、编队、执行单体任务、设置全局标志和循环。TimeOut 初始化看似对所有任务都赋值,但不能据此说每个步骤都按同一计时器自动跳过;主分派后的通用超时检查明确针对 Guard,而其他步骤有自己的完成条件。这正是读函数体比只看字段名可靠的原因。任务分派与 Guard 超时

脚本的“循环”也没有隐藏的高级虚拟机:TMission_LoopCurrentMission 写为指定下标减一,再标记进入下一步,让统一的下标递增逻辑完成跳转。TMission_Set_Global 则设置场景全局标志并立即进入下一步,这样队伍到达某处或完成进攻后,可以通知关卡导演开展下一阶段。巡逻任务会周期性让领队搜寻附近目标;当前目标是对象时协调攻击,是位置时协调移动。这些简单原语组合起来,就能制造比单条 Hunt 命令丰富得多的战场行为。循环与全局通知巡逻分派

6. House 把生产、敌情与进攻节奏连接起来

HouseClass::AI 中,启用基地建设或 IQ 达到生产阈值会联动设置 IsBaseBuilding/IsStarted/IsAlerted。警戒已启动且 AlertTime 归零时,它选择队伍类型、创建实例,然后重新随机化下次间隔。Suggested_New_Team 会按所属方、是否自动创建、该类型已存在数量与最大数量筛选;不能将注释中已废弃的候选评分逻辑当作实际算法。阵营激活与自动造队队伍候选筛选

生产则有两条重要来源。战役的 AI_Unit 观察活跃队伍缺口与 IsPrebuilt 类型需求,扣掉可招募现有单位,再考虑能否建造与资金。基地建设模式另有基于可造单位的随机权重:带武器车辆权重 20,非采矿车的其他可造车辆权重 1。两段代码可以在同一次调用中先后运行,因此不能说战役与遭遇战只有两个互不重叠的独立 AI。车辆生产需求与随机选择

AI_Building 先可参考关卡的 Base.Next_Buildable,再在基地建设模式下按电力、收入、建筑比例、科技及资金形成候选。例如电力接近不足就加入电厂;炼矿厂数量低于比例且有条件时加入炼矿厂;兵营也有比例和上限。它让电脑扩张的外观像一种策略,但本质是手工规则与状态统计驱动。建筑选择

7. “专家系统”有实装,也有看起来完整的空接口

Expert_AI 会失效旧敌人引用,并结合阵营距离、对己方的击杀、相对兵力建筑规模等选择敌方。它也形成各战略动作的 urgency,再从高到低调用对应处理函数。这是规则系统,不是机器学习、博弈树搜索或行为树解释器。敌方评估紧急度分派

不过 AI_Build_Power/Defense/Offense/Income 在这份代码里只是 return(false),一些对应检查也返回 URGENCY_NONE。真正的电厂和兵力建设逻辑在前述 AI_Building/AI_Unit 等路径。紧急度循环旁的注释还说“最高优先级已行动则停止”,但函数体没有据 acted 提前退出的代码。这些都应作为历史代码现状理解,不能为了讲出漂亮架构而补上不存在的行为。空实现

实装的 AI_Attack 会依据随机选择让部分有武器单位改为 Hunt,或让守基地单位调整站位,然后重设进攻计时器;没有生产设施时的 AI_Fire_Sale 可卖掉建筑并全面出击。玩家感受到的“最后一波总攻”,因此可能来自阵营规则,而不一定由关卡作者单独写了一支特殊队伍。进攻与节奏清仓总攻

难度也不能仅解释成“思考更聪明”。Assign_Handicap 明确调整火力、地面与空中速度、装甲、射速、费用、维修延迟与建造速度等倍率;多人模式还结合所扮演国家的修正。IQ 则是功能门槛之一。这两套概念各有作用,不能把难度枚举名称直接当作玩家感知难度的全部定义。难度倍率

8. 调试一个“不进攻”的电脑应该检查什么

沿因果链看比从总入口逐行单步更有效:关卡是否打开生产或自动创建;Team 是否已创建、配方是否允许当前数量;所需单位能否生产、能否被招募;队伍是否满员、在重组或挂起;脚本目标是否合法、成员是否仍有 NavCom;单体 Mission 是否已经 Commence;最后才检查路线、武器和射程。

这种诊断方法连接了地图与寻路以及关卡触发器:队伍层看似停止,根因可能是唯一桥梁被炸断;战略层看似缺钱,根因可能是矿车一直等待矿场;剧情未触发又可能根本没有启动 IsAlerted。共享状态让系统便宜地协作,也让局部现象很难单独解释。

源码阅读路线与待验证边界

建议按 MissionClass::AI/Commence → FootClass::Mission_Guard → UnitClass::Mission_Harvest → TeamTypeClass::Fill_In → TeamClass::Can_Add/AI/Coordinate_Move → HouseClass::AI/AI_Unit/AI_Building/Expert_AI 阅读。

尚未通过游戏运行测量各难度胜率、生产偏好或队伍卡住概率;这些需要规则资源、固定地图与重复回放。本文能确认的是决策结构和分支条件。尤其不要根据功能名字认定 AI 会完整执行空接口注释中的意图,也不要将这套脚本系统解释为现代神经网络智能体。

红色警戒初代 · 源码剖析18 章正文 / 5 份附录
输入关键词,检索 24 份完整文档
全文检索 · 点击结果直达对应小节