Zh/Response System

The Response System（响应系统）用于决定 NPC（或玩家）在想要说什么或是否说什么时应该使用哪种语言（和/或动画）。
大多数响应系统用户都是为了响应游戏中发生的事件而发言。语音可以从代码或地图的输入中触发。触发语音涉及响应概念，它对应于系统中的响应。概念是用于特定事件的语音类别，例如注意到危险 (TLK_DANGER) 或看到敌人 (TLK_STARTCOMBAT)。虽然一个概念可以单独用于基本和简单的对话，但语音也可以通过各种标准来触发，这些标准描述了与该概念和/或说话者的当前状态和周围世界相关的事物。这允许复杂的对话树涉及不同情况下的不同台词。
例如，当玩家杀死敌方 NPC 时，HL2 情节系列中的会说出TLK_PLAYER_KILLED_NPC概念。这个概念对于与 NPC 相关的条件或它被杀死的方式有额外的标准。如果玩家通过爆头杀死了 NPC，则响应系统将选择需要该标准的响应，这通常会导致艾利克斯称赞玩家的射击或评价这是爆头。
响应系统通常使用scripts/talker目录中的脚本。当它接收到一个概念及其标准时，它会在响应脚本中搜索与其概念和标准相匹配的规则(rules)。当它找到一个时，它会使用该规则的响应(response)或响应选择。响应可以是、，甚至是实例化的。
尽管有这些优点，但大多数 NPC 默认不使用此系统。例如，默认只使用声音脚本，不使用响应系统。
以下是使用响应系统的地方的快速列表：
* 在 中，大多数玩家同伴 NPC（、等）使用 NPC 语音响应系统。
* 在 中，所有兵种(all classes)都使用响应系统来进行语音命令和嘲讽响应。
* 在 中，所有幸存者都使用响应系统来响应玩家在世界上的行为和事件。
* 在 中，Atlas 和 P-body（合作模式下的机器人）都使用响应系统来进行嘲讽手势。
* 在 中，所有玩家角色都使用响应系统并跟踪已使用的响应。
* 在 中，参见 。
== 目的 ==
创建响应系统，以便 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 ? 敌人是 吗？
::*"You're not getting MY head! 你没有得到我的头！"
::*"I hate headcrabs! 我恨猎头蟹！"
你还可以使用标准组合。
:*Did I use a shotgun against a really close to me? 我用霰弹枪对付离我很近的 吗？
::*"You got too close to my shotgun, headcrab! 你离我的霰弹枪太近了，猎头蟹！"
你还可以控制单独的行，使其在一定时间内不重复，或者根本不重复。
== 构成 ==
响应系统由四个核心部分组成：Concepts(概念), Criteria(标准), Rules(规则)，和 Response Groups(响应组)。它们的使用方法如下：
#一个 NPC 为一个语音概念(Concept)请求一行语音。
#*例如，假设我们的 NPC 为TLK_SHOT概念请求一行语音。 NPC 被敌人击中时会说出这个概念。
#NPC 收集了一堆反映 NPC 当前状态的标准(Criteria)以及有关世界状态的其他相关数据。许多概念也有修饰符(Modifier)，这是特定概念特有的标准，通常反映事件本身。
#*当 NPC 为TLK_SHOT概念请求一行时，游戏会将 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或输入来调用概念。
以下是半条命 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)是一组条件，其中包含与对话者的当前状态相关的数据，以及每当请求尝试进行对话时概念的情况。它可以被解释为一组 。以下是一个由创建的标准集的例子，她试图说话因为她刚刚杀死了当前的敌人：
 concept = TLK_ENEMY_DEAD NPC 尝试说话的概念。
 map = d3_c17_07 当前地图的名称。
 classname = npc_alyx 正在说话的NPC的。
 name = alyx 正在说话的NPC的。
 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的当前敌人的。
 speed = 79.235 正在说话的NPC的移动速度。
 weapon = weapon_alyxgun 正在说话的NPC握着的武器。
 distancetoplayer = 211.240692 正在说话的NPC距玩家的距离。
 seeplayer = 1 正在说话的NPC能否看见玩家。
 seenbyplayer = 0 正在说话的NPC是否在玩家的视野内。
 readiness = agitated 正在说话的NPC的级别。
 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".
