本节摘要:引擎里贴图、音效、场景、材质、脚本共享一个统一身份——资源。资源经导入系统转换进内部格式,按引用计数缓存共享,同名重载拿同一份。在资源之上可以自定义数据类型,把游戏数值从代码抽离成可编辑的配置对象——这就是数据驱动。本节合并讲解资源管线与数据存储配置(原书两节内容)。
引擎世界有个容易被忽略的统一性:你在编辑器里碰到的一切——图片、声音、场景、字体、着色器、脚本——都是"资源"这个家族的成员。它们共享同一套生死规则(第 2 章的引用计数加缓存)、同一套加载接口、同一种序列化方式。理解这个统一性,资产管理的所有问题就归成了一类问题。
一张美术给的图片进项目后,经历三站。
**第一站:导入。**引擎检测到新文件,自动生成一份导入配置(贴图的压缩方式、音效的循环标记、模型的缩放单位),把原文件转换成引擎内部的高效格式。导入配置本身也是资源,跟着项目走。这一站的关键认知:原文件是源头,导入产物是运行时用的——改压缩参数会触发重导入,这是"改了设置怎么没生效"类问题的答案(先存一次触发重导入)。
**第二站:加载与缓存。**场景引用了资源,加载场景时资源随之进内存并进缓存。此后任何地方再要同名资源,缓存直接给同一份(第 2 章讲过的共享语义)。缓存的好处是省内存省时间,代价是"我以为改的是副本"这类误会——改的是全体引用者共享的本尊。
**第三站:序列化。**资源保存为引擎格式的文本或二进制文件,文本格式可读、适合版本管理,二进制紧凑、适合发布。游戏跑起来后玩家进度那类"运行时才产生的数据"则走另一条存储通道(配置文件或用户目录),与资源管线分开——资产在项目里、进度在用户处,这条界线划清,存档系统就不会走歪。
资源加载的三种策略对应三类体验诉求。
启动全量加载:开局把主菜单要的东西全装进内存,之后切换丝滑;代价是启动等待。适合体量小的游戏。关卡级加载:每关加载自己需要的,切换关卡时卸旧装新——需要加载遮罩遮盖空档(第 3 章常驻层的应用)。流式加载:世界很大时按区域分块,玩家靠近哪块装哪块,远处的卸掉——开放世界与大型矿井的正解,实现复杂度也最高。
# 关卡级加载:加载场景作为中转站 func load_level_with_progress(path: String) -> void: get_tree().change_scene_to_file(&"res://scene/loading.tscn") await get_tree().process_frame # 让加载画面先画出来 var loader := ResourceLoader.load_threaded_request(path) var progress := [] while ResourceLoader.load_threaded_get_status(path, progress) \ == ResourceLoader.THREAD_LOAD_IN_PROGRESS: %Bar.value = progress[0] * 100.0 await get_tree().process_frame var packed := ResourceLoader.load_threaded_get(path) get_tree().change_scene_to_packed(packed)
后台加载接口让加载在独立线程进行,主线程继续渲染加载界面——进度条因此是真的进度而不是装饰。音频的长文件另有流式播放模式(边读边播不整载),环境音与背景音乐的内存开销靠它压下来。
引擎允许你定义自己的资源类型——这是数据驱动的钥匙。把"一种矿石"从散落的常量变成一个资源类:
# 矿石定义:数据与行为分离 class_name OreConfig extends Resource @export var display_name: String = "铜矿" @export var value := 5 @export var color := Color(0.72, 0.45, 0.2) @export var hardness := 1 # 需要几级镐 @export var debris: PackedScene # 碎裂特效引用
在编辑器里新建一份这种资源,填好数值,保存为文件。游戏里任何地方引用它:瓦片数据指向它、背包按它计价、碎裂特效按它的颜色发粒子。一种矿石的全部事实集中在一个文件里——策划加新矿种不用找程序员,复制一份资源改数字即可。
# 使用侧:代码只认数据对象,不认识任何具体矿石 func add_ore(cfg: OreConfig) -> void: _ores[cfg.display_name] = _ores.get(cfg.display_name, 0) + 1 money += cfg.value
数据驱动的工程收益清单:热改快(改数值不用重新构建,编辑器里改完直接跑);分工清(策划管数据文件、程序管行为代码);组合活(稀有度、掉落表、装备词条全是数据资源,互相引用自由组合);测试易(数值平衡做成多套数据文件,一键切换对比)。代价只有一条前期投资:类型设计要想清楚。经验法则是按"名词"建资源类型(矿石、武器、敌人、升级项),每个类型是一张可填的表格。
运行时数据(存档、设置、最高分)住在用户目录,与项目资源分开。引擎提供两件趁手工具:配置文件(键值分节的文本,简单数据首选,第 5 章已用于存档)与序列化到资源(复杂结构存成用户目录下的资源文件,读取时类型自动恢复)。选择依据:数据简单扁平用配置文件,结构嵌套带类型用资源序列化。设置类数据(音量、键位、语言)与进度类数据(存档)分文件存——玩家清存档不应该把设置也清了。
⚠️ 常见坑:把玩家进度塞进项目资源路径下。项目资源随构建打包进安装包,玩家目录才是可写区——写错位置的存档在导出版本里会静默失败(第 10 章导出后测试存档,是发布检查单的必过项)。

感官与数据两翼丰满。第 9 章向外突围:网络让世界多人同在,工程化让开发本身提速。