以SCM插件为基座开发的简易经营游戏思路 - 菜单篇
前言
也是又开上新坑了,因为zxnf的各种事宜(美术风格,数值设计……)被整的心力交瘁,有些难产,所以做一个更轻量的同人小游戏散散心。
……话是这么说,好像体量越来越大了吧啊喂!各种剧情,大的小的都想塞进来,自己以前写的,甚至自己之前没能写出来的大长篇……
不过这些剧情上的细节先稍微放放,先做出一个能玩的原型再说。因为前段时间发愁zxnf的事,也意外发掘到了一些虽然没能用上,但必可活用于下一次的优质插件,就比如标题中所说的SceneCustomMenu,这游戏中的系统基本上都是依托这个插件,配合变量、公共事件和数据库备注来建立的。
加上我发现中文网络几乎没人介绍这个插件,这下不得不写了。
插件
插件原版的下载,不过插件参数全是片假名,这谁看得懂啊
也可以用我翻译的版本(利用规约允许修改和分发)
为了统一参数的叫法,后面会用我的翻译版本来讲解
效果展示
本来想外链,但是显示宽度总有问题,只能点链接观看了
可能有点慢,实在不行只能看百度网盘了
为了照顾不方便看视频的朋友还是贴几张图
另外叠个甲:本文展示的截图只为演示,不代表最终成品效果!
首先,这是按下取消键打开菜单时最初的样子

选择运营、物资、状态时,会直接在界面里打开相应的窗口



选择运营 - 安排工作后,又会打开新的界面,这里可以给角色安排行动,在右边列表选择后,左边的“行动:未计划”也会变为相应的行动名称

打开人员,也是一个新界面,显示每个人物的属性和简介,按下确定键翻页(左边留空是因为这里预定显示立绘,但我没画好)

存档、选项和退出使用的是默认界面
事务用的是Unagiootoro的插件QuestSystem,其实也可以直接用scm写,甚至我最开始也是直接用弹出窗口写的,但是这样我发现任务文本不太好处理,所以就用插件了(刚好这个插件也能用插件命令打开界面)
顺带一提,地图显示变量窗口所用的插件是LL_VariableWindow.js
入内一观
导入插件应该不需要我来教了
另外这个插件需要一个叫PluginCommonBase的前置,在官方dlc文件夹里,记得也要导入放在上面

哇去,怎么这么长的。这个插件预留了20个可以自定义场景的空位,第一次导入的时候应该只有前三个内置的演示设置。只要通过调用脚本
1 | SceneManager.callCustomMenu('Scene_ActorList'); |
或者用插件命令 - 调用场景,标识符填Scene_ActorList,就能在游戏中打开内置场景了,展示了这个插件能干的一些事
菜单命令
双击一个场景空位,打开一个新的参数窗口

场景标识符就是这个场景的名字,类似于核心脚本中的Scene_Menu、Scene_Item、Scene_Skill这类的,格式也差不多。这里取名为Scene_MenuNew,不要和已有的场景重名,必须要是独一无二的。
帮助窗口就类似于默认的物品界面中,显示物品简介的那个窗口,可以选择显示的位置或者干脆不显示,这里选择显示在顶部
初始事件是打开这一场景时,立即执行的事件,后面再来进行设置
最重要的是这个窗口列表,这里用于设置构成这个场景的所有窗口

我敲,这么多的。不过下面这一堆可以先不用管,先把最上面的那个菜单命令做好再管剩下的。(另外staffList和staffDesc其实我弃用了,但是还没删)
双击空位,设置一个新的窗口

