乐于分享
好东西不私藏

UE 编辑器工具入门 6:批量修复 UI 纹理导入异常

UE 编辑器工具入门 6:批量修复 UI 纹理导入异常

在 UE 项目里,UI 纹理看起来只是“导入一张图”。

但如果纹理被 UE 误判成法线贴图,或者 Alpha 通道没有按预期显示,最终效果就会非常怪:颜色偏了、透明没了、图标也不再是设计稿里的样子

这篇文章记录一次 UI 纹理修复工具的完整过程:

  1. • 先手动定位问题参数
  2. • 再尝试用蓝图自动化处理
  3. • 最后用 C++ 补上蓝图够不到的关键一步
  4. • 顺带补充 UE5.7 里可以通过 Reload 资产解决的方案

1- 问题现象:UI 纹理导入后不对劲

这次遇到的是两类常见问题:

  • • 纹理被错误识别为法线贴图(Normal Map)
  • • Alpha 通道显示异常,透明效果不符合预期

下面是两张 UI 原始设计图,正常情况下应该保持清晰的颜色和透明边缘。

其中:

  • • file图标:带透明通道的 PNG,预期显示为白色图标
  • • icon贴图:带半透明渐变边缘,预期边缘平滑过渡

但导入 UE 后,效果明显不对:

  • • file 图片被当成了法线贴图
  • • 插件图标的透明通道没有正常显示

这些问题当然可以手动改参数解决。

但如果项目里有很多张 UI 纹理,靠人工逐个检查就很容易漏,而且维护成本也高


2- 手动修复:先找出异常参数

先打开出问题的纹理,看一下它被 UE 设置成了什么状态。

以被误判为法线贴图的文件图标为例,异常参数主要有这几个:

  • • CompressionSettings = TC_Normalmap:被设置成法线压缩格式
  • • SRGB = false:关闭了色彩空间转换
  • • TextureGroup = WorldNormalMap:纹理组也被分到了法线相关类别

2.1 改成 UI 纹理格式

手动修复时,可以把纹理参数改回 UI 使用的配置。

修改后再验证效果,两张图就都恢复成正常的 UI 纹理显示了。

到这里,问题已经能解决。

不过手动方式只适合少量资产。如果数量一多,还是需要做成编辑器工具。


3- 蓝图尝试:先做一个自动化脚本

既然目标是批量处理纹理,第一反应当然是用编辑器蓝图脚本。

思路很简单:

  1. 1. 选中需要处理的纹理资产
  2. 2. 判断资产类型是否为 Texture2D
  3. 3. 把压缩格式、纹理组、sRGB 等参数改成 UI 需要的配置

3.1 创建蓝图脚本

先创建一个支持资产操作的蓝图脚本。

然后重写支持的资产类型,让它只处理纹理资产。

接着写一个处理 UI 纹理的脚本函数,核心逻辑就是修改纹理压缩格式等参数。

3.2 蓝图方案的问题

运行脚本测试后,可以看到:普通情况能处理,但那张被误判成法线贴图的纹理仍然没有恢复正常,甚至颜色还变绿了。

这说明蓝图方案有边界。

它可以处理大部分常规参数修改,但对于“已经被 UE 按法线贴图加载过”的纹理,仅仅改属性还不够。

这里缺的不是参数,而是一次重新初始化。

蓝图方案的问题主要有三个:

  1. 1. 可以改常规属性,但处理不了某些底层加载状态
  2. 2. 无法直接调用部分编辑器底层 API,比如 PostLoad()
  3. 3. 对被错误识别为法线贴图的纹理,单纯改参数不能彻底刷新数据

所以,这一步只能转向 C++。


4- C++ 方案:补上关键的 PostLoad

蓝图做不到的部分,就用 C++ 编辑器工具来处理。

整体思路仍然不复杂:

  1. 1. 创建一个编辑器插件
  2. 2. 添加一个资产操作工具类
  3. 3. 选中纹理后批量修改参数
  4. 4. 修改完成后调用 PostLoad() 重新初始化纹理数据

4.1 Build.cs 添加依赖

因为要做编辑器工具,并且会用到 Blutility、UnrealEd 等编辑器模块,所以 .build.cs 里需要添加相关依赖。

PrivateIncludePaths.AddRange(    new string[]    {        System.IO.Path.GetFullPath(Target.RelativeEnginePath) + "Source/Editor/Blutility/Private"    });PrivateDependencyModuleNames.AddRange(    new string[]    {        "CoreUObject",        "Engine",        "Slate",        "SlateCore",        "InputCore",        "EditorFramework",        "EditorStyle",        "UnrealEd",        "LevelEditor",        "InteractiveToolsFramework",        "EditorInteractiveToolsFramework",        "Blutility"    });

4.2 头文件声明

在资产操作工具里,先声明支持的资产类型,以及编辑器里可以点击执行的函数。

// 重载可应用该脚本函数的资产类型virtual UClass* GetSupportedClass();// 在编辑器中显示的脚本函数UFUNCTION(CallInEditor, Category = "纹理", meta = (DisplayName = "将纹理应用 UI 格式"))void Texture2DApplyUIFormat();

4.3 核心 C++ 逻辑

核心逻辑如下:

