cs1.6 amxx编程 for精研教程 -- 作者:偶萤蛉(Oinling)

目录:

1:for循环的语法格式

2:for表达式详解

2.1:for

2.2:表达式列表入口

2.3:表达式1

2.4:表达式2

2.5:表达式3

2.6:表达式列表出口

2.7:循环体入口

2.8:循环体出口

3:实例-循环基本使用方法

3.1:省略了表达式1/2/3的死循环

3.2:正向循环

3.3:反向循环

3.4:continue跳过循环

3.5:break停止循环

3.6:return退出函数

3.7:goto跳出循环

3.8:破坏循环条件

4:实例-循环实际应用

4.1:计算数量

4.2:收集满足条件的对象

4.3:找最低值(寻找最近的活者)

4.3:找最高值(寻找最远的活者)

5:将for语句的代码执行流程打印到控制台

1:for循环的语法格式

for 表达式列表入口 表达式1; 表达式2; 表达式3 表达式列表出口 循环体入口 循环体出口

参考:

for (new num; num < 10; num++) { }

2:for表达式详解

for循环的主要功能是重复执行循环体内的代码.

在循环过程中,开发者可设计多次重复的相同操作,也可根据不断改变的变量,做一些不同的操作.

熟练使用循环可简化某些相似度极高但参数有细微变化的代码.

for表达式内的代码执行顺序:

表达式1最先执行,仅执行一次.然后前往表达式2.

表达式2执行之后若等于0,前往循环体出口(停止循环).否则前往循环体入口,进入内部执行代码.

循环体内代码执行之后,前往表达式3.

表达式3执行完毕后,前往表达式2,开始下一轮循环.

表达式1通常用于声明变量并初始化.因此表达式1也可称呼为:初始化表达式.

表达式2通常用于判断变量是否等于0.因此表达式2也可称呼为:条件表达式.

表达式3通常用于控制变量自增或自减,使表达式2有机会等于0.因此表达式3也可称呼为:迭代表达式.

循环停止之后,表达式1和循环体内用newnew const声明的符号会被销毁,因此循环体出口下方代码不允许调用这些符号.

staticstatic const声明的符号虽然不会被销毁,但受到作用域限制,不允许被循环体出口下方代码调用.

2.1:for

for只能在函数体内使用.使代码不再是自上而下运行,而是在指定规则下多次重复一个区域内的代码.

2.2:表达式列表入口

表达式列表入口用 ( 符号表示.

2.3:表达式1

表达式1是可省略的.

若不省略,可填写newnew const声明符号.也可调用自定义符号做运算.

表达式1必须使用 ; 符号结尾.用于区分表达式1和表达式2.

不允许填写除了newnew const以外的说明符,以及所有控制流保留词.

2.4:表达式2

表达式2是可省略的.

若不省略,可填写一个或数个条件表达式.

表达式2必须使用 ; 符号结尾.用于区分表达式2和表达式3.

不允许填写所有说明符,控制流保留词.

2.5:表达式3

表达式3是可省略的.

若不省略,可调用自定义符号做运算.不允许填写说明符,控制流保留词.

表达式3不能使用 ; 符号结尾.它已经是最后一个表达式.

2.6:表达式列表出口

表达式列表出口用 ) 符号表示.

2.7:循环体入口

循环体入口用 { 符号表示.

循环体出入口是可省略的,若省略,则循环体内仅允许填写一条语句.

2.8:循环体出口

循环体出口用 } 符号表示.

循环体出入口是可省略的,若省略,则循环体内仅允许填写一条语句.

3:实例-循环基本使用方法

3.1:省略了表达式1/2/3的死循环

使用以下代码,将导致LogOneLine函数无限重复运行.游戏无法退出函数,无法做其它事情,无法刷新下一帧.

#include amxmodx public plugin_init() { register_plugin("循环测试", "1.0.0", "偶萤蛉(Oinling)"); new counter, timer = tickcount(); for (;;) { LogOneLine("循环次数:%011d, 循环持续时间:%08.2f秒", ++counter, float(tickcount() - timer) / 1000.0); } } /** * 写一行字到"cstrike/addons/amxmodx/logs/L今年今月今日.log". * * @param formatString 要写入的文字,可使用格式占位符:%b %c %d %i %u %f %X %x %a %s %L %l %N %n %% * @param ... 可根据格式占位符的需求填写不限量的引用参数. * * @return 返回实际写入的字节数 * @error 未填写格式占位符所需的参数 */ static LogOneLine(const formatString[], any:...) { static buffer[256]; vformat(buffer, charsmax(buffer), formatString, 2); static filePathLength, filePath[64]; filePathLength = get_localinfo("amx_logdir", filePath, charsmax(filePath)); filePathLength += get_time("/L%Y%m%d.log", filePath[filePathLength], charsmax(filePath) - filePathLength); if (file_exists(filePath)) delete_file(filePath); return log_amx(buffer); }