窗口标识符也是类似于给窗口取名,不过不用写Window_,直接写它的名字就行
窗口布局
在设置窗口的位置之前,最好用ps或ase之类的软件大致画一下想要的界面,并计算好宽高。
稍微解释一下怎么算宽高,默认情况下的分辨率是816x624,这是可以在数据库中进行设置的
但是,如果在这个插件中设置界面,或者自己写界面插件时,真的将x和y坐标都设为0时,会发现窗口不会真的显示在整个画面的左上角,而是会留出顶部返回键的位置!
在界面中决定布局坐标的,实际上是UI区域的尺寸。
在游戏测试中按F8打开控制台,在控制台输入
1 | console.log(Graphics.boxWidth, Graphics.boxHeight); |
就会在控制台显示当前界面的UI截面尺寸,可以以此为参考来设置坐标。
另外一提,默认的窗口内边距是4px,行高为36px.
因为指令列表的位置贴在左上角,所以X和Y都为0就行。宽度为192,列数为1,高度和行数根据命令数自动适应都填0。
绘制项目内容
下拉看到命令列表,双击一下,在这里设置这个窗口中所有的命令

双击空位,设置命令的文本,按需设置帮助文本

显示开关ID和可选开关ID按需设置,其他的包括确定事件都不用管,我们先只让命令文本和帮助文本显示出来
这样将所有要显示的命令设置好后,保存回到最开始的插件参数列表,选择场景替换列表

把菜单替换为刚才创建的新场景,这样按下取消键时,就会显示我们刚才设置的场景
现在应该可以看到命令列表了,不过现在选择按下确定后,什么都不会发生

确定事件
打开menuCommand的窗口设置,看到确定事件设置,这里用来设置在这个窗口按下设置时,发生的事情


但是呢,我们这个命令列表里有很多命令,要怎么判断光标选中的是哪个命令,好执行对应的事件呢??
先退出确定事件,再往下拉看到存储索引变量

索引是什么?可以理解为列表按顺序排列的编号,从0开始。以本文中的命令列表为例,每个选项对应的索引如下:

然后插件会将光标选中的命令索引存入这里设置的变量中,并随着光标所在的选项改变而实时变化
比如在刚打开菜单时,光标在最开头的“经营”位置,此时的索引为0,存储变量值为0;然后光标向下移动一行,移动到“物资位置”,现在的索引变为1,那么存储的变量值也会自动变为1(没错,不用按确定键,索引存储变量的值就会动态改变)
那么现在判断命令的方法就有思路了:通过公共事件,判断当前的索引存储变量值为多少,然后执行相应的事件。如果为0,就打开经营菜单;如果为1,就打开物资菜单……以此类推。
在这里设置了索引存储变量后,进入公共事件,命名为【主菜单-选项处理】,开始设置按下确定后的判断事件
1 | ◆如果:主菜单-index = 0 |
前四个命令是和自定义菜单有关的,这里先留空,只写后面事务、存档、设置、退出的分歧。
事务各位自己按需处理,存档画面直接用事件命令就好,这里贴出调用自带设置和退出画面的脚本:
1 | SceneManager.push(Scene_Options); //设置画面 |
另外记得在后面跟一个打开菜单的脚本或事件命令,因为从这几个画面取消退出之后,还是要回到菜单画面
最后回到menuCommand窗口的确定事件,选择刚才设置的公共事件,再保存调试,现在选择事务、存档、设置和退出,应该可以正常打开对应的画面了。
弹出窗口
窗口布局
接下来,就要分别绘制选择命令时,弹出的窗口了,还是同样先为每个窗口设置坐标和宽高
x坐标和宽度还好说,前面介绍了X/Y相对坐标窗口,只要将X坐标设为0,X相对坐标窗口设为菜单命令的标识符,这个窗口就会贴在菜单命令右侧显示,然后宽度填0,就会自动填满剩余的空白区域。
但是比较难办的,就是显示在右下角的金币窗口和每个窗口的高度了,由于前文所述的UI区域和帮助窗口的限制,窗口的高度是不确定的,所以只能自己一点点调了。
这里贴出需要用到的窗口和各自的位置宽高设置,由于我的工程分辨率是960x720,所以不能完全照搬
- 金币窗口gold:贴在最右下角,比较麻烦,大家自己调吧
- 经营列表manageList:选择经营后弹出的三项选项,贴在菜单命令右边
- 物资界面:主要分为两个部分
- 物资列表itemList:贴在菜单命令右边,7行2列,宽度设为0
- 物资描述itemDesc:贴在菜单命令右边,物资列表下边,显示4行,宽度设为0
- 状态窗口status:填满剩下空位,宽度为0,高度只能自己手动设置,这里设置为560
现在直接进行测试,会看见这些窗口全都堆在一起了,那么接下来就要通过确定事件和取消事件,来进行窗口的显示和隐藏
显示窗口
以经营列表(manageList)为例,下拉窗口的参数,找到显示开关ID

