Emuera 原生游戏运行流程浅析
你暂时逃掉的课,总有一天要补回来。(悲)
参考资料:《官方文档 / フロー図》
个人吐槽
我一向是不太看得起 Emuera 内置的所谓「标准游戏流程」的。
(没有考据过,大概也是从 eramaker 时代继承下来的?)
也就是 SHOP / TRAIN 那一套。
这种流程死板而臃肿,让我见到的第一眼就心生不喜。
时间转瞬即逝,我个人接触 era 也快一年了。
我曾经两度壮志雄心想要写一个 era 游戏,两度被劝退,就是因为这套东西。
我自己写当然是从
@SYSTEM_TITLE就开始接手,自行设计游戏流程了。
每每当我尝试创作的时候,阅读别人的 era 游戏代码以作参考,大多都是用的这套。
然后我就会不由自主地考虑能不能用在我的游戏里。
接着光是想象就被恶心到,遂放弃。周而复始这个过程。
这玩意不是我不想看,我就能不看的。
我也是负责运营社群的一员,有人好声好气来反应 bug 我难道还能晾着人家吗。
在这种无限循环的折磨中,我着实被耗尽了耐心。
但几经波折,兜兜转转还是回到了这里。
毕竟对没有任何编程基础的新手来说,你不能强求他们先去学习枯燥的数据结构。
哪怕你有心深造,我也不建议你零基础直接生啃数据结构。
这种干瘪的工作真的会极大程度上磨灭你的创作激情。
Emuera 自带的标准流程,确实是对新人(开发者)最友好的选择了。
为了完成足够详尽的开发教程,尽管内心仍然抗拒不已,我还是不得不捏着鼻子()去梳理这套玩意。什么恶堕剧情
TITLE 标题界面#
调用方式:
- 在启动 Emuera 并读取所有 erb 文件后自动运行
- 使用
BEGIN TITLE主动调用
使用默认的标题界面#
如果没有定义 @SYSTEM_TITLE 函数,就显示默认的标题界面,如下图所示:
至于这里自动读取并显示的信息可以在
GameBase.csv和_Replace.csv修改。
然后是仅有的两个选项:
- 选择
[0] 最初からはじめる- 初始化游戏数据(
RESETDATA/ADDCHARA 0等 ) - 开始新游戏
BEGIN FIRST
- 初始化游戏数据(
- 选择
[1] ロードしてはじめる- 如果定义了
@TITLE_LOADGAME直接调用 - 如果没有定义
@TITLE_LOADGAME则显示默认的存档加载界面
(与LOADGAME命令加载的界面有微妙的不同)
- 如果定义了
自定义标题界面#
如果定义了 @SYSTEM_TITLE 函数,则直接调用。
后续的处理自然也由制作者自行决定并实现。
需要注意的是,如果在自定义的 @SYSTEM_TITLE 里没有执行 BEGIN / LOADDATA 命令就 RETURN 终止运行会报错。
因为一个游戏在任何情况下都不应该自行结束。
游戏必须是一个永无止境的循环,当然了,中途肯定可以停下来询问玩家的输入。
征询玩家接下来要怎么做,退出游戏 或者 继续游玩 都是玩家的自由。
但一定是无限的循环运行,一定不能自行其是地退出。
官方流程图#

FIRST 新游戏#
调用方式:
- 在默认的标题界面选择
[0] 最初からはじめる - 使用
BEGIN FIRST主动调用
一般来说,这里是游戏开始之初,进行初次配置 / 捏人设定角色的步骤。
必须自定义 @EVENTFIRST 函数,未定义会报错。
另外,如果没有执行 BEGIN 命令就结束运行也会报错,理由之前说过了。
官方流程图#

SHOP 商店#
调用方式:
LOADDATA后自动运行- 使用
BEGIN SHOP主动调用
是否自动存档由配置项 オートセーブを行なう:YES 决定。
设置为 YES 自动存档,NO 不会自动存档。
必须自定义 @SHOW_SHOP 函数,未定义会报错。
显示商店界面#
调用 @SHOW_SHOP 之后要求输入购买的商品,需要在 0 ~ 99 之间(这个范围可以在 _Replace.csv 里编辑販売アイテム数 来修改)。
当输入范围外的数值时,会调用 @USERSHOP,可以通过这个函数将标准的
此外,
PRINT_ITEMSHOP命令显示ITEMNAME和ITEMSALES两个数组中元素数量较小的那个数组的范围(默认为 1000),可以在VariableSize.csv中修改。
处理购买指令#
处理购买时:
- 检查
ITEMSALES(有存货)是否在0以上,MONEY是否大于ITEMPRICE。 - 如果购买失败(判定没有通过),要求玩家重新输入。
eramaker 购买失败时会从
@SHOW_SHOP重新开始。
购买成功时:
BOUGHT变量(上次购买)赋值为ITEM的编号。ITEM:BOUGHT(背包 / 仓库)+1。MONEY减去ITEMPRICE:BOUGHT的值(付费)。- (可选)如果定义了
@EVENTBUY,则调用@EVENTBUY函数。 - 最后返回
@SHOW_SHOP。
除非在某个地方执行 BEGIN 命令,否则永远不会退出 SHOP 流程。
再强调一遍:由于制作者可以接过玩家输入的指令另行处理(@USERSHOP)、以及「永远自循环」的特性,因此可以把默认的「商店」页面做成别的功能,比如「待机状态主页面」。
事实上很多现有的 era 游戏也确实是这样做的。
官方流程图#

TRAIN 调教#
调用方式:
- 使用
BEGIN TRAIN主动调用
首先会对一些变量进行初始化:
一、ASSIPLAY:0 = 0、PREVCOM:0 = -1、NEXTCOM:0 = -1
手动将
>= 0的数值赋给NEXTCOM会导致死循环,此处不讨论。
Emuera 的NEXTCOM是为了重现(兼容)eramaker 的原有行为,包括上述缺陷。
并没有设计新的用途。
二、TFLAG 全部归零,所有角色的 GOTJUEL、TEQUIP、EX、PALAM、SOURCE 归零
由于离开
TRAIN时不会再次初始化,因此存档时会在存档文件中保存这些值。
推荐在@SAVEINFO函数(存档前的可选函数)中将角色的GOTJUEL、TEQUIP、EX、PALAM手动重置为 0 以压缩存档文件大小。
三、STAIN:2 = 2、STAIN:3 = 1、STAIN:4 = 8、其他 STAIN 归零(意义不明)
STAIN数组的初始值可以在_Replace.csv指定汚れの初期値来修改。
显示调教界面#
调用 @SHOW_STATUS 后显示可用的 TRAIN 选项。
调教指令数量上限
MAX_TRAIN可以设置VariableSize.csv的TRAINNAME来修改。
根据 TRAINNAME 的内容查找 @COM_ABLExx 函数是否存在:
- 如果
@COM_ABLExx函数不存在或返回非零值,显示可以执行的TRAINNAME。 - 如果
@COM_ABLExx返回 0,则不能执行,也不会显示TRAINNAME。
这个查询结果(是否可以执行 TRAINNAME)会被缓存下来。
下次运行时不会再次重复调用 @COM_ABLExx。
この時に実行可能かどうかを記憶しておく。
(実行時に再度@COM_ABLExx を呼び出すわけではない。)
哟,这不是 Python 3.9 的 @functools.cache 嘛,领先十年怎么说领先十。
显示 TRAINNAME 结束后:
- 调用
@SHOW_USERCOM。 - 在
@SHOW_USERCOM运行结束后,初始化UP、DOWN、LOSEBASE。
(以及CUP、CDOWN、DOWNBASE。)
即:在@USERCOM/@EVENTCOM函数中才会看到初始化后的效果。 - 要求输入。
处理调教指令#
CALLTRAIN(重复调教指令 n 次)和DOTRAIN(强制执行调教指令)这两个命令的用法可以参考 官方文档。
将输入与 @COM_ABLExx 的结果进行比对,如果可以执行,调用对应的 @COMxx:
- 将
TRAIN番号赋值给SELECTCOM变量 - 所有角色的
NOWEX数组中的所有元素初始化为 0 - (可选)调用
@EVENTCOM - 调用对应的
@COMxx- 如果
@COMxx返回值为 0,返回@SHOW_STATUS - 如果
@COMxx返回值不为 0:- 调用
@SOURCE_CHECK- 如果存在
@EVENTCOMEND,将所有角色的SOURCE的所有元素初始化为 0 - 如果不存在
@EVENTCOMEND或者在@EVENTCOMEND中从未执行过WAIT,则生成一个WAIT
- 如果存在
- 返回
@SHOW_STATUS
- 调用
- 如果
如果输入不是可执行(存在 TRAIN番号)的命令,调用 @USERCOM 后直接返回到 @SHOW_STATUS。
执行
UPCHECK会将+ UP - DOWN结算给PALAM:TARGET,然后UP/DOWN归零。
| 操作目标 | PALAM上升量 | PALAM下降量 | 触发结算 命令 | BASE损失量 | 临时旗标 |
|---|---|---|---|---|---|
| TARGET | UP | DOWN | UPCHECK显示结果 | LOSEBASE | TFLAG |
| 其他角色 | CUP | CDOWN | CUPCHECK 角色编号不显示结果 | DOWNBASE | TCVAR |
除非在某个地方执行 BEGIN 命令进行跳转,否则永远不会退出 TRAIN 流程。
官方流程图#

AFTERTRAIN 调教结算#
调用方式:
- 使用
BEGIN AFTERTRAIN主动调用

- 如果没有执行
BEGIN命令就运行结束会报错
ABLUP 能力升级#
调用方式:
- 使用
BEGIN ABLUP主动调用
显示升级界面#
依次调用 @SHOW_JUEL、@SHOW_ABLUP_SELECT 后要求输入。
处理升级指令#
- 如果输入在
0 ~ 99范围内,查找对应的@ABLUP- 如果存在对应的
@ABLUP函数,调用该函数后返回@SHOW_JUEL - 如果不存在对应的
@ABLUP函数,要求重新输入
- 如果存在对应的
- 如果输入在范围之外,调用
@USERABLUP后返回@SHOW_JUEL
如果是 eramaker 中未定义
@ABLUP函数,直接返回到@SHOW_JUEL。
除非在某个地方执行 BEGIN 命令,否则永远不会退出 ABLUP 流程。
官方流程图#

TURNEND 结束回合#
调用方式:
- 使用
BEGIN TURNEND主动调用

- 如果没有执行
BEGIN命令就运行结束会报错
SAVEGAME 保存存档#
调用方式:
- 执行
SAVEGAME命令时调用
执行 @SAVEINFO 的时机在写入存档之前。

LOADGAME 读取存档#
调用方式:
- 执行
LOADGAME命令时调用
BEGIN 命令自带 RETURN,即 BEGIN 之后的代码永远不会运行(类似 JUMP)。
而 LOADGAME / SAVEGAME 会和 CALL 一样运行结束时回到调用它的地方。
但如果执行 LOAD,会无视调用它的地方,跳转到 LOADDATAEND。

LOADDATAEND 读取数据结束#
调用方式:
- 在
LOADGAME函数执行LOAD步骤时调用 - 执行
LOADDATA命令之后调用
执行 LOAD 时,之前的状态(如调用中的函数)都将被清除。

eramaker 什么都不会做,直接跳转到
@SHOW_SHOP。
- 如果定义了
@SYSTEM_LOADEND,执行@SYSTEM_LOADEND,并在执行结束前使用BEGIN跳转 - 如果没有定义
@SYSTEM_LOADEND,并且定义了@EVENTLOAD,执行@EVENTLOAD,并在执行结束前使用BEGIN跳转- 如果没有使用
BEGIN跳转,则像通常情况一样跳转到@SHOW_SHOP
- 如果没有使用