强行关闭游戏后,可以打开"cstrike/addons/amxmodx/logs/L今年今月今日.log"文件,查阅循环次数与持续时间(可能会是空白,再试几次死循环就有文字了).

该例子告诉我们,代码运行期间,游戏不会刷新下一帧,任何玩家都别想做事情了.

因此,使用循环时,一定要避免死循环.比如在表达式2位置填写条件表达式.

3.2:正向循环

使用以下代码,在进入游戏,载入地图,打开控制台之后,可以看见server_print函数被重复运行了10次.

#include amxmodx public plugin_init() { register_plugin("循环测试", "1.0.0", "偶萤蛉(Oinling)"); for (new number; number < 10; number++) { server_print("[AMXX]number = %d", number); } }

打印结果:

[AMXX]number = 0 [AMXX]number = 1 [AMXX]number = 2 [AMXX]number = 3 [AMXX]number = 4 [AMXX]number = 5 [AMXX]number = 6 [AMXX]number = 7 [AMXX]number = 8 [AMXX]number = 9

number的初始值为0,每次小于10都打印一次,然后自增1.因此打印结果从0开始,直至9.总共10次.

最终,因为number等于10,不满足表达式2的条件(小于10),停止了循环.

3.3:反向循环

使用以下代码,在进入游戏,载入地图,打开控制台之后,可以看见server_print函数被重复运行了10次.

#include amxmodx public plugin_init() { register_plugin("循环测试", "1.0.0", "偶萤蛉(Oinling)"); for (new number = 10; number; number--) { server_print("[AMXX]number = %d", number); } }

打印结果:

[AMXX]number = 10 [AMXX]number = 9 [AMXX]number = 8 [AMXX]number = 7 [AMXX]number = 6 [AMXX]number = 5 [AMXX]number = 4 [AMXX]number = 3 [AMXX]number = 2 [AMXX]number = 1

number的初始值为10,每次非0都打印一次,然后自减1.因此打印结果从10开始,直至1.总共10次.

最终,因为number等于0,不满足表达式2的条件(非0),停止了循环.

若想让循环体内访问number的值时得到的是9至0,可以这么做:

for (new number = 10; number--;) { server_print("[AMXX]number = %d", number); }

表达式2会先检查number非0,再使number自减1,再进入循环体执行代码(或停止循环).

也就是说,是否进入循环体,由自减前的number决定,而不是由自减后的number决定.

若想让循环体内访问number的值时得到的是9至0,可以这么做:

for (new number = 10; number--;) { server_print("[AMXX]number = %d", number); }

表达式2会先检查number非0,再使number自减1,再进入循环体执行代码(或停止循环).

也就是说,是否进入循环体,由自减前的number决定,而不是由自减后的number决定.

打印结果:

[AMXX]number = 9 [AMXX]number = 8 [AMXX]number = 7 [AMXX]number = 6 [AMXX]number = 5 [AMXX]number = 4 [AMXX]number = 3 [AMXX]number = 2 [AMXX]number = 1 [AMXX]number = 0

3.4:continue跳过循环

循环体内,可使用continue保留词.在for的循环体中,它的作用是立即前往表达式3.

因此,一旦运行了continue,在它下方的代码不会运行.

continue常常与if case default等语句一起使用.只要满足某些条件,便跳过循环体中剩余部分的代码.

以下代码,可展示continue跳过循环体中剩余部分代码的功能:

#include amxmodx public plugin_init() { register_plugin("循环测试", "1.0.0", "偶萤蛉(Oinling)"); server_print("[AMXX]第1轮循环,访问数值0至9:"); for (new number; number < 10; number++) { if (number == 1) continue; if (number == 3) continue; if (number == 5) continue; if (number == 7) continue; if (number == 9) continue; server_print("[AMXX]number = %d", number); } server_print("[AMXX]第2轮循环,访问数值0至19:"); for (new number; number < 20; number++) { switch (number) { case 3 .. 18: continue; } server_print("[AMXX]number = %d", number); } }

打印结果:

[AMXX]第1轮循环,访问数值0至9: [AMXX]number = 0 [AMXX]number = 2 [AMXX]number = 4 [AMXX]number = 6 [AMXX]number = 8 [AMXX]第2轮循环,访问数值0至19: [AMXX]number = 0 [AMXX]number = 1 [AMXX]number = 2 [AMXX]number = 19

在第1轮循环中.由于number满足等于1 3 5 7 9的条件,因此游戏中并未执行server_print("[AMXX]number = %d", number)语句.

在第2轮循环中,由于number满足等于3至18的条件,因此游戏中并未执行server_print("[AMXX]number = %d", number)语句.

以下代码可展示continue立即前往表达式3的特性,在它下方的代码是不会运行的.

