Zh/VScript Fundamentals: Difference between revisions
Dazainerau (talk | contribs) (→术语表) |
m (Multipage removal) |
||
(19 intermediate revisions by 6 users not shown) | |||
Line 1: | Line 1: | ||
{{ | {{LanguageBar|title = VScript 基本原理}} | ||
|title=VScript 基本原理 | |||
}} | |||
本页面内容由[//steamcommunity.com/id/utagawashii/ Dazai Nerau]译自{{L|Vscript_Fundamentals|英文版页面}}. 欢迎任何人补充新内容或者修改其中的错误. | |||
这篇文章旨在阐述有关 | |||
这篇文章旨在阐述有关{{L|VScript}}脚本的基本概念以及使用方法. | |||
== 表和脚本域 == | == 表和脚本域 == | ||
Line 21: | Line 18: | ||
== 实体脚本 == | == 实体脚本 == | ||
''详阅文章 | ''详阅文章{{L|Entity Scripts}}'' | ||
Line 28: | Line 25: | ||
添加一个脚本到一个服务器端实体的<code>vscripts</code> (实体脚本)键值将使得该脚本作为实体脚本被加载. 该脚本将在实体产生后被自动执行, 并加载到一个脚本域内,该脚本域由唯一标识符+实体名称或种类名组成; <code>_<unique ID>_<entity name></code>, 并被放在根表中. 如果一个实体没有任何脚本被执行, 可以通过 <code>CBaseEntity::ValidateScriptScope()</code> 方法来手动创建一个脚本域. | 添加一个脚本到一个服务器端实体的<code>vscripts</code> (实体脚本)键值将使得该脚本作为实体脚本被加载. 该脚本将在实体产生后被自动执行, 并加载到一个脚本域内,该脚本域由唯一标识符+实体名称或种类名组成; <code>_<unique ID>_<entity name></code>, 并被放在根表中. 如果一个实体没有任何脚本被执行, 可以通过 <code>CBaseEntity::ValidateScriptScope()</code> 方法来手动创建一个脚本域. | ||
{{ | {{Note|起源2 有公共脚本域和私有脚本域之分. 实体脚本会被加载到私有脚本域之中.}} | ||
可以通过在<code>vscripts</code> 键值中指定多个脚本来加载其他脚本, 或者使用 <code>RunScriptFile</code> 输入. | 可以通过在<code>vscripts</code> 键值中指定多个脚本来加载其他脚本, 或者使用 <code>RunScriptFile</code> 输入. 在同一实体上运行的所有脚本将加载到相同的脚本域, 覆盖任何相同的变量和函数. | ||
Think函数可以被设置于<code>thinkfunction</code> | Think函数可以被设置于<code>thinkfunction</code> {{L|Keyvalue|键值}}中或者由 <code>AddThinkToEnt()</code> 函数来添加, 被指定的函数会以0.1秒一次的频率被调用. 函数也可以通过I/O系统的 <code>RunScriptCode ''function_name(argument, ...)''</code>和<code>RunScriptFunction ''function_name''</code>这两个输入来手动调用。 | ||
实体脚本有 <code>self</code> (起源1) 或者 <code>thisEntity</code> (起源2)这两个能够引用它自身实体的脚本句柄. | 实体脚本有 <code>self</code> (起源1) 或者 <code>thisEntity</code> (起源2)这两个能够引用它自身实体的脚本句柄. | ||
=== | === 预定义的钩子函数 === | ||
实体能够从C++ | 实体能够从C++端(游戏的底层代码)中调用其脚本域的函数, 我们称之为钩子函数(Hook Functions), 简称钩子.公共实体类被写入了一些预定义的函数调用(predefined function calls),以在特定事件发生时允许脚本执行代码. 例如, 在一个实体脚本中创建一个名为 <code>Precache()</code> 的函数将在实体生成后立即调用该函数, 以允许脚本预缓存自定义的资源. 这些钩子函数不需要注册,并且只要名字没写错的话就会在合适的时候被调用. 请参阅游戏的API文档以了解每个类可用的钩子函数. | ||
{{todo|Does Source 2 implement this?}} | {{todo|Does Source 2 implement this?}} | ||
== | == {{L|Inputs_and_Outputs|I/O系统}}交互 == | ||
=== 触发输出 === | === 触发输出 === | ||
如果游戏的API支持的话, 脚本可以通过<code>EntFire()</code> 和 <code>DoEntFire()</code> 来对地图上的实体触发输出. 可能还有更多类似的函数, 取决于游戏. (译者注:例如CSGO还有<code>EntFireByHandle()</code>这一函数) | 如果游戏的API支持的话, 脚本可以通过<code>EntFire()</code> 和 <code>DoEntFire()</code> 来对地图上的实体触发输出. 可能还有更多类似的函数, 取决于游戏. (译者注:例如CSGO还有<code>EntFireByHandle()</code>这一函数) | ||
如果函数支持''<code>activator</code>'' 和 ''<code>caller</code>'' 的参数, 就可以使用"!self" 或者 "!activator" 关键字将输出发送给实体, | 如果函数支持''<code>activator</code>'' 和 ''<code>caller</code>'' 的参数, 就可以使用"!self" 或者 "!activator" 关键字将输出发送给实体,而不必知道其targetname,前提是句柄有效. | ||
使用<code>RunScriptCode</code> 输入可以通过I/O系统执行任何VScript代码. 代码将在被调用实体的脚本域中运行. | 使用<code>RunScriptCode</code> 输入可以通过I/O系统执行任何VScript代码. 代码将在被调用实体的脚本域中运行. | ||
{{ | {{Warning|在Hammer的输出中绝对不要使用双引号, 因为这样做会破坏地图文件. 这意味着字符串无法通过 <code>RunScriptCode</code> 被传递.}} | ||
Line 82: | Line 79: | ||
当实体接收到了输入, 游戏代码会尝试在接收实体的实体脚本中调用格式为 <code>Input<Name of Input>()</code> 的脚本函数. 如果该函数返回 <code>false</code>, 输入就不会被触发.(译者注:这意味着输入可以被打断) | 当实体接收到了输入, 游戏代码会尝试在接收实体的实体脚本中调用格式为 <code>Input<Name of Input>()</code> 的脚本函数. 如果该函数返回 <code>false</code>, 输入就不会被触发.(译者注:这意味着输入可以被打断) | ||
{{ | {{Note|输入名称区分大小写, 并使用骆驼拼写法格式.}} | ||
与关联实体输出的函数一样, <code>activator</code> 和 <code>caller</code> 这两个变量会被设置为激活实体和调用实体. | 与关联实体输出的函数一样, <code>activator</code> 和 <code>caller</code> 这两个变量会被设置为激活实体和调用实体. | ||
Line 111: | Line 108: | ||
== 术语表 == | == 术语表 == | ||
;Entity handle(实体句柄,也称为EHANDLE) | ;Entity handle(实体句柄,也称为EHANDLE) | ||
:一个能够传递C++ 的[[CHandle#EHANDLE|EHANDLE]] | :一个能够传递C++ 的[[CHandle#EHANDLE|EHANDLE]]的不透明的实体引用(类似于指针). 只能与其他句柄进行比较或传递给期望需要用到它们的API函数, 很少被使用到. | ||
;Script handle(脚本句柄) | ;Script handle(脚本句柄) | ||
:具有C++ | :具有C++实体对象的访问器和修改器的实体实例. 在C++代码中表示为HSCRIPT typedef. | ||
;Script scope(脚本域) | ;Script scope(脚本域) | ||
: | :脚本的执行上下文(Execution context).它是一个包含了VScript的变量,函数和类的表. | ||
== API参考 == | == API参考 == | ||
{{L|Left 4 Dead 2/Script Functions|L4D2脚本函数列表}} | |||
{{L|List of Portal 2 Script Functions|Portal2脚本函数列表}} | |||
{{L|List_of_Counter-Strike:_Global_Offensive_Script_Functions|CSGO脚本函数列表}} | |||
{{L|List of Contagion Script Functions|Contagion脚本函数列表}} | |||
{{L|Dota_2_Workshop_Tools/Scripting/API|Dota2脚本API}} | |||
== 推荐阅读 == | == 推荐阅读 == | ||
* | * {{L|VScript}} | ||
* | * {{L|Squirrel}} | ||
* | * {{L|Dota_2_Workshop_Tools/Scripting|Dota2 脚本}} | ||
* | * {{L|L4D2_Vscripts|求生之路2脚本}} | ||
* {{L|L4D2_Director_Scripts|求生之路2导演脚本}} | |||
{{ACategory|Scripting}} | |||
{{ACategory|Tutorials}} |
Latest revision as of 12:04, 12 July 2024
本页面内容由Dazai Nerau译自英文版页面 . 欢迎任何人补充新内容或者修改其中的错误.
这篇文章旨在阐述有关VScript 脚本的基本概念以及使用方法.
表和脚本域
脚本环境由关联数组或者 表 构成 , 彼此之间互相嵌套.
脚本被加载时, 它就会被放置在一个被称为它的脚本域的表中, 然后脚本中的任何代码都能够被执行. 执行完代码后,所有的变量、函数以及类都会被保存在脚本域中.
脚本句柄
与游戏中的实体进行交互是经由 脚本句柄 得以实现的, 脚本句柄是一种用于引用特定实体的对象. 脚本句柄包含了访问器(accessor)和修改器(mutator)方法以读取和修改实体的属性. 方法的可用性取决于游戏和实体的类型. 请阅读各种游戏的脚本的API参考以了解详情。
所有正处于游戏中的服务器端的实体都可以通过CEntities
类的对象Entities
来搜寻或者迭代。
实体脚本
详阅文章Entity Scripts
常见的VScript用法是使用实体脚本来扩充实体的功能.
添加一个脚本到一个服务器端实体的vscripts
(实体脚本)键值将使得该脚本作为实体脚本被加载. 该脚本将在实体产生后被自动执行, 并加载到一个脚本域内,该脚本域由唯一标识符+实体名称或种类名组成; _<unique ID>_<entity name>
, 并被放在根表中. 如果一个实体没有任何脚本被执行, 可以通过 CBaseEntity::ValidateScriptScope()
方法来手动创建一个脚本域.

可以通过在vscripts
键值中指定多个脚本来加载其他脚本, 或者使用 RunScriptFile
输入. 在同一实体上运行的所有脚本将加载到相同的脚本域, 覆盖任何相同的变量和函数.
Think函数可以被设置于thinkfunction
键值 中或者由 AddThinkToEnt()
函数来添加, 被指定的函数会以0.1秒一次的频率被调用. 函数也可以通过I/O系统的 RunScriptCode function_name(argument, ...)
和RunScriptFunction function_name
这两个输入来手动调用。
实体脚本有 self
(起源1) 或者 thisEntity
(起源2)这两个能够引用它自身实体的脚本句柄.
预定义的钩子函数
实体能够从C++端(游戏的底层代码)中调用其脚本域的函数, 我们称之为钩子函数(Hook Functions), 简称钩子.公共实体类被写入了一些预定义的函数调用(predefined function calls),以在特定事件发生时允许脚本执行代码. 例如, 在一个实体脚本中创建一个名为 Precache()
的函数将在实体生成后立即调用该函数, 以允许脚本预缓存自定义的资源. 这些钩子函数不需要注册,并且只要名字没写错的话就会在合适的时候被调用. 请参阅游戏的API文档以了解每个类可用的钩子函数.
I/O系统 交互
触发输出
如果游戏的API支持的话, 脚本可以通过EntFire()
和 DoEntFire()
来对地图上的实体触发输出. 可能还有更多类似的函数, 取决于游戏. (译者注:例如CSGO还有EntFireByHandle()
这一函数)
如果函数支持activator
和 caller
的参数, 就可以使用"!self" 或者 "!activator" 关键字将输出发送给实体,而不必知道其targetname,前提是句柄有效.
使用RunScriptCode
输入可以通过I/O系统执行任何VScript代码. 代码将在被调用实体的脚本域中运行.

RunScriptCode
被传递.
示范代码:
// 将拥有脚本域的实体的生命值设置成500
DoEntFire( "!self", "SetHealth", "500", 0, self, self )
关联输出
通过CBaseEntity::ConnectOutput(string output, string function)
方法, 实体输出可以关联到实体的脚本域中的函数.
在函数被调用期间, 变量 activator
和 caller
会被设置为I/O链中激活实体与调用实体的句柄, 从而可以轻松地找到是哪个玩家触发了某些内容.


示范代码:
// 当玩家使用时,点燃道具
function IgniteSelf()
{
DoEntFire( "!self", "Ignite", "", 0, self, self )
}
// 关联OnPlayerUse输出与脚本函数.
self.ConnectOutput( "OnPlayerUse", "IgniteSelf" )
输入钩子
当实体接收到了输入, 游戏代码会尝试在接收实体的实体脚本中调用格式为 Input<Name of Input>()
的脚本函数. 如果该函数返回 false
, 输入就不会被触发.(译者注:这意味着输入可以被打断)

与关联实体输出的函数一样, activator
和 caller
这两个变量会被设置为激活实体和调用实体.
示范代码:
// 一个门或者按钮的脚本, 监听Unlock输入
// 禁止门被打开, 除非尝试开门5次(进行了5次unlock输入).
UnlockCounter <- 5 // 实体脚本域的本地计数器
// 接收到Unlock输入时调用
function InputUnlock()
{
UnlockCounter--
if( UnlockCounter <= 0 )
{
return true // 允许解锁
}
return false // 打断输入
}
术语表
- Entity handle(实体句柄,也称为EHANDLE)
- 一个能够传递C++ 的EHANDLE的不透明的实体引用(类似于指针). 只能与其他句柄进行比较或传递给期望需要用到它们的API函数, 很少被使用到.
- Script handle(脚本句柄)
- 具有C++实体对象的访问器和修改器的实体实例. 在C++代码中表示为HSCRIPT typedef.
- Script scope(脚本域)
- 脚本的执行上下文(Execution context).它是一个包含了VScript的变量,函数和类的表.