| 欢迎, 游客 |
您必须先 注册 才能在我们的网站上发帖.
|
| 论坛统计 |
» 会员总数: 5
» 新进会员: 老白金
» 主题总数: 13
» 帖子总数: 17
完整统计
|
| 在线用户 |
当前共有 1 位用户在线 » 0 会员 | 1 游客
|
|
|
| 脚本基础 |
|
发布者: siwode - 02-27-2026, 12:48 AM - 板块: 教程
- 暂无回复
|
 |
大家好,今天我想给大家讲讲 "enum"、"if"、"else"、"else if"。
那么开始吧:
Enum
-
它是一个数组,你可以在其中存放多个变量(变量可以是不同类型)。声明方式如下:
PHP代码: enum test { peremen1, peremen2, peremen3 }
注意:最后一个变量后面不需要加逗号!
另外,为了让这个数组正常工作,还需要再声明一个变量,用它来访问枚举中的各个字段:
PHP代码: new cars[test]; // 这个变量是全局共用的(所有人一份) new cars[MAX_PLAYERS][test]; // 这个变量是每个玩家各自一份
示例:
好,先说如何使用“共用变量”的情况。比如你做了 3 个仓库,对于所有玩家来说,仓库里的物资数量当然是一样的,那么就很适合用这种方式:
PHP代码: enum test { sklad, sklad1, sklad2 } new sss[test]; // 现在来使用这个变量: sss[sklad] = 10; // 这里把它的值设为 10
后续你可以按自己的需求自由发挥。下面我们来看第二个例子:让每个变量都针对每个玩家单独保存:
PHP代码: enum test { pLevel, pExp, pMoney } new Player[MAX_PLAYERS][test]; // 变量这样使用: public OnPlayerConnect(playerid) { Player[playerid][pMoney] = 500; // 这里在玩家连接时,把 money 设为 500 return 1; }
if , else , else if
-
条件语句,用于做各种判断/检查。
先给大家介绍一下常见的运算符(用于条件判断):
&& - 且(AND)
|| - 或(OR)
== - 等于
!= - 不等于
>= - 大于或等于
<= - 小于或等于
> - 大于
< - 小于
示例:
首先,我来演示一下如何使用第一个运算符 &&:
PHP代码: new test = 1; // 创建变量并赋值为 1 new ttt = 2; // 创建变量并赋值为 2 // 现在进行判断: if(test == 1 && ttt == 2) // 如果 test 等于 1 且 ttt 等于 2,那么: { // 执行代码 }
“或”运算符 || 也是一样的,只是把“且”换成“或”。我想你已经明白如何写判断了。
下面我们来讲 else(否则) 和 else if(否则如果):
PHP代码: new test = 10; // 创建变量并赋值为 10 if(test == 10) // 如果 test 等于 10,那么 { // 执行代码 } else // 否则 { // 执行代码 } // 顺便说一下,你可能注意到了,else 也常用于对话框中,例如 DIALOG_STYLE_MSGBOX: if(dialogid == 1) // 如果对话框 ID 等于 1 { if(response) { // 玩家点了第 1 个按钮时的动作 } else // 否则 { // 玩家点了第 2 个按钮时的动作 } }
下面我给出一个 else if 的例子:
PHP代码: new test = 5; // 创建变量并赋值为 5 new ttt = 1; // 创建变量并赋值为 1 if(test > ttt) // 如果 test 大于 ttt { // 执行代码 } else if(test == ttt) // 否则如果 test 等于 ttt { // 执行代码 } else // 否则:如果 test 不大于 ttt 且不等于 ttt,那么 { // 执行代码 }
到这里就结束了。如果有问题,欢迎提问!
作者: Mike
P.S. 部分内容参考了教程。
|
|
|
|
| 如何将模组(脚本)拆分为多个 Include 文件(包含文件) |
|
发布者: siwode - 02-27-2026, 12:41 AM - 板块: 教程
- 暂无回复
|
 |
