零基础开发 era 游戏 #15 SAV 存档文件
配置文件相关设置项#
使用专门的 ./sav/
文件夹保存#
详见「1.7. セーブデータを sav フォルダ内に作成する」。
セーブデータをsavフォルダ内に作成する:YES
存档栏位默认显示的数量#
详见「1.10. 使用するセーブデータ数」。
表示するセーブデータ数:20
这个值有一个专门的行内函数 SAVENOS()
来读取,有需要的话可以直接使用。
和直接读取配置文件
GETCONFIG("表示するセーブデータ数")
的结果是一样的。
PRINTFORML SAVENOS()={SAVENOS()} PRINTFORML GETCONFIG("表示するセーブデータ数")={GETCONFIG("表示するセーブデータ数")}
存档文件以二进制形式存储#
セーブデータをバイナリ形式で保存する:YES
以 UTF-8-BOM
编码格式保存#
详见「5.13. セーブデータを UTF-8 で保存する」。
セーブデータをUTF-8で保存する:YES
通用的公共存档 global.sav
#
内置公共存档变量 Global.csv
/ Globals.csv
#
这里定义的索引名类似其他 *.csv
文件那样,可以用来作为内置数组变量的别名。
编辑 ./csv/Global.csv
:
0,公共存档数值1 1,公共存档数值2 2,公共存档数值3
之后 GLOBAL:0
就等于 GLOBAL:公共存档数值1
,以此类推。
编辑 ./csv/Globals.csv
:
0,公共存档字符串A 1,公共存档字符串B 2,公共存档字符串C
之后 GLOBALS:0
就等于 GLOBALS:公共存档字符串A
,以此类推。
保存公共存档 SAVEGLOBAL
#
编辑 ./erb/全局存档变量.erh
:
#DIMS GLOBAL SAVEDATA GS_全局存档字符串变量, 4 = "初始值1", "初始值2", "初始值3", "初始值4"
运行 SAVEGLOBAL
命令会将内置的 GLOBAL
/ GLOBALS
数组,以及带有 SAVEDATA
关键词定义的全局存档变量变量保存到通用的公共存档文件 global.sav
中。
读取公共存档 LOADGLOBAL
#
编辑 ./erb/游戏业务逻辑.erb
:
@SYSTEM_TITLE WHILE 1 DRAWLINE PRINTL 〇 由 CSV 文件定义索引名称 - Global.csv / Globals.csv PRINTPLAINFORM GLOBAL:1 = [{GLOBAL:公共存档数值2}] (%GLOBALNAME:1%) PRINTL PRINTPLAINFORM GLOBALS:2 = [%GLOBALS:公共存档字符串C%] (%GLOBALSNAME:2%) PRINTL PRINTL PRINTL 〇 由 ERH 文件定义变量 - 头文件.erh PRINTL GS_全局存档字符串变量 REPEAT 4 PRINTPLAINFORM [%GS_全局存档字符串变量:COUNT%] PRINTS " " REND PRINTL PRINTL PRINTBUTTON "[1] 保存", 1 PRINT_SPACE 200 PRINTBUTTON "[2] 加载", 2 PRINT_SPACE 200 PRINTBUTTON "[3] 更新", 3 INPUT SELECTCASE RESULT CASE 1 SAVEGLOBAL PRINTL 已保存到 global.sav CASE 2 LOADGLOBAL PRINTL 已从 global.sav 加载数据 CASEELSE ; 将公共存档变量更新为当前时间 GLOBAL:公共存档数值2 = GETTIME() GLOBALS:公共存档字符串C '= @"%GETTIMES()% 随机={RAND(1000, 10000)}" GS_全局存档字符串变量:0 = 我爱你 GS_全局存档字符串变量:1 = 你一定要幸福啊 GS_全局存档字符串变量:2 = 祝你身体健康 GS_全局存档字符串变量:3 '= @"真没有阴阳怪气 {RAND:100}" PRINTL 已生成新的数据 ENDSELECT WEND QUIT
这个 demo 演示了完整的读写公共存档 global.sav
的流程,请自行体验效果。
重置公共存档相关变量 RESETGLOBAL
#
将 GLOBAL
初始化为 0
。
将 GLOBALS
初始化为 ""
(空字符串)。
专属的特定存档 save*.sav
#
检查特定存档 CHKDATA
#
注意执行 CHKDATA
(无论是 命令 还是 行内函数)之后,会将 存档名
赋值给 RESULTS
。
如果有需要,最好及时打印或自行转存(以免执行其他命令后又覆盖了 RESULTS
)。
@SYSTEM_TITLE #LOCALSIZE 1 WHILE 1 PRINTL 请输入要检查的存档栏位: INPUT SIF RESULT < 0 CONTINUE LOCAL = RESULT SELECTCASE CHKDATA(LOCAL) CASE 0 PRINTPLAINFORM 检测到合法的存档文件 [{LOCAL}] %RESULTS% PRINTL CASE 1 PRINTFORML {LOCAL} 号存档栏位没有数据 CASE 2 PRINTFORML {LOCAL} 号存档与本游戏不匹配 CASE 3 PRINTFORML {LOCAL} 号存档的游戏版本已不再受支持 CASE 4 PRINTFORML {LOCAL} 号存档文件存在其他问题 CASEELSE PRINTFORML {LOCAL} 号存档发生未知错误 ENDSELECT WEND QUIT
保存特定存档 SAVEDATA
#
很弱智的一点是 SAVEDATA
无论成功还是失败都是没有返回值的。
你必须自行结合 CHKDATA
来判断存档文件是否存在(从无到有说明保存成功,理论上)。
编辑 ./erb/特定存档变量.erh
:
#DIMS SAVEDATA GS_特定存档, 4 = "初始时间", "初始地点", "初始人物", "初始金钱"
编辑 ./erb/游戏业务逻辑.erb
:
@SYSTEM_TITLE #LOCALSIZE 1 #LOCALSSIZE 1 WHILE 1 DRAWLINE PRINTFORML 时间=%GS_特定存档:0% 地点=%GS_特定存档:1% 人物=%GS_特定存档:2% 金钱=%GS_特定存档:3% PRINTL 请输入要保存的存档栏位: INPUT SIF RESULT < 0 CONTINUE LOCAL = RESULT GS_特定存档:0 '= GETTIMES() GS_特定存档:1 '= "" REPEAT 5 GS_特定存档:1 += @"%UNICODE(RAND(97, 123))%" REND GS_特定存档:2 '= "" REPEAT 3 GS_特定存档:2 += @"%UNICODE(RAND(65, 91))%" REND GS_特定存档:3 '= @"{RAND(1000, 10000)}" LOCALS '= @"%GS_特定存档:0% %GS_特定存档:1% %GS_特定存档:2% %GS_特定存档:3%" IF SAVE_DATA(LOCAL, LOCALS) PRINTFORML 存档到 {LOCAL} 栏位成功 ELSE PRINTFORML 存档到 {LOCAL} 栏位失败 ENDIF WEND QUIT @SAVE_DATA(save_id, save_name) #FUNCTION #DIM save_id #DIMS save_name SAVEDATA save_id, save_name SELECTCASE CHKDATA(save_id) CASE 0 PRINTPLAINFORM 保存新存档 [{save_id, 2}] %RESULTS% PRINTL RETURNF 1 CASE 1 PRINTFORML {save_id} 号存档栏位没有数据 CASE 2 PRINTFORML {save_id} 号存档与本游戏不匹配 CASE 3 PRINTFORML {save_id} 号存档的游戏版本已不再受支持 CASE 4 PRINTFORML {save_id} 号存档文件存在其他问题 CASEELSE PRINTFORML {save_id} 号存档发生未知错误 ENDSELECT RETURNF 0
加载特定存档 LOADDATA
#
编辑 ./erb/特定存档变量.erh
:
#DIMS SAVEDATA GS_特定存档, 4 = "初始时间", "初始地点", "初始人物", "初始金钱"
编辑 ./erb/游戏业务逻辑.erb
:
@SYSTEM_TITLE WHILE 1 DRAWLINE PRINTFORML 时间=%GS_特定存档:0% 地点=%GS_特定存档:1% 人物=%GS_特定存档:2% 金钱=%GS_特定存档:3% PRINTL PRINTBUTTON "[1] 保存", 1 PRINT_SPACE 200 PRINTBUTTON "[2] 加载", 2 PRINT_SPACE 200 PRINTBUTTON "[3] 更新", 3 INPUT SELECTCASE RESULT CASE 1 SAVEDATA 0, "" PRINTL 已存档到 save00.sav CASE 2 IF !CHKDATA(0) PRINTL 从 save00.sav 加载存档 LOADDATA 0 ELSE PRINTL 从 save00.sav 加载存档失败 ENDIF CASEELSE GS_特定存档:0 '= GETTIMES() GS_特定存档:1 '= "" REPEAT 5 GS_特定存档:1 += @"%UNICODE(RAND(97, 123))%" REND GS_特定存档:2 '= "" REPEAT 3 GS_特定存档:2 += @"%UNICODE(RAND(65, 91))%" REND GS_特定存档:3 '= @"{RAND(1000, 10000)}" PRINTL 已生成新的数据 ENDSELECT WEND QUIT @EVENTLOAD BEGIN TITLE
删除特定存档 DELDATA
#
很弱智的一点是无论成功与否(在删除之前,指定的存档是否存在)都不会报错。
建议搭配使用 CHKDATA
进行判断。
@SYSTEM_TITLE #LOCALSIZE 1 WHILE 1 DRAWLINE PRINTL 请输入想要删除的存档栏位: INPUT LOCAL = RESULT SELECTCASE LOCAL CASE 0 TO 99 IF !CHKDATA(LOCAL) DELDATA LOCAL PRINTFORML 成功删除 {LOCAL} 号存档 ELSE PRINTFORML 指定的 {LOCAL} 号存档不存在 ENDIF CASEELSE PRINTFORML 非法的存档编号 {LOCAL} ENDSELECT WEND QUIT
重置非公共存档变量 RESETDATA
#
将除了 GLOBAL
和 GLOBALS
之外的全部变量都初始化。
有初始值的赋初值,没有初始值的赋值为 0 或空字符串。
角色数据 chara_*.dat
#
保存角色数据 SAVECHARA
#
ADDVOIDCHARA 0 SAVECHARA "角色备份文件名", "这是一个角色数据文件的备注", 0
会把序号为 0
的角色数据存储到 ./dat/chara_角色备份文件名.dat
文件。
检测角色数据 CHKCHARADATA
#
@SYSTEM_TITLE #LOCALSSIZE 1 LOCALS = 角色备份文件名 IF !CHKCHARADATA(LOCALS) PRINTFORML 可以读取 ./dat/chara_%LOCALS%.dat PRINTFORML 该角色备份的备注为:%RESULTS% ELSE PRINTFORML 没有找到 ./dat/chara_%LOCALS%.dat ENDIF QUIT
读取角色数据 LOADCHARA
#
@SYSTEM_TITLE #LOCALSSIZE 1 LOCALS = 角色备份文件名 PRINTFORML 读取之前 角色数量为 {CHARANUM} IF !CHKCHARADATA(LOCALS) PRINTFORML 读取角色数据成功 %RESULTS% LOADCHARA LOCALS ELSE PRINTFORML 没有找到 ./dat/chara_%LOCALS%.dat ENDIF PRINTFORML 读取之后 角色数量为 {CHARANUM} QUIT
搜索角色数据 FIND_CHARADATA
#
会搜索 ./dat/
目录下的角色数据文件。
可以使用 *
通配符,比如 *数据*
可以匹配到 有一个数据
/ 数据备份
。
@SYSTEM_TITLE #LOCALSIZE 1 LOCAL = FIND_CHARADATA("*角色*") IF LOCAL REPEAT LOCAL PRINTFORML 匹配到角色数据 ./dat/chara_%RESULTS:COUNT%.dat REND ELSE PRINTFORML 没有匹配到角色数据 ENDIF QUIT
内置存档系统#
保存游戏(写入存档)界面 SAVEGAME
#
执行 SAVEGAME
命令就会调用内置的存档保存系统。
无论「保存成功」还是「取消保存」都会返回到调用的地方。
@SYSTEM_TITLE SAVEGAME PRINTFORML 存档结束 上次存档的备注为 SAVEDATA_TEXT=%SAVEDATA_TEXT% QUIT
加载游戏(读取存档)界面 LOADGAME
#
简单对默认的 @SYSTEM_TITLE
(标题界面)和 @TITLE_LOADGAME
(存档加载界面)进行一个刻的复:
@SYSTEM_TITLE #LOCALSSIZE 1 WHILE 1 DRAWLINE PRINTL 这里是 @SYSTEM_TITLE ALIGNMENT CENTER SIF STRLENSU(GAMEBASE_TITLE) PRINTFORML %GAMEBASE_TITLE% IF GAMEBASE_VERSION > 0 LOCALS '= TOSTR(GAMEBASE_VERSION, "D8") PRINTFORML {TOINT(SUBSTRINGU(LOCALS, 0, 5))}.%SUBSTRINGU(LOCALS, 5)% ENDIF SIF STRLENSU(GAMEBASE_AUTHOR) PRINTFORML %GAMEBASE_AUTHOR% PRINTFORML (%GAMEBASE_YEAR%) PRINTL SIF STRLENSU(GAMEBASE_INFO) PRINTFORML %GAMEBASE_INFO% ALIGNMENT LEFT DRAWLINE FONTSTYLE 1p2 PRINTPLAINFORM [0] 开始新游戏 FONTSTYLE 0 PRINT_SPACE 100 PRINTBUTTON "[0] 保存存档", 0 PRINTL PRINTBUTTON "[1] 读取存档", 1 INPUT SELECTCASE RESULT CASE 0 SAVEGAME CASE 1 TRYCCALL TITLE_LOADGAME ; 相当于优先 CALL TITLE_LOADGAME ; 如果想看原生的 LOADGAME 是什么样子 ; 把下面的 @TITLE_LOADGAME 函数的名字稍作修改即可 CATCH LOADGAME ENDCATCH CASEELSE CONTINUE ENDSELECT WEND PRINTL 只有 BEGIN FIRST 的话,理论上不需要死循环也永远不会运行到这一行,因为 BEGIN 的本质是 JUMP PRINTW 永远无法到达的「游戏结束」 QUIT @TITLE_LOADGAME DRAWLINE PRINTL 这里是 @TITLE_LOADGAME WHILE 1 PRINTL 要读取哪个存档? REPEAT SAVENOS() ; 相当于 GETCONFIG("表示するセーブデータ数") IF CHKDATA(COUNT) PRINTFORML [{COUNT, 2}] ---- ELSE PRINTFORML [{COUNT, 2}] %RESULTS% ENDIF REND IF CHKDATA(99) PRINTFORML [99] ---- ELSE PRINTFORML [99] %RESULTS% ENDIF PRINTFORML [100] 返回 INPUT SELECTCASE RESULT CASE 0 TO 19 SELECTCASE CHKDATA(RESULT) CASE 0 LOADDATA RESULT CASE 1 PRINTL 该存档栏位没有数据 CASE 2 PRINTL 该存档与本游戏不匹配 CASE 3 PRINTL 该存档的游戏版本已不再受支持 CASE 4 PRINTL 存档文件存在其他问题 CASEELSE PRINTL 读取存档时发生未知错误 ENDSELECT CASE 99 IF CHKDATA(99) PRINTL 读取自动存档失败 ELSE LOADDATA 99 ENDIF CASE 100 BREAK CASEELSE PRINTL 输入的值无效 ENDSELECT WEND PRINTL 放弃读取存档 返回标题界面 @SYSTEM_LOADEND DRAWLINE PRINTL 这里是 @SYSTEM_LOADEND PRINTL 存档加载完成 PRINTL 上次读取的存档相关数据: PRINTFORML %"", 4%游戏版本号为 {LASTLOAD_VERSION} PRINTFORML %"", 4%存档栏位为 {LASTLOAD_NO} PRINTFORML %"", 4%存档名为 %LASTLOAD_TEXT% @EVENTLOAD DRAWLINE PRINTL 这里是 @EVENTLOAD PRINTL 接下来本来会自动跳转到 SHOP 界面 PRINTL 即 BEGIN SHOP(需要自行定义 @SHOW_SHOP 函数) 等价于 JUMP SHOW_SHOP PRINTL 我们在这里劫持游戏流程 让它回到标题界面 BEGIN TITLE
EE+EM 扩展功能#
强力安利这个 EM+EE 改进版。
这是目前功能最强大、设计最先进、更新最勤奋的 Emuera 版本,没有之一。
大家快来用啊。
两位作者都在我们的 里,有任何问题随时可以反馈。
将 XML 和 MAP 加入存档保存#
启用本功能必须先设置 セーブデータをバイナリ形式で保存する:YES
。
即启用「将存档文件使用二进制格式存储」。
编辑 ./csv/VarExt*.csv
:
; 可以在一行内设置多个(用逗号分开) ; 也可以使用多行、多个文件(例如 VarExt1.csv / VarExt2.csv / VarExt3.csv) ; 空格只是为了排版便于阅读,实际读取时会自动删除开头 / 末尾的空格 ; 设置希望保存在 global.sav 中的 MAP 的 ID GLOBAL_MAPS, MyMap, MyMap2 GLOBAL_MAPS, MyMap3 ; 设置希望保存在 global.sav 中的 XmlDocument 的 ID GLOBAL_XMLS, 0, MyXml ; 设置希望保存在 save*.sav 中的 MAP 的 ID SAVE_MAPS, MyMap4 ; 设置希望保存在 save*.sav 中的 XmlDocument 的 ID SAVE_XMLS, 1, MyXml2
注意事项:
- 不会存储空值,设置之后还要内存中确实存在这个变量,才会进行保存。
- 如果存档中已经保存的数据,没有在
VarExt*.csv
中设定,则会被丢弃。 - 保存 MAP / XML 的新存档兼容本家 / 私家改造版 / 旧版本的 EE+EM。
存储存档文件时进行压缩#
启用此功能需要 EMv12+EEv20 及以上版本。
启用本功能必须先设置 セーブデータをバイナリ形式で保存する:YES
。
即启用「将存档文件使用二进制格式存储」。
然后可以启用 EmueraEE+EM 的专属配置项 セーブデータを圧縮して保存する:YES
。
会进一步将二进制的存档文件压缩保存。
注意:压缩保存后的存档文件不再兼容本家 / 私家改造版 / 旧版本的 EE+EM。