#include amxmodx public plugin_init() { register_plugin("循环测试", "1.0.0", "偶萤蛉(Oinling)"); for (new number; number < 10; number++) { if (number < 5) { server_print("[AMXX]number < 5, continue"); continue; server_print("[AMXX]测试"); // 编译器警告:这一行的代码无法访问 } server_print("[AMXX]number = %d", number); } }

打印结果:

[AMXX]number < 5, continue [AMXX]number < 5, continue [AMXX]number < 5, continue [AMXX]number < 5, continue [AMXX]number < 5, continue [AMXX]number = 5 [AMXX]number = 6 [AMXX]number = 7 [AMXX]number = 8 [AMXX]number = 9

从打印结果可看出,continue的上一行代码确实执行了,那么接下来就会执行continue.

continue下一行的"[AMXX]测试"并未被打印出来,说明continue执行完毕后并未再向下执行代码.

从打印结果可看到下一行的数值在递增,进一步证明了continue会直接跳转至表达式3,执行number的自增运算.

需要注意,如果是在多层for循环体内使用continue保留词,只会前往最深层的表达式3.

可用以下代码进行测试:

#include amxmodx public plugin_init() { register_plugin("循环测试", "1.0.0", "偶萤蛉(Oinling)"); for (new number; number < 3; number++) { for (new n2; n2 < 3; n2++) { if (n2 == 1) continue; server_print("[AMXX]number = %d, n2 = %d", number, n2); } server_print("[AMXX]number = %d", number); } server_print("[AMXX]循环-结束!"); }

打印结果:

[AMXX]number = 0, n2 = 0 [AMXX]number = 0, n2 = 2 [AMXX]number = 0 [AMXX]number = 1, n2 = 0 [AMXX]number = 1, n2 = 2 [AMXX]number = 1 [AMXX]number = 2, n2 = 0 [AMXX]number = 2, n2 = 2 [AMXX]number = 2 [AMXX]循环-结束!

可以看到,number等于0至2,一个都没少.

n2等于0至2的循环中,只有n2 = 1被跳过了.

这证明了continue只作用于其所在循环体中,最深层的循环.

3.5:break停止循环

循环体内,可使用break保留词.它能停止任何循环语句.

一旦运行了break,在它下方的剩余代码不会被运行.

break常常与if case default等语句一起使用.只要满足某些条件,便立即停止循环.

以下代码,可展示break停止循环的功能:

#include amxmodx public plugin_init() { register_plugin("循环测试", "1.0.0", "偶萤蛉(Oinling)"); new number for (; number < 10; number++) { if (number == 5) { server_print("[AMXX]number = %d,满足break条件.", number); break; } server_print("[AMXX]number = %d", number); } server_print("[AMXX]循环-结束! number = %d", number); }

打印结果:

[AMXX]number = 0 [AMXX]number = 1 [AMXX]number = 2 [AMXX]number = 3 [AMXX]number = 4 [AMXX]number = 5,满足break条件. [AMXX]循环-结束! number = 5

可见,break一旦执行,会直接停止循环.从最后一个打印可看出,numberbreak之前与之后都等于5,说明break不像continue会前往表达式3,而是直接停止循环.

需要注意,如果是在多层for循环体内使用break保留词,只会停止最深层的for循环.

可用以下代码进行测试:

#include amxmodx public plugin_init() { register_plugin("循环测试", "1.0.0", "偶萤蛉(Oinling)"); for (new number; number < 3; number++) { for (new n2; n2 < 3; n2++) { if (n2 == 1) break; server_print("[AMXX]number = %d, n2 = %d", number, n2); } server_print("[AMXX]number = %d", number); } server_print("[AMXX]循环-结束!"); }

打印结果:

[AMXX]number = 0, n2 = 0 [AMXX]number = 0 [AMXX]number = 1, n2 = 0 [AMXX]number = 1 [AMXX]number = 2, n2 = 0 [AMXX]number = 2 [AMXX]循环-结束!

可以看到,number等于0至2,一个都没少.

n2等于0至2的循环中,只有n2 = 0被打印出来.

这证明了break只作用于其所在循环体中,最深层的循环.

3.6:return退出函数

循环体内可以使用return保留词,return的作用是立即退出函数,回到调用函数的位置.

这一点,即便是在循环体内也不会改变.

以下代码,展示return停止循环,退出函数的功能.

#include amxmodx public plugin_init() { register_plugin("循环测试", "1.0.0", "偶萤蛉(Oinling)"); server_print("[AMXX]准备调用ForTester()函数."); ForTester(); server_print("[AMXX]已返回调用ForTester()的位置."); } static ForTester() { server_print("[AMXX]已进入ForTester()内部,执行各种代码."); for (new number; number < 10; number++) { if (number == 5) { return; server_print("[AMXX]return下方的代码."); // 编译器警告:无法访问这一行的代码. } server_print("[AMXX]number = %d", number); } server_print("[AMXX]循环-结束!"); }

