Response System/zh
本页面由大康、Coma、Alanwong35和Yakumo koishi正在翻译中。欢迎任何人补充新内容或者修改其中的错误。
The Response System(响应系统)用于决定 NPC(或玩家)在想要说什么或是否说什么时应该使用哪种语言(和/或动画)。



大多数响应系统用户都是为了响应游戏中发生的事件而发言。语音可以从代码或地图的输入中触发。触发语音涉及响应概念,它对应于系统中的响应。概念是用于特定事件的语音类别,例如注意到危险 (TLK_DANGER
) 或看到敌人 (TLK_STARTCOMBAT
)。虽然一个概念可以单独用于基本和简单的对话,但语音也可以通过各种标准来触发,这些标准描述了与该概念和/或说话者的当前状态和周围世界相关的事物。这允许复杂的对话树涉及不同情况下的不同台词。
例如,当玩家杀死敌方 NPC 时,HL2 情节系列中的npc_alyx
会说出TLK_PLAYER_KILLED_NPC
概念。这个概念对于与 NPC 相关的条件或它被杀死的方式有额外的标准。如果玩家通过爆头杀死了 NPC,则响应系统将选择需要该标准的响应,这通常会导致艾利克斯称赞玩家的射击或评价这是爆头。
响应系统通常使用scripts/talker
目录中的脚本。当它接收到一个概念及其标准时,它会在响应脚本中搜索与其概念和标准相匹配的规则(rules)。当它找到一个时,它会使用该规则的响应(response)或响应选择。响应可以是声音脚本、句子,甚至是实例化的排编场景。
尽管有这些优点,但大多数 NPC 默认不使用此系统。例如,npc_pigeon
默认只使用声音脚本,不使用响应系统。
以下是使用响应系统的地方的快速列表:
- 在
半衰期2 中,大多数玩家同伴 NPC(公民、艾利克斯等)使用 NPC 语音响应系统。
- 在
军团要塞2 中,所有类(all classes)都使用响应系统来进行语音命令和嘲讽响应。
- 在
求生之路系列 中,所有幸存者都使用响应系统来响应玩家在世界上的行为和事件。
- 在
传送门2 中,Atlas 和 P-body(合作模式下的机器人)都使用响应系统来进行嘲讽手势。
- 在
反恐精英:全球攻势 中,所有玩家角色都使用响应系统并跟踪已使用的响应。
- 在
半衰期:爱莉克斯 中,参见 HLALYX:Response_rules.txt。

CAI_ExpresserHost<>
模板类的CBaseFlex
派生的类上,该模板类在CAI_BaseActor
上自动实现。所有 NPC 和玩家都已从CBaseFlex
派生,因此你通常只想使用CAI_ExpresserHost<>
,但响应系统可以使用DispatchResponse()
函数以更有限的方式在任何实体上使用,这就是env_speaker
的工作方式。GetResponseSystem()
可用于使概念搜索特定的响应系统树,如果你计划在没有CAI_ExpresserHost<>
的情况下使用DispatchResponse()
,则必须将其覆盖。目的
创建响应系统,以便 NPC、玩家等使用统一且强大的系统来管理语音。它不仅允许开发人员和作家创建复杂的对话树,而且还允许模组制作者轻松修改或向游戏添加新语音,而无需编写代码或修改 BSP 文件,同时仍然可以完全控制语音的方式和使用次数。
该系统最适合用于来自 AI 系统的语音,因为这些台词将在整个游戏中被多次说出。 (例如,市民在装填武器时会说些什么。很多市民会在游戏过程中装填武器。)
这是一个来自 NPC 使用响应系统的复杂对话树的示例:
- I just killed an enemy. 我刚刚杀死了一个敌人。
- Did I use a shotgun? 我用了散弹枪吗?
- "Eat shotgun! 吃散弹枪!"
- Was the enemy really close? 敌人真的很近吗?
- "You got too close! 你离的太近了!"
- "Thanks for getting close! 感谢你靠近!"
- Was the enemy a
npc_headcrab
? 敌人是npc_headcrab
吗?
- "You're not getting MY head! 你没有得到我的头!"
- "I hate headcrabs! 我恨猎头蟹!"
你还可以使用标准组合。
- Did I use a shotgun against a
npc_headcrab
really close to me? 我用霰弹枪对付离我很近的npc_headcrab
吗?
- "You got too close to my shotgun, headcrab! 你离我的霰弹枪太近了,猎头蟹!"
- Did I use a shotgun against a
你还可以控制单独的行,使其在一定时间内不重复,或者根本不重复。
构成