只要这里指定的开关为ON时,就会立刻在屏幕上显示这个窗口,开关为OFF,就会立刻关闭。这个流程是立即执行的,延迟很小
有了这个思路,就可以给其他的窗口设置好开关ID了:
- 经营列表:主菜单-打开运营
- 物品列表、物品描述:主菜单-打开物资(因为这两个都同属于物资,所以用同一个开关来控制即可)
- 状态窗口:主菜单-打开状态
- 金币窗口:主菜单-金币窗口
有了思路,回到【主菜单-选项处理】,可以把剩下的一部分条件分歧补上了
1 | ◆如果:主菜单-index = 0 |
开关操作:打开对应的显示开关,让窗口显示出来
插件指令-窗口操作:激活对应的窗口(物资就是激活到物资列表中),让光标可以在其中移动、选定
变量为2的场合是人员,要打开一个新的场景,我们还没做,所以先搁置。
你可能会问:不对啊,金币窗口呢?这个等到最后再来解决
现在进行测试,此时只会显示一个菜单指令窗口,可以通过菜单指令打开对应窗口,但是这样还无法隐藏窗口、重新激活菜单指令了,所以还需要一步:配置取消事件
关闭窗口
我们再重新说明一下运作的流程
- 打开菜单,只显示菜单指令窗口,此时激活的是菜单命令
- 在命令按下确定,触发确定事件,根据索引变量的条件分歧,执行对应的操作,打开并激活对应的窗口
- 激活了对应的窗口后,可以在这个窗口内进行移动和操作
- 按下取消键,隐藏打开的窗口,重新激活菜单命令
现在我们要处理的就是最后一步,设定取消事件。还是以经营列表manageList为例,在确定事件下面就是取消事件

- 公共事件:按下取消键时执行的公共事件
- 窗口标识符:按下取消键后,激活的窗口的标识符,因为要重新激活菜单命令窗口,所以这里填菜单命令窗口的标识符
先保存好回到公共事件,新建一个名为【主菜单-取消处理】的公共事件,为所有窗口的取消事件创建一个统一的公共事件
1 | ◆开关操作:#0101 主菜单-打开物资 = 关闭 |
这样在按下取消键时,就会关闭开关,从而隐藏窗口。
然后把运营列表、物资列表和状态窗口的取消事件都绑上【主菜单-取消处理】,窗口标识符填菜单命令窗口。
另外以防万一,在这三个窗口的确定事件中,把窗口标识符都填上各自自己的标识符,这样防止玩家过度操作,在本窗口按下确定键后激活其他窗口,让光标固定在这个窗口内
此时测试游戏,就可以执行一套完整的流程了。
金币窗口的处理
金币窗口的显示逻辑是这样的:
- 打开菜单时,会显示在右下角
- 在菜单选中物资、状态这两个会打开填满屏幕的窗口时,自动隐藏
- 退出上面这两个选项时,重新显示金币窗口
首先要让金币窗口初始显示,回到场景的插件参数中,选择初始事件