打印结果:

[AMXX]准备调用ForTester()函数. [AMXX]已进入ForTester()内部,执行各种代码. [AMXX]number = 0 [AMXX]number = 1 [AMXX]number = 2 [AMXX]number = 3 [AMXX]number = 4 [AMXX]已返回调用ForTester()的位置.

从打印结果可看出,在满足number等于5这个条件后,return下方的3个打印全都没有执行,而是直接打印了"[AMXX]已返回调用ForTester()的位置.".

这证明了return不但停止了循环(5至9统统未被打印),而且还直接退出了函数,回到调用函数的位置.

3.7:goto跳出循环

循环体内可以使用goto保留词,goto的作用是立即前往函数体内的某个标记位置.

以下代码,展示goto跳出循环的功能.

#include amxmodx public plugin_init() { register_plugin("循环测试", "1.0.0", "偶萤蛉(Oinling)"); server_print("[AMXX]准备调用ForTester()函数."); ForTester(); server_print("[AMXX]已返回调用ForTester()的位置."); } static ForTester() { server_print("[AMXX]已进入ForTester()内部,执行各种代码."); for (new number; number < 10; number++) { if (number == 5) { goto Label@@@@@; // 跳转至Label@@@@@标记所在位置. server_print("[AMXX]goto下方的代码.", number); } server_print("[AMXX]number = %d", number); } Label@@@@@: // 声明一个名叫Label@@@@@的标记,使goto可以跳转到这里. server_print("[AMXX]循环-结束!"); }

打印结果:

[AMXX]准备调用ForTester()函数. [AMXX]已进入ForTester()内部,执行各种代码. [AMXX]number = 0 [AMXX]number = 1 [AMXX]number = 2 [AMXX]number = 3 [AMXX]number = 4 [AMXX]循环-结束! [AMXX]已返回调用ForTester()的位置.

从打印结果可看出,在满足number等于5这个条件后,"[AMXX]goto下方的代码."并未被打印,而是直接打印了"[AMXX]循环-结束!".

这证明了goto直接跳出了循环(5至9统统未被打印).

如果想要同时停止多层循环,利用goto直接跳出,会是个不错的方法.

3.8:破坏循环条件

在某些情况下,我们可以直接更改表达式2中所使用的变量,另其计算结果为0,已达到停止循环的效果.

这种方法不像continuebreak一样会忽略循环体中剩余的代码,因此比较罕见.

如下所示:

#include amxmodx public plugin_init() { register_plugin("循环测试", "1.0.0", "偶萤蛉(Oinling)"); for (new number; number < 3; number++) { for (new n2; n2 < 3; n2++) { if (n2 == 1) { n2 = 2; number = 2; } server_print("[AMXX]number = %d, n2 = %d", number, n2); } server_print("[AMXX]number = %d", number); } server_print("[AMXX]循环-结束!"); }

打印结果:

[AMXX]number = 0, n2 = 0 [AMXX]number = 2, n2 = 2 [AMXX]number = 2 [AMXX]循环-结束!

可以看到,在n2满足等于1的条件后,将n2 number赋值为2.结果是打印了"[AMXX]number = 2, n2 = 2".

接下来会前往执行n2++,结果是n2自增为3.接下来会前往判断n2 < 3,由于3 < 3等于0,无法再次进入循环体.

因此深层循环停止,接下来打印了"[AMXX]number = 2".

再前往number++,结果是number自增为3,再前往判断number < 3,由于3 < 3等于0,无法再次进入循环体.

因此外层循环停止,最终打印"[AMXX]循环-结束!".

这种写法需要对for循环有足够深的理解,需要知道代码具体的执行流程.

由于该写法的应用场景很罕见,不用太过在意.不过了解一下也是好的.能提高开发者的逻辑思维能力.

4:实例-循环实际应用

4.1:计算数量

以下代码,用for循环计数,实现了两个功能:

玩家在未死亡情况下,按下[E]键,在聊天框打印游戏中活人数量.

玩家在未死亡情况下,按下[CTRL]键,在聊天框打印游戏中活着的匪徒数量.

