UE 引擎升级的编译/链接错误案例集
UE 引擎升级的编译/链接错误案例集
跨大版本升级(4.17 → 4.27)合并完代码后,真正耗时间的是修上万个编译错误和一批链接错误。这篇把典型的错误分类记下来,方法论层面的合并/追踪经验另见 引擎大版本升级实践。
大多数错误可以归到几类:官方拆分/重命名了类型、纯虚函数新增、函数签名变化、委托参数个数变化、宏被替换、重载二义性。下面按类型举例。
类型被拆分:FSkeletalMeshResource
4.17 里的 FSkeletalMeshResource 在新版本被拆成两个类:
FSkeletalMeshModel:编辑器侧的源模型数据(导入后的 LOD 模型)。FSkeletalMeshRenderData:运行期渲染用的数据。
原来直接访问 FSkeletalMeshResource 的定制代码要按用途分流:编辑器/构建路径走 FSkeletalMeshModel,渲染路径走 FSkeletalMeshRenderData。两者之间通过 BuildFromLODModel 之类的构建函数把源模型烘成渲染数据,序列化逻辑也随之拆开。合并定制序列化代码时要对应到新的类上,否则会编译不过或序列化数据错位。
纯虚函数新增:FPrimitiveSceneProxy::GetTypeHash
UE4.27 给 FPrimitiveSceneProxy 加了纯虚函数 GetTypeHash(),所有自定义的 SceneProxy 子类如果没实现就编不过(cannot instantiate abstract class)。解决办法是给每个定制 Proxy 补上 GetTypeHash() 的实现。
名字冲突:GetTypeHash 全局重载
定制代码里如果自己定义了一个全局 GetTypeHash,可能和引擎新加的产生重定义/二义性冲突。把自己的那个塞进匿名 namespace 里限制作用域即可:
namespace
{
uint32 GetTypeHash(const FMyKey& Key) { /* ... */ }
}
重载二义性:AddClassFilter
某些接口新增了重载后,原来能唯一匹配的调用变成二义性(ambiguous call)。典型是过滤类的 AddClassFilter 之类——传裸 UClass* 时匹配不唯一。显式用 TSubclassOf<> 包一层消除二义性:
Filter.AddClassFilter(TSubclassOf<AActor>(SomeClass));
const 对象绑定非 const 成员委托
在 const 对象上绑定指向非 const 成员函数的委托会编译失败。要么把目标成员函数标成 const,要么在绑定处去掉 const 限定拿到非 const 引用。核心是委托的 CreateUObject/CreateSP 要求对象和成员函数的 const 属性匹配。
函数签名变化:ImageWrapper::GetRaw
IImageWrapper::GetRaw 的签名在新版本变了(参数/返回值形式调整)。所有调用点按新签名改一遍。这类签名变更没有捷径,只能顺着编译错误逐个改,改的时候顺手确认返回的数据语义没变。
委托参数个数变化:FOnWorldTickStart
FOnWorldTickStart 的委托参数从 2 个变成 3 个(新版本多传了一个参数)。所有绑定到它的回调函数签名都要跟着加参数,否则绑定处编译失败。委托参数个数变化是升级里很常见的一类,报错信息通常是「委托签名不匹配」,按新声明补参数即可。
宏被替换:ENQUEUE_UNIQUE_RENDER_COMMAND → ENQUEUE_RENDER_COMMAND
老的 ENQUEUE_UNIQUE_RENDER_COMMAND* 系列宏被 ENQUEUE_RENDER_COMMAND 取代(配合 lambda 写法)。定制的渲染命令入队代码要改成新宏:
ENQUEUE_RENDER_COMMAND(MyRenderCommand)(
[Params](FRHICommandListImmediate& RHICmdList)
{
// ...
});
链接错误与循环依赖
编译过了不代表链接过。升级里典型的链接错误来自模块依赖变化——某个符号被挪到了别的模块,或者定制代码引入了循环依赖。
一个具体例子:UNavigationSystemBase 的父类被放在 Engine 模块里,如果定制代码在一个不依赖(或不能依赖)Engine 的模块里引用它,就会 unresolved external。解法是理清模块边界,把引用挪到正确的模块,或调整 Build.cs 的依赖声明——但要警惕加依赖引入新的循环依赖。
处理原则
- 顺着编译器走:这类错误没有巧办法,按报错逐个改。VSOutputEnhancer 之类插件能让错误输出更易读,UnrealVS 可以单独编某一个模块缩短反馈循环。
- 难修的先跳过:遇到一时修不动的,注释 + 加 TODO 标记,保证主线尽快编过,回头再补。
- 分清编译错误和链接错误:先把引擎工程的编译、链接都修通,再去修游戏工程,别两头同时开。