把开关设成先前让你设置的主菜单-金币窗口开关就行了,在场景打开的时候,就会瞬间打开这个开关(别问我为什么要用脚本打开,我傻了)
进入公共事件【主菜单-选项处理】,补全对应的条件分歧
1 | ◆如果:主菜单-index = 1 |
在打开物资和状态窗口时,关闭金币窗口。
然后到【主菜单-取消处理】:
1 | ◆开关操作:#0101 主菜单-打开物资 = 关闭 |
按下取消后,重新显示金币窗口
这样测试游戏,除了部分还没有设定场景的选项之外,菜单现在可以正常运行了
窗口的内容
经营列表
和菜单命令类似,在指令列表中填入显示的指令,按需设置启用开关,设置索引变量(要和菜单命令的索引变量分开用一个新的),再在确定事件绑上一个根据索引变量进行条件分歧的公共事件就行。这里不再赘述。
另外,每个窗口都可以显示在帮助窗口中显示的文本,按需设置。
物资列表
物资列表,顾名思义,就是要读取并显示当前身上持有的所有物资和数量。
获取项目列表
打开物资列表窗口参数,下拉找到列表获取脚本

这个脚本用于获取游戏中对应的数据,并在窗口中按列表显示对应的数据
不会写脚本也没关系,这里的下拉列表内置了许多常用配置,基本都可以直接拿来用。在下拉列表中找到图中展示的
1 | $gameParty.allItems(); // 持有物品 |
它会获取当前持有的所有物品,并在列表中进行展示
先使用这个脚本,然后保存,在数据库中的物品类别新建几个物品,并在每个物品的备注栏中输入类似如下的内容
1 | <material> |
这些东西要拿来干什么,后面会解释,先照着抄进去就是了
然后进入测试,给自己一点物品,打开物资列表,可以看到这里已经可以显示我们所持有的物品了

不过,直接这样显示的物品只会显示图标和物品名,不会显示后面的持有数(这是我后面自己加的),所以还需要确定项目绘制脚本
项目的绘制
回到物品列表的窗口参数,下拉找到项目绘制脚本

这里应该是空的。双击一个位置