本主题写给那些还没有完全理解“把模组拆分成 include(inc)文件”意味着什么的人。很多人把它理解为:从主文件(gamemode.pwn)里剪下一些代码片段,然后复制到单独的文件(name.inc)里。这个方法不能说好,反而更偏向于不好。
这种拆分代码的逻辑通常是:“这里行数很多,我把它们挪到单独文件里,这样主模组里行数就少了。”但在后续开发中,这反而会把事情搞复杂:你不得不同时打开一堆文件来回切换,还要一直记住调用顺序、每个变量/函数的作用域等等。
我会尽量解释:怎样做会更好;以及到底应该如何理解“模块化”,并且如何把这一切和模组(gamemode)配合起来。这里我所说的“模块”,指的是一个完全自治或部分自治的系统,它有自己的 API 函数用于交互,并被放在一个或多个独立的 include 文件中。
核心原则:所有交互(处理流程)发生在模组里,而计算/实现细节放在 include 文件里完成。
首先,你需要确定将会使用哪些系统。接着把这些系统分成两类:独立 与 依赖。
“独立”表示它可以脱离其他系统单独使用;“依赖”表示它需要与其他系统配合使用。在模组里连接 include 的顺序应该是:先引入 独立 系统,然后再引入 依赖 系统。
那怎么判断一个系统属于哪一类?
账号系统 是服务器上最重要的系统,没有它几乎无法想象服务器如何运行。这个系统几乎与所有其他系统都有关联。关键要理解:我们是在其他系统中调用账号系统的函数,而不是在账号系统内部去调用其他系统的函数。因此它属于 独立 系统(模块)。它应该包含什么?例如:
代码: CheckPlayerAccount(playerid) - 检查账号是否存在(是否已注册)
GetPlayerAccountID(playerid) - 获取账号 ID
GetPlayerAccountName(playerid) - 获取账号(玩家)名称
我们来详细看看 CheckPlayerAccount。它应该用在哪里、做什么?我们在 OnPlayerConnect 中调用它。在这个函数里,我们向数据库发送 SELECT 查询,根据玩家名字查找玩家信息。
立刻会有个问题:名字从哪里获取、存在哪里?你当然可以在 OnPlayerConnect 里用 GetPlayerName 去取名字,但玩家名字本质上属于“账号信息”,对吧?既然如此,与其在 OnPlayerConnect 里调用 GetPlayerName,不如把这一步移动到 CheckPlayerAccount 里更合理。
代码: [gamemode.pwn]
public OnPlayerConnect(playerid)
{
CheckPlayerAccount(playerid);
return 1;
}
代码: [account.inc]
new account_name[MAX_PLAYERS][MAX_PLAYER_NAME];
#define GetPlayerAccountName(%0) account_name[%0]
stock CheckPlayerAccount(playerid)
{
GetPlayerName(playerid, account_name[playerid], MAX_PLAYER_NAME);
static const string[] = "SELECT * FROM `accounts` WHERE `name` = '%e' LIMIT 1;";
new query[szeof string + MAX_PLAYER_NAME];
mysql_format(db, query, sizeof query, string, account_name[playerid]);
mysql_tquery(db, query, "OnCheckPlayerAccount", "i", playerid);
return 1;
}
再说两句数组 account_name 和宏 GetPlayerAccountName。既然我们在写一个独立系统,最好让它拥有自己的 API 函数,以及用来存储信息的变量/数组。专门写一个返回 字符串 的函数并不是最佳实践,所以我们用 new 声明数组 account_name,让它可以在其他 include 中被访问。然后我们做了一个“看起来像函数”的宏:GetPlayerAccountName。
那为什么要用宏/函数,而不是直接访问变量/数组?
首先是为了避免未来自己犯错;同时提升可读性,避免以“不正确的方式”使用变量/数组。另一个便利是:所有 API 函数都能在命名上有自己的“根”,例如 Account,让你一眼知道这个函数属于哪个系统。需要强调:为每个变量都做一个函数/宏不是硬性规则,只是建议;有时直接用会更合理。
发送查询后,我们在 OnCheckPlayerAccount 中等待结果。现在要决定:这个函数应该写在模组里还是 include 里?这里我们把它写在 include 里。
在数组 account_name 后添加:
代码: [account.inc]
static enum E_ACCOUNT_INFO
{
account_id,
}
static account_info[MAX_PLAYERS][E_ACCOUNT_INFO];
在宏 GetPlayerAccountName 后添加:
代码: [account.inc]
stock GetPlayerAccountID(playerid)
{
return account_info[playerid][account_id];
}
现在创建 OnCheckPlayerAccount:
代码: [account.inc]
forward OnCheckPlayerAccount(playerid);
public OnCheckPlayerAccount(playerid)
{
if(cache_num_rows())
{
cache_get_value_name_int(0, "pID", account_info[playerid][account_id]);
// 加载其余所有信息
return OnPlayerConnected(playerid, true);
}
else
return OnPlayerConnected(playerid, false);
}
那么 OnPlayerConnected(playerid, bool: status) 是什么?在哪里用?我们传入的参数是什么?
这个函数表示:我们已经向数据库查询并拿到了该玩家的响应。参数 status 表示玩家是否已注册。
如果找到了任何信息,就加载并用 true 调用 OnPlayerConnected;否则用 false。这个函数的使用应该在 gamemode.pwn 中,但它的 forward 声明应写在 include 中。
代码: [account.inc]
forward OnPlayerConnected(playerid, bool:status);
代码: [gamemode.pwn]
public OnPlayerConnect(playerid)
{
CheckPlayerAccount(playerid);
return 1;
}
public OnPlayerConnected(playerid, bool:status)
{
if(status)
// 登录/授权
else
// 注册
return 1;
}
这样一来,我们就完成了:从账号系统调用函数,在 include 内部完成处理,然后把结果回传到模组。
模组 - include / include - 模组 的交互关系应该大体都遵循这种思路。
再说 依赖 系统,例如 派系。流程类似,只不过我们允许在该系统里调用其他系统的函数,比如账号系统:拿到玩家 ID 或名字后再做后续处理。
本质上就是:把该系统的所有动作都放进它的 include 里,而把得到的结果交给模组进行处理。
如果你很难正确分配动作、尤其是遇到作用域问题,那通常就说明这段代码应该挪回模组中。
还可以谈谈更复杂的系统:当一个系统使用不止一个 include 时,可以创建一个单独的文件夹,把需要的 include 都放进去。
比如你有一个背包(inventory)系统:它需要保存玩家基础数据、UI 相关数据、以及与玩家交互的函数。你可以创建 inventory 文件夹,并创建 core 和 ui 两个 include。
ui 里放绘制 UI 所需的一切,而 core 放主要数据与交互逻辑。
如果在模组里这样引入:
代码: #include "../modules/inventory/core.inc"
#include "../modules/inventory/ui.inc"
会出现问题:在 core 里无法访问 ui 的函数。
如果这样引入:
代码: #include "../modules/inventory/ui.inc"
#include "../modules/inventory/core.inc"
又会出现相反的问题:在 ui 中无法获取 core 中的数据。
怎么解决?当然可以把一切都合成一个 include,让常量和函数彼此可见,但这里换个方式:把 ui include 在 core include 内部引入。
代码: [core.inc]
// 创建所有必要的宏、常量、数组和变量
#include "../modules/inventory/ui.inc"
// 创建与玩家交互的函数
而在模组中只保留对 core include 的引入。这样就把系统的一个 include 的引入 “隐藏”在另一个(主)include 里,作用域问题也就不存在了。
再补充一点:凡是只在 include 内部使用的变量/函数,应该用 static 来创建,避免它们在其他地方被随意调用。
另外,也可以在函数名前面加下划线 “_” 来明确表示:该函数不对其他 include 暴露;例如 OnCheckPlayerAccount 可以写成 _OnCheckPlayerAccount。
当你有一个“由多个 include 组成”的系统(如背包),并且你需要某个函数在所有被引入的 include 中可见、但在模组中不可见时,单纯用 static 不行(因为 static 会导致其他 include 也看不到)。这里有个绕过方法:
代码: [core.inc]
stock TestFunction()
{
return 1;
}
// 在 core.inc 的末尾
#define TestFunction _TestFunction
我们先把函数做成全局的,然后再“隐藏”它。由于宏是在 include 的末尾定义的,所以它对模组生效,而对 include 本身不生效。
同时宏在调用解析上优先于函数,因此通过这个宏,我们把对 TestFunction 的调用重定向到一个不存在的函数 _TestFunction。这样如果在模组里调用 TestFunction,就会报错:该函数不存在。
再补充:还有一种方法,可以在不同 include 中使用相同的函数名,但该函数在模组中无法被调用。可能不太常用,不过示例如下:
代码: [core_1.inc]
stock TestFunction()
{
return 1;
}
// 在 core_1.inc 的末尾
static stock PREFIX1_Test(){}
#if defined _ALS_TestFunction
#undef TestFunction
#else
#define _ALS_TestFunction
#endif
#define TestFunction PREFIX1_TestFunction
代码: [core_2.inc]
stock TestFunction()
{
return -1;
}
// 在 core_2.inc 的末尾
static stock PREFIX2_Test(){}
#if defined _ALS_TestFunction
#undef TestFunction
#else
#define _ALS_TestFunction
#endif
#define TestFunction PREFIX2_TestFunction
最后我个人建议:给自己的系统写注释,这样未来你能更清晰地知道:该系统哪些内容能在模组里使用。
我通常会在引入之前加一个提示:
代码: /*
Name_... -> function
NAME_... -> define / const
name_... -> var
# -> define
* -> const
@ -> callback
i -> iterator
e -> enum
p -> pvar
s -> svar
- -> new
> -> function
cmd -> command
*/
然后像这样引入 include:
代码: #include "../modules/account.inc"
/*
# INVALID_ACCOUNT_ID (-1)
# GetPlayerAccountName(%0) (account_name[%0])
# GetPlayerAccountPassword(%0) (account_password[%0])
* LEN_ACCOUNT_NAME (MAX_PLAYER_NAME)
* SIZE_ACCOUNT_NAME (LEN_ACCOUNT_NAME+1)
* LEN_ACCOUNT_PASSWORD (64)
* SIZE_ACCOUNT_PASSWORD (LEN_ACCOUNT_PASSWORD+1)
@ OnPlayerConnected(playerid, bool:status)
i Account
- account_name[MAX_PLAYERS][SIZE_ACCOUNT_NAME]
- account_password[MAX_PLAYERS][SIZE_ACCOUNT_PASSWORD]
> CheckPlayerAccount(playerid)
> GetPlayerAccountID(playerid)
*/
|
|
|
|
|