响应系统由四个核心部分组成:Concepts(概念), Criteria(标准), Rules(规则),和 Response Groups(响应组)。它们的使用方法如下:
- 一个 NPC 为一个语音概念(Concept)请求一行语音。
- 例如,假设我们的 NPC 为
TLK_SHOT
概念请求一行语音。 NPC 被敌人击中时会说出这个概念。
- 例如,假设我们的 NPC 为
- NPC 收集了一堆反映 NPC 当前状态的标准(Criteria)以及有关世界状态的其他相关数据。许多概念也有修饰符(Modifier),这是特定概念特有的标准,通常反映事件本身。
- 当 NPC 为
TLK_SHOT
概念请求一行时,游戏会将 NPC 当前的健康状况等作为默认标准集的一部分进行组合。然后,它附加了概念的修饰符,其中可能包括射击他们的敌人的类型、他们受到的伤害量等。
- 当 NPC 为
- 概念和标准 被传递到 NPC 的响应系统中,该系统通常是所有 NPC 共享的单个全局实例。
- 响应系统搜索其规则(Rules)的大列表。
- 每个规则都包含一个标准列表,该列表根据 NPC 预先设置的标准进行测试。请注意,概念在此阶段被视为高优先级标准。
- 每个规则都会根据有多少标准是正确的而获得一个分数。如果至少一个标准被标记为“必需”(required)并且不满足,则永远不会选择该规则。大多数标准都设置为必需,但不需要的标准只会提高规则的分数。
- 在我们的
TLK_SHOT
示例中,可能有多个包含不同行的TLK_SHOT
规则。一条规则可能包含测试“NPC 是否离射击他的敌人非常近”的标准。另一个规则可能会测试是否是特定类型的敌人向他开枪(例如联合军士兵)。另一个规则可能会测试 NPC 是否在射击后剩余的生命值低于 25%。
- 在我们的
- 响应系统对其列表中的所有规则进行评分,并选择得分最高的一项。得分最高的规则指定一个响应组(Response Group)。
- 响应组只是可能的响应列表,每个响应可能是一行语音和/或动画。一个响应根据响应组和个人响应的设置选择。选择有效响应后,NPC将执行该响应。
- 在我们的
TLK_SHOT
示例中,假设选择了<25% 健康规则。该规则将有一个响应组,其中可能包含一系列行,例如“One more shot like that, and I'm done for! 再打一枪,我就完蛋了!”、“Oh man, I'm in trouble! 哦,伙计,我有麻烦了!”或“I need medical attention over here! 我需要医疗照顾!” TLK_SHOT
的另一条规则可以检查射击他们的敌人是否是联合军士兵,并指向一个响应组,其中包含“That Combine soldier's got me pinned down! 那个联合军士兵让我被压制了!”或“Help me with this soldier! 帮我对付这个士兵!”。另一个规则可以检查敌人是否是联合武装直升机,并用“That gunship is kicking my butt! 那艘武装直升机在踢我的屁股!”和“Someone help me take down that gunship before it kills me! 有人帮我在它杀了我之前拿下那艘武装直升机!”指向一个组。
- 在我们的
- 如果没有任何规则符合给定的标准[或者所选的反应组没有重复并且已经筋疲力尽(exhausted)],NPC不会说任何话。
概念
concept(概念) 是一个字符串 ,它表示角色语音尝试的高级原因。 代码中定义了一组概念,这些概念将被自动调用,但从技术上讲,概念只是字符跟踪的特殊高级标准。它们与任何东西无关,你可以在响应文件中自由创建你自己的。你可以在代码中或使用参与者的DispatchResponse
或SpeakResponseConcept
输入来调用概念。
以下是半条命 2 中使用的一些预定义 NPC 概念的列表:
TLK_HELLO 当我第一次遇见玩家。 TLK_IDLE 当我被闲置了一段时间。 TLK_USE 当玩家尝试使用(+use)我。 TLK_PLPUSH 当玩家把我推开。 TLK_STARE 当玩家盯了我一会儿。 TLK_DANGER 当我察觉周围有危险(比如手雷)。 TLK_WOUND 当我已经受伤。 TLK_HIDEANDRELOAD 当我打算藏起来然后换弹。 TLK_PLYR_PHYSATK 当我被玩家扔过来的物体砸中了。
不是所有的 NPC 都会说所有的概念,也不是所有的 NPC 在相同的情况下都会说概念。有关完整列表,请参阅响应概念列表。
标准集(Criteria Set)
标准(Criteria)是一组条件,其中包含与对话者的当前状态相关的数据,以及每当请求尝试进行对话时概念的情况。它可以被解释为一组 键值。以下是一个由npc_alyx
创建的标准集的例子,她试图说话因为她刚刚杀死了当前的敌人:
concept = TLK_ENEMY_DEAD NPC 尝试说话的概念。 map = d3_c17_07 当前地图的名称。 classname = npc_alyx 正在说话的NPC的classname。 name = alyx 正在说话的NPC的targetname。 health = 75 正在说话的NPC的生命值。 healthfrac = 0.9375 正在说话的NPC的生命值与它最大生命值的比值(默认 npc_alyx 的最大生命值是80)。 skill.cfg = 1 目前的 skill 级别。 timesinceseenplayer = 0.090000 从正在说话的NPC看见玩家时过去的时间。 distancetoenemy = 312.639679 正在说话的NPC距它当前的敌人的距离。 activity = ACT_RUN 正在说话的NPC正在执行的动画活动。 npcstate = [NPCState::Combat] 正在说话的NPC的 AI 状态。 enemy = npc_combine_s 正在说话的NPC的当前敌人的classname。 speed = 79.235 正在说话的NPC的移动速度。 weapon = weapon_alyxgun 正在说话的NPC握着的武器。 distancetoplayer = 211.240692 正在说话的NPC距玩家的距离。 seeplayer = 1 正在说话的NPC能否看见玩家。 seenbyplayer = 0 正在说话的NPC是否在玩家的视野内。 readiness = agitated 正在说话的NPC的readiness级别。 playerhealth = 100 玩家目前生命值。 playerhealthfrac = 1.000 玩家目前生命值的生命值与它最大生命值的比值。 playerweapon = weapon_shotgun 玩家握着的武器。 playeractivity = ACT_WALK 玩家正在执行的动画活动。 playerspeed = 0.000 玩家的移动速度。
这个概念默认没有任何修改器。上列表中所有的标准都是通用的且是为每个概念收集的。
上面列表中的标准可以通过规则的标准列表进行检查,并用于决定将哪个响应组用于所需概念。比如说:
- “enemy(敌人)”标准可用于选择对 TLK_ENEMY_DEAD 概念的正确响应。艾利克斯(Alyx)可以说“I took care of that soldier! 我解决了那个士兵!”或“I took care of that headcrab! 我解决了那个猎头蟹!”而不是做一般性说话。
- 如果她(艾利克斯)在击杀敌人时生命值小于20%,healthfrac 字段(field)可用于选择“Phew, that was close! 唷,太险了!”一句。
- distancetoenemy 字段可用于她在远距离或近距离杀死敌人时选择不同的语句。比如说“Those guys are scary when they get that close! 那些人靠得那么近就很可怕!”或“It's not easy hitting 'em at that range. 在那个范围内击中他们并不容易。”
尽管上面列出的标准是一般性的而不是针对概念而设置的,但标准在不同的情况下总是会有所不同,并且可能并不总是可用(比如说不在战斗中的 NPC 不会有 enemy 或 distancetoenemy 标准)。此外,地图制作者可以为特定的 NPC 或游戏中的所有 NPC 附加额外的标准。有关详细信息,请参阅 #Response contexts。
规则标准
根据规则是有一个标准列表的,这些标准会根据人物的标准集进行测试。当规则得到分数(point)时,会根据给定数据检查每个标准,并且该规则会收到成功匹配的标准的分数(point)。用于规则的标准获得分数由标准的权重决定。
标准在脚本文档中的定义格式(见下文):
criterion <criterion name> <key to check> <desired value> <optional: weight X> <optional: required>
参数如下:
- criterion name
- 不得与现有标准匹配。
- key to check
- 该标准将检查的人物标准集中的键值。
- desired value
- 条件集中的键的请求值。以下是使用格式:
- Numeric values: "0", "1", or "100".
- Inverse Numeric values: "!=0" (如果值不等于 0 则匹配)
- String value: "npc_alyx", "weapon_alyxgun", or "npc_combine_s".
- Enumerated value: "[NPCState::Combat]".
- 范围(Ranges):
- ">0" : 如果值大于 0,则匹配
- "<=0.5" : 如果值小于或等于 0.5,则匹配
- ">10,<=50" : 如果值大于 10 且小于或等于 50,则匹配
- ">0,<[NPCState::Alert] : 如果值大于 0 且小于 NPCState::Alert 的枚举值,则匹配。
注意:这默认不支持通配符。
- weight X
- 这是一个可选参数,其中 X 是该标准在匹配时值所得的分数(point)。如果未指定,则标准默认为 1 point。
- required
- 这是一个可选参数,要想使用rules containing,则得需要此标准。如果所需标准未成功匹配,则包含它的规则所得分数会为0并立即跳过。大多数标准都使用此参数。
关于半条命2的一些实例:
- 下面的例子中定义了一个名为PlayerNear的标准 (这是用于检查与确保玩家在以对话NPC以中心500格的范围内)
criterion "PlayerNear" "distancetoplayer" "<500" required
- 下面的例子中则定义了一个名为IsCitizen的标准 (这是用于检查与确保说话的NPC是
npc_citizen
)
criterion "IsCitizen" "classname" "npc_citizen" "required"
- 下面的例中定义了一个名为IsMap_d3_c17_12的标准,(这是用于检查与确保游戏当在d3_c17_12.bsp上。这也有助于使一张地图中的所有citizen说出与其他地图不同的台词)
criterion "IsMap_d3_c17_12" "map" "d3_c17_12" "required"
- 下面的例子中定义了一个名为IsBob的标准,(这是用于检查与确保说话的NPC的目标名称为“bob”。这是游戏中独特的citizen,这个标准很容易让他说出特定的台词)
criterion "IsBob" "targetname" "bob" required

