2. 创建拓展包与新武将#
2.1. 扩展包的内容#
如在上一章所述,新月杀的游戏扩展文件全部位于packages文件夹。我们先看看游戏中现有的扩展是怎样的。打开packages/shzl(神话再临):
拓展包shzl的内部文件#
2.1.1. init.lua#
init.lua文件是扩展包的核心,严格意义上,扩展包只会加载init.lua,所以扩展中其他所有的lua文件都需要通过init.lua来加载。打开shzl的init.lua:
1require("packages.shzl.i18n.en_US")
2
3local prefix = "packages.shzl.pkg."
4
5local wind = require (prefix.."wind")
6local fire = require (prefix.."fire")
7local forest = require (prefix.."forest")
8local mountain = require (prefix.."mountain")
9
10local shadow = require (prefix.."shadow")
11
12local thunder = require (prefix.."thunder")
13
14local god = require (prefix.."god")
15
16Fk:loadTranslationTable{ ["shzl"] = "神话再临" }
17
18return {
19 wind,
20 fire,
21 forest,
22 mountain,
23 shadow,
24 thunder,
25 god,
26}
我们可以看到通过require,对应目录下的wind风、fire火等子扩展包都加载进来了。同理,也可以像标准包一样require要用的翻译文件、辅助函数。总而言之,只有通过require才能加载init以外的lua。
我们简单地解析一下这几行代码的作用,以第1行为例:
local prefix = "packages.shzl.pkg."
local wind = require (prefix.."wind")
这行代码的意思是“定义一个局部变量——wind,然后将require的值赋给wind”,
wind前面的
local代表的就是它是 局部变量 ,没有了local,它就是 全局变量 。 全局变量和局部变量都是 变量 。变量在使用前,需要在代码中进行 声明 ,即创建该变量;在lua里面,local a,就是创建了一个局部变量a。 变量可以被访问,我们可以拿他来参与运算,也可以再赋值。 一般而言,要 慎用全局变量 ,因为它们很容易相互打架,造成污染。全局变量会被编辑器标为红色。=便是编程中最常见的 赋值 符号,代表将右边的东西赋给左边。(可不是相等的意思,相等是==)在lua里面,local a = 5,就是创建了一个局部变量a并用5为其赋值。require则是本行代码最重要的部分,它返回之后紧跟着的双引号内的lua文件所代表的全部东西,什么都有。 下面几行都是相同的道理,分别载入了风火林山阴雷神7个子扩展包,每个文件都有各个子扩展包的武将,例如曹仁、张角就在packages/shzl/pkg/wind/init.lua文件里面。init.lua或者别的lua的最后都会有一个
return,意思是返回整个模块,代表对整个文档的总结。 这里返回了我们在上方定义的所有引用,并用{}包成了一个整体。 return中的wind就指向前面声明过了的wind。局部变量在声明完毕之后,引用的时候就不用local啦(不然就变成再创建一个局部变量了)。
每个扩展包lua都需要return一个扩展包,否则这个扩展包就是不存在的。
提示
require 会将全局变量也加载进来,因此代码中要慎用全局变量。
提示
当然也可以在init.lua塞进扩展包全部的内容,standard标准包就是这样的。理论上这也可以,但是还是分开比较好,尤其是有下文所述限制的前提下。
2.1.2. 子扩展包#
一个大的扩展包系列会含有多个小扩展包,例如神话再临的风林火山,一将成名的2011~……等。子扩展包是游戏中武将一览的划分区间。这样做既是为了分类美观、查找方便,也是限制单个lua文件大小。
提示
Lua语言规定单个文件至多只能有200个局部变量,因此根据估算,我们建议一个子扩展包的武将数量不要超过40。
2.1.3. audio, image#
顾名思义是存放扩展包语音、图片的文件夹。对于我们的第一个扩展做得简单些,只需要了解:audio中的death为武将阵亡语音,skill为技能语音;image中的general为武将图片。
2.1.4. 其他文件#
包括开源声明、git托管文件等,先不管它们。
2.2. 创建拓展包#
我们模仿shzl的形式创建一个扩展。首先在packages文件夹下建立一个新文件夹。老传统,我们的第一个扩展就叫study学习包~
新建文件夹#
然后在study文件夹中创建一个init.lua文件。可以直接从其他包复制,也可以新建一个txt文本然后改名。用VSCode打开它,但在修改之前,请先稍微动一下手,点击右下角这一块,将你的默认缩进改为空格:2,
编码改为UTF-8(默认是这个,但最好确认一下)
注意编码必须是UTF-8,空格=2只是推荐缩进#
然后修改代码,加载子扩展包:
新建文本文件并改名#
在study文件夹中,我们先创建一个扩展包合集pkg文件夹,这里用来存放我们所有的子扩展包。 image和audio暂时先不着急创建
新建pkg文件夹#
最后开始新建我们的xuexi子扩展包。
新建xuexi子扩展包#
然后,让我们回到study文件夹中的init.lua文件:
1local xuexi = require "packages/study/pkg/xuexi"
2
3return {
4 xuexi,
5}
提示
我们在代码中利用到的标点都是半角符号,也就是英文输入模式下的标点。比如第1行里的引号不是
“”而是"",第4行的逗号不是,而是,。(当然,通过设置输入法,我们也可以在中文输入模式下打出半角符号~)注意第4行开头的缩进。由于这一行属于下一个层级,我们需要往后缩进,让代码的结构更加清晰。在新月杀里,我们约定缩进2个空格。
以UTF-8无BOM格式编码。
所有文件尾必须留一个换行符,敲一个回车即可。(在这里的代码片段中,可能无法看到最后的空行,看示例代码更为清楚)
重要
我们现在所做的一切操作结果都可以在 引用示例.zip 中找到,可以多多参考对照。
然后我们进入study/pkg/xuexi文件夹,创建init.lua文件和一个skills文件夹: init.lua主要用来初始化我们子扩展包xuexi的内容,skills文件夹是保存子扩展包xuexi的武将技能的地方。
现在我们将xuexi文件夹下面的init.lua,修改为:
1local extension = Package:new("xuexi")
2extension.extensionName = "study"
3
4extension:loadSkillSkelsByPath("./packages/study/pkg/xuexi/skills")
5
6Fk:loadTranslationTable{
7 ["xuexi"] = "学习",
8}
9
10return extension
1行,声明以创建局部变量
extension,将其赋值为创建出的名为xuexi的扩展包。这里的名字是子扩展包的名字。 接下来的代码中,extension就将代表整个子扩展包。2行为这个子扩展包,也就是
extension,指定extensionName,也就是它所属的大扩展包,其名字必须为我们文件夹的名字, 整个大扩展包都是根据这个名字寻找路径的,包括lua,包括图片,所以不要填错。4行是加载函数,加入该函数后,扩展包就会自动加载skills里面所有的技能,即使是某技能没有出现在任何武将牌上,也会被加载进来。 这里的路径是相对于当前扩展包的路径,所以要用"./" 开头。
6-8行是翻译表,加入该函数后,我们就可以在游戏中看见我们定义的学习扩展包变成中文了,如果没有翻译表就会以英文名称作为其的名字。。
10行返回扩展包,这样程序就知道我们这个子扩展包的代码到这里就结束啦。
这样我们的扩展包就创建完成了!~之后我们的代码操作都会在这个 xuexi/init.lua文件和xuexi/skills文件夹 之内 别跑到 study/init.lua 去啦。
创建好的skills文件夹和子扩展init.lua文件#
保存 好所有动过的文件。用 单机启动 打开游戏,查看武将一览,可以看到已经出现我们的学习包了,不过现在还是空的没有武将。
学习包!说起来study还是英文呢,要不自己加一句翻译?#
注意
新的文件、修改过的代码文件都需要在 保存 之后 重新打开游戏 才能生效。我们开发新月杀一般都是在 单机启动 环境下进行的。 所以,如果你需要在电脑端游玩,请务必下载两个客户端文件,一个用于游戏,一个用于开发测试。
2.3. 创建武将#
创建新武将的话,首先也是要先想好武将的名字。
但是这件事情就简单多了,一般来说都是用TA的姓名的汉语拼音直接作为名字的。
例如曹操就是caocao,司马懿就是simayi,
当然,英文人物也可以用英文名,如某位凯撒大哥就是caesar。
看起来武将的名字比扩展包的名字好写,但其实不然。 要知道,在新月杀诞生的时候,别说界限突破了,谋攻篇都出来了,到了现在(2024年),同名武将多到可以凑个军五甚至军八…… 所以对于这些同名武将,新月杀提供了一种方法来自动识别——
当你注册一个名字格式为XXXX__YYYY(这里是双下划线,如mou__caocao)的新武将时,系统会自动将这个名字沿中间切成两半:其中右边的YYYY是“武将真名”trueName,而左边的XXXX则是这个武将的特有标示,也就是“武将包名”,或称前缀。
如果两个武将拥有同样的“武将真名”,那么这两个武将就是同名武将,
例如ex__caocao和mou__caocao的“武将真名”都是caocao,那么这两个武将就是同名武将。
按照传统,我们也该写个学习包专属的孙悟空的,但是……十周年和欢乐杀已经有孙悟空了(悲),所以在命名上我们也该规矩一点……
意味着学习包的孙悟空名字最好改成st__sunwukong了,
这里前缀取的是"st",study嘛。在你自己的拓展包中,根据情况选定合适的前缀吧。
具体代码就是local sunwukong = General:new(extension, "st__sunwukong", "god", 5),
之后你的xuexi/init.lua看起来就像这样:
重要
要注意把 return extension 留在文档末尾,以下我们的操作都要在这一行 return extension 之前完成!
1local extension = Package:new("xuexi")
2extension.extensionName = "study"
3extension:loadSkillSkelsByPath("./packages/study/pkg/xuexi/skills")
4Fk:loadTranslationTable{
5 ["xuexi"] = "学习",
6}
7
8local sunwukong = General:new(extension, "st__sunwukong", "god", 5)
9
10return extension
创建武将本质上是创建一个“武将对象”,为此,让我们看向武将对象的构造函数:
--- 构造函数,不可随意调用。
---@param package Package @ 武将所属包
---@param name string @ 武将名字
---@param kingdom string @ 武将所属势力
---@param hp integer @ 武将初始体力
---@param maxHp integer @ 武将初始最大体力
---@param gender Gender @ 武将性别
function General:initialize(package, name, kingdom, hp, maxHp, gender)
各参数的作用见上方,这里详细解释一下:
package是该武将所属的扩展包,鉴于第一行已经定义了一个extension,我们就用它了。name是这个武将的名字,就用刚才起的名字"st__sunwukong"吧"孙悟空"是中文翻译的显示名称,而"st__sunwukong"是武将的代码名称,在所有扩展包中武将的代码名称必须是唯一的。
kingdom是这个武将所属的势力,按照传统,我们填个"god",也就是神势力。hp和maxHp分别代表体力值和体力上限,maxHp默认与hp一致,而hp默认为4。gender就是武将性别,默认为General.Male(男性)。
以上代码就往学习包里塞了一位名为“st__sunwukong”的5血5上限神势力男性武将~
至此武将的创建就完成了!但是如果我们现在进入游戏的话,他会显示为英文……那就用Fk:loadTranslationTable补一下翻译吧:
1Fk:loadTranslationTable{
2 ["xuexi"] = "学习",
3 ["st"] = "学",
4}
5
6local sunwukong = General:new(extension, "st__sunwukong", "god", 5)
7Fk:loadTranslationTable{
8 ["st__sunwukong"] = "孙悟空",
9 ["#st__sunwukong"] = "齐天大圣",
10 ["designer:st__sunwukong"] = "设计者",
11 ["cv:st__sunwukong"] = "配音演员",
12 ["illustrator:st__sunwukong"] = "画师",
13}
翻译表其实是个很纯粹的东西,你输入特定的字符串,就会根据翻译表输出对应的翻译字符。 以下是武将信息里除了武将名以外的其他信息:
"#武将名"是武将的称号,理论上要多长就有多长,不过考虑到实际显示,建议限制在7个字以内。"designer:武将名"是武将的设计者,默认为“官方”。"cv:武将名"是武将的配音演员,默认为“官方”。"illustrator:武将名"是武将的画师,默认为“官方”。
这些在进入游戏后的武将一览都能看到。
保存一下,到游戏里看看我们的孙悟空吧。打开武将一览->学习包:
我们刚刚创建的悟空在此#
这里可以看见,我们命名的代码名称为st__sunwukong,我们可以拆分一下为"st"+"__"+"sunwukong", 其中"st"是我们刚才在翻译表中添加的前缀,这个会显示在武将的右下角作为角标【学】
"__"作为连接的字符串,是用来检测角标和武将真名的
新月杀会把武将的代码名称中,检测到的第一个"__"双下划线作为连接字符,连接字符前面的就是武将角标,后面的就是武将真名。
"sunwukong"就是我们刚才定义的武将真名。
好了,赶紧拿着你新写的这位武将去单挑吧!
PS:五体力白板孙悟空的单挑能力还是不弱的呢。^_^