for 表达式列表入口 表达式1; 表达式2; 表达式3 表达式列表出口
循环体入口
循环体出口
参考:
for (new num; num < 10; num++)
{
}
for循环的主要功能是重复执行循环体内的代码.
在循环过程中,开发者可设计多次重复的相同操作,也可根据不断改变的变量,做一些不同的操作.
熟练使用循环可简化某些相似度极高但参数有细微变化的代码.
for表达式内的代码执行顺序:
表达式1最先执行,仅执行一次.然后前往表达式2.
表达式2执行之后若等于0,前往循环体出口(停止循环).否则前往循环体入口,进入内部执行代码.
循环体内代码执行之后,前往表达式3.
表达式3执行完毕后,前往表达式2,开始下一轮循环.
表达式1通常用于声明变量并初始化.因此表达式1也可称呼为:初始化表达式.
表达式2通常用于判断变量是否等于0.因此表达式2也可称呼为:条件表达式.
表达式3通常用于控制变量自增或自减,使表达式2有机会等于0.因此表达式3也可称呼为:迭代表达式.
循环停止之后,表达式1和循环体内用new
或new const
声明的符号会被销毁,因此循环体出口下方代码不允许调用这些符号.
用static
或static const
声明的符号虽然不会被销毁,但受到作用域限制,不允许被循环体出口下方代码调用.
for
只能在函数体内使用.使代码不再是自上而下运行,而是在指定规则下多次重复一个区域内的代码.
表达式列表入口用 (
符号表示.
表达式1是可省略的.
若不省略,可填写new
或new const
声明符号.也可调用自定义符号做运算.
表达式1必须使用 ;
符号结尾.用于区分表达式1和表达式2.
不允许填写除了new
与new const
以外的说明符,以及所有控制流保留词.
表达式2是可省略的.
若不省略,可填写一个或数个条件表达式.
表达式2必须使用 ;
符号结尾.用于区分表达式2和表达式3.
不允许填写所有说明符,控制流保留词.
表达式3是可省略的.
若不省略,可调用自定义符号做运算.不允许填写说明符,控制流保留词.
表达式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位置填写条件表达式.
使用以下代码,在进入游戏,载入地图,打开控制台之后,可以看见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),停止了循环.
使用以下代码,在进入游戏,载入地图,打开控制台之后,可以看见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
循环体内,可使用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
只作用于其所在循环体中,最深层的循环.
循环体内,可使用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
一旦执行,会直接停止循环.从最后一个打印可看出,number在break
之前与之后都等于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
只作用于其所在循环体中,最深层的循环.
循环体内可以使用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统统未被打印),而且还直接退出了函数,回到调用函数的位置.
循环体内可以使用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
直接跳出,会是个不错的方法.
在某些情况下,我们可以直接更改表达式2中所使用的变量,另其计算结果为0,已达到停止循环的效果.
这种方法不像continue
或break
一样会忽略循环体中剩余的代码,因此比较罕见.
如下所示:
#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
循环有足够深的理解,需要知道代码具体的执行流程.
由于该写法的应用场景很罕见,不用太过在意.不过了解一下也是好的.能提高开发者的逻辑思维能力.
以下代码,用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);
}
}
以下代码,用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);
}
}
以下代码可寻找数组中的最小值.通常可用于寻找拥有最低分数的玩家,距离最近的玩家等等.
#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]游戏中不存在除你以外的活者.");
}
}
}
以下代码可寻找数组中的最大值.通常可用于寻找拥有最高分数的玩家,距离最远的玩家等等.
#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]游戏中不存在除你以外的活者.");
}
}
}
用以下代码,展示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