[关闭]
@sogouwap 2017-06-04T17:12:55.000000Z 字数 4915 阅读 5060

Lv.2 普通 - AMXX插件编写教程


前言

本教程由小灰编写,这是普通难度的教程
适用于对插件 学习一段时间后 再阅读
我们的QQ群:139659650


快速切换

点击上述列表中 未打钩 旁边的切换按钮,以切换学习等级。

常见问题

这个教程学习下来需要多少时间?
你需要花一些时间直到你掌握它,并且每天都要坚持编写代码。根据理解力的不同,需要的时间也不一样(三个月,六个月,或一个星期)

我需要准备什么样的系统?

你需要 Windows 7/8/10 或者 Windows XP 系统。



一、判断 if ()

在插件中我们可以做上一些指定的条件来完成判断,根据判断结果来实现不同的功能

代码为 if (条件) ,其中条件是我们需要填写的内容,我们还可以在通过这个判断后添加 return,让其代码不往下执行

示例并实践:

1. 判断玩家是否存活 (调用模块: amxmodx.inc)

需要用到的代码是 is_user_alive(index)
返回值:如果存活返回1,否则 返回0

这里,我们还需要一个基础代码 if ( ) ,中文是如果的意思,其中括号填入 需要判断的条件
index 代表目标索引,意义为我们需要判断谁。

示例:

  1. if( is_user_alive(id) ) //如果玩家存活
  2. {
  3. }

如果我们需要判断这个玩家是否已经死亡,这里我们需要学到 ! 反义标识(惊叹号),在判断条件的前面加入。

示例:

  1. if(!is_user_alive(id)) //如果玩家已经死亡
  2. {
  3. }

2. 判断玩家持有武器 (调用模块: amxmodx.inc)

我们可以判断玩家的当前手上的武器
武器对应ID我们需要参考 编译器/include/amxconst.inc 文件中。在此文件中有一个代码部分叫 CSW_ ,这就是武器CSW ID号,比如: 武器 AK47 的 CSW_是 CSW_AK47

判断玩家持有的武器,我们需要用到 get_user_weapon(index)

该代码可以获取玩家持有的武器 CSW序号 ,在这里我们 可以通过两个 判断符 来实现判断

  1. == //绝对等于
  2. != //不等于

我们分别来看示例以及说明:

不同的判断方式

他由两个等号组成(注意中间没有空格),我们需要做的是 在条件和判断中间加上 == 即可

示例:

  1. if( get_user_weapon(id) == CSW_AK47)
  2. {
  3. client_print(id, print_chat, "Your are Weapon AK47")
  4. }

这样我们就判断了 ,如果玩家手上当前持有的是CSW_AK47 那么继续括号中的 client_print 函数内容(发送信息给索引目标)

在一些情况下,我们可能需要判断 如果玩家手上的武器不是那个武器,这种情况下我们需要用到不等于 != 来实现目的,和绝对等于一样,插入的代码位置不变化

示例:

  1. if(get_user_weapon(id) != CSW_AK47) //如果玩家手上的武器不是AK47
  2. {
  3. client_print(id, print_chat, "i Current Weapon Not Using Ak47")
  4. }

3. 判断玩家所属阵营 (调用模块: amxmodx.inc)

在一些情况下,我们需要判断 目标 索引(index) 所属的当前阵营是什么

这时候我们需要用到的代码是 get_user_team(index)

该代码的返回值不止一个,他们分别是:

  1. 返回 0 //玩家没有选择阵营
  2. 返回 1 //阵营是T阵营
  3. 返回 2 //阵营是CT阵营
  4. 返回 3 //阵营是观察者

判断玩家持有武器 一样,我们需要添加代码的也是 == 或者 !=

示例:(如果玩家是CT阵营)

  1. if( get_user_team(id) == 2)
  2. {
  3. }

多重判断

有时候我们需要判断多个条件,这时候我们就需要用到多重判断。

将的表达式代码 &&(并且)||(或者) 添加到两个条件中来实现多重判断。(需要添加到两个条件中间)

