[关闭]
@sogouwap 2019-08-13T08:51:31.000000Z 字数 7837 阅读 13655

Lv.1 — AMXX插件编写教程

初次了解插件


AMXX插件全名为AMXMODX,是基于半条命HL这款游戏开发的可扩展的模块,这个模块由国外大佬编写,可安装到到反恐精英CS上,可通过后期编写插件来实现原本反恐精英CS没有的效果。
可以实现的效果包括但不限于

  • 火箭筒、导弹、追踪类导弹
  • 游戏数据修改(生命值、攻击力、防御力等)
  • 新增武器

插件基础模块

什么是基础模块?
很多事情都必须要有基础才可以实现,比如说建立大楼需要有地基,那么插件也是一样,如果没有基础模块就无法安装插件。

1. 使用控制台进行测试

接下来你会看见控制台返回的信息

2. 返回数据

一般而言,控制台会返回的信息只有两种

控制台检查模块是否安装

1.'显示 无效的命令' 或 '无反应'

这代表 反恐精英CS1.6 没有安装插件。

你可以 点击这里下载

模块安装过程

  • 首先下载上述文件
  • 找到 反恐精英1.6 游戏所在文件夹(如 X:/CS1.6
  • 将压缩包里面的文件替换到 X:/CS1.6/cstrike,选择全部替换
  • X:/ 代表你的CS所在磁盘,并不是指定是X盘

至此, 你的反恐精英CS1.6 插件模块(AmxModx) 已经安装完成。

2. 显示 '无效的命令' 或有反应

这意味着 反恐精英CS1.6 这款游戏的插件模块已经安装完成。
接下来你可以安装单独的插件了,安装独立插件的这个过程分为两步

第一步 -> 放置插件

第二步 -> 修改插件配置文件

  • 打开 反恐精英CS1.6 然后找到 cstrike 文件夹
  • 在上述位置找到并打开 addons/amxmodx/configs/ 文件夹。然后找到 plugins.ini 配置文件
  • 使用 "记事本" 打开配置文件,在该文件中最后一行通过回车新建一行写入 "插件的名字"

例如 :111.amxx 这样的名字

注意:
配置文件中,插件的名字不能写中文名,不然会无法读取插件。

我们知道插件可以安装进去,可是插件是怎么得到的?
这里我们需要先了解一下编译器

插件与由来

我们知道AMXX是插件文件,可是他是怎么来的呢,你也许会说:“这么简单,网上下载就是“,那有没有想过网上的插件是怎么来的呢。

其实,任何插件都是通过编译器得到的。

什么是编译器?

编译器就是将我们写的程序,比如用C, JAVA, C++, 汇编等语言写的程序,经过编译器的转换,把这些语言转换成计算机或者微型处理器能够识别的机器代码,它是由0和1以组成的序列,说白了,就是相当于英语翻译成中文的工具一样。

— 百度知道

如何获取编译器?

你可以在 [ 链接:https://pan.baidu.com/s/1c4ez9i4 密码:d1m6 ] 这里下载编译器,下载完成后可以解压到一个新建文件夹中以备用

编译器如何使用?

代码组成与结构


主结构

每个源代码中由不同的代码块组成,大致包含以下内容:

模块定义


Include 头文件

头文件定义通常在源代码中的最顶部,关键字为 include < 模块名>的内容,这就是模块定义。
他至关重要,是整个源码的心脏。插件的代码由头文件所定义的代码来编写,不在头文件包含的代码,是无法被编译器编译的。

通常常见为:

  1. #include <模块名>

插件使用什么 模块 中的代码进行工作,就在这里定义

常用模块:

常用模块解释


AmxModx

基础模块,必须定义。
他内置了大量代码来让开发者无比强大

代码:

#include <amxmodx>

Fun


这个模块比较适合新手,可以用简单的代码实现常用效果

代码:

#include <fun>

Engine


不太常用的模块,他提供的代码非常复杂,不推荐使用这个

代码:

#include <engine>

FakeMeta


这个模块我们简称为 FM (取自 FakeMeta 的大写首字母)
可实现绝大部分的功能,是一个非常常见的模块

代码:

#include <fakemeta>

HamSanwich


此模块我们简称为 Ham (取 Hamsandwich的前三个字母)
可实现绝大部分的功能,是一个常用的模块

代码:

#include <hamsandwich>


cstrike

可实现大部分功能,是一个比较常用的模块

#include <cstrike>

变量


变量是对“值”的引用,使用变量等同于引用一个值。每一个变量都有一个变量名。

  1. new a

上面的代码声明新建变量a

变量类型

为了方便理解,我们可以把变量想象成一款水杯
当然并不是所有水杯都一样,比如 "玻璃杯",“不锈钢杯”,等等。

所以变量也分为几种可定义的类型,分别是:

临时变量 (Temporary Variable)


这是一种在函数内部的变量,通常其他函数无法读取

临时变量看起来像这样:

  1. public test( )
  2. {
  3. new abc
  4. }

上述例子中的 abc 即为临时变量,如果不特殊处理。将无法被其他函数获取值

全局变量 (Global Variable)


全局变量不同于临时变量,他可以被其他函数所获取,通常用于变量判断,如:“是否这局已经使用过了"、"当前累计的造成的伤害是多少","保存以及存储的坐标",等等。

这种变量通常写在 注册部分的上面,并且 不处于函数内部

一般看起来像这样:

  1. #include <amxmodx>
  2. new LaveSpace //定义的全局变量
  3. public plugin_init()
  4. {
  5. register_plugin("test", "1.0", "Test Plugin")
  6. }

常见位置处于 #includeplugin_init() 的中间

上面的 `LaveSpace` 就是全局变量

而除了上面两种变量类型,还有其他一种变量类型可以来组合搭配

浮点变量Float


除了临时变量全局变量,这两种外。
还有一种叫浮点变量,他可以配合 临时变量全局变量 来使用,是一种组合型的变量。

浮点是很常用的一种变量类型,和普通变量的区别是:这种变量他只能存储浮点数(带小数的数,比如 12.5

代码看起来像这样:

  1. new Float:变量名

技巧:
- 浮点变量标记 Float 的大小写不要写错了,不然会出现编译错误。

布尔(Bool)


这种变量又不同于浮点变量,但他可以以 "临时变量"或"全局变量" 的形式存在。

此变量仅有两个标记方式:

  1. new bool:变量名

Bool变量的 标记方式

true 代表是的、真
false 代表否定、假
我们可以通过下面的代码进行标准型判断

  1. new bool:atp
  2. public aa()
  3. {
  4. //如果atp变量为真
  5. if(atp == true)
  6. {
  7. }
  8. //反之如果atp变量为假
  9. else if( atp == false)
  10. {
  11. }
  12. }

注意:
布尔变量浮点变量 是独立的两个变量类型。
要么用布尔变量,要么用浮点,仅仅只能选择一种

函数


解释

函数的英文名为 function,也就是功能。

打一个简单的比方:

"在超市买散装食品的时候,你需要一个口袋,将口袋把散装食品装起来,然后去称称。然后就可以得到一个标签,贴在这个口袋上,收银员会扫描这个标签上的条形码,来获取称称时的价格

理解方法:
"散装食品" 我们就可以理解为许多许多的单个代码,而 "口袋" 就是将 "单个代码" 封装了起来,变成了一个 "方便拿走" 的东西(即功能)。

现在言归正传

在源代码中,函数一般以 public 开头,例如:

  1. public 函数名 ()

同理,”函数名“ 也可以像 ”变量名“ 那样由你自己去定义。

函数类型

函数可简单分为三种类型

公用函数(public)


这是很常见的函数类型。

比如说:

  1. public 函数名()

函数名可以由你自己去写,但需要你自己记得住

非公用函数


这种变量是一种比较特殊的变量
他不同于公用函数,这种变量没有 public 定义部分

他的代码仅仅是:

  1. 函数名()

有疑惑?
不用担心,这种方式比较少见,一般不这样写

待调用函数(stock)


这是一种特殊的函数,他不同于非公用函数公用函数
他只在被其他代码调用的时候才被读取,除此之外他都以注释代码段的方式存在。

  1. stock 函数名()

注意:
- stock函数的内容,只在有其他函数调用的时才会被读取并触发。
- stock函数在没有被触发的时候,即使里面有错误也不会影响编译。

索引 (Index)


解释

比方:

就如一个人的名字一样,可以让你知道 “他” 是谁

索引是用于函数传递信息用的一种标识。
一般由 ”触发者“ 所引用,他的位置常见处于函数名字后面的括弧内

比如说:

  1. public test( 索引 )

对了,有时索引不止一个

因为可能函数会同时传递多个参数,所以不止一个。

比如说:

  1. public addhealth(id, health)
  2. {
  3. }

提示:
"索引" 又可以称为 "参数",但参数不一定是索引。

字符串 (String)


解释

来自 "百度百科" :

字符串或串 (String) 是由数字、字母、下划线组成的一串字符

在代码中,含有 “ “ (双引号) 的内容就是字符串

比如说:

  1. "你好"
  2. "Hatsune Miku"
  3. “厉害了我的哥6666
  4. “你在这局已经购买了这个道具,无法再次购买“
  5. 139659650

提示:
对于 ”字符串“ 没什么太多限制,不过要注意的是,字符串必须两边都有"" (双引号),不然会造成编译错误。

数组(Array) [ ]


来自 "百度百科" 的解释:

数组是一种存放一组值的集合,这组值可以是数字、字符串或者其他东西

皮一下我非常开心

之前我们说了变量,可以用水杯来代替,那数组怎么通过这种方式理解呢
那就是水杯里面装的 "水"。

水装的多少可以理解为 “数组” 内的大小
数组中可以存储值,而数组又是在变量基础上来存储的,所以必须要有变量才能存储 "数组"

言归正传

数组的作用是来存储每个玩家或每个实体的单独数据。
为什么要单独存储?因为如果仅仅用一个变量来存储所有玩家的数据,那么就会导致,上一个玩家的数据被下一个玩家的数据所覆盖,这是无论如何也不想发生的事情。

这时候就要用到数组来解决

一个变量可以存储一名玩家的数据,如果想存储多个玩家的数据,并且同时使用这个变量,就会为这个变量添加数组来存储。

数组(Array)是什么样子

在代码里,在变量命中后面写上[ ],视为数组。
数组一般和变量共同使用,他的作用是让 “同一个变量存储更多的东西"

例如:

  1. new plane[]

上例代码中 [] 就是数组的容器,
这个容器可以设定一个大小,比如:[3]

数组的大小(Array Size)

数组 是一个从0开始的容器,在 [ ] 里面设定的值叫数组最大值

例如:

  1. `ArrayTest[3]`

这个变量中的数组,最大值是 "3"
所以数组从0开始,包含了[0][1][2][3]

函数传递及触发


什么是传递?

在学校运动会中,有一种比赛叫:“接力跑”,由一名同学跑完规定一圈后,将手中的棒子传递给下一个待跑的同学,这位同学接到之前同学拿过来的棒子后,就会开始奔跑,为胜利而战。

就如 ”接力棒” 一样,函数之间可以传递一些数据,让其他函数跟着执行未完成的任务。

比如说:

  1. public aaa()
  2. {
  3. bbb()
  4. )

aaa 函数触发了 bbb 函数

什么是触发?

像我们家里电灯开关一样,当我们打开开关的时候,电灯会发亮,反之会熄灭。这就是电灯开关触发了电灯泡,让其开启或关闭。

那在 ”反恐精英CS1.6“ 这款游戏中,有什么可以明显看出的触发吗?
有的,比如说:在 cs_assault(72街仓库)这张地图中,作为CT的你去解救人质,进入人质房后,对着人质按 “E” 键,他们便会跟随你。

除此之外,有一些地图中,会有一种开关按 “E” 才能开门或开灯。

也比如:一些道具插件,输入某些命令才能打开菜单或者得到道具。

一些相同,一些不同

触发的方式和传递无比接近,
但传递是由 "前一个函数"传递"给另一个函数",而触发时由一个"命令"或者一个"行为"来传递

判断以及表达式


在代码中可以做各种判断,来实现你需要的表达

判断是以 if 开头然后接上括号,在括号内的内为 表达式

  1. if(表达式)

基础表达式


表达式 释义
&& 并且
> 大于
< 小于
=
大于或等于
<= 小于或等于
!= 不等于
== 绝对等于

举个栗子


若要判断 World 变量大于4 并且小于 10

代码为:

  1. if( World > 4 && World < 10 )
  2. {
  3. }

若要判断 Earth 变量大于 6 或者小于 10

代码为:

  1. if( Earth > 6 || Earth < 10 )
  2. {
  3. }

运算符


代码中的运算符就像计算器中的加减乘除 一样

常用运算符

运算符 释义
+ 加上
- 减去
* 乘以
/ 除以
+= 加上指定值
-= 减去指定值
*= 乘以指定值
/= 除以指定值

示例

比如要表达 5 + 4 > 6 这种判断,可配合判断写出如下代码

  1. if( 5 + 4 > 6 )
  2. {
  3. }

大括号组合


在代码中,当函数里的内容超过一行,这时候我们需要用到大括号 { } 把内容包围起来。

比如说:

  1. public test (id)
  2. {
  3. new hello
  4. next_function()
  5. }

但如果在一个函数中出现了多个条件,这种情况下括号就需要出入的括号数量统一

示例:出入括号统一

  1. public test(id)
  2. { //入1
  3. if(abc)
  4. { //入2
  5. } //出 1
  6. } //出 2

在上述示例中,有两个 { 号,所以结尾也要 两个 },也就是说需要将代码内容完全包围起来

注释


//开头直到行末的字符被视为行注释,被注释的代码编译器会自动忽略

  1. //这是一行注释
  2. if(!is_user_alive(id)) //这也是注释

除了行注释,还有一种是用 /*...*/将多行代码包裹起来,把一大 "块" 视为一个注释

  1. /* 从这里开始是块注释
  2. 仍然是注释
  3. 仍然是注释
  4. 注释结束 */

THINK

想一下,怎么将下面的代码注释掉,使他不再执行

  1. public client_connect(id)
  2. {
  3. client_print(id, print_chat, "test")
  4. }

TIP:
注释后的代码将不会被编译器所读取到,所以游戏中也不会有那段代码原有的效果

插件初始化 ( INIT)

每个插件在游戏载入地图的时候,会初始化。
所触发的函数就是 plugin_init,我们可以在这个初始化函数中写上需要为这个插件所注册的信息

通常我们需要注册的基本信息就是,“插件的名字”,“版本号”,"作者名字”

这里我们要用到下面这个代码:

  1. register_plugin(注册的信息以及参数 ) //基本信息注册

这个代码拥有三个可供填写的参数,他们以字符串存在,并且每个参数会以 (,)逗号来隔开。

这三个参数分别是:

如果以字符串配合逗号来分隔,就是下面这样

  1. register_plugin("插件名字", "版本号", ”作者名")

配合基础代码组合后

  1. #include <amxmodx> //定义所需头部模块
  2. //插件初始化
  3. public plugin_init()
  4. {
  5. //注册插件基本信息
  6. register_plugin(“插件名字", "版本号", ”作者名")
  7. }

提示:
- 上面的这个就是代码的基础,任何代码都会以这种形式展开。
- 双引号是必须是切换至英文输入法才可以,不能写中文输入法的双引号,不然会无法编译插件(报错)。

赋值


变量是能赋值的,例如将 Test 变量赋值为 4

  1. new Test = 4

当赋值后,做判断的时候就可以判断这个变量中的值,例如

  1. if( Test > 0)

除此之外,还有另一种赋值方式,和上面的代码基本一样,区别只是 不在建立变量的时候直接赋值,看上去像这样

  1. new Test
  2. if(abc < 0)
  3. {
  4. abc = 6
  5. }

通常来说第二种非立即赋值,大部分情况下会先做一个判断,然后再来赋值

小提示:
- 任何变量在创建后,他的值都为 "0"。如果在创建变量时直接赋值,那么这个变量在创建后值就会发生变化,而不是默认的 "0"
- 注意,不可以给 "Bool(布尔)“ 变量赋于其他的值(因为他只拥有 false以及 true)

多重判断


超过两个(包含两个)的判断视为多重判断。
多重判断可以让我们写出的代码更精确的执行我们需要的效果。

常见写法有两种,一种是堆叠多重

将两个或两个以上的判断堆在一起,这种情况下就不用写 大括号 { } 来包括起来

  1. if( iTestB > 2) //这里不需要写 {
  2. if(TestA < 3)
  3. {
  4. //做些什么
  5. }

上面代码中,由于从第二个判断后没有更多判断表达,所以需要加上 { } 大括号

单行多重

除了堆叠,还有另一种是单行多重,也就是将所有的表达式都写在一个判断中

  1. //表达式为:iTest > 2 并且 TestA < 3 或者 iTestC 刚好等于 5
  2. if( iTestB > 2 && TestA < 3 || iTestC == 5)
  3. {
  4. //要做些什么
  5. }

判断的时机

一般会在一些特殊的时候进行判断,例如说:

小提示:
多利用多重判断可以让判断更精确、更有效、减少BUG错误

循环


要计算1+2+3,我们可以直接写表达式:
1 + 2 + 3; // 6

要计算1+2+3+...+10,勉强也能写出来。
但是,要计算1+2+3+...+10000,直接写表达式就不可能了。
为了让计算机能计算成千上万次的重复运算,我们就需要循环语句。

FOR

AMXX的循环有两种,一种是 for 循环,通过初始条件、结束条件和递增条件来循环执行语句块。

  1. new i
  2. new n = 1000
  3. for(i=1; i<33; i++)
  4. {
  5. n = n - 17
  6. }

让我们来分析一下for循环的控制条件:

会怎么样

当这段循环开始执行后,会执行32次,每次会触发 n = n - 17,也就是 32 x 17 = 544,而默认变量n创建时赋值为1000,也就是说这段代码将会变成下面这样计算

1000 - (32 x 17) = 456

在游戏算出来的结果:
这是游戏中的效果 - 循环FOR

While

for循环在已知循环的初始和结束条件时非常有用。
而上述忽略了条件的for循环容易让人看不清循环的逻辑,此时用while循环更佳。

while循环只有一个判断条件,条件满足,就不断循环,条件不满足时则退出循环。比如我们要计算100以内所有奇数之和,可以用while循环实现:

  1. new x = 0
  2. new n = 99
  3. while (n > 0) {
  4. x = x + n
  5. n = n - 2
  6. }

值得注意

循环是让计算机做重复任务的有效的方法,有些时候,如果代码写得有问题,会让程序陷入“死循环”,也就是永远循环下去。AMXX插件的死循环会让游戏无法执行当前代码的逻辑,某些游戏会直接挂掉或闪退(卡死未响应等),因此,要特别注意死循环的问题。

提示:
在编写循环代码时,务必小心编写初始条件和判断条件。

Tutorial End -初级教程结束

你可以查看:下一章 (入门)

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