FileAccess
2026/4/14大约 8 分钟
最后同步日期:2026-04-15 | Godot 官方原文 — FileAccess
FileAccess
定义
FileAccess 是 Godot 提供的文件读写工具类。你可以把它想象成一个"文件管家"——有了它,你才能让游戏读取和写入文件,比如保存游戏进度、加载配置信息、读取 JSON 数据等。
它不需要创建节点,而是通过静态方法(如 FileAccess.Open())直接获取一个文件访问对象,然后就能读写了。用完之后必须调用 Close() 关闭文件,就像用完水龙头要关上一样。
常用方法
| 方法 | 返回值 | 说明 |
|---|---|---|
Open(path, flags) | FileAccess | 打开文件。flags 指定读/写模式 |
Close() | void | 关闭文件,释放资源 |
GetAsText() | string | 把整个文件内容读取为文本字符串 |
StoreString(text) | void | 向文件写入一段文本字符串 |
GetLine() | string | 读取一行文本(遇到换行符停止) |
StoreLine(text) | void | 写入一行文本(自动添加换行符) |
GetPascalString() | string | 读取一个带长度前缀的字符串 |
StorePascalString(text) | void | 写入一个带长度前缀的字符串 |
Get8() / Get16() / Get32() / Get64() | int | 读取 8/16/32/64 位整数 |
GetFloat() / GetDouble() | float / double | 读取单精度/双精度浮点数 |
Store8() / Store16() / Store32() / Store64() | void | 写入 8/16/32/64 位整数 |
StoreFloat() / StoreDouble() | void | 写入单精度/双精度浮点数 |
GetLength() | long | 获取文件总大小(字节数) |
GetPosition() | long | 获取当前读写位置 |
Seek(position) | void | 跳转到指定字节位置 |
SeekEnd(position) | void | 从文件末尾往前跳转 |
EofReached() | bool | 是否已读到文件末尾 |
FileExists(path) | bool(静态) | 检查文件是否存在 |
OpenFailed() | bool | 上次 Open 操作是否失败 |
打开模式(ModeFlags)
| 模式 | 说明 |
|---|---|
Read | 只读模式,文件必须存在 |
Write | 只写模式,文件不存在会创建,存在会清空 |
ReadWrite | 读写模式,文件不存在会创建,存在保留内容 |
WriteRead | 读写模式,文件不存在会创建,存在会清空 |
代码示例
基础用法:读写文本文件
C#
using Godot;
public partial class FileDemo : Node
{
public override void _Ready()
{
// ---- 写入文件 ----
using var writeFile = FileAccess.Open("user://demo.txt", FileAccess.ModeFlags.Write);
if (writeFile != null)
{
writeFile.StoreLine("第一行:欢迎来到 Godot!");
writeFile.StoreLine("第二行:这是一个示例文件。");
writeFile.StoreLine("第三行:文件操作很简单。");
GD.Print("文件写入完成");
// 运行结果: 文件写入完成
}
else
{
GD.PrintErr("写入失败: ", FileAccess.GetOpenError());
}
// ---- 读取文件 ----
using var readFile = FileAccess.Open("user://demo.txt", FileAccess.ModeFlags.Read);
if (readFile != null)
{
// 方式 1:逐行读取
while (!readFile.EofReached())
{
var line = readFile.GetLine();
if (line != null)
{
GD.Print(line);
}
}
// 运行结果: 第一行:欢迎来到 Godot!
// 运行结果: 第二行:这是一个示例文件。
// 运行结果: 第三行:文件操作很简单。
}
else
{
GD.PrintErr("读取失败: ", FileAccess.GetOpenError());
}
}
}GDScript
extends Node
func _ready():
# ---- 写入文件 ----
var write_file = FileAccess.open("user://demo.txt", FileAccess.WRITE)
if write_file:
write_file.store_line("第一行:欢迎来到 Godot!")
write_file.store_line("第二行:这是一个示例文件。")
write_file.store_line("第三行:文件操作很简单。")
write_file.close()
print("文件写入完成")
# 运行结果: 文件写入完成
else:
push_error("写入失败: " + str(FileAccess.get_open_error()))
# ---- 读取文件 ----
var read_file = FileAccess.open("user://demo.txt", FileAccess.READ)
if read_file:
# 方式 1:逐行读取
while not read_file.eof_reached():
var line = read_file.get_line()
if line != "":
print(line)
read_file.close()
# 运行结果: 第一行:欢迎来到 Godot!
# 运行结果: 第二行:这是一个示例文件。
# 运行结果: 第三行:文件操作很简单。
else:
push_error("读取失败: " + str(FileAccess.get_open_error()))实际场景:保存和加载游戏存档
C#
using Godot;
using System.Text.Json;
/// <summary>
/// 游戏存档系统:使用 FileAccess 将游戏数据序列化为 JSON 并保存。
/// </summary>
public partial class SaveManager : Node
{
private static readonly string SavePath = "user://save_game.json";
// 存档数据结构
public class SaveData
{
public string PlayerName { get; set; } = "";
public int Level { get; set; } = 1;
public float Health { get; set; } = 100f;
public float PositionX { get; set; }
public float PositionY { get; set; }
public float PositionZ { get; set; }
}
/// <summary>
/// 保存游戏
/// </summary>
public bool SaveGame(SaveData data)
{
var json = JsonSerializer.Serialize(data, new JsonSerializerOptions { WriteIndented = true });
using var file = FileAccess.Open(SavePath, FileAccess.ModeFlags.Write);
if (file != null)
{
file.StoreString(json);
GD.Print("游戏已保存到: ", SavePath);
// 运行结果: 游戏已保存到: user://save_game.json
return true;
}
GD.PrintErr("保存失败: ", FileAccess.GetOpenError());
return false;
}
/// <summary>
/// 加载游戏
/// </summary>
public SaveData LoadGame()
{
if (!FileAccess.FileExists(SavePath))
{
GD.Print("没有找到存档文件");
return null;
}
using var file = FileAccess.Open(SavePath, FileAccess.ModeFlags.Read);
if (file != null)
{
var json = file.GetAsText();
var data = JsonSerializer.Deserialize<SaveData>(json);
GD.Print($"存档已加载: {data.PlayerName} Lv.{data.Level}");
// 运行结果: 存档已加载: Hero Lv.5
return data;
}
GD.PrintErr("加载失败");
return null;
}
}GDScript
extends Node
## 游戏存档系统:使用 FileAccess 将游戏数据序列化为 JSON 并保存。
const SAVE_PATH := "user://save_game.json"
## 保存游戏
func save_game(player_name: String, level: int, health: float, position: Vector3) -> bool:
var data = {
"player_name": player_name,
"level": level,
"health": health,
"position_x": position.x,
"position_y": position.y,
"position_z": position.z,
}
var json_string = JSON.stringify(data, "\t")
var file = FileAccess.open(SAVE_PATH, FileAccess.WRITE)
if file:
file.store_string(json_string)
file.close()
print("游戏已保存到: " + SAVE_PATH)
# 运行结果: 游戏已保存到: user://save_game.json
return true
push_error("保存失败: " + str(FileAccess.get_open_error()))
return false
## 加载游戏
func load_game() -> Dictionary:
if not FileAccess.file_exists(SAVE_PATH):
print("没有找到存档文件")
return {}
var file = FileAccess.open(SAVE_PATH, FileAccess.READ)
if file:
var json_string = file.get_as_text()
file.close()
var json = JSON.new()
var error = json.parse(json_string)
if error == OK:
var data = json.data
print("存档已加载: %s Lv.%d" % [data.player_name, data.level])
# 运行结果: 存档已加载: Hero Lv.5
return data
push_error("加载失败")
return {}进阶用法:读写二进制文件(自定义地图格式)
C#
using Godot;
using System.Collections.Generic;
/// <summary>
/// 自定义二进制地图格式读写示例。
/// 文件结构:
/// [4 字节] 魔数 "GMAP"
/// [4 字节] 版本号 (int32)
/// [4 字节] 地图宽度
/// [4 字节] 地图高度
/// [width * height 字节] 每个格子一个字节,表示地形类型
/// </summary>
public partial class MapFileIO : Node
{
private static readonly byte[] MagicBytes = { (byte)'G', (byte)'M', (byte)'A', (byte)'P' };
/// <summary>
/// 保存地图到二进制文件
/// </summary>
public bool SaveMap(string path, int width, int height, byte[] tiles)
{
using var file = FileAccess.Open(path, FileAccess.ModeFlags.Write);
if (file == null)
{
GD.PrintErr("无法创建地图文件: ", FileAccess.GetOpenError());
return false;
}
// 写入文件头
foreach (var b in MagicBytes)
file.Store8(b);
file.Store32(1); // 版本号
file.Store32((uint)width);
file.Store32((uint)height);
// 写入地形数据
for (int i = 0; i < tiles.Length; i++)
{
file.Store8(tiles[i]);
}
GD.Print($"地图已保存: {width}x{height}, {tiles.Length} 格");
// 运行结果: 地图已保存: 32x24, 768 格
return true;
}
/// <summary>
/// 从二进制文件加载地图
/// </summary>
public (int width, int height, byte[] tiles) LoadMap(string path)
{
using var file = FileAccess.Open(path, FileAccess.ModeFlags.Read);
if (file == null)
{
GD.PrintErr("无法读取地图文件: ", FileAccess.GetOpenError());
return (0, 0, null);
}
// 验证魔数
for (int i = 0; i < 4; i++)
{
if (file.Get8() != MagicBytes[i])
{
GD.PrintErr("无效的地图文件格式");
return (0, 0, null);
}
}
// 读取文件头
uint version = file.Get32();
uint width = file.Get32();
uint height = file.Get32();
GD.Print($"地图版本: {version}, 尺寸: {width}x{height}");
// 读取地形数据
var tileCount = (int)(width * height);
var tiles = new byte[tileCount];
for (int i = 0; i < tileCount; i++)
{
tiles[i] = file.Get8();
}
GD.Print($"地图已加载: {tileCount} 格");
// 运行结果: 地图已加载: 768 格
return ((int)width, (int)height, tiles);
}
}GDScript
extends Node
## 自定义二进制地图格式读写示例。
## 文件结构:
## [4 字节] 魔数 "GMAP"
## [4 字节] 版本号 (int32)
## [4 字节] 地图宽度
## [4 字节] 地图高度
## [width * height 字节] 每个格子一个字节,表示地形类型
const MAGIC := "GMAP"
## 保存地图到二进制文件
func save_map(path: String, width: int, height: int, tiles: PackedByteArray) -> bool:
var file = FileAccess.open(path, FileAccess.WRITE)
if file == null:
push_error("无法创建地图文件: " + str(FileAccess.get_open_error()))
return false
# 写入文件头
file.store_pascal_string(MAGIC) # 带长度前缀的字符串
file.store_32(1) # 版本号
file.store_32(width)
file.store_32(height)
# 写入地形数据
for tile in tiles:
file.store_8(tile)
file.close()
print("地图已保存: %dx%d, %d 格" % [width, height, tiles.size()])
# 运行结果: 地图已保存: 32x24, 768 格
return true
## 从二进制文件加载地图
func load_map(path: String) -> Dictionary:
var file = FileAccess.open(path, FileAccess.READ)
if file == null:
push_error("无法读取地图文件: " + str(FileAccess.get_open_error()))
return {}
# 验证魔数
var magic = file.get_pascal_string()
if magic != MAGIC:
push_error("无效的地图文件格式")
file.close()
return {}
# 读取文件头
var version = file.get_32()
var width = file.get_32()
var height = file.get_32()
print("地图版本: %d, 尺寸: %dx%d" % [version, width, height])
# 读取地形数据
var tile_count = width * height
var tiles = PackedByteArray()
for i in tile_count:
tiles.append(file.get_8())
file.close()
print("地图已加载: %d 格" % tile_count)
# 运行结果: 地图已加载: 768 格
return {
"width": width,
"height": height,
"tiles": tiles,
}注意事项
路径前缀说明:Godot 使用特殊路径前缀来定位文件:
user://-- 游戏的用户数据目录(存档、设置等),这个目录在每台电脑上的具体位置不同,但游戏总是能找到它。Windows 上一般在%APPDATA%/Godot/app_userdata/项目名/。res://-- 游戏的资源目录(项目文件夹中的文件)。打包后这些文件通常是只读的,不要试图往这里写东西。- 绝对路径也可以使用(如
C:/data/file.txt),但跨平台兼容性差,不推荐。
用完必须关闭:打开文件后,必须调用
Close()(C# 中使用using语句会自动关闭)。忘记关闭会导致文件被锁定,其他代码无法访问该文件。Write 模式会清空文件:以
Write模式打开文件时,文件原有内容会被全部清空。如果你想在文件末尾追加内容,应该使用ReadWrite模式并先SeekEnd(0)。检查文件是否打开成功:
Open()可能返回null(文件不存在、权限不足等)。务必检查返回值,不要假设文件一定能打开。Web 平台限制:在浏览器中运行时(HTML5 导出),
FileAccess的功能受到严重限制。user://只能用特定方式访问,res://只读。需要持久化数据时应使用JavaScriptBridge或 Web Storage API。
