风蚀之月

Send to unreal贴图丢失问题

06 Feb 2026 UE5 Blender

在使用Send to Unreal的过程中,发现对于部分模型,会出现贴图导入错误的问题。

目前使用的其实是第三方维护的Send to Unreal,因为官方的已经很久没有更新了。使用的工具的版本信息:

贴图丢失

在进行模型导入后,部分贴图会进入一种错误的状态,当保存时会报错并提示:

Filename ‘_11’ resolves to ‘None’ and cannot be used

且在编辑器中无法看到贴图资产。材质和Mesh表现正常,但是重启后由于这些贴图没有保存,就会变成没有贴图的状态。

这个问题可以手动解决,只要在材质中打开使用的贴图,然后另存为一下就可以了。但是由于有的模型材质比较多,手动处理会非常麻烦,介于手动处理的步骤只是在使用编辑器功能,所以肯定是能自动处理的。

查找未保存的资产

引擎是有提供接口的,毕竟当点击关闭的时候编辑器会提示需要保存的资产,用这个窗口的文本直接搜索就能找到对应的代码了。

// A list of all packages that need to be saved
TArray<UPackage*> PackagesToSave;
if (bSaveMapPackages)
{
    FEditorFileUtils::GetDirtyWorldPackages(PackagesToSave, ShouldIgnorePackageFunction);
}
// Don't iterate through content packages if we don't plan on saving them
if (bSaveContentPackages)
{
    FEditorFileUtils::GetDirtyContentPackages(PackagesToSave, ShouldIgnorePackageFunction);
}

由于只关注贴图保存问题,所以使用GetDirtyContentPackages就可以了。

资产重新保存

一开始是想用UEditorAssetLibrary::RenameLoadedAsset来解决的,但是发现无论怎么修改调用方式都会出现错误。

与ChatGpt沟通了半天,终于找到了解决方案:

bool FixInvalidTexture(UTexture2D* Texture)
{
    if (!Texture)
    {
        UE_LOG(LogTemp, Warning, TEXT("FixInvalidTexture: Bad texture"));
        return false;
    }

    UPackage* OldPackage = Texture->GetOutermost();
    FString OldPackageName = OldPackage->GetName();
    FString OldTexturePath = GetPathNameSafe(Texture);

    // ---------- 1. 生成合法新路径 ----------
    FString AssetName = FPackageName::GetShortName(OldPackageName);

    // 以数字或 _ 开头 → 修复
    if (!FChar::IsAlpha(AssetName[0]))
    {
        AssetName = TEXT("T") + AssetName;
    }

    FString NewPackagePath =
        FPackageName::GetLongPackagePath(OldPackageName) / AssetName;

    // ---------- 2. 创建新 Package ----------
    UPackage* NewPackage = CreatePackage(*NewPackagePath);
    if (!NewPackage)
        return false;

    // ---------- 3. 重命名 UObject 到新 Package ----------
    Texture->Rename(
        *AssetName,
        NewPackage,
        REN_DontCreateRedirectors | REN_NonTransactional);

    // ---------- 4. 标记 & 注册 Asset ----------
    Texture->MarkPackageDirty();
    Texture->PostEditChange();

    FAssetRegistryModule::AssetRenamed(Texture, OldTexturePath);
    FAssetRegistryModule::AssetCreated(Texture);

    // ---------- 5. 保存 ----------
    FString Filename =
        FPackageName::LongPackageNameToFilename(
            NewPackagePath,
            FPackageName::GetAssetPackageExtension());

    UE_LOG(LogTemp, Log, TEXT("FileName=%s"), *Filename);

    FSavePackageArgs TsArgs;
    TsArgs.TopLevelFlags = EObjectFlags::RF_Public | EObjectFlags::RF_Standalone;
    bool bSaveSuccess = UPackage::SavePackage(
        NewPackage,
        Texture,
        *Filename,
        TsArgs
    );

    return bSaveSuccess;
}

这里传入的是贴图,执行成功时会自动将_开头的贴图名称添加T前缀进行保存。

如果要修复的贴图不是这个规则的,可以在以数字或 _ 开头 → 修复处修改名称逻辑

结合上面的寻找未保存资产的接口使用即可:

TArray<UPackage*> PackagesToSave;
//FEditorFileUtils::GetDirtyWorldPackages(PackagesToSave);
FEditorFileUtils::GetDirtyContentPackages(PackagesToSave);
UEditorLoadingAndSavingUtils::SavePackages(PackagesToSave, false);

// 理论上这里其实不需要两层遍历,不过为了日志清晰,还是按照Package为单位来处理
for (UPackage* IterPkg : PackagesToSave)
{
    if (!IterPkg)
    {
        continue;
    }

    UE_LOG(LogTemp, Log, TEXT("UCommonAssetEditFuncLib::SaveBadTextures: Inspecting Package=(%p)%s"), IterPkg, *GetFullNameSafe(IterPkg));
    // 获取包内主资源(贴图)
    TArray<UObject*> PkgObjects;
    TArray<UPackage*> PackagesTmp = { IterPkg };
    UPackageTools::GetObjectsInPackages(&PackagesTmp, PkgObjects);
    for (UObject* IterObj : PkgObjects)
    {
        // 仅处理贴图
        UTexture2D* Texture = Cast<UTexture2D>(IterObj);
        if (!Texture)
        {
            UE_LOG(LogTemp, Log, TEXT("UCommonAssetEditFuncLib::SaveBadTextures: Object no texture=(%p)%s"), IterObj, *GetFullNameSafe(IterObj));
            continue;
        }
        UE_LOG(LogTemp, Log, TEXT("UCommonAssetEditFuncLib::SaveBadTextures: Object=(%p)%s"), IterObj, *GetFullNameSafe(IterObj));
        FixInvalidTexture(Texture);
    }
}

这样处理其实还是有问题的,需要在编辑器中选中材质所在文件夹然后点击右键全部重新保存一次。目测是相关的引用没有正确的更新,但是一时半会也找不到自动关联到这些材质或者触发保存的方式,所幸只是手动点击一下鼠标,就不继续投入时间了。