Skip to content

发布资源缺失排查指南 ​

本文用于排查这样一类问题:编辑器里调试正常,但玩家通过游戏平台进入游戏后,出现模型看不到、粒子不播放、音效听不到、动画不对或运行时报资源缺失。

本页的资源统计链用于客户端预下载云资源。项目 res 内模型、贴图、音频已封装在客户端包中,不要求资源统计或 objref.txt 条目;缺失时检查本地文件、引用及项目打包。模型包导入见 项目模型资源包导入。动画/碰撞的派生产物生成是另一项需求,不等同于项目资源预下载统计。

这类问题通常只会在真实发布链路里暴露,因为编辑器调试环境常常已经下载了全量官方资源;而玩家进入游戏前,平台只会按项目发布出的资源引用清单自动下载需要的资源。

如果问题发生在“外网环境正常,迁移到线上环境后自定义模型、粒子或音频缺失”,还要先区分是不是自定义云端资源没有同步。外网和线上的自定义云端资源互相独立,资源统计无法凭空补齐目标环境不存在的云资源。先看 部署环境与运行时环境指南,再继续排查 objref.txt / full.ref。

先理解这条链路 ​

项目发布时,客户端资源清单的关键输入之一是项目根目录下的:

text
ref/objref.txt

这份文件由 dotnet build -c Client-Resource 触发的 IFileResourceAnalyzer 在编译期间生成。分析器会扫描 GameEntry 项目的 C# 代码,找出写进 IFile 类型、IFile 属性或 IFile 字段中的资源路径,并把结果写到项目根目录的 ref/objref.txt。同时它还会生成:

text
ref/model_animation.json
ref/model_collision.json

model_collision.json 收集设置了 GameDataModel.CollisionBake 的模型,供编辑器生成服务端预烘焙模型碰撞。运行时产物与排错方式见 服务端预烘焙模型碰撞。

发布流程再基于 objref.txt、地图依赖和其他 ref 文件,合成最终的:

text
ref/full.ref

玩家进入游戏前,平台优先依据发布产物中的 full.ref 和相关依赖清单决定要下载哪些资源。

为什么编辑器里正常,实机却缺资源 ​

最常见的原因不是“资源文件不存在”,而是“资源没有进入发布清单”。典型场景:

  • 编辑器调试机已经有全量官方资源,本地运行能直接命中缓存。
  • 发布时平台只按项目声明的资源去下载,未进入清单的资源不会自动补齐。
  • 代码里通过字符串拼接、运行时计算、配置热拼接或反射间接得到资源路径,静态分析器无法可靠还原。

因此,实机缺资源时,第一反应不应该只是去查资源库,也要查这条资源统计链是否把它统计进了 objref.txt。

IFile 分析器能识别什么 ​

IFileResourceAnalyzer 不是运行时录制器,而是编译期静态分析器。它当前主要识别这些模式:

  • 直接写在 IFile 类型上的字面量路径。
  • 对包含 IFile 属性或字段的对象初始化器赋值。
  • 对 IFile 属性或字段的直接赋值。
  • 传给轻量 helper 的字面量参数,前提是该参数在 helper 内直接转发到 IFile 属性或字段,例如 RegisterGameSound("sound/custom/click.ogg") 最终只做 sound.Asset = asset。
  • GameDataModel.Asset、HumanoidSourceAnimations 以及模型动画映射相关的常见写法。
  • 常见的单位技能动画链:GameDataUnit(及其子类)的 Model + Abilities → GameDataAbility.Animation → GameDataAnimation 中的动画素材。分析器会把解析到的技能动画合并到单位模型对应的 model_animation.json 项中。
  • 运行时要挂载的第一人称武器、手臂、瞄具或枪械视图模型,只要最终要随包发布,也应以 GameDataModel / GameDataAnimation / 显式资源清单的方式让分析器看见。
  • 普通字符串字面量和 "path"u8 这类 UTF-8 字面量。

典型可识别写法:

csharp
var effect = ParticleSystem.Load("effect/eff_tongyong/huoqiu2/particle.effect");

