GDScript 入门笔记(十六):文件读写与数据持久化

游戏需要保存进度、读取配置、加载存档,这些都离不开文件读写。在 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 不支持 Vector2Color 等自定义类型,需要手动转换(存成分量或字符串)。
  • 存档数据较多时,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 的最后一个基础主题:资源与引用,或者也可以开始实战小项目。

相关推荐
会飞的拖把1 小时前
Python序列详解:列表、元组、字符串的通用操作(超详细版)
开发语言·windows·python
ByNotD0g2 小时前
跨站脚本攻击- XSS
笔记·web安全
摇滚侠2 小时前
《Spring Boot 3:高级与架构设计》第 1 章 BeanDefinitionRegistry 阅读笔记 3
spring boot·笔记·后端
reasonsummer2 小时前
【办公类-115-01】20260906育儿知识(家园小报)批量制作(2026年9月-2027年6月)
开发语言·数据库·c#
君顾12 小时前
智慧场馆解决方案小程序系统实战:从架构设计到上线指南
java·开发语言·智慧场馆
zhougl9962 小时前
Dockerfile实战教程
java·开发语言·spring boot
FfHUCisI2 小时前
Golang 网络轮询器 netpoll:把阻塞 IO 变成事件通知
开发语言·网络·golang
XiaoQiao6669992 小时前
C 语言常用头文件及里面核心函数
c语言·开发语言
statistican_ABin2 小时前
中国宠物经济发展分析报告——基于R语言的2018-2024年宠物市场多维度分析
开发语言·r语言·宠物