规则
一个规则 (Rule) 包括标准列表和至少一个响应组。该规则为成功匹配演讲者的标准集的每个标准接收分数。得分最高的规则将指向其中一个响应组,该响应组用于确定NPC将使用的确切语音。规则是在脚本文件中定义的(见下文)。使用以下格式:
rule <rule name> { criteria <criterion name 1> [optional: <criterion name 2> <criterion name 3> etc.] response <response group name> [optional: <response group name 2> etc.] [optional: matchonce] [optional: applyContext <data>] }
参数如下:
- rule name : 规则的名称。不得与现有规则匹配。
- criteria : 规则应根据的标准集进行评分。
- response : 如果此规则得分最高,则应选择的响应组列表。
- matchonce : 一个可选参数,如果指定了该参数,则一旦选择该规则一次,该参数将导致该规则被停用。
- applyContext : 应用响应上下文的可选参数。有关详细信息,请参阅#响应上下文。
例如,以下文本定义了一个名为 CitizenTalkStare 的规则。ConceptTalkStare 是一个检查说话的 NPC 想说的概念是否为“TLK_STARE”的标准。IsCitizen 是一个检查说话的 NPC 是否是公民的标准。NPCIdle 是一个检查以确保 NPC 的状态为“NPCState::Idle”的标准。如果此规则得分最高,则将使用的响应组为 CitizenTalkStare。
rule CitizenTalkStare { criteria ConceptTalkStare IsCitizen NPCIdle response CitizenTalkStare }
请注意,规则名称和响应组名称可以是相同的,因为规则名称只需要在规则之间唯一,而响应组名称只需在组之间唯一。
响应组
A response group contains a set of possible responses, along with some optional data that defines how the responses should be used. When a response group is chosen by a rule, a response is chosen from the list and given back to the speaker to use. Response groups are defined inside the script files (see below). The following format is used:
response <response name> { [optional: permitrepeats] [optional: sequential] [optional: norepeat] <response type> <response> <optional: ...> <response type> <response> <optional: ...> <response type> <response> <optional: ...> }
The response group parameters are as follows:
- permitrepeats : If specified, responses in this group are allowed to repeat. If unspecified, the default behavior is to use all responses in the list before repeating any,
- sequential : If specified, responses will be used in the order they're listed in the group. If unspecified, the default behavior is to randomly choose responses from the list.
- norepeat : If specified, once all responses in the list have been played, the response group will be disabled. Any rules that choose this response group will return no response to the speaker.
响应
响应是由系统选择然后由说话者使用的实际响应。响应组可以根据需要列出尽可能多的响应,每个响应都应该以下类型中的一个:
- speak : 该响应是一个音效脚本或原始音频文件。
- sentence : 该响应是
sentences.txt
中的一段句子 - scene : 该响应是一个
.vcd
文件。详见场景编排的实现。 - response : The response is a reference to another response group which should be selected instead.
- print : 该响应是一段在
developer 1
模式下位于说话者位置生成的占位用响应。 - entityio (存在于自
以来)[证实] : 该响应是一个I/O事件 ,它以说话者作为触发者(activator)对特定的实体触发IO事件,格式为
entityio "<name> <input> <param>"
。这与Followup Responses不一样,后者将会在下文进行介绍
Post-response delay parameters
Each response supports a variety of optional parameters.
- nodelay : After the response has finished, the speaker is allowed to speak again immediately.
- defaultdelay : After the response has finished, the speaker won't be allowed to speak for a random amount, between 2.8 & 3.2 seconds.
- delay X : After the response has finished, the speaker won't be allowed to speak for X seconds. X can also be a range, e.g. "5.5,7.5"
- weapondelay X : When the response starts, the speaker will not fire their weapon for X seconds. Only available on HL2 NPC allies by default.
- speakonce : Prevents the response from being used more than once.
- odds X : If specified, then when this response is chosen, there is a chance the speaker will say nothing instead of saying the response. The odds is a (0-100)% the chance of cancelling the response, meaning an odds of 25 gives a 25% chance of saying nothing.
- respeakdelay : If specified, this response may not be used unless the concept hasn't been spoken in at least X seconds. X can also be a range, e.g. "5.5,7.5"
- soundlevel : If specified, this soundlevel should be used for the response instead of the default SNDLVL_TALKING.
- displayfirst : If specified, this response should be used first (ignores the weight parameter).
- displaylast : If specified, this response should be used last (ignores the weight parameter).
- weight : If specified, used to weight the selection of the responses in the list. By default, all responses have a weight of 1. Please note that responses won't be repeated until all of the other responses have been chosen, meaning once all of the high-weight responses are chosen, the system will only count the responses of less weight. This can be counteracted with permitrepeats.
- noscene : Stops the Response System from creating an auto-generated scene for a speak response.
- stop_on_nonidle : When the response is spoken, stops the scene when the NPC enters a non-idle state. In Source 2013, only works in HL2 episodic mods on scene responses.
- predelay : When the response is chosen, it won't actually be spoken until X seconds have passed. Only works on scene responses. X can also be a range, e.g. "5.5,7.5"
For example, the following response group is used by citizens to respond to the TLK_STARE concept. Citizens will use the responses in the list in the order they're listed (due to the sequential parameter). Each response, when chosen, stops the NPC from talking for a random amount of time between 10 & 20 seconds.
response "CitizenTalkStare" { sequential scene "scenes/npc/$gender01/doingsomething.vcd" delay "10,20" scene "scenes/npc/$gender01/getgoingsoon.vcd" delay "10,20" scene "scenes/npc/$gender01/waitingsomebody.vcd" delay "10,20" }
脚本文件
/scripts/talker/response_rules.txt
是基本脚本文件,包含响应规则系统使用的所有条件、规则和响应组。该文件还可以包括使用 #include 关键字的其他文件,这允许你根据 NPC、地图等干净地划分规则。请注意,一些实体,如 env_speaker
,指定了自己的脚本文件,其中包含实体要使用的条件、规则和响应组的子集。有关火车站终端公告使用的示例,请参见 scripts/talker/terminal_pa.txt
。
响应上下文
响应上下文类似于地图作者设置的标准。它们通过“Response Contexts”(ResponseContext
)键值或AddContext
输入进行设置,格式如下:key:value;key:value;key:value;
……其中key是“键”,value是“值”。

