• 0 票 - 平均分 0
  • 1
  • 2
  • 3
  • 4
  • 5
[wiki系列] openmp/samp脚本基础
#1

[wiki系列] openmp/samp 脚本基础



来自 SAMP Wiki



目录





入门!



下面是一个可能最简单的脚本示例:

代码:
#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(严格来说它不是回调,但行为类似)。

语句



printreturn 行都以 ;(分号)结尾,分号表示一条语句的结束(语句是一组函数和运算符的组合,用于完成某项操作,类似自然语言中的句子)。大多数人把每条语句写在单独一行,但这不是必须的,只是为了更清晰。下面这种写法同样有效:

代码:
main() {
    print("Hello World!");
    return 1;
}

{}(大括号)用于将一组语句组合在一起执行(类似自然语言中的段落)。如果你写成:

代码:
main()
    print("Hello World!");
    return 1;

编译器会报错,因为 return 1; 没有被括起来,不属于 main 函数。大括号将多条语句组合成一条复合语句,而函数体只能包含一条语句。没有大括号时,printreturn 是两条独立的语句,函数只能有一条,所以第二条语句就游离在函数之外,这是非法的。

不过,你也可以用逗号运算符 , 来扩展复合语句,但不推荐这样做(不是最佳编码习惯)。示例如下:

代码:
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/
  回复


论坛跳转:


浏览此主题的用户: 1 位客人