该技能围绕虚幻引擎的存档与读档(Save & Load)机制展开,核心是使用 USaveGame 对象承载需要持久化的数据:把要保存的数据放入 UPROPERTY 成员,再将对象写入命名槽位并读回。槽位 API 位于 UGameplayStatics,实际文件 I/O 委托给 ISaveGameSystem,由各平台替换实现。
适用场景
保存和恢复玩家进度、物品栏、世界标记或按玩家设置;检查存档槽是否存在、删除或枚举槽位、多槽位/多档案流程;将运行时 Actor 状态(动态对象)序列化为 SaveGame 对象内的字节数组;在增删字段后迁移存档数据;理解哪些内容应放入 USaveGame、哪些应放入配置(UGameUserSettings/.ini)。
心智模型
USaveGame 子类:数据容器,UPROPERTY 成员即被序列化的内容。UGameplayStatics:入口点,负责创建/保存/加载/删除/检查槽位。ISaveGameSystem:平台适配层,FGenericSaveGameSystem 写入 .sav 文件。FArchive(ArIsSaveGame):序列化标志,UPROPERTY(SaveGame) 字段受其过滤。FObjectAndNameAsStringProxyArchive:包装 FMemoryWriter/Reader 以捕获 Actor 状态。
SaveGameToSlot 会写入 USaveGame 对象上所有非 transient 的 UPROPERTY;SaveGame 说明符在 USaveGame 自身上不起作用,它只在用 archive 模式选择性序列化 Actor 属性时才有意义。
定义 SaveGame
示例中 UMySaveGame 继承 USaveGame,包含 SaveVersion、PlayerName、PlayerLevel、LastCheckpoint、UnlockedAbilities 以及 DynamicActorData 等 UPROPERTY 成员。成员规则:必须是 UPROPERTY,裸字段对序列化器不可见;优先使用普通数据(整数、浮点、字符串、名称、枚举、结构体及其数组);绝不存储活动的 UObject* 或 Actor 指针,应存储稳定标识符(槽位索引、FName、FSoftObjectPath),加载时再解析回活动对象;FTransform、FVector、FRotator 和自定义 USTRUCT 只要成员都是 UPROPERTY 即可。
同步保存与加载
通过 UGameplayStatics::CreateSaveGameObject 创建对象,赋值后调用 SaveGameToSlot(Save, TEXT("Slot0"), 0);加载前用 DoesSaveGameExist 检查,再调用 LoadGameFromSlot 并 Cast;删除用 DeleteGameInSlot。UserIndex 区分按用户/档案的存档,PC 上通常为 0;槽位是任意字符串,应使用一致命名约定。
异步保存与加载
同步保存/加载会阻塞游戏线程。在游戏进行中触发的存档应使用异步变体:AsyncSaveGameToSlot 与 AsyncLoadGameFromSlot,它们在游戏线程序列化,但把文件读写交给工作线程。两个委托声明于 Kismet/GameplayStatics.h:43-47。
序列化 Actor 状态
对于动态世界对象(可破坏物、箱子、NPC),把 Actor 状态捕获到 USaveGame 内的字节数组。用 UPROPERTY(SaveGame) 标记要持久化的属性,再用 FObjectAndNameAsStringProxyArchive(ArIsSaveGame = true)过滤;只有标记 UPROPERTY(SaveGame) 的属性会在 ArIsSaveGame 设置时被写入。示例展示了用 FMemoryWriter/FMemoryReader 配合代理 archive 进行序列化与恢复。
按玩家存档(UE 5.3+)
ULocalPlayerSaveGame 扩展 USaveGame,内置版本管理、HandlePostLoad/HandlePreSave/HandlePostSave 钩子,以及与特定 ULocalPlayer 绑定的同步/异步辅助方法,是多本地玩家或需要结构化版本管理时推荐的按用户存档基类。示例展示了 LoadOrCreateSaveGameForLocalPlayer 与 AsyncLoadOrCreateSaveGameForLocalPlayer 的用法。
版本与迁移
从第一天起就添加 UPROPERTY() int32 SaveVersion = 1,模式变更时递增。新增 UPROPERTY 向后兼容(缺失字段按 C++ 默认值加载);重命名或删除字段是破坏性变更,需在加载逻辑中处理,例如在 PostLoad 中按版本迁移。对 ULocalPlayerSaveGame,重写 GetLatestDataVersion() 并在 HandlePostLoad 中修复。始终对加载对象做空检查,损坏或不匹配的存档会返回 nullptr。
保存内容的设计
持久化权威游戏状态:进度、统计、解锁、世界对象状态、设置。不要序列化整个活动关卡,应捕获重建世界所需的最小数据。跨关卡/会话数据在会话期间属于 GameInstance 或子系统,在存档点持久化到 USaveGame 槽位。游戏与图形设置通常更适合 UGameUserSettings / 配置 .ini 文件。
常见陷阱
非 UPROPERTY 字段不可见;UPROPERTY(SaveGame) 在 SaveGame 对象自身上不起作用;存储活动指针在重载后无意义;游戏进行中对大数据同步保存会造成可见卡顿;没有版本字段会导致迁移困难;LoadGameFromSlot 后不空检查会崩溃;保存与加载的槽位名/UserIndex 必须完全一致;UPROPERTY(Transient) 字段被显式排除在所有序列化之外。
版本说明
ULocalPlayerSaveGame 于 UE 5.3 引入(位于 SaveGame.h);UAsyncActionHandleSaveGame 更早(UE 4.x)。SaveGameToMemory / LoadGameFromMemory / SaveDataToSlot / LoadDataFromSlot 自 UE 5.3 起可用。ISaveGameSystem 平台层在 UE5 中稳定;PC 上写入 <Project>/Saved/SaveGames/ 下的 .sav 文件,主机平台替换为平台特定实现。