var model = new GameDataModel(heroModel)
{
    Asset = "characters/general/sk_basic2/model.prefab"u8,
    HumanoidSourceAnimations =
    [
        "anim/human/barehanded_anim/hand_05/skill_01.ani",
    ],
};

HumanoidSourceAnimations 不再是技能动画进入烘焙列表的唯一入口。例如,单位的 Model 和 Abilities、技能的 Animation、以及对应 GameDataAnimationSimple.File / 组合动画素材都使用静态且可追踪的写法时,分析器可以自动展开这条引用链。

不过这仍然是编译期静态分析,不是完整的运行时求值。helper 封装、运行时条件、动态集合修改、反射、复杂工厂或无法静态还原的 link 都可能让分析器漏掉动画。对于必须稳定烘焙的动画,仍建议把路径显式写进对应 GameDataModel.HumanoidSourceAnimations 作为保险。

这里的显式字段只解决动画清单声明。Maker 导入的外部 MDL 必须先按 Maker 人形工作流 读取源/目标骨架并完成显式重定向;不能因为模型已进入 GameDataModel.Asset 或已填写 HumanoidSourceAnimations,就跳过 boneMap、alignmentBones 和实际动作验收。

IFile 分析器识别不到什么 ​

下面这些写法都应默认视为高风险,因为它们未必能稳定进入 objref.txt:

  • 字符串拼接,例如 "effect/" + effectName + "/particle.effect"。
  • 插值字符串里带变量,例如 $"effect/{effectName}/particle.effect"。
  • 从配置表、Json、数据库、网络消息中读出后再传给资源 API。
  • 多层封装后才构造路径,最终赋值点看不到字面量。
  • 用普通 string 到处传路径,最后才在深层函数里喂给资源 API。
  • 只在运行时批量 new GameDataSound / new GameDataActorSound,并从数组、配置或逻辑分支里生成 Asset 路径;这类写法即使表面用了 GameDataSound 类型,也不等同于静态可收集数据。

换句话说,分析器更擅长“代码里就能直接看见的资源路径”,不擅长“运行时才算出来的资源路径”。

如果你的代码是 AI 助手生成的,或者手写时图方便直接拼接字符串,尤其要警惕这一点。

应该先看哪些文件 ​

先看项目根目录下这几个文件:

text
<ProjectRoot>/
├── ref/objref.txt
├── ref/model_animation.json
├── ref/model_collision.json
└── Client-Resource.log

意义分别是:

  • ref/objref.txt:编译期统计出的原始资源清单。
  • ref/model_animation.json:模型和动画的映射结果,主要帮助动画依赖展开。
  • ref/model_collision.json:声明了 CollisionBake 的模型与烘焙配置,供编辑器生成服务端碰撞产物。
  • Client-Resource.log:手动执行资源统计时保留的编译日志,便于搜诊断码和异常。

如果是通过编辑器发布流程自动统计,成功后日志可能被清理;这时更适合用手动方式再跑一遍,保留日志排查。

最直接的排查步骤 ​

1. 先确认资源路径本身写对了 ​

先对照 项目资源路径指南 检查:

  • UI 图片是否放在 ui/ 下,代码路径不带 ui/ 前缀。
  • 项目私有模型、粒子、音频是否放在 res/ 下,代码路径不带 res/ 前缀。
  • 是否误把引擎资源当成 user_files/... 普通文件来处理。

路径规则写错时,统计成功也没有意义,因为运行时本身就加载不到。

2. 手动生成一次资源统计 ​

项目根目录通常自带:

text
生成资源统计ref.bat

它本质上执行的是:

powershell
dotnet build src\GameEntry.csproj -c Client-Resource

直接在项目根目录执行这份 bat,可以把发布流程拆开单独验证。这样做的好处是:你可以立刻查看本次统计生成的 ref/objref.txt,而不需要先走完整发布。

3. 打开 ref/objref.txt 搜具体资源 ​