#include amxmodx #include fakemeta // 声明全局变量gMaxClients,用于储存:服务器最大玩家数 new gMaxClients; public plugin_init() { register_plugin("循环测试", "1.0.0", "偶萤蛉(Oinling)"); // 为"玩家预思考事件"添加后置挂钩,挂钩的回调函数名为:Player_PreThink_PostHook, // 每当"玩家预思考事件"被触发,就在触发之后调用一次回调函数 register_forward(FM_PlayerPreThink, "Player_PreThink_PostHook", 1); // 获取服务器最大玩家数(选择地图时可以设置这个数量),储存至gMaxClients全局变量 gMaxClients = global_get(glb_maxClients); } // 声明公共函数:Player_PreThink_PostHook,充当"玩家预思考事件后置挂钩"的回调函数 public Player_PreThink_PostHook(thinkerEntId) { // 若思考者不是活人,返回 if (!is_user_alive(thinkerEntId)) return; // 获取上次,这次思考时按住的按键,分别存入oldButtons和buttons变量 new oldButtons = pev(thinkerEntId, pev_oldbuttons); new buttons = pev(thinkerEntId, pev_button); // 如果思考者按下"使用键"(一般是[E]键) if (!(oldButtons & IN_USE) && buttons & IN_USE) { // 声明counter变量,用于计数 new counter; // 声明playerEntId变量,用于表示玩家实体索引,在循环体内访问gMaxClients至1号玩家实体索引 for (new playerEntId = gMaxClients; playerEntId; playerEntId--) { // 若该索引指向一个活的玩家,而不是虚无或死者.那么counter计数器自增1 if (is_user_alive(playerEntId)) counter++; } // 打印计数结果 client_print(thinkerEntId, print_chat, "[AMXX]目前有%d个活着的玩家.", counter); } // 如果思考者按下"下蹲键"(一般是[CTRL]键) if (!(oldButtons & IN_DUCK) && buttons & IN_DUCK) { // 声明counter变量,用于计数 new counter; // 声明playerEntId变量,用于表示玩家实体索引,在循环体内访问gMaxClients至1号玩家实体索引 for (new playerEntId = gMaxClients; playerEntId; playerEntId--) { // 若该索引指向一个活的1号队伍玩家,而不是虚无或死者或其它队伍.那么counter计数器自增1 if (is_user_alive(playerEntId) && get_user_team(playerEntId) == 1) counter++; } // 打印计数结果 client_print(thinkerEntId, print_chat, "[AMXX]1号队伍(匪徒)目前有%d个活着的玩家.", counter); } }

4.2:收集满足条件的对象

以下代码,用for循环计数,并收集满足条件的对象.实现了以下功能:

玩家在未死亡情况下,按下[E]键,选中所有活人的实体索引,存入数组.

从数组中随机挑选一个元素,杀死该元素所指的玩家.

#include amxmodx #include fakemeta // 声明全局变量gMaxClients,用于储存:服务器最大玩家数 new gMaxClients; public plugin_init() { register_plugin("循环测试", "1.0.0", "偶萤蛉(Oinling)"); // 为"玩家预思考事件"添加后置挂钩,挂钩的回调函数名为:Player_PreThink_PostHook, // 每当"玩家预思考事件"被触发,就在触发之后调用一次回调函数 register_forward(FM_PlayerPreThink, "Player_PreThink_PostHook", 1); // 获取服务器最大玩家数(选择地图时可以设置这个数量),储存至gMaxClients全局变量 gMaxClients = global_get(glb_maxClients); } // 声明公共函数:Player_PreThink_PostHook,充当"玩家预思考事件后置挂钩"的回调函数 public Player_PreThink_PostHook(thinkerEntId) { // 若思考者不是活人,返回 if (!is_user_alive(thinkerEntId)) return; // 获取上次,这次思考时按住的按键,分别存入oldButtons和buttons变量 new oldButtons = pev(thinkerEntId, pev_oldbuttons); new buttons = pev(thinkerEntId, pev_button); // 如果思考者按下"使用键"(一般是[E]键) if (!(oldButtons & IN_USE) && buttons & IN_USE) { // 声明counter变量,用于计数 new counter; // 声明容量为32的数组变量players,用于存放玩家的实体索引(CS1.6服务器最多只允许32名玩家进入) new players[32]; // 声明playerEntId变量,用于表示玩家实体索引,在循环体内访问gMaxClients至1号玩家实体索引 for (new playerEntId = gMaxClients; playerEntId; playerEntId--) { // 若该索引指向一个活的玩家,而不是虚无或死者.那么存入players[counter],然后counter自增1 if (is_user_alive(playerEntId)) players[counter++] = playerEntId;++; } // 打印计数结果 client_print(thinkerEntId, print_chat, "[AMXX]目前有%d个活着的玩家.他们的实体索引已被存入活人数组.", counter); // 若数量不为0 if (counter) { // 声明elementId变量,在0至counter-1范围中随机取值,存入elementId变量 new elementId = random(counter); client_print(thinkerEntId, print_chat, "[AMXX]在活人数组中,随机选中了[%d]号元素.元素存放的是玩家实体索引[%d].", elementId, players[elementId]); // 访问players的elementId号元素,杀死该元素所指的玩家 user_kill(players[elementId]); client_print(thinkerEntId, print_chat, "[AMXX]实体索引为[%d]的玩家 -- %n -- 杀死!.", players[elementId], players[elementId]); } // 打印计数结果 client_print(thinkerEntId, print_chat, "[AMXX]目前有%d个活着的玩家.", counter); } }

4.2:找最低值(寻找最近的活者)