这里就可以通过脚本,来确定项目如何绘制
默认情况下,如果不设置项目绘制脚本,列表中的项目只会显示当前元素的名称
而通过项目绘制脚本,不仅可以显示名称,还能显示各种文本,图标,甚至图片。所以我们要用它来显示物品的名称和持有数
下拉列表中找到任意文本绘制并选择,稍作修改:
1 | this.drawTextEx(`${item.name}`, r.x, r.y, r.width); // 任意文本绘制(含控制字符转换) |
用这个脚本,就能绘制出数据库中道具的名称。接下来对括号内这些参数进行说明
xxx:要显示的文本,用${}扩起来可以显示对应的变量内容item:这个插件中特有的一种变量,简单点说就是获取相应项目中的数据。我也不知道咋解释,比如说我们获取的是道具列表,那这个item就包含了道具的所有数据库内容,比如item.name就是道具名称,获取的是角色列表,就包含了角色的相关数据……等等,反正用多了就自己明白了(
r.x,r.y:项目内容的xy坐标偏移,以项目方框的左上角为原点,因为项目内容绘制不会自动适应坐标,所以在绘制内容较多的情况下需要自己调偏移rwidth:项目宽度,一般不用管
我这里不用显示图标,所以只是显示了物品名,如果需要图标+物品名的显示效果,可以用:
1 | this.drawItemName(item, r.x, r.y, r.width); // 物品、技能名称(含图标) |
然后在可编辑的选项列表中选择下面的空位,可以在一个项目内使用多个绘制脚本

在下拉菜单中找到物品持有数,稍作修改
1 | this.drawText('x' + $gameParty.numItems(item), r.x, r.y, r.width, 'right'); // 物品持有数 |
这里使用的字符串写法是'x' + $gameParty.numItems(item),它是将两个字符串拼到一起,所以就不用打``这个符号了,前面的’x’是显示一个x字母,后面的$gameParty.numItems(item)是显示物品的持有数,它会右对齐显示在项目中
现在打开测试,就应该是我展示的效果了
项目过滤
为了方便后续的开发,还需要做一个工作。选择过滤脚本

这里会对上一项中所获取的列表进行过滤,只会显示满足脚本条件的项目
先前让你在备注栏输入的<material>就派上用场了,选择脚本并稍作修改
1 | item.meta['material']; // 备注栏有<material>的描述 |
这样物资列表中就只会显示备注栏中有<meterial>的道具了
物资描述
关联列表
我们想让物资描述窗口随着在物资列表中的选择而立刻刷新,该怎么办呢??
在物资描述窗口找到列表窗口标识符

填入物资列表的标识符,这样物资描述就会和物资列表关联,在物资列表选中木材,描述窗口就会显示木材的相关描述。
项目绘制
首先将列表获取脚本选择为
1 | null; // 无(单项目显示窗口用) |
这样它就会将整个窗口都显示为单个项目,而不是列表那样
然后进行项目绘制,输入如下内容
1 | this.drawTextEx(`${item.meta.desc}`, r.x, r.y, r.width); // 任意文本绘制(含控制字符转换) - 物品描述备注栏中<desc: xxx>的内容 |
这里就会用上之前让你在物品备注栏输入的<desc: xxx>了
用插件用得比较多的应该会比较熟悉,经常会在备注栏中输入由尖括号包裹的内容,这个就叫做“元数据”
自带的数据库只能设置固定的几项数据,而rmmz支持插件从数据库的备注栏中读取额外的内容,也就是元数据,一般用尖括号来包裹。在本文的例子中,<desc: xxx>就是属于元数据。
那如何让这个插件来读取元数据的内容呢?使用item.meta.desc,item是当前的项目,meta是指元数据,而desc则是具体的元数据名称,也就是冒号前面所设置的内容,一般没什么限制,输入中文英文日文都可以,怎么方便怎么来。这样,就会显示<desc: xxx>中xxx的内容了。
this.drawTextEx这个方法支持控制符和换行,可以用这个方法来写出大段的五彩斑斓的长串文字。
这样就可以显示对应的详细描述了。

另外如果有显示大图标的需求,只要有足够空位,可以选择并修改
1 | this.drawNotePicture('noteValue', r.x, r.y, 'left', 'center', 1.0, 1.0); // 绘制指定备注栏的图片 |
来显示大图标。将notevalue改为你的元数据名称,再将想要显示的图片放进img/pictures文件夹
然后在备注栏中输入<元数据名称:你的图片名>,就可以显示对应的图片了
但是需要自己调一下坐标偏移。
状态窗口

基本上和物资描述如法炮制,就不从头到尾赘述了,只强调几点
为了方便通过确定键和取消键来开闭窗口,所以这里会激活状态窗口,此时整个窗口应该都会被光标包围
要解决这个问题,只要给它换一个扣掉了光标的窗口皮肤就行,可以在窗口的参数里改
再提示一下项目脚本绘制,这里的日期和时间是通过变量来显示的,这里放一下写法
1 | this.drawTextEx(`当前日期:${v(2)}`, r.x+ 4, r.y + 4, r.width); // 任意文本绘制(含控制字符转换) |
因为还没想好可以显示什么,所以目前只有日期和时间,以后还会慢慢再充实,写法也没什么区别
偏移需要自己调整,只提示一下默认的行距是36px
v(x)是插件自带的变量写法,${v(x)}就能在字符串中显示对应的变量值(怎么说呢。。多熟悉js语法吧)
等等,变量里可以存字符串的吗?
可以,真的可以:

后面的经营系统也会用到这一点
(另外包括前面元数据,主要涉及到js代码的部分都必须要用半角符号,这应该算是常识吧)
金币窗口
也不多说了,只是贴一下脚本的写法
1 | this.drawTextEx(`\\c[6]资金\\c[0]`, r.x, r.y, r.width); |
结语
这样,一个简单的菜单就写好了。
接下来会讲解有关人员界面以及经营系统的搭建。

