搞起
「松饼人物编辑器:从入门到欧耶」系列教程(一)
编辑器是个啥
松饼人物编辑器可以用来创建和编辑「原创角色」,自由指定名字、头像、以及技能。 原创角色可直接上传到松饼本体的「人物下载」列表中。
到松饼麻雀 0.10 版本为止,人物编辑器集成于松饼本体之中。 为了提高人物编辑的便利性,从 0.11 版本开始, 人物编辑器已从松饼本体中独立,成为了 Visual Studio Code 的一个插件。 松饼本体自带的编辑器仍可在「麻将部备品」页面中找到, 但我们更鼓励使用 Visual Studio Code,以获得更为丝滑的创作体验。
当前状态
目前人物编辑器处于开发阶段,有些系统自带的角色无法用编辑器做出来。 完整版的编辑器预计可以做出所有的系统自带角色。
人物编辑范例 列举了目前可以通过编辑器轻松实现的自带角色。
原创角色的能力暂时无法在未来视中发动。
关于 Lua
若想自制技能,就要会 Lua。 Lua 是一门简单易上手的编程语言, 无论是小萝莉还是中年老阿姨,看一眼就能学会,无需特别的专业背景。
本系列教程假定读者完全不会编程 —— 讲编辑器的同时,也会根据需要顺带讲一些编程的基础。
安装 Visual Studio Code 及松饼插件
- 前往 官网 下载并安装 Visual Studio Code
- 打开 Visual Studio Code,点击左侧的 Extensions 图标, 搜索并安装松饼插件「Pancake Mahjong」
- 完事了
新建人物
在 Visual Studio Code 中,打开「File」菜单,点「Open Folder…」。 然后随便找个你喜欢的地方建个空文件夹,打开之。 以后创建的人物都放到这个文件夹里就行了。 有了专门的文件夹,将来上传人物会容易很多。
然后,按 Ctrl + Shift + P,出现迷之输入框。 在其中输入「new girl」并回车。 这个输入是基于模糊匹配的,输到一半时,只要下面的第一项为 「Pancake Mahjong: New Girl」,就可以直接按回车了。 接着会弹出新建文件的对话框。 起一个文件名,并点击「创建人物」后,将看到如下页面:
左侧的文件列表中多出了两个文件。 其中「.girl.json」文件为人物的基本属性文件,包括名称、头像等属性。 另一个「.girl.lua」文件是人物的技能文件。
基本属性一顿编辑,然后点「保存」就行了。 然后切换到「.girl.lua」的标签,会看到如下代码:
我们可以对其稍做修改,比如改成:
print("意大利人还行x")
修改后,不要忘记按「Ctrl + S」保存。 炒鸡重要!写代码要养成有事没事保存两下的习惯。
测试
保存代码后,就可以开始测试了。回到基本属性页面,点下方的「测试」。
此时,右下角会提示「未配置松饼主程序路径」。 这是因为我们还没有告诉 Visual Studio Code 松饼主程序在哪儿, 需要配置一下。
点菜单栏中的 File -> Preferences -> Settings,打开设置面板。
在其中搜索「Pancake」,即可列出松饼插件的相关选项。
将松饼主程序的完整路径
(形如C:\Some\Folder\mjpancake.exe
)
粘贴到 App Path 一栏中,配置就完成了。
这个东西只需要设置一次,以后不需要再设置。
回到人物属性页面,再次点击「测试」。 这次松饼正常启动了,自动进入了选人的界面,并且 1P 为新建的人物。
你会发现,一桌开始时, 中间会蹦出一句「意大利人还行x」。 这正是我们刚才在代码里改的东西,说明我们的代码正常工作了。
虽然技能很简单,但它也是一个完整的人物 —— 有名字、有头像、而且有一段完整的 Lua 代码。
调试
上面的例子讲的是一切顺利的理想情况。 在现实中,很有可能看不到「意大利人还行x」, 而是得到一些奇怪的输出,比如下图:
出现这种情况,说明你的代码有问题,比如这样:
print(意大利人还行x)
这样:
Print("意大利人还行x")
这样:
print(“意大利人还行x”)
或者这样:
print("意大利人还行x"
我们遇到什么报错都不要怕,微笑着面对它。 读懂报错信息,是一切的切入点。
比如上面例子中的错误信息:
[string "print(意大利人还行x)"]:1: unexpected symbol near '<\230>'
这名话是什么意思呢?
首先,这句话里最重要的,就是中间的那个:1
。
这个:1
表示错误出在代码的第一行。
当然,我们的代码只有一行,所以错误也只能出在第一行。
但以后代码写长了,成百上千行,这个行数就至关重要了,
因为你总不能像个无头苍蝇一样乱找,那样猴年马月也找不到错在哪里。
另外有一点需要留意:错误信息里的行数不总是 100% 准确的。 比如有时他说第 100 行有错,但实际上错误可能出在第 99 行或第 101 行。
确认错误所在的行数,是第一步。接下来的第二步,就是看行数后面的那句话:
unexpected symbol near <\230>
。这又是个什么鬼?
unexpected
: 不符合期望的symbol
: 符号near <\230>
: 在<\230>
附近
<\230>
这种东西多半指中文字符。也就是「在中文字符附近出现了奇怪的东西」。
这又是个啥意思?
其实仔细对比一下,就会发现其实就是少写了一对引号。 那既然少写了引号,为什么不直接说少写了引号,反而说得这么委婉呢?
你需要认请一个事实 —— 电脑既不知道你想干什么,也不知道你该怎么做。 如果它知道,机器人早就取代人类了,还要你何用。 它给你一个大概有用的错误信息,它已经尽梨了。 我们能做到的,就是多写代码,身经百战,丰富人生的经验 —— 这样一来,每当看到错误信息,都会在脑内自动将鸟语译成人话,心里有个 B 数。 比如上面那条错误信息,老司机一看就知道,多半是多敲或少敲了个什么符号。
在初学阶段,可以故意尝试写一些有错误的代码, 从而熟悉各类报错信息,免得到了遇到真正的错误时一脸懵逼。
代码详解
回顾我们刚写的代码:
print("意大利人还行x")
print
的作用是输出文字。
在print
后面加一对括号,括号里面再加一对引号,
引号里面加任意文字,就可以把它们输出出来。
print
这 5 个字母必须小写。括号和引号必须是英文的。
这里的print
,是一个「函数」。
所谓函数,就是有内涵的 B 数。
比如这个print
,它的内涵就是「输出文字」;
至于输出的具体原理,这个函数它自己是心里有 B 数的,
我们不用管。
我们只需要把"意大利人还行x"
这个「参数」传给这个函数,
剩下的事情都会由这个函数来完成。
函数就像银行柜台的工作人员,参数就像是交给工作人员的材料。
第二个栗子
新建人物,输入以下代码并保存。
function ondraw()
print("哇!")
end
三狗战测试,发现每当有人摸牌时都会输出一次「哇!!」。 这意味着,我们成功地实现了一个完整的摸牌挂 —— 每次摸牌时,这个挂都会生效 —— 虽然只是打印一行字。
不过多数情况下,自己的进张和他家的进张需要区别对待 —— 比如给自己塞有效牌,给他家塞铳牌什么的。 所以我们继续改进代码,让程序区另对待自己和他家的摸牌 —— 自己摸牌时输出「哇!」,而他家摸牌时输出「emmmm…」。
function ondraw()
if who == self then
print("哇!")
else
print("emmm...")
end
end
三狗战测试,发现程序表现和预期一致,一脸计划通。
虽然这个挂仍然没什么卵用,但好歹是个完整的进张挂, 并且可以区别对待自己和他家的摸牌。
代码详解
回顾第一段代码:
function ondraw()
print("哇!")
end
这段代码「定义」了一个名叫ondraw
的函数。
ondraw
函数做了一件事情 —— 输出"哇!"
。
函数的「定义」会在后续章节中详细讲解。 现在我们只需要知道以下两点:
- 被包围在
function ondraw()
和end
之间的代码只会在摸牌时被执行 - 完全裸露在外的代码只会在一桌开始时被执行
定义摸牌挂的函数名必须叫ondraw
,所有字母都小写。
再看第二段代码:
function ondraw()
if who == self then
print("哇!")
else
print("emmm...")
end
end
多出来的东西叫「if
语句」。
if
语句的作用,是让程序根据某个条件的不同,
而去执行不同的代码。
条件要写在if
和then
之间。
条件被满足时,执行then
和else
之间的代码;
条件未被满足时,执行else
和end
之间的代码。
中间这 5 行代码的意思是:
「如果摸牌人等于自己,则输出"哇!"
,否则输出"emmm..."
」。
我们再详解一下这里的who == self
是什么鬼。
在ondraw
函数定义的内部,who
代表当前摸牌人。
如果是自己摸牌,那么执行这段代码时,who
就是自己;
如果是下家,who
就是下家,以此类推。
而self
则始终代表自己。中间的==
是等号。
在 Lua 中,两个=
组成的==
才是等号,用一个=
是不行的。
很多编程语言都是这个尿性,习惯就好。
缩进
每行代码前面的空格不是必须的。 也就是说,我们刚刚看过的代码,写成这样也可以正常执行:
function ondraw()
if who == self then
print("哇!")
else
print("emmm...")
end
end
但你若是把代码写成这样,就危险了。
首先,这种代码更容易出 bug。
有空格时,我们一眼就能看出,if
套在ondraw
里面,
两个print
分别套在then
和else
里面 ——
逻辑关系一目了然。
然而,去掉空格以后,
我们必须仔细读,并理解这三行代码的内容,才能看出这个关系。
人这种动物并不擅长阅读代码。
难读的代码,会导致错误的理解,从而导致错误的思考,从而滋生 bug。
其次,把这种代码给人看,容易被吊起来啪啪啪。 正如刚才所说,人这种动物,并不擅长阅读代码(只擅长追番以及找绅士图)。 本来就不擅长了,还不好好排格式,把这种乱七八糟的代码给人看, 这不找揍呢吗。 尤其是代码遇到问题,想在网上求助的时候, 若是把这种代码贴上去,很有可能贞操不保。
这种用于体现代码嵌套关系的空格,叫作「缩进」。 尽管缩进不影响代码的执行结果, 该加缩进的地方也都必须加上缩进, 既为了自己,也为了别人。 缩进的规律很简单: 逻辑上被包含的代码块始终要处于包含它的代码块的右侧。
缩进的空格不用一个一个地去敲。 在 Visual Studio Code(以及其它绝大多数编辑器)中, 按下 Tab 键,就会增加一级缩进; 按下 Shift + Tab 键则会减少一级缩进。 按下回车换行时,如果该缩进,编辑器通常也会自动为你加上缩进。 一个 Tab 所对应的空格数可以在 Visual Studio Code 的设置中修改。 比较普遍的 Tab 长度是 4 个空格或 2 个空格。
练习题
- 新建一个人物,在一桌开始时输出「呵呵」,在每次有人摸牌时输出「哈哈」。
- 修改第 1 题中创建的人物,在他家摸牌时输出改为「嘿嘿嘿」,其余的不变。
- 有人丢给你一段没有缩进的代码,你会怎么做?
每一讲的习题答案都在下一讲的开头。
下一讲:数据类型