游戏需要保存进度、读取配置、加载存档,这些都离不开文件读写。在 Godot 里,处理文件的核心类是 FileAccess,配合 JSON、ConfigFile 等工具可以很方便地实现数据持久化。这篇笔记整理我常用的文件操作和持久化方案,覆盖从简单的文本读写到结构化数据存储。
一、文件读写基础:FileAccess
Godot 4 使用 FileAccess 类来读写文件。它取代了 Godot 3 中的 File 类。基本流程是:打开文件 → 读写内容 → 关闭文件。
1. 写入文本文件
用 FileAccess.open() 打开文件,指定路径和模式。模式有:
FileAccess.WRITE:写入模式,会覆盖已有内容FileAccess.READ:读取模式FileAccess.READ_WRITE:读写模式FileAccess.APPEND:追加模式
写入文本:
gdscript
var file = FileAccess.open("user://save.txt", FileAccess.WRITE)
if file:
file.store_string("Hello Godot\n")
file.store_string("第二行内容")
file.close()
else:
print("打开文件失败")
注意:使用 user:// 前缀表示用户数据目录,这是保存游戏存档和配置的推荐位置,不同平台会映射到对应的可写目录(比如 Windows 的 %APPDATA%,Android 的沙盒目录)。
2. 读取文本文件
读取整个文件内容:
gdscript
var file = FileAccess.open("user://save.txt", FileAccess.READ)
if file:
var content = file.get_as_text()
print(content)
file.close()
else:
print("文件不存在或无法读取")
逐行读取:
gdscript
var file = FileAccess.open("user://save.txt", FileAccess.READ)
if file:
while not file.eof_reached():
var line = file.get_line()
print(line)
file.close()
3. 检查文件是否存在
使用 FileAccess.file_exists() 静态方法。
gdscript
if FileAccess.file_exists("user://save.txt"):
print("存档存在")
二、存储路径说明
Godot 中有两种路径前缀:
res://:资源目录,指向项目文件夹,只读(在导出后不可写)。用于读取游戏自带资源。user://:用户数据目录,可读写,用于保存存档、配置、截图等。
永远不要把存档写入 res:// ,导出后这会失败或没有权限。保存数据一律用 user://。
三、JSON 持久化
JSON 是最常用的数据交换格式,适合存储结构化数据,比如玩家属性、背包内容、设置选项等。Godot 内置了 JSON 类。
1. 将字典写入 JSON 文件
gdscript
var data = {
"player_name": "Tom",
"level": 5,
"health": 100,
"inventory": ["sword", "potion", "shield"]
}
var file = FileAccess.open("user://save.json", FileAccess.WRITE)
if file:
file.store_string(JSON.stringify(data, "\t"))
file.close()
JSON.stringify() 将字典转换成 JSON 字符串,第二个参数是缩进(可选),方便阅读。
2. 从 JSON 文件读取
gdscript
var file = FileAccess.open("user://save.json", FileAccess.READ)
if file:
var content = file.get_as_text()
var data = JSON.parse_string(content)
if data != null:
print(data["player_name"])
print(data["inventory"])
file.close()
JSON.parse_string() 返回字典或数组,如果格式错误返回 null。
3. 注意事项
- JSON 中的数字会被解析为
float,如果需要整数,记得用int()转换。 - JSON 不支持
Vector2、Color等自定义类型,需要手动转换(存成分量或字符串)。 - 存档数据较多时,JSON 文件可读性好,但体积和加载速度不如二进制。
四、ConfigFile 配置存储
ConfigFile 是 Godot 提供的一种类似 INI 的格式,适合保存设置和键值对。它自动处理类型,读写方便。
1. 写入配置
gdscript
var config = ConfigFile.new()
config.set_value("player", "name", "Tom")
config.set_value("player", "level", 5)
config.set_value("audio", "volume", 0.8)
var err = config.save("user://settings.cfg")
if err == OK:
print("配置保存成功")
set_value(section, key, value) 将值存入指定分段。
2. 读取配置
gdscript
var config = ConfigFile.new()
var err = config.load("user://settings.cfg")
if err == OK:
var player_name = config.get_value("player", "name", "默认名字")
var volume = config.get_value("audio", "volume", 1.0)
print(player_name, volume)
else:
print("配置文件加载失败")
get_value(section, key, default) 如果键不存在,返回默认值,避免崩溃。
3. ConfigFile 的优势
- 自动处理类型(不需要手动转换)
- 支持分组,结构清晰
- 读写简单,适合游戏设置
我通常用 ConfigFile 存设置,用 JSON 存复杂存档。
五、二进制文件读写
对于需要高效存储或数据量大的情况(比如地图、关卡数据),可以使用二进制方式。FileAccess 提供了多种方法:
store_8(),store_16(),store_32(),store_64()store_float(),store_double()store_buffer()写入字节数组
读取对应 get_8(), get_16(), get_float() 等。
示例:写入和读取整数数组
gdscript
var numbers = [10, 20, 30, 40]
# 写入
var file = FileAccess.open("user://data.bin", FileAccess.WRITE)
if file:
for n in numbers:
file.store_32(n)
file.close()
# 读取
var loaded_numbers = []
var file2 = FileAccess.open("user://data.bin", FileAccess.READ)
if file2:
while not file2.eof_reached():
loaded_numbers.append(file2.get_32())
file2.close()
注意二进制文件不可读,必须按写入时的顺序和类型读取。
六、目录操作
除了文件,有时需要创建或遍历目录。DirAccess 类处理目录。
创建目录
gdscript
var dir = DirAccess.open("user://")
if dir:
dir.make_dir("saves")
遍历目录下的文件
gdscript
var dir = DirAccess.open("user://saves")
if dir:
dir.list_dir_begin()
var file_name = dir.get_next()
while file_name != "":
if not dir.current_is_dir():
print("文件:", file_name)
file_name = dir.get_next()
dir.list_dir_end()
七、实际应用:保存和加载游戏进度
结合前面知识,写一个简单的存档系统:
gdscript
extends Node
var save_path = "user://save_game.json"
func save_game(player_data: Dictionary):
var file = FileAccess.open(save_path, FileAccess.WRITE)
if file:
file.store_string(JSON.stringify(player_data, "\t"))
file.close()
print("游戏已保存")
func load_game() -> Dictionary:
if not FileAccess.file_exists(save_path):
return {}
var file = FileAccess.open(save_path, FileAccess.READ)
if file:
var content = file.get_as_text()
var data = JSON.parse_string(content)
file.close()
if data is Dictionary:
return data
return {}
func _ready():
# 保存示例
var player = {
"name": "Tom",
"level": 3,
"position": [10.5, 20.0],
"inventory": ["sword", "potion"]
}
save_game(player)
# 加载示例
var loaded = load_game()
if loaded:
print("加载玩家:", loaded["name"], "等级:", loaded["level"])
八、总结
文件读写和持久化是游戏开发的基本功。在 Godot 中:
- 用
FileAccess读写文件,注意使用user://路径 - 简单文本用
store_string/get_as_text - 结构化数据用 JSON,方便读写和调试
- 设置类数据用 ConfigFile,自动处理类型
- 大量二进制数据用
store_32等方法
建议从小项目开始就养成良好的存档习惯,避免后期重构。下一篇我们进入 GDScript 的最后一个基础主题:资源与引用,或者也可以开始实战小项目。