03-21-2026, 10:34 PM
(此帖子最后修改于: 03-21-2026, 11:03 PM 由 小鸟unsigned.)
[wiki系列] openmp/samp 脚本基础
来自 SAMP Wiki
目录
- 1 入门!
- 1.1 Include
- 1.2 调用
- 1.3 语句
- 2 函数
- 2.1 调用
- 2.2 定义
- 2.3 参数
- 3 变量
- 3.1 声明
- 3.2 赋值
- 3.3 数组
- 3.3.1 声明
- 3.3.2 访问
- 3.3.3 示例
- 3.4 字符串
- 3.4.1 基本用法
- 3.4.2 转义字符
- 3.5 标签
- 3.6 作用域
- 3.6.1 局部(local)
- 3.6.2 静态局部(static local)
- 3.6.3 全局(global)
- 3.6.4 全局静态(global static)
- 4 控制结构
- 5 关键字
入门!
下面是一个可能最简单的脚本示例:
代码:
#include <open.mp>
main()
{
print("Hello World!");
return 1;
}我们将逐一讲解各个部分,先从第一行开始。
Include
代码:
#include <open.mp>这一行会把
qawno/includes/open.mp.inc 文件中的所有代码加载到你的脚本中,这样你就可以使用它提供的一切内容。其中它还包含了:代码:
#include <args>
#include <console>
#include <core>
#include <file>
#include <float>
#include <string>
#include <time>
#include <omp_core>
#include <omp_player>
#include <omp_actor>
#include <omp_checkpoint>
#include <omp_class>
#include <omp_database>
#include <omp_dialog>
#include <omp_gangzone>
#include <omp_http>
#include <omp_menu>
#include <omp_network>
#include <omp_object>
#include <omp_pickup>
#include <omp_textdraw>
#include <omp_variable>
#include <omp_vehicle>
#include <omp_textlabel>
#include <omp_npc>这些文件包含了 open.mp(samp) 的所有核心功能,因此只需这一行,你就拥有了 open.mp(samp) 的全部函数(函数后面会详细说明)。
调用
接下来是函数调用的两部分。
main() 是你自己编写代码的函数,由外部调用;print(string[]) 是代码在别处定义、由你调用的函数。目前这个脚本只会加载、打印一行字符串(即在服务器控制台输出 “Hello World!”,不带引号——这是所有脚本语言的传统),然后结束。代码:
return 1;这条语句将值 1 返回给调用
main 的地方,告诉它执行结果(具体返回值在这里不重要,但在其他地方很重要)。你现在已经写出了第一个(非常基础)的脚本。如果你在 qawno 中选择“文件 → 新建”,它会给你一个更大的模板,包含所有回调(见下文),包括
main(严格来说它不是回调,但行为类似)。语句
print 和 return 行都以 ;(分号)结尾,分号表示一条语句的结束(语句是一组函数和运算符的组合,用于完成某项操作,类似自然语言中的句子)。大多数人把每条语句写在单独一行,但这不是必须的,只是为了更清晰。下面这种写法同样有效:代码:
main() {
print("Hello World!");
return 1;
}{}(大括号)用于将一组语句组合在一起执行(类似自然语言中的段落)。如果你写成:代码:
main()
print("Hello World!");
return 1;编译器会报错,因为
return 1; 没有被括起来,不属于 main 函数。大括号将多条语句组合成一条复合语句,而函数体只能包含一条语句。没有大括号时,print 和 return 是两条独立的语句,函数只能有一条,所以第二条语句就游离在函数之外,这是非法的。不过,你也可以用逗号运算符
, 来扩展复合语句,但不推荐这样做(不是最佳编码习惯)。示例如下:代码:
main()
print("Hello World!"), return 1;函数
函数是一段完成特定任务的代码,可以从其他地方调用。它还可以将执行结果返回给调用它的地方。
调用
代码:
print("Hello World!");如“入门”部分所述,这会调用
print 函数(定义在 open.mp.inc 中,所以需要 #include),并让它在服务器控制台显示 “Hello World!”。函数由函数名(如
print)和参数列表(用 () 括起来)组成。参数列表向函数传递额外数据。如果没有参数,你就需要成千上万个函数:代码:
printa();
printaa();
printab();
...函数可以有任意数量的参数(从 0 开始,至少支持 128 个):
代码:
printf("Hello World!", 1, 2, 3, 4, 5, 6);现在不用关心这个函数具体做什么,只需知道它有 7 个参数,用逗号分隔。
定义
除了调用已有函数,你也可以自己编写并调用函数:
代码:
#include <open.mp>
main()
{
return MyFunction();
}
MyFunction()
{
print("Hello World!");
return 1;
}这段代码功能与最初的示例完全相同,只是结构不同。当模式启动时
main() 被自动调用,它再调用自定义函数 MyFunction()。MyFunction() 在控制台打印消息,然后返回 1 给 main(),main() 再把这个值返回给服务器。因为
return MyFunction(); 是一条完整语句,你也可以写成:代码:
#include <open.mp>
main() return MyFunction();
MyFunction()
{
print("Hello World!");
return 1;
}但大多数人为了清晰起见还是分开写。你也可以完全不使用
MyFunction 的返回值:代码:
#include <open.mp>
main()
{
MyFunction();
return 1;
}
MyFunction()
{
print("Hello World!");
return 1;
}参数
参数是一种特殊的变量,你不需要自己声明,它来自调用函数的地方:
代码:
#include <open.mp>
main()
{
return MyFunction("Hello World!");
}
MyFunction(const string[])
{
print(string);
return 1;
}这段代码功能相同,但现在我们把要显示的内容传给了
MyFunction()。调用时把字符串 “Hello World!” 传递给函数,存储在名为 string 的变量中([] 表示这是一个数组,后面会解释)。然后调用 print,把 string 的内容传给它(没有引号,所以它是一个变量)。变量
变量本质上是内存中的一块空间,用于存储数据,可以随时读取和修改。变量由一个或多个 cell 组成,一个 cell 是 32 位(4 字节),默认有符号,可存储 -2147483648 到 2147483647(注意 -2147483648 在 PAWN 中定义不完善,显示时可能异常)。由多个 cell 组成的变量叫数组,字符串是一种特殊的数组,每个 cell 存放一个字符(打包字符串每个 cell 可存 4 个字符,但这里不讨论)。
声明
要创建一个新变量,必须先声明它:
代码:
new
myVariable;这会创建一个名为
myVariable 的变量,初始值为 0。赋值
代码:
new
myVariable = 7;声明变量并设置初始值为 7。现在打印它会显示 7。要打印非字符串变量,需要使用前面提到的
printf():代码:
new
myVariable = 7;
printf("%d", myVariable);现在只需知道这会在服务器控制台输出变量的值(当前是 7)。
代码:
new
myVariable = 7;
printf("%d", myVariable);
myVariable = 8;
printf("%d", myVariable);这段代码会先打印 7,然后把变量改为 8,再打印新的值。
变量操作示例(更多运算符请参考其他章节):
代码:
myVariable = myVariable + 4; // 等价于
myVariable += 4; // 增加 4
myVariable -= 4; // 减少 4
myVariable *= 4; // 乘以 4
myVariable /= 4; // 除以 4数组
声明
数组是一种可以同时存储多个数据、动态访问的变量。数组大小在编译时就固定了,必须提前知道需要多少个槽位。最常见的例子是
MAX_PLAYERS 数组,每个玩家一个槽位,确保数据不会互相干扰。代码:
new
myArray[5];这会创建一个有 5 个槽位的数组。不能这样做:
代码:
new
myVariable = 5,
myArray[myVariable]; // 错误!因为 PAWN 在编译时就分配内存,数组大小必须是常量。
访问
要给数组某个位置赋值,必须指定下标(index),下标可以是变量:
代码:
new
myArray[5];
myArray[2] = 7;数组现在的内容是:
0, 0, 7, 0, 0注意:下标从 0 开始计数,而不是 1。
如果你尝试访问
myArray[5](超出范围),可能会导致服务器崩溃。下标可以是数字、变量,甚至是返回值的函数。
代码:
new
myArray[5],
myIndex = 2;
myArray[myIndex] = 7;数组中的元素可以像普通变量一样使用:
代码:
myArray[2] = myArray[2] + 1;
myArray[2] += 1;
myArray[2]++;示例
最常见的数组是
MAX_PLAYERS 数组(MAX_PLAYERS 是一个常量,默认 1000)。下面对比用普通变量和数组处理 4 个玩家数据(假设 MAX_PLAYERS 为 4):传统写法(4 个变量):
代码:
new
gPlayer0, gPlayer1, gPlayer2, gPlayer3;
SetPlayerValue(playerid, value)
{
switch (playerid)
{
case 0: gPlayer0 = value;
case 1: gPlayer1 = value;
case 2: gPlayer2 = value;
case 3: gPlayer3 = value;
}
}数组写法(推荐):
代码:
new
gPlayers[MAX_PLAYERS];
SetPlayerValue(playerid, value)
{
gPlayers[playerid] = value;
}数组写法无论玩家数量多少,都只需要一行代码,清晰且高效。
字符串
基本用法
字符串是一种特殊的数组,用于存放多个字符组成文本。每个字符默认占用一个 cell。字符串以 NULL 终止(遇到 0 就结束,不是字符 '0')。
代码:
new
myString[16] = "Hello World!";这会创建一个可容纳 15 个字符的字符串,初始值为 “Hello World!”。内部实际存储为:
代码:
104 101 108 108 111 0 x x x x x x x x x x(0 是终止符,后面的 x 无关紧要)
你可以像操作数组一样修改字符串:
代码:
new
myString[16] = "Hello World!";
myString[1] = 97; // 97 是 'a'结果变为 “hallo”。
更易读的写法:
代码:
myString[1] = 'a';单引号表示单个字符。
把某个位置设为终止符:
代码:
myString[1] = '\0'; // 或 = 0;字符串会变成 “h”。
转义字符
反斜杠
\ 是特殊字符,用于创建无法直接输入的字符:代码:
new
myString[4] = "\""; // 字符串内容就是一个双引号 "常用转义序列:
| 转义符 | 含义 | 说明 |
|---|---|---|
\0 / EOS |
NULL 字符 | 字符串结束 |
\n |
换行 | Linux 新行(Windows 也可用) |
\r |
回车 | Windows 新行用 \r\n |
\\ |
反斜杠 | 实际输出 \ |
\' |
单引号 | 在单引号中用 '\' ' |
\" |
双引号 | 在字符串中用 \" |
\xNNN; |
十六进制字符 | 用十六进制值设置字符 |
\NNN; |
十进制字符 | 用十进制值设置字符 |
标签
标签是变量的额外信息,用于定义其用途和可使用范围。标签分为强标签(首字母大写)和弱标签。
示例:
代码:
new
Float:a = 6.0;Float 就是标签,表示这是一个浮点数(小数)。代码:
native SetGravity(Float:gravity);这要求参数必须是
Float 类型:代码:
SetGravity(6.0);
new Float:fGrav = 5.0;
SetGravity(fGrav);使用错误标签会产生 tag mismatch 警告:
代码:
SetGravity(MyTag:7); // 错误标签区分大小写。
你可以自定义标签:
代码:
new myTag: variable = 0,
AppleTag: another = 1;直接相加时需要用
_: 去除标签,否则会报标签不匹配。作用域
作用域决定变量在哪里可用。主要有四种:局部、静态局部、全局、全局静态。变量必须先声明后使用。
局部(local)
在函数内用
new 声明:代码:
MyFunc()
{
new var1 = 4;
printf("%d", var1);
{
new var2 = 8;
printf("%d %d", var1, var2);
}
// var2 已失效
}
// var1 已失效局部变量每次进入函数都会重置:
代码:
for (new i = 0; i < 3; i++)
{
new j = 1;
printf("%d", j);
j++;
}输出:
代码:
1
1
1静态局部(static local)
与局部变量作用域相同,但值会在函数多次调用间保留:
代码:
for (new i = 0; i < 3; i++)
{
static j = 1;
printf("%d", j);
j++;
}输出:
代码:
1
2
3全局(global)
在函数外声明,可在任何函数中使用,且永不重置:
代码:
new
gMyVar = 4;
MyFunc()
{
printf("%d", gMyVar);
}全局静态(global static)
与全局类似,但仅限声明所在文件使用:
File1.pwn
代码:
static
gs_MyVar = 4;
MyFunc()
{
printf("%d", gs_MyVar);
}
#include "File2"File2.pwn
代码:
MyFunc2()
{
// gs_MyVar 在这里不存在
printf("%d", gs_MyVar); // 错误
}static 也可用于函数,实现类似“私有”函数的效果。#GTA# #圣安地列斯# #侠盗猎车手# #圣安地列斯联机# #samp# #gta联机# #gtasa联机# #openmp# #omp# #open.mp# #gtasa#
社区交流群: 673335567
论坛: https://open-mp.cn/