:* 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是)
: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 ]
}
参数如下：
*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
}
请注意，规则名称和响应组名称可以是相同的，因为规则名称只需要在规则之间唯一，而响应组名称只需在组之间唯一。
=== 响应组 ===
一个响应组包含一组可能的响应，以及一些可选的数据，用于定义如何使用这些响应。当规则选择了一个响应组时，从列表中选择一个响应并交给发言者使用。
响应组是在脚本文件内定义的（见下文）。使用以下格式：
response <response name>
{
 [optional: permitrepeats]
 [optional: sequential]	 
 [optional: norepeat]		 
 <response type> <response> <optional: ...>
 <response type> <response> <optional: ...>
 <response type> <response> <optional: ...>
}
响应组的参数如下：
* permitrepeats：如果指定，该组中的响应可以重复。如果未指定，默认行为是在重复任何响应之前使用列表中的所有响应。
* sequential：如果指定，将按照在组中列出的顺序使用响应。如果未指定，默认行为是从列表中随机选择响应。
* norepeat：如果指定，一旦播放了列表中的所有响应，响应组将被禁用。选择该响应组的任何规则都将对发言者返回无响应。
==== 响应 ====
响应是由系统选择然后由说话者使用的实际响应。响应组可以根据需要列出尽可能多的响应，每个响应都应该以下类型中的一个：
*speak : 该响应是一个或原始音频文件。
*sentence : 该响应是中的一段句子
*scene : 该响应是一个.vcd文件。详见。
*response : The response is a reference to another response group which should be selected instead.
*print : 该响应是一段在developer 1模式下位于说话者位置生成的占位用响应。
*entityio : 该响应是一个 ，它以说话者作为触发者（activator）对特定的实体触发IO事件，格式为entityio "<name> <input> <param>"。这与Followup Responses不一样，后者将会在下文进行介绍
==== 响应后延迟参数 ====
每个响应都支持各种可选参数。
* nodelay：在响应完成后，允许发言者立即再次发言。
* defaultdelay：在响应完成后，发言者将在2.8到3.2秒之间的随机时间内不被允许发言。
* delay X：在响应完成后，发言者将在X秒内不被允许发言。X也可以是一个范围，例如"5.5,7.5"。
* weapondelay X：当响应开始时，发言者将在X秒内不能开火武器。默认仅在HL2 NPC盟友上可用。
* speakonce：防止重复使用响应。
* odds X：如果指定，选择此响应时，发言者有可能保持沉默而不是说出响应。odds 是取消响应的机会（0-100％），例如25表示不说话的几率为25%。
* respeakdelay：如果指定，除非概念在至少X秒内没有被说过，否则不能使用此响应。X也可以是一个范围，例如"5.5,7.5"。
* soundlevel：如果指定，应该在响应中使用该声音级别，而不是默认的SNDLVL_TALKING。
* displayfirst：如果指定，应首先使用此响应（忽略权重参数）。
* displaylast：如果指定，应最后使用此响应（忽略权重参数）。
* weight：如果指定，用于加权选择列表中的响应。默认情况下，所有响应的权重均为1。请注意，响应在选择了所有其他响应之前不会重复，这意味着一旦选择了所有高权重的响应，系统将只计算权重较低的响应。可以通过permitrepeats来抵消这一点。
* noscene：阻止响应系统为speak响应创建自动生成的场景。
* stop_on_nonidle：当响应被说出时，当NPC进入非空闲状态时停止场景。在Source 2013中，仅在HL2剧集模组中的scene响应上起作用。
* predelay：当选择响应时，直到过了X秒才会实际说出来。仅在scene响应上起作用。X也可以是一个范围，例如"5.5,7.5"。
例如，下面的响应组由用于回应TLK_STARE概念。市民将按照它们在列表中的顺序使用响应（由于 sequential 参数）。每个被选择的响应都会停止NPC发言一段随机时间，介于10到20秒之间。
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"
}
== 脚本文件 ==
 是基本脚本文件，包含响应规则系统使用的所有条件、规则和响应组。该文件还可以包括使用 #include 关键字的其他文件，这允许你根据 NPC、地图等干净地划分规则。请注意，一些实体，如 ，指定了自己的脚本文件，其中包含实体要使用的条件、规则和响应组的子集。有关火车站终端公告使用的示例，请参见 。
== 响应上下文 ==
响应上下文类似于地图作者设置的标准。它们通过“Response Contexts”（ResponseContext）键值或AddContext输入进行设置，格式如下：key:value;key:value;key:value;……其中key是“键”，value是“值”。
== 后续响应 ==
引入了“后续响应”，即在响应后发生的事件。它们被触发为响应参数，类似于odds、predelay等。
* fire：通过I/O系统使用发言者作为激活器和调用者触发一个输入。格式为fire <target> <input> <delay>。不支持参数。
* then：导致在实体上分派另一个响应。格式为then <target> <concept> <response contexts> <delay>。用于使角色动态地相互响应。
可以使用一些独特的目标名称：
* self：发言响应的实体。
* subject：使用发言者“Subject”上下文中找到的实体名称（如果存在）。游戏为响应设置了这个。
* from：使用发言者“From”上下文中找到的实体名称（如果存在）。游戏将其设置为上一个后续响应者的名称，允许后续响应来回弹跳。
* any：分派到rr_followup_maxdist（默认为1800）中存储的任何有效响应者。
* all：分派到rr_followup_maxdist（默认为1800）中存储的所有有效响应者。
来自Left 4 Dead 2中的示例coach.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.
}
== 高级响应规则的使用 ==
响应系统可以以许多高级和专业的方式使用。以下是一些用于系统高级用途的技巧和窍门：
;使用DispatchResponse触发自定义响应
:实际上，任何可以在脚本文件中找到的响应概念都可以传递给NPC，只要它不限于AI预定义的TLK_*概念。
:例如，您可能希望在玩家成功解决谜题的一部分时，旁观组中的NPC发表祝贺的响应概念。
:HL2中的玩家盟友还具有更高级处理和条件的SpeakResponseConcept输入。
查看EP1/EP2中Alyx的响应脚本或L4D幸存者的响应脚本，以获取更多高级响应系统使用示例。
== 调试 ==
== 注意 ==
* 要能够使用响应系统，必须使用由 类派生的 NPC。
* 参见scripts/talker/npc_*.txt文件以获取特定 NPC 响应规则的示例。
* scripts/talker/response_rules.txt是响应规则系统的列表。如果为新 NPC 添加新的响应规则脚本文件，请确保在scripts/talker/response_rules.txt文件的末尾通过 #include 语句包括该文件。
== 另请参阅 ==
(译注:以下链接链接到英文页面)
* 
* 
* 
* 
* 
== 外部链接 ==
* Elan Ruskin 在 GDC 2012 上的“用于上下文对话和游戏逻辑的规则数据库”演示文稿，它描述了响应系统及其演变。 (幻灯片：)
* Two Bots One Wrench: Environmentally Responsive Speech Video, 演示视频 #1
* Two Bots One Wrench: Memory And Context, 演示视频 #2
* Two Bots One Wrench: Conversation, 演示视频 #3
Category:AI Programming