批量资源加载:找到一部分不能算依赖集合就绪
自动关联目录:批量资源加载:找到一部分不能算依赖集合就绪
核心判断:批量加载成功有两层含义:每个逻辑资源都解析成功,且解析出的对象都加载完成。只要找到一个路径就发请求,会把“部分解析”伪装成“整批 Ready”。
一、业务问题:组合表现通常不是一份资源
角色外观、武器、特效和 UI 页面往往由网格、材质、动画、粒子或图集共同组成。某个可选装饰缺失可以降级,但骨架或主网格缺失时不能把完成回调解释为整套表现可提交。
二、实际实现:加载封装完整,解析返回值却丢了集合语义
实际资源管理器统一提供短名到软路径的解析、同步与异步 StreamableHandle、句柄管理选项和加载耗时采集。这些集中能力值得保留。
批量入口先遍历短名:能解析的路径加入输出,不能解析的直接跳过;最终只检查输出数组是否非空。同步和异步批量接口都依赖这个布尔结果,因此请求 {A, B, C} 时,只要 A 存在,就会加载 A 并正常触发完成回调。回调只拿到已解析路径,无法知道 B、C 曾被请求。代码中用于判断负数组长度的分支永远不会成立,也无法弥补这个缺口。
这里不是简单的空指针问题,而是 API 把两种合法业务混成了一个 bool:
AllRequired:全部依赖必须齐全才能提交;BestEffort:能加载多少算多少,但必须报告缺项和降级结果。
三、目标方案:先生成解析计划,再决定是否加载
enum class EResolveStatus { Ready, Missing, Ambiguous, InvalidType };
struct FResolvedItem {
FLogicalAssetId Requested;
ERequirement Requirement; // Required / Optional
EResolveStatus Status;
FSoftObjectPath Path;
};
struct FBatchPlan {
uint64 RequestId;
TArray<FResolvedItem> Items; // 与输入同序、同基数
bool CanIssue() const {
return Algo::NoneOf(Items, [](const auto& I) {
return I.Requirement == Required && I.Status != Ready;
});
}
};
FBatchPlan Plan = ResolveAll(Request);
if (!Plan.CanIssue()) return FailBeforeLoad(Plan);
auto Paths = SelectReadyPaths(Plan);
return Stream(Paths, [Plan](FLoadReceipt Receipt) {
CommitOrFallback(Plan, Receipt);
});解析层必须保留输入到输出的对应关系,并检查重复逻辑 ID、类型和歧义。加载层只接受冻结的 Plan;完成层同时验证 Handle 结果与 Plan。多个资源必须原子上屏时,再用 AppearanceRevision 或 EffectRevision 一次提交,而不是每个对象加载完就局部替换。
四、兼容迁移与验证
不能直接把所有旧批量调用改成“缺一失败”,因为部分预热可能有意使用 BestEffort。迁移应先新增显式策略和结构化回执,旧接口记录“请求数 != 解析数”,再按调用场景收紧。
测试矩阵应包含:全部命中、首项缺失、中间项缺失、全部缺失、重复输入、同名歧义、可选回退、加载阶段失败和取消。关键指标是 RequestedCount、ResolvedCount、LoadedCount、MissingRequiredCount、FallbackCount,而不是只有一次总耗时。
五、反思
现有封装解决了“怎样统一请求 UE 资源”,但没有回答“这次请求代表的业务集合是否完整”。StreamableHandle 能证明它收到的路径结束了,不能证明调用方最初要求的依赖集合已经满足。