以下代码可寻找数组中的最小值.通常可用于寻找拥有最低分数的玩家,距离最近的玩家等等.

#include amxmodx public plugin_init() { register_plugin("循环测试", "1.0.0", "偶萤蛉(Oinling)"); // 假设总共有10个数值 new values[10] = { 430, 320, 210, 100, 50, 60, 30, 40, 10, 20 }; // 声明充当索引记录器的变量,用于记录存有最小值的元素的索引,并初始化为-1. // 由于有效索引是0至9,因此-1是一个无效索引.如果for查询完毕后索引记录器依然是-1, // 说明未能找到最小值(values容量为0或所有元素都被continue跳过) new selectedElementId = -1; // 声明两个充当数值记录器的变量,分别用于记录此次循环的数值,与目前已知的最小值 new value, minValue; // 根据表达式1和2的设定,循环体内的elementId将会是9至0号 for (new elementId = sizeof(values); elementId--;) { // 访问values数组的elementId号元素,将其数值存入value value = values[elementId]; // 若当前数值小于已知最小值,或此前尚未记录最小值(一旦记录,selectedElemtId就不再小于0) if (value < minValue || selectedElementId < 0) { // 将value作为已知最小值,存入minValue,用于下一轮作比较 minValue = value; // 记录已知最小值的元素索引 selectedElementId = elementId; server_print("[AMXX]已知最小值 : values[%d] = %d", elementId, value); } else server_print("[AMXX]未能满足记录条件 : values[%d] = %d", elementId, value); } if (selectedElementId < 0) server_print("[AMXX]选中元素索引为-1,说明数组容量为0,或循环中跳过了数组的所有元素."); else server_print("[AMXX]选中元素索引为%d,values[%d]的值%d,是values数组中最小的一个.", selectedElementId, selectedElementId, minValue); }

打印结果为:

[AMXX]已知最小值 : values[9] = 20 [AMXX]已知最小值 : values[8] = 10 [AMXX]未能满足记录条件 : values[7] = 40 [AMXX]未能满足记录条件 : values[6] = 30 [AMXX]未能满足记录条件 : values[5] = 60 [AMXX]未能满足记录条件 : values[4] = 50 [AMXX]未能满足记录条件 : values[3] = 100 [AMXX]未能满足记录条件 : values[2] = 210 [AMXX]未能满足记录条件 : values[1] = 320 [AMXX]未能满足记录条件 : values[0] = 430 [AMXX]选中元素索引为8,values[8]的值10,是values数组中最小的一个.

可见7至0号元素的值统统不满足"小于已知最小值"或"尚未记录最小值"两个条件.因此最小值的索引记录器,数值记录器再也没有更新.

循环结束后,便可确认values[8]存有最小值.

用以下代码,使玩家在按下[E]键时,查找除自己以外的最近活者.

#include amxmodx #include fakemeta // 声明全局变量gMaxClients,用于储存:服务器最大玩家数 new gMaxClients; public plugin_init() { register_plugin("循环测试", "1.0.0", "偶萤蛉(Oinling)"); // 为"玩家预思考事件"添加后置挂钩,挂钩的回调函数名为:Player_PreThink_PostHook, // 每当"玩家预思考事件"被触发,就在触发之后调用一次回调函数 register_forward(FM_PlayerPreThink, "Player_PreThink_PostHook", 1); // 获取服务器最大玩家数(选择地图时可以设置这个数量),储存至gMaxClients全局变量 gMaxClients = global_get(glb_maxClients); } // 声明公共函数:Player_PreThink_PostHook,充当"玩家预思考事件后置挂钩"的回调函数 public Player_PreThink_PostHook(thinkerEntId) { // 若思考者是个死人,返回 if (!is_user_alive(thinkerEntId)) return; // 获取上次,这次思考时按住的按键,分别存入oldButtons和buttons变量 new oldButtons = pev(thinkerEntId, pev_oldbuttons); new buttons = pev(thinkerEntId, pev_button); // 如果思考者按下"使用键"(一般是[E]键) if (!(oldButtons & IN_USE) && buttons & IN_USE) { // 声明origin与dest数组,用于储存思考者与其他玩家的坐标 new Float:origin[3], Float:dest[3]; // 对thinkerEntId所指玩家访问pev_origin常量所指的属性(坐标),将坐标值存入origin pev(thinkerEntId, pev_origin, origin); // 实体索引记录器,当前距离记录器,已知最小距离记录器 new selectedEntId, Float:dist, Float:minDist; // 根据表达式1/2/3的设定,循环体内playerEntId将会是gMaxClients至1 for (new playerEntId = gMaxClients; playerEntId; playerEntId--) { // 跳过思考者自己 if (playerEntId == thinkerEntId) continue; // 跳过非活者 if (!is_user_alive(playerEntId)) continue; // 对playerEntId所指玩家访问pev_origin常量所指的属性(坐标),将坐标值存入dest pev(playerEntId, pev_origin, dest); // 计算origin与dest坐标的直线距离,存入dist dist = get_distance_f(origin, dest); // 若当前距离小于已知最小距离,或尚未记录最小距离和最近玩家实体索引 if (dist < minDist || !selectedEntId) { // 记录最小距离 minDist = dist; // 记录最近玩家的实体索引 selectedEntId = playerEntId; } } // 若selectedEntId非0(已记录最近玩家的实体索引) if (selectedEntId) { client_print(thinkerEntId, print_chat, "[AMXX]与你最近的活者是:%n,距离:%.2f", selectedEntId, minDist); // 杀死selectedEntId所指玩家 user_kill(selectedEntId); } // 若selectedEntId为0(可能游戏中除了思考者以外已经没有其他活者) else { client_print(thinkerEntId, print_chat, "[AMXX]游戏中不存在除你以外的活者."); } } }

