函数(Variable)是由用户创建的标识符,能够接受输入参数,执行多条语句,并返回数值。
每个脚本至少需要拥有1个公共函数,由外界执行,否则无法编译脚本。
任何一个公共函数都可以成为脚本的入口,有了入口,才能执行运行时表达式(例如浮点数算术,或调用私有函数)。
一些需要在多个位置重复使用的代码也可以封装为函数,用不同的参数调节内部代码执行的细节。
声明、定义函数的方法有很多,
不使用关键字可以声明forward函数、脚本公共函数、插件私有函数。
使用关键字可以声明forward函数、native函数、脚本公共函数、插件私有函数、文件私有函数、备用函数。
forward函数、native函数的定义需要额外实现。
脚本公共函数、插件私有函数、文件私有函数、备用函数的定义与声明是同一条语句。
若定义main、entry函数,不允许有参数。
本接下来的声明、定义语句讲解中,()是必填项目,【】是可选项目。
声明语句的编写者称为A。
声明语句的引用者、定义语句的编写者称为B。
【维度声明】对于A和B无意义,因为B返回数组时,A只会得到B插件的数组地址。
AMX Mod X并未提供跨插件访问变量地址的功能,而且若是栈变量,已被销毁。
【插件状态声明】无意义,这是解析器的错误设计,B可以在定义语句中设定函数绑定B插件状态。
A若强行填写【插件状态声明】,会导致B的引用指令被警告,定义语句有可能报错。
A可以用AMX Mod X的dll模块或amxx插件创建、执行forward函数的转发器。这里不讨论dll模块,只讨论amxx插件。
A需引用amxmodx.inc文件,用CreateMultiForward、ExecuteForward函数创建、执行转发器。
函数声明通常应该被A写在inc文件中,供B引用,由B声明同名的公共函数,实现forward函数的定义。
当A执行转发器时,会按创建的规则执行每个B实现的公共函数。
若A不创建、执行转发器,forward函数声明没有存在意义。
总结:
主插件创建转发器,声明函数原形,其它插件定义函数,由主插件执行。
适用于制作简单的事件处理系统,跨插件通信。
声明语句、定义语句的编写者称为A。
声明语句的引用者、函数的使用者称为B。
【维度声明】对于A和B无意义,因为A返回数组时,B只会得到A插件的数组地址。
AMX Mod X并未提供跨插件访问变量地址的功能,而且若是栈变量,已被销毁。
若该函数的定义是AMX Mod X核心模块实现的,则可以返回数组。
A可以用AMX Mod X的dll模块或amxx插件定义native函数。这里不讨论dll模块,只讨论amxx插件。
A需编写公共函数定义语句,再引用amxmodx.inc文件,用register_native函数将公共函数与native函数互相绑定。
函数声明通常应该被A写在inc文件中,供B引用,B调用native函数时,实际调用的是A定义的公共函数。
若native函数未绑定一个公共函数,B调用时会触发报错:使用了未知函数。
总结:
主插件绑定native函数声明和公共函数定义,其它插件执行native函数等于执行公共函数。
适用于制作模块化插件,主插件负责实现复杂功能,其它插件只需执行主插件暴露的函数。
公共函数是外部模块、插件以及当前插件所有文件中皆可使用的函数。
公共函数通常是供dll模块或其它amxx插件使用,每个amxx插件都必须至少拥有1个公共函数。
当公共函数与fowrard函数同名时,该函数会被外部模块或插件调用。
当用户将公共函数名称告知其它模块或插件时,对方可调用该函数。
例如get_func_id、set_task、register_forward、menu_create等函数需要用户填写某个公共函数名称。
虽然不允许填写维度声明,但公共函数可以返回数组。
其它插件只会收到该数组在当前插件内的地址,毫无意义。
AMX Mod X并未提供跨插件访问变量地址的功能,而且若是栈变量,已被销毁。
若插件调用自己的公共函数,可以得到数组的副本。
公共函数的设计初衷是供外部调用,常需执行严格的参数检查、环境检查,不建议插件调用自己的公共函数。
当插件被暂停或停止,外部模块或插件无法调用其公共函数。
外部可以通过猜测函数id或不小心调用公共函数,导致难以预料的事情发生。
因此,若无必要,尽量用私有函数代替公共函数。
公共函数与伪公共变量不同,公共函数拥有者是插件,两个插件的同名公共函数并不是同一个公共函数。
伪公共变量的拥有者是AMX Mod X插件平台,两个插件的同名伪公共变量,会是同一个变量。
文件私有函数通常被称呼为静态函数,仅在当前文件可用。
例如,源码A引用了源码B(inc、sma、pawn等等),源码B内部声明了静态函数,则A或外部模块/插件无法调用这个函数。
虽然不允许填写维度声明,但静态函数可以返回数组。
毕竟这是用户自定义、自用的,所以肯定知道返回值的维度和尺寸,维度声明也就不重要了。
作为私有函数,只要用户确定参数来源安全,即可省略运行时参数检查,提高代码效率。
若无跨文件、跨插件访问的需求,应该尽量使用静态函数代替其它类型的函数。
若函数未被使用,编译时会触发警告。
与文件私有函数类似,但多了一个备用特性。
若该函数未被使用,编译器不会检查函数内部语法错误,不会触发未使用警告,直接舍弃。
备用函数的设计初衷是按需编译,方便函数库(inc文件)的设计,确保未使用的函数不会增加插件的文件体积。
插件私有函数,仅在当前插件相关的文件中可用。
例如,源码A引用源码B,源码B引用源码C,源码B内部声明了插件私有函数,则A、B、C皆可调用此函数。
虽然不允许填写维度声明,但插件私有函数可以返回数组。
毕竟这是用户自定义、自用的,所以肯定知道返回值的维度和尺寸,维度声明也就不重要了。
作为私有函数,只要用户确定参数来源安全,即可省略运行时参数检查,提高代码效率。
若有跨文件访问、无跨插件访问的需求,应该尽量使用插件私有函数代替其它类型的函数。
若函数未被使用,编译时会触发警告。
与插件私有函数类似,但多了一个备用特性。
若该函数未被使用,编译器不会检查函数内部语法错误,不会触发未使用警告,直接舍弃。
备用函数的设计初衷是按需编译,方便函数库(inc文件)的设计,确保未使用的函数不会增加插件的文件体积。
【值类型标签】语法拆解:
【值类型标签】用于设置函数标签,约束返回值标签。
若不填写,则默认使用_:标签。
因此,下面两行代码是相同的:
static _:func() return 32;
static func() return 32;
若填写的值类型标签不存在,编译器会自动创建这个标签。
当函数与拥有不同标签的表达式做运算、或与返回值标签不同时,通常会触发编译器警告:
// 函数func拥有_标签,返回值32.0拥有Float标签,触发警告:“标签不匹配”
static func() return 32.0;
static CsTeams:GetTeamId(playerEntId) return CsTeams:get_user_team(playerEntId);
public Player_PreThinkThink(playerEntId)
{
// 函数GetTeamId拥有CsTeams标签,整数字面量1拥有_标签,触发警告:“标签不匹配”
if (GetTeamId(playerEntId) == 1)
{
}
}
调用函数时,观察标签可得知函数返回值的类型,用相同标签的变量可以储存返回值。
【参数列表】语法拆解:
【插件状态声明】语法拆解:
【自动机声明】语法拆解:
注解:
将函数划分到指定自动机和状态之下(<>留空可划分到默认自动机-默认状态下)。
一个插件可以拥有多个自动机,一个自动机可以拥有多个状态,不同分支下的函数可以同名。
用户可使用state关键字切换插件当前使用的自动机和状态。
只有默认分支和活跃分支下的函数能被调用,所以此功能应用场景通常是:根据条件切换分支,执行其内的同名函数(功能不同)。
【函数体】是一个复合语句块。可以是一条表达式语句或控制流语句,或是被{}包裹的多条任意语句。
调用函数实际上就是进入函数体执行代码,完毕后退出函数体。