如果你怀疑某个模型、粒子或音效没被统计,直接在 ref/objref.txt 搜对应路径。例如:

  • characters/general/sk_basic2/model.prefab
  • effect/eff_tongyong/huoqiu2/particle.effect
  • sound/.../*.ogg

判断标准很简单:

  • 能搜到:说明 Client-Resource 这一步已经看见了它。
  • 搜不到:优先怀疑写法超出了分析器能力边界,或赋值点根本没有进入 IFile 分析链。

4. 编译失败时看 Client-Resource.log ​

如果 bat 运行失败,或 objref.txt 没刷新,就看:

text
<ProjectRoot>/Client-Resource.log

可重点搜索这些诊断码:

  • IFILE001:分析器整体执行失败。
  • IFILE002:资源清单成功生成。
  • IFILE003:输出路径解析信息。
  • IFILE004:无法确定输出目录。
  • IFILE005:无法创建输出目录。
  • IFILE006:写 objref.txt 失败。
  • IFILE008:model_animation.json 生成成功。
  • IFILE009:写 model_animation.json 失败。
  • IFILE010:无法创建 model_collision.json 输出目录。
  • IFILE011:写 model_collision.json 失败。
  • IFILE012:model_collision.json 生成成功。

5. 再看发布产物里的 ref/full.ref ​

如果 objref.txt 已经有目标资源,但实机仍然缺资源,再看发布目录里的:

text
<PublishRoot>/<ProjectName>/ref/full.ref

full.ref 是最终给发布链消费的清单。它不一定和 objref.txt 逐行相同,因为发布阶段还会做依赖展开,例如从 prefab 继续补出模型、材质、纹理、动画等底层依赖。

排查重点是:

  • objref.txt 里有没有目标资源。
  • 发布包里的 ref/objref.txt 是否和源项目一致。
  • full.ref 里是否已经展开出对应依赖。

常见现象和优先怀疑点 ​

模型看不到 ​

优先检查:

  • model.prefab 是否出现在 ref/objref.txt。
  • 发布后的 full.ref 是否继续展开出了对应 model/m.mdl、.material、纹理。
  • 是否把项目私有模型错误地放到了 ui/ 或 user_files/,而不是 res/。

第一人称手臂或枪看不到 ​

如果编辑器里正常、平台里只缺第一人称持枪视图,优先检查:

  • 挂载的第一人称模型路径是否只出现在运行时代码字符串里,例如 characters/_user/fpsarms1_4u1j/model.prefab。
  • 这类资源是否进入了 GameDataModel.Asset、GameDataModel.HumanoidSourceAnimations、GameDataAnimation,或项目自己的显式资源清单。
  • ref/objref.txt 和 ref/model_animation.json 是否真的包含了这些路径。
  • 平台缓存里是否只下到了枪本体,但没有下到手臂模型或 anim/*.ani。

这类问题通常不是“资源文件写错目录”,而是“资源没有进入发布清单,所以平台没下载”。

粒子看不到 ​

优先检查:

  • particle.effect 是否出现在 ref/objref.txt。
  • 粒子路径是不是运行时拼出来的。
  • 粒子引用的纹理、材质是否在 full.ref 中被展开出来。

粒子是最容易因为字符串拼接而漏统的一类资源。

音效听不到 ​

优先检查:

  • .ogg 或其他音频资源路径是否直接以字面量形式出现。
  • 是否通过配置表、字符串变量或运行时动态注册间接传给 SoundResource.Load(...) / GameDataSound.Asset。
  • AI 生成代码是否为了省事在启动时临时注册一批 GameDataSound;静态音频定义应优先进入数据文件、代码静态定义或显式资源清单。
  • 客户端和服务端是否混淆了资源路径与普通文件路径。

动画不对或部分动作丢失 ​

优先检查:

  • ref/model_animation.json 是否生成。
  • GameDataModel.Asset 是否用了分析器能识别的写法。
  • 单位的 Model / Abilities、技能的 Animation 与最终 GameDataAnimation 素材是否形成了分析器能静态追踪的引用链。
  • 如果这条链比较动态,是否已在 GameDataModel.HumanoidSourceAnimations 中显式列出必要动画。
  • 是否把动作名当成运行时字符串拼接,而不是把具体动画资源路径显式列出。

编辑器调试时 Humanoid 技能动画播放不出来 ​

如果现象是:官方 Humanoid 动画资源存在,例如 dash.ani,但编辑器调试时技能动作不播放,并且项目 res/anim 下没有对应模型的烘焙动画,优先按两步判断:

  1. 原始 Humanoid 动画资源是否存在。
  2. 当前模型对应的烘焙动画是否已经生成到项目 res/anim/<ModelName>/...。

前者缺失时,先修正动画名或动画库资源引用;后者缺失时,不要只改代码,应先执行编辑器菜单:

text
调试 / 全量资源统计并调试

该命令会先执行全量资源统计,生成或刷新 ref/model_animation.json,再根据其中的模型-动画映射生成 Humanoid 烘焙动画,最后启动调试。AI 助手也可以通过 SCE Editor MCP 调用:

json
{ "tool": "debug_start_with_resource_statistics", "arguments": {} }

如果只想生成资源和动画而不启动调试,调用:

json
{ "tool": "resource_statistics_generate_animations", "arguments": {} }

生成后重点检查:

  • <ProjectRoot>/ref/model_animation.json 是否包含目标模型和 dash.ani 等动画。
  • <ProjectRoot>/res/anim/<ModelName>/... 是否出现对应烘焙结果。
  • 编辑器客户端日志和信息面板中是否提示“原始 Humanoid 动画不存在”或“原始动画存在但烘焙动画不存在”。

如果自动统计始终无法覆盖某种代码结构,还有两种保险方式:

  1. 对已经完成骨骼适配的模型,将动画显式加入对应 GameDataModel.HumanoidSourceAnimations,再执行“统计资源并生成”。Maker 外部模型先完成 官方动作重定向,不要把这一步当作首次适配。
  2. 手动编辑 <ProjectRoot>/ref/model_animation.json,然后只执行编辑器菜单 “工具 → 资源统计和动画重定向 → 生成”。不要再点“统计资源”或“统计资源并生成”,否则编译期分析结果会重新生成并覆盖手工配置。

对代码写法的建议 ​

如果某个资源必须进入发布清单,优先选择“显式可分析”的写法,而不是“运行时动态组装”的写法。

优先:

csharp
var hitEffect = ParticleSystem.Load("effect/effect_new/effect_hit/eff_hit_01/particle.effect");

谨慎:

csharp
var hitEffect = ParticleSystem.Load($"effect/{folder}/{effectName}/particle.effect");

如果业务上确实必须动态选择资源,至少要额外准备一个可静态分析的资源表、常量清单或显式映射,让所有候选资源都能被编译期看到。

不要把运行时 new GameDataSound 当作资源声明机制。它可以作为已经明确收集过资源后的运行时播放配置,但不应该成为 AI 助手生成音效清单的默认方式。运行时挂载模型、第一人称手臂、枪械视图和动画路径也一样:可以动态选择,但所有候选资源必须额外进入 GameDataModel、GameDataAnimation、HumanoidSourceAnimations 或显式资源清单。轻量 helper 只有在参数字面量直接流入 Sound / IFile 字段,并且 Client-Resource 后确认 ref/objref.txt 出现对应路径时,才可视为发布安全。

推荐给 AI 助手用户的额外约束 ​

如果你经常让 AI 直接生成技能、特效、模型或音频相关代码,建议在项目规则里明确要求:

  • 资源路径尽量使用字面量,不要随手字符串拼接。
  • 音频优先通过静态 GameDataSound.Asset / GameDataActorSound 数据或显式资源清单声明;模型、第一人称手臂、武器视图和动画优先通过 GameDataModel / GameDataAnimation / HumanoidSourceAnimations 或显式资源清单声明;不要让 AI 只在运行时代码中临时注册音效或拼出资源路径。
  • 新增资源引用后,手动执行一次 生成资源统计ref.bat 检查 ref/objref.txt。
  • 发布前遇到实机缺资源,优先检查 objref.txt、model_animation.json 和 full.ref。

这样可以把“编辑器里没问题、实机才出错”的问题尽量前置到开发阶段。

一句话判断法 ​

如果编辑器里正常、发布后缺资源,先不要只盯着资源库;先问自己两件事:

  1. 这条资源路径是不是写对了。
  2. 这条资源路径是不是以分析器能看懂的方式进入了 ref/objref.txt。

只要第二条答案是否定的,真实玩家环境就很可能下载不到它。