4.2:找最高值(寻找最远的活者)

以下代码可寻找数组中的最大值.通常可用于寻找拥有最高分数的玩家,距离最远的玩家等等.

#include amxmodx public plugin_init() { register_plugin("循环测试", "1.0.0", "偶萤蛉(Oinling)"); // 假设总共有10个数值 new values[10] = { 430, 320, 210, 100, 50, 60, 30, 40, 10, 20 }; // 声明充当索引记录器的变量,用于记录存有最小值的元素的索引,并初始化为-1. // 由于有效索引是0至9,因此-1是一个无效索引.如果for查询完毕后索引记录器依然是-1, // 说明未能找到最小值(values容量为0或所有元素都被continue跳过) new selectedElementId = -1; // 声明两个充当数值记录器的变量,分别用于记录此次循环的数值,与目前已知的最大值 new value, maxValue; // 根据表达式1和2的设定,循环体内的elementId将会是9至0号 for (new elementId = sizeof(values); elementId--;) { // 访问values数组的elementId号元素,将其数值存入value value = values[elementId]; // 若已知最大值小于当前数值,或此前尚未记录最大值(一旦记录,selectedElemtId就不再小于0) if (maxValue < value || selectedElementId < 0) { // 将value作为已知最大值,存入maxValue,用于下一轮作比较 maxValue = value; // 记录已知最大值的元素索引 selectedElementId = elementId; server_print("[AMXX]已知最大值 : values[%d] = %d", elementId, value); } else server_print("[AMXX]未能满足记录条件 : values[%d] = %d", elementId, value); } if (selectedElementId < 0) server_print("[AMXX]选中元素索引为-1,说明数组容量为0,或循环中跳过了数组的所有元素."); else server_print("[AMXX]选中元素索引为%d,values[%d]的值%d,是values数组中最大的一个.", selectedElementId, selectedElementId, maxValue); }

打印结果为:

[AMXX]已知最大值 : values[9] = 20 [AMXX]未能满足记录条件 : values[8] = 10 [AMXX]已知最大值 : values[7] = 40 [AMXX]未能满足记录条件 : values[6] = 30 [AMXX]已知最大值 : values[5] = 60 [AMXX]未能满足记录条件 : values[4] = 50 [AMXX]已知最大值 : values[3] = 100 [AMXX]已知最大值 : values[2] = 210 [AMXX]已知最大值 : values[1] = 320 [AMXX]已知最大值 : values[0] = 430 [AMXX]选中元素索引为0,values[0]的值430,是values数组中最大的一个.

可见8,6,4号元素的值不满足"大于已知最大值"或"尚未记录最大值"两个条件.因此最大值的索引记录器,数值记录器没有更新.

接下来的3,2,1,0都满足的更新记录器的条件.循环结束后,便可确认values[0]存有最大值.

用以下代码,使玩家在按下[E]键时,查找除自己以外的最远活者.