Followup responses
求生之路 introduced "followups", events which occur following the response. They are triggered as response parameters similar to odds, predelay, etc.

- fire : Fires an input through the I/O system with the speaker as the activator and caller. Format is
fire <target> <input> <delay>
. Does not support parameters. - then : Causes another response to be dispatched on an entity. Format is
then <target> <concept> <response contexts> <delay>
. Used for making characters dynamically respond to each other.

A few unique targetnames can be used:
- self : The entity speaking the response.
- subject : Uses the entity name found in the speaker's "Subject" context, if it exists. The game sets this for
info_remarkable
responses. - from : Uses the entity name found in the speaker's "From" context, if it exists. The game sets this to the name of the previous followup respondent, allowing followups to bounce back and forth.
- any : Dispatched to any valid respondent within the range stored in
rr_followup_maxdist
(1800 by default). - all : Dispatched to all valid respondents within the range stored in
rr_followup_maxdist
(1800 by default).

Example from Left 4 Dead 2 incoach.txt
:
Response _c1m4startelevator4bCoach { scene "scenes/Coach/WorldC1M4B01.vcd" then mechanic _c1m4startelevator5a foo:0 -2.313 //Son, you got a DEAL. scene "scenes/Coach/WorldC1M4B02.vcd" then mechanic _c1m4startelevator5a foo:0 -5.790 //Ha HA! All the way to New Orleans! Baby, that sounds like a PLAN. scene "scenes/Coach/WorldC1M4B05.vcd" then mechanic _c1m4startelevator5b foo:0 -6.334 //Normally I wouldn't do this. But in these circumstances, I think Mr. Gibbs, Jr. ain't gonna mind. scene "scenes/Coach/WorldC1M4B10.vcd" then mechanic _c1m4startelevator5b foo:0 -2.685 //Forgive us Jimmy, but we need your car. }
Advanced response rules usage
The Response System can be used in many advanced and specialized ways. Here's a few tips and tricks for advanced usage of the system:
- Firing custom responses with
DispatchResponse
- Absolutely anything can be passed to an NPC as a response concept, so long as it is to be found somewhere in the script files. You aren't limited to the AI's predefined TLK_* concepts at all.
- For example, you might want NPCs from a onlooking group to speak congratulatory response concepts when a player successfully solves part of a puzzle.
- Player allies in HL2 also have a
SpeakResponseConcept
input with more advanced handling and conditions.
See the response scripts for Alyx in EP1/EP2 or the response scripts for the L4D survivors for more examples of advanced response system usage.
调试

注意
- 要能够使用响应系统,必须使用由 CAI_BaseActor 类派生的 NPC。
- 参见
scripts/talker/npc_*.txt
文件以获取特定 NPC 响应规则的示例。 scripts/talker/response_rules.txt
是响应规则系统的列表。如果为新 NPC 添加新的响应规则脚本文件,请确保在scripts/talker/response_rules.txt
文件的末尾通过 #include 语句包括该文件。
另请参阅
(译注:以下链接链接到英文页面)
外部链接
- Elan Ruskin 在 GDC 2012 上的“用于上下文对话和游戏逻辑的规则数据库”演示文稿,它描述了响应系统及其演变。 (幻灯片:[1])
- Two Bots One Wrench: Environmentally Responsive Speech Video, 演示视频 #1
- Two Bots One Wrench: Memory And Context, 演示视频 #2
- Two Bots One Wrench: Conversation, 演示视频 #3