像这种有目标指向性的思路,一般有两种或者多种写法。
比如: (CT或者T) 或者 (不是观察者 并且 不是没有选择阵营)

方式(1)

  1. if( get_user_team(id) == 1 || get_user_team(id) == 2)
  2. {
  3. }

方式(2)

  1. if(get_user_team(id) != 0 && get_user_team(id) != 3)
  2. {
  3. }

以上两种示例,都可达成共同的目的,可根据个人习惯来改变写法

二、数组 ( [ ] )

被定义的变量后面有 [] 并且里面含有数值的代码称之为 数组,比如:

  1. thismodel[33]

数组默认中是没有数值的,也就是 0

我们可以在后期给数组定义上去,其中 [ ] 中的内容代表需要给谁定义,也就是 索引(index)
比如玩家数量最大是 32,我们就需要在数组中写上 33,需要预留一位才行。

示例:

  1. iSave[33]

数组可以帮我们做许多事情,比如 开关

现在,我们来讲一下 `开关` 的写法

开关 (on/off)

听上去是个很常见的东西,可是在代码中是怎么样的呢

开关:可以打开也可以关闭。
即:触发一次 打开 再触发一次 关闭

首先我们定义,开关数组

  1. new swtich[33]

然后开始写开关,首先我们判断 如果该数组是 0 的话

示例:

  1. if(switch[id] )
  2. {
  3. }

0 话怎么样?这种情况下我们就需要动脑子了,仔细想想就明白了:思路 — 如果是0改成1,如果是1改成0

那么代码就是这样的:

  1. if(!switch[id])
  2. {
  3. switch[id] = 1
  4. }

清晰易懂对吧?那么现在已经实现了 如果关闭就打开,那接下来的应该就是 如果打开就关闭

示例:

  1. if(!switch[id])
  2. {
  3. switch[id] = 1
  4. }
  5. if(switch[id])
  6. {
  7. switch[id] = 0
  8. }

如果我们以如上的代码思路来写,我们会遇到一个问题
是什么问题呢?我们翻译一下代码

  1. 如果关闭就打开
  2. 如果打开就关闭

这样乍看上去好像没什么不对,但是这里就是一个逻辑问题了,这种开关做上去就会出现这种问题:一旦开关刚刚打开马上就被关闭了

这种情况肯定是我们不想遇到的吧。那么应该怎么解决呢?

这里我们可以用到 else if 或者 else 来完成目的。

这两个的写法几乎一样,他和 双重if 的区别 是 只会被执行一次,而不是多次触发

  1. if(!switch[id])
  2. {
  3. switch[id] = 1
  4. }
  5. else if(switch[id])
  6. {
  7. switch[id] = 0
  8. }
  1. if(!switch[id])
  2. {
  3. switch[id] = 1
  4. }
  5. else
  6. {
  7. switch[id] = 0
  8. }

这样我们就完成开关了
else ifelse 这两种有额外的搭配写法,这里暂时不说

三、随机数 (random)

有一些情况下我们需要做一种特殊效果,他带有一种随机性,可以由插件自身来决定是否 可以继续执行

随机值分三种:

  1. 随机整数 (1)
  1. random(2) //随机整数:最大值
  1. 随机整数 (2)
  1. random_num(1, 2) //随机整数:(最小值, 最大值)
  1. 随机浮点数
  1. random_float(20.0, 140.0) //随机浮点数(最小值,最大值), 括号中需要填写浮点数

那么我们应该如何搭配呢?

示例: (1)

  1. if( random_num( 20, 60 ) > 45)
  2. {
  3. //如果在 (20, 60) 之间的随机结果大于 45
  4. }

示例:(2)

  1. if( random(25) == 14)
  2. {
  3. //如果在 (0, 40) 之间的随机结果刚好等于 14
  4. }

注意:
random(最大值) 中的最小值取 0

四、#define定义

在代码中可以将 一个值 表示为 另一个值

例如:我们可以定义 ABC = 1,或 ABC = 一串信息

  1. #define ABC 123 //定义(标识,数据)

