零基础开发 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 TITLEEE+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。