#include amxmodx #include fakemeta // 声明全局变量gMaxClients,用于储存:服务器最大玩家数 new gMaxClients; public plugin_init() { register_plugin("循环测试", "1.0.0", "偶萤蛉(Oinling)"); // 为"玩家预思考事件"添加后置挂钩,挂钩的回调函数名为:Player_PreThink_PostHook, // 每当"玩家预思考事件"被触发,就在触发之后调用一次回调函数 register_forward(FM_PlayerPreThink, "Player_PreThink_PostHook", 1); // 获取服务器最大玩家数(选择地图时可以设置这个数量),储存至gMaxClients全局变量 gMaxClients = global_get(glb_maxClients); } // 声明公共函数:Player_PreThink_PostHook,充当"玩家预思考事件后置挂钩"的回调函数 public Player_PreThink_PostHook(thinkerEntId) { // 若思考者是个死人,返回 if (!is_user_alive(thinkerEntId)) return; // 获取上次,这次思考时按住的按键,分别存入oldButtons和buttons变量 new oldButtons = pev(thinkerEntId, pev_oldbuttons); new buttons = pev(thinkerEntId, pev_button); // 如果思考者按下"使用键"(一般是[E]键) if (!(oldButtons & IN_USE) && buttons & IN_USE) { // 声明origin与dest数组,用于储存思考者与其他玩家的坐标 new Float:origin[3], Float:dest[3]; // 对thinkerEntId所指玩家访问pev_origin常量所指的属性(坐标),将坐标值存入origin pev(thinkerEntId, pev_origin, origin); // 实体索引记录器,当前距离记录器,已知最小距离记录器 new selectedEntId, Float:dist, Float:maxDist; // 根据表达式1/2/3的设定,循环体内playerEntId将会是gMaxClients至1 for (new playerEntId = gMaxClients; playerEntId; playerEntId--) { // 跳过思考者自己 if (playerEntId == thinkerEntId) continue; // 跳过非活者 if (!is_user_alive(playerEntId)) continue; // 对playerEntId所指玩家访问pev_origin常量所指的属性(坐标),将坐标值存入dest pev(playerEntId, pev_origin, dest); // 计算origin与dest坐标的直线距离,存入dist dist = get_distance_f(origin, dest); // 若已知最大距离小于当前距离,或尚未记录最大距离和最远玩家实体索引 if (maxDist < dist || !selectedEntId) { // 记录最大距离 maxDist = dist; // 记录最近玩家的实体索引 selectedEntId = playerEntId; } } // 若selectedEntId非0(已记录最远玩家的实体索引) if (selectedEntId) { client_print(thinkerEntId, print_chat, "[AMXX]与你最远的活者是:%n,距离:%.2f", selectedEntId, maxDist); // 杀死selectedEntId所指玩家 user_kill(selectedEntId); } // 若selectedEntId为0(可能游戏中除了思考者以外已经没有其他活者) else { client_print(thinkerEntId, print_chat, "[AMXX]游戏中不存在除你以外的活者."); } } }

5:将for语句的代码执行流程打印到控制台

用以下代码,展示for语句的代码执行流程.

#include amxmodx new gNumber; public plugin_init() { register_plugin("循环测试", "1.0.0", "偶萤蛉(Oinling)"); for (E1(); E2(); E3()) { BodyCode(); } ForEnd(); } static E1() { server_print("表达式1 ... : 执行gNumber = 0"); gNumber = 0; } static bool:E2() { server_print("表达式2 ... 此时gNumber为%d : 判断gNumber < 10", gNumber); return gNumber < 10; } static E3() { server_print("表达式3 ... 此时gNumber为%d : 执行gNumber++", gNumber); gNumber++; } static BodyCode() { server_print("循环体代码 ... 此时gNumber为%d", gNumber); } static ForEnd() { server_print("循环体结束... 此时gNumber为%d", gNumber); }

打印结果:

表达式1 ... : 执行gNumber = 0 表达式2 ... 此时gNumber为0 : 判断gNumber < 10 循环体代码 ... 此时gNumber为0 表达式3 ... 此时gNumber为0 : 执行gNumber++ 表达式2 ... 此时gNumber为1 : 判断gNumber < 10 循环体代码 ... 此时gNumber为1 表达式3 ... 此时gNumber为1 : 执行gNumber++ 表达式2 ... 此时gNumber为2 : 判断gNumber < 10 循环体代码 ... 此时gNumber为2 表达式3 ... 此时gNumber为2 : 执行gNumber++ 表达式2 ... 此时gNumber为3 : 判断gNumber < 10 循环体代码 ... 此时gNumber为3 表达式3 ... 此时gNumber为3 : 执行gNumber++ 表达式2 ... 此时gNumber为4 : 判断gNumber < 10 循环体代码 ... 此时gNumber为4 表达式3 ... 此时gNumber为4 : 执行gNumber++ 表达式2 ... 此时gNumber为5 : 判断gNumber < 10 循环体代码 ... 此时gNumber为5 表达式3 ... 此时gNumber为5 : 执行gNumber++ 表达式2 ... 此时gNumber为6 : 判断gNumber < 10 循环体代码 ... 此时gNumber为6 表达式3 ... 此时gNumber为6 : 执行gNumber++ 表达式2 ... 此时gNumber为7 : 判断gNumber < 10 循环体代码 ... 此时gNumber为7 表达式3 ... 此时gNumber为7 : 执行gNumber++ 表达式2 ... 此时gNumber为8 : 判断gNumber < 10 循环体代码 ... 此时gNumber为8 表达式3 ... 此时gNumber为8 : 执行gNumber++ 表达式2 ... 此时gNumber为9 : 判断gNumber < 10 循环体代码 ... 此时gNumber为9 表达式3 ... 此时gNumber为9 : 执行gNumber++ 表达式2 ... 此时gNumber为10 : 判断gNumber < 10 循环体结束... 此时gNumber为10