那这有什么用?

示例:

  1. #include <amxmodx>
  2. #define ShowInfo "这是一些信息"
  3. public Show_Print(id)
  4. {
  5. client_print(id, print_chat, "ShowInfo: %s", ShowInfo)
  6. }

小技巧:
在定义 #define 的时候,可以加上 " " 来让识别率变得更高

五、游戏时间 get_gametime()

我们可以获取游戏时间(即:我们在游戏中呆了多久时间,按计算)。并且使用游戏时间可以帮助我们解决许多问题

代码是 : get_gametime() ,获取到的值是浮点数

比如我们在游戏中玩了 5分钟 ,那么通过 get_gametime() 获取到的值就是 300.0

我们甚至还可以配合 数组 存储来完成不可思议的效果

冷却间隔时间(CD)

示例:

  1. #include <amxmodx>
  2. new Float:SaveTime[33]
  3. public plugin_init()
  4. {
  5. register_plugin("插件演示", "1.0", "Hui")
  6. register_clcmd("say /test", "func_test")
  7. }
  8. public test(id)
  9. {
  10. new Float:LeftTime = SaveTime[id] - get_gametime()
  11. if(LeftTime > 0.0)
  12. {
  13. client_print(id, print_chat, "冷却时间剩余: %.0f秒", LeftTime)
  14. }
  15. SaveTime[id] = get_gametime() + 2.0
  16. }

小技巧:
我们可以通过 游戏时间 来判断是否大于或小于等于,
如果是 小于或等于 即代表时间到了,反之 就代表时间还没到。

  1. if( SaveTime[id] <= get_gametime() )
  2. {
  3. //时间到了
  4. }
  5. else
  6. {
  7. //时间没到
  8. }

六、注册客户端命令

我们可以为客户端注册一些命令,以触发一些特定函数

注册的代码为 register_clcmd("注册什么命令", "触发什么函数")

示例:

  1. public plugin_init()
  2. {
  3. register_clcmd("say /abc", "the_func")
  4. }
  5. public the_func(id)
  6. {
  7. client_print(id, print_chat, "触发函数成功")
  8. }
上例中即代表 按控制台输入 `say /abc` 触发了 `the_func` 函数

注意:
1. 当所填写的 被触发函数名 不存在的时候,插件会出现 runtime error 错误(因为找不到被触发函数,所以插件不会继续正常工作)
2. 在 注册什么命令 的地方必须添加 "" 引号包围起来
3. 在 注册什么命令 的地方设定的命令即 控制台 输入的命令。若是需要按 Y键 输入的命令则需要将命令最前面改为 say
4. 若是一个之前被其他插件注册过的命令,却又重新注册一遍,此时你注册的新命令将不会工作,而只能使用之前被插件注册过的命令(简单说:无法在已经注册过的命令上重新注册一遍)

七、让客户端执行命令

我们可以让客户端执行一些指定的命令,比如丢弃武器 drop, 切换到指定武器,甚至还可以 让玩家发送一些Y键的文字信息 让对方说话,如果不爽对方还可以让对方客户端强制退出游戏。

可是,这一切应该怎么实现?

这里我们需要用到 client_cmd 这个代码

他由三个参数组成:

  1. 1. 让谁执行命令(index索引)
  2. 2. 执行的命令是什么
  3. 3. 附加的获取方式

一般我们用不到 参数3, 写法如下

示例:

  1. client_cmd(id, "drop") //让玩家执行控制台的drop命令(丢弃武器,默认G键)

那如果配合参数3应该怎么用?

一般我们用参数3是用来获取的一些数据,比如说:

  1. //踢出这个ID的玩家
  2. client_cmd(id, "kick #%d", get_user_userid(id))

注意:
1. client_cmd只能给有效的客户端玩家执行,如果给非玩家(比如地图本身)那么就会出现控制台错误,无效的实体。

Tutorial End -普通教程结束

你可以查看 上一章(入门) / 下一章(进阶)

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注