UClass* UTexture2DAssetActionUtility::GetSupportedClass(){    return UTexture2D::StaticClass();}void UTexture2DAssetActionUtility::Texture2DApplyUIFormat(){    TArray<UObject*> SelectedAssets = UEditorUtilityLibrary::GetSelectedAssets();    for (UObject* SelectedObject : SelectedAssets)    {        UTexture2D* CurrentTexture = Cast<UTexture2D>(SelectedObject);        if (CurrentTexture == nullptr)        {            continue;        }        CurrentTexture->CompressionSettings = TextureCompressionSettings::TC_EditorIcon;        CurrentTexture->CompressionNoAlpha = false;        CurrentTexture->DeferCompression = false;        CurrentTexture->LODGroup = TEXTUREGROUP_UI;        CurrentTexture->MipGenSettings = TMGS_FromTextureGroup;        CurrentTexture->SRGB = true;        // 关键步骤:属性修改后重新初始化纹理数据        CurrentTexture->PostLoad();    }}

这里最关键的一句是:

CurrentTexture->PostLoad();

纹理参数设置完以后,需要调用它重新初始化数据。

如果不做这一步,前面把压缩格式、纹理组、sRGB 等参数都改了,法线贴图误识别的问题仍然可能没有真正恢复。

4.4 插件需要设置为 Editor 模块

因为插件使用了 UnrealEd 这类编辑器模块,所以 .uplugin 里要把模块类型设置为 Editor

"Modules": [    {        "Name": "DtstackEditorFuncLibrary",        "Type": "Editor",        "LoadingPhase": "Default"    }]

5- 为什么需要 PostLoad?

PostLoad() 是 UE 对象加载流程中的一个虚函数。

通常情况下,它会在对象加载到内存、属性完成反序列化之后调用,用来做一些依赖属性数据的初始化工作。

这次的问题本质上就是:

纹理已经被 UE 按错误类型加载过了,后面只改属性,并不一定能让内部数据状态立刻同步恢复。

所以我们需要在修改纹理属性之后,再触发一次重新初始化。

PostLoad() 常见用途包括:

  • • 数据修复:对象加载后,根据已有属性修正内部数据
  • • 初始化依赖项:某些初始化逻辑依赖属性已经加载完成
  • • 版本兼容处理:属性结构变化后,处理旧版本资产的数据兼容

测试后可以看到,file 图标已经恢复正常。


6- 效果对比:蓝图与 C++ 的差异

最后把几种状态放在一起对比一下。

6.1 原始异常状态

将两张纹理直接放到 UMG 中,可以看到显示效果不符合预期。

6.2 蓝图脚本执行后

蓝图脚本能改一部分参数,但如果纹理已经被误判为法线贴图,仍然可能无法彻底修复。

另外,UI 纹理如果没有勾选 sRGB,颜色也容易失真。

6.3 C++ 脚本执行后

C++ 方案在修改参数后额外调用了 PostLoad(),所以可以把这类异常纹理真正刷新回来。


7- 补充:UE5.7 可以通过 Reload 资产解决

后续又找到了一个蓝图方向的补充方案。

 UE5.7 中,修改完纹理参数后,可以保存资产,再重新加载资产

这样也能让纹理恢复正常。

也就是说,如果项目使用的是 UE5.7,可以考虑走这条更偏蓝图的方案:

  1. 1. 修改纹理参数
  2. 2. 保存资产
  3. 3. Reload 资产
  4. 4. 再验证显示效果

这个思路和 C++ 里调用 PostLoad() 的目的类似,都是为了让纹理在参数变化后重新刷新内部数据状态。

8-另一个替代方案:

1.执行 第3节 蓝图脚本2.应用Paper2D纹理设置3.修复结果


结语:自动化工具要处理的不只是参数

这次问题表面上是 UI 纹理显示异常。

但真正踩坑的地方在于:有些资产问题并不是“改几个属性”就结束了。

当纹理已经被 UE 按错误类型加载过时,还需要让资产重新初始化,才能把内部数据状态一起刷新回来。

所以,编辑器工具开发里有一个很实用的判断:

如果蓝图脚本改了参数但效果不生效,问题可能不在参数本身,而在资产是否完成了重新加载或重新初始化。

对少量纹理,可以手动改。

对常规批量处理,可以用蓝图脚本。

如果涉及更底层的资产状态刷新,就需要 C++ 或 UE5.7 的 Reload 资产能力来补上最后一步。

欢迎交流更好的优化方案。

下篇预告

前面几篇里,我们已经做过右键脚本、编辑器工具控件,也尝试过给脚本传入参数。

但当工具参数越来越多时,如果还手动摆输入框、下拉框、复选框,界面很快就会变得又乱又难维护。

所以下一篇,我们继续把编辑器工具做得更像 UE 原生工具:

用 DetailsView 生成参数面板。

它可以直接显示 UObject 或 Struct 里的属性,让工具参数像 UE Details 面板一样被编辑。

大家平时在 UE 里最讨厌的重复劳动是什么?可以公众号留言私信或者添加微:qingdoushashou 告诉我,后续我抽一个写教程。

觉得好的话点个赞吧希望你能在这里学到点东西,每天进步一点点

扫描二维码关注账号:

以上内容在多平台同步发布:1.知乎:旺仔好喝-黄河2.公众号:UE5研究院3.ima知识库:虚幻引擎UE成长之路