03/14/2020
Cook 单个资源
HotPatcher有一个函数叫做OnCookPlatform函数,需要资源名字和烘培平台就可以烘培资源了。简单把这个函数提取出来,我这边使用的是控制台来烘焙鼠标选中Content面板中的资源。

控制台命令
传递烘培资源的平台,然后创建烘培的路径和开始烘培,最后完成烘培结果的显示。
- GetCookedAssetDir():获取烘培路径
- CookPackage():烘培函数,即生成文件
- CreateSaveFileNotify():完成烘培结果显示
// Cheat command to load all assets of a given type
static FAutoConsoleCommand CookChoosedAssets(
TEXT("CookChoosedAssets"),
TEXT("Cook Choosed Assets in the Content Broweser"),
FConsoleCommandWithArgsDelegate::CreateStatic(
[](const TArray<FString>& Platforms)
{
//Cook A single asset
//1. Get Asset Path + Name on the ContentBrowser
FContentBrowserModule& ContentBrowserModule = FModuleManager::Get().LoadModuleChecked<FContentBrowserModule>(TEXT("ContentBrowser"));
TArray<FAssetData> AssetsData;
ContentBrowserModule.Get().GetSelectedAssets(AssetsData);
if (AssetsData.Num() == 0)
{
UE_LOG(LogTemp, Warning, TEXT("Plase Choose assets"));
return;
}
TArray<UPackage*> AssetsPackage;
for (const auto& AssetData : AssetsData)
{
AssetsPackage.AddUnique(AssetData.GetPackage());
}
for (auto CurrentPlatform : Platforms)
{
//Create folder and Cook assets
FString CookedDir = FPaths::ConvertRelativePathToFull(FPaths::Combine(FPaths::ProjectSavedDir(), TEXT("Cooked")));
for (const auto& AssetPacakge : AssetsPackage)
{
FString CookedSavePath = GetCookAssetsSaveDir(CookedDir, AssetPacakge, CurrentPlatform);
//Start Cook Assets
bool bCookStatus = CookPackage(AssetPacakge, Platforms, CookedDir);
auto Msg = FText::Format(
LOCTEXT("CookAssetsNotify", "Cook Platform {1} for {0} {2}!"),
UKismetTextLibrary::Conv_StringToText(AssetPacakge->FileName.ToString()),
UKismetTextLibrary::Conv_StringToText(CurrentPlatform),
bCookStatus ? UKismetTextLibrary::Conv_StringToText(TEXT("Successfuly")) : UKismetTextLibrary::Conv_StringToText(TEXT("Faild"))
);
SNotificationItem::ECompletionState CookStatus = bCookStatus ? SNotificationItem::ECompletionState::CS_Success : SNotificationItem::ECompletionState::CS_Fail;
CreateSaveFileNotify(Msg, CookedSavePath, CookStatus);
UE_LOG(LogTemp, Warning, TEXT("Cook Single Assets, %s"), bCookStatus?TEXT("True"):TEXT("False"));
}
}
UE_LOG(LogTemp, Warning, TEXT("Finish Cook Single Assets"));
}), ECVF_Default);
- 学会使用ContentBrowser模块获取选中的资源并保存路径
- 认识FAssetData和UPackage类
- UPackage 和EditorEngine 都有一个Save函数
// Copyright Epic Games, Inc. All Rights Reserved.
#include "HAL/IConsoleManager.h"
#include "ContentBrowserModule.h"
#include "IContentBrowserSingleton.h"
#include "UObject/Package.h"
#include "IPlatformFileSandboxWrapper.h"
#include "Interfaces/IPluginManager.h"
#include "Misc/SecureHash.h"
#include "Kismet/KismetTextLibrary.h"
#include "Widgets/Notifications/SNotificationList.h"
#include "Framework/Notifications/NotificationManager.h"
#include "Interfaces/ITargetPlatformManagerModule.h"
#include "Interfaces/ITargetPlatform.h"
#include "IPlatformFileSandboxWrapper.h"
#include "Misc/SecureHash.h"
#include "Editor.h"
#define LOCTEXT_NAMESPACE "EditorCommand"
static FString ConvertToFullSandboxPath(const FString& FileName, bool bForWrite)
{
FString ProjectContentAbsir = FPaths::ConvertRelativePathToFull(FPaths::ProjectContentDir());
if (FileName.StartsWith(ProjectContentAbsir))
{
FString GameFileName = FileName;
GameFileName.RemoveFromStart(ProjectContentAbsir);
return FPaths::Combine(FApp::GetProjectName(), TEXT("Content"), GameFileName);
}
if (FileName.StartsWith(FPaths::EngineContentDir()))
{
FString EngineFileName = FileName;
EngineFileName.RemoveFromStart(FPaths::EngineContentDir());
return FPaths::Combine(TEXT("Engine/Content"), EngineFileName);;
}
TArray<TSharedRef<IPlugin> > PluginsToRemap = IPluginManager::Get().GetEnabledPlugins();
// Ideally this would be in the Sandbox File but it can't access the project or plugin
if (PluginsToRemap.Num() > 0)
{
// Handle remapping of plugins
for (TSharedRef<IPlugin> Plugin : PluginsToRemap)
{
FString PluginContentDir;
if (FPaths::IsRelative(FileName))
PluginContentDir = Plugin->GetContentDir();
else
PluginContentDir = FPaths::ConvertRelativePathToFull(Plugin->GetContentDir());
// UE_LOG(LogHotPatcherEditorHelper,Log,TEXT("Plugin Content:%s"),*PluginContentDir);
if (FileName.StartsWith(PluginContentDir))
{
FString SearchFor;
SearchFor /= Plugin->GetName() / TEXT("Content");
int32 FoundAt = FileName.Find(SearchFor, ESearchCase::IgnoreCase, ESearchDir::FromEnd);
check(FoundAt != -1);
// Strip off everything but <PluginName/Content/<remaing path to file>
FString SnippedOffPath = FileName.RightChop(FoundAt);
FString LoadingFrom;
switch (Plugin->GetLoadedFrom())
{
case EPluginLoadedFrom::Engine:
{
LoadingFrom = TEXT("Engine/Plugins");
break;
}
case EPluginLoadedFrom::Project:
{
LoadingFrom = FPaths::Combine(FApp::GetProjectName(), TEXT("Plugins"));
break;
}
}
return FPaths::Combine(LoadingFrom, SnippedOffPath);
}
}
}
return TEXT("");
}
static FString GetCookAssetsSaveDir(const FString& BaseDir, UPackage* Package, const FString& Platform)
{
FString Filename;
FString PackageFilename;
FString StandardFilename;
FName StandardFileFName = NAME_None;
//Content folder
if (FPackageName::DoesPackageExist(Package->FileName.ToString(), NULL, &Filename, false))
{
StandardFilename = PackageFilename = FPaths::ConvertRelativePathToFull(Filename);
FPaths::MakeStandardFilename(StandardFilename);
StandardFileFName = FName(*StandardFilename);
}
//MakeSure Engine or Project or Plugin Contents
FString SandboxFilename = ConvertToFullSandboxPath(*StandardFilename, true);
// UE_LOG(LogHotPatcherEditorHelper,Log,TEXT("Filename:%s,PackageFileName:%s,StandardFileName:%s"),*Filename,*PackageFilename,*StandardFilename);
FString CookDir = FPaths::Combine(BaseDir, Platform, SandboxFilename);
return CookDir;
}
static bool CookPackage(UPackage* Package, const TArray<FString>& Platforms, const FString& SavePath)
{
bool bSuccessed = false;
const bool bSaveConcurrent = FParse::Param(FCommandLine::Get(), TEXT("ConcurrentSave"));
bool bUnversioned = false;
uint32 SaveFlags = SAVE_KeepGUID | SAVE_Async | SAVE_ComputeHash | (bUnversioned ? SAVE_Unversioned : 0);
EObjectFlags CookedFlags = RF_Public;
if (Cast<UWorld>(Package))
{
CookedFlags = RF_NoFlags;
}
if (bSaveConcurrent)
{
SaveFlags |= SAVE_Concurrent;
}
ITargetPlatformManagerModule& TPM = GetTargetPlatformManagerRef();
const TArray<ITargetPlatform*>& TargetPlatforms = TPM.GetTargetPlatforms();
TArray<ITargetPlatform*> CookPlatforms;
for (ITargetPlatform* TargetPlatform : TargetPlatforms)
{
if (Platforms.Contains(TargetPlatform->PlatformName()))
{
CookPlatforms.AddUnique(TargetPlatform);
}
}
if (Package->FileName.IsNone())
return bSuccessed;
for (auto& Platform : CookPlatforms)
{
struct FFilterEditorOnlyFlag
{
FFilterEditorOnlyFlag(UPackage* InPackage, ITargetPlatform* InPlatform)
{
Package = InPackage;
Platform = InPlatform;
if (!Platform->HasEditorOnlyData())
{
Package->SetPackageFlags(PKG_FilterEditorOnly);
}
else
{
Package->ClearPackageFlags(PKG_FilterEditorOnly);
}
}
~FFilterEditorOnlyFlag()
{
if (!Platform->HasEditorOnlyData())
{
Package->ClearPackageFlags(PKG_FilterEditorOnly);
}
}
UPackage* Package;
ITargetPlatform* Platform;
};
FFilterEditorOnlyFlag SetPackageEditorOnlyFlag(Package, Platform);
FString CookedSavePath = GetCookAssetsSaveDir(SavePath, Package, Platform->PlatformName());
// delete old cooked assets
if (FPaths::FileExists(CookedSavePath))
{
IFileManager::Get().Delete(*CookedSavePath);
}
// UE_LOG(LogHotPatcherEditorHelper,Log,TEXT("Cook Assets:%s"),*Package->GetName());
Package->FullyLoad();
TArray<UObject*> ExportMap;
GetObjectsWithOuter(Package, ExportMap);
for (const auto& ExportObj : ExportMap)
{
ExportObj->BeginCacheForCookedPlatformData(Platform);
}
GIsCookerLoadingPackage = true;
//UE_LOG(LogTemp, Warning, TEXT("Cook Assets:%s save to %s"), *Package->GetName(), *CookedSavePath);
FSavePackageResultStruct Result = GEditor->Save(Package, nullptr, CookedFlags, *CookedSavePath,
GError, nullptr, false, false, SaveFlags, Platform,
FDateTime::MinValue(), false, /*DiffMap*/ nullptr);
GIsCookerLoadingPackage = false;
bSuccessed = Result == ESavePackageResult::Success;
}
return bSuccessed;
}
void CreateSaveFileNotify(const FText& InMsg, const FString& InSavedFile, SNotificationItem::ECompletionState NotifyType)
{
auto Message = InMsg;
FNotificationInfo Info(Message);
Info.bFireAndForget = true;
Info.ExpireDuration = 5.0f;
Info.bUseSuccessFailIcons = false;
Info.bUseLargeFont = false;
const FString HyperLinkText = InSavedFile;
Info.Hyperlink = FSimpleDelegate::CreateLambda(
[](FString SourceFilePath)
{
FPlatformProcess::ExploreFolder(*SourceFilePath);
},
HyperLinkText
);
Info.HyperlinkText = FText::FromString(HyperLinkText);
FSlateNotificationManager::Get().AddNotification(Info)->SetCompletionState(NotifyType);
}
注意事项
如何引用库文件和改写Build.cs
// Copyright Epic Games, Inc. All Rights Reserved.
using UnrealBuildTool;
public class TestPython : ModuleRules
{
public TestPython(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange(new string[] {
"Core",
"CoreUObject",
"Engine",
"InputCore",
"ContentBrowser",
"SandboxFile",
"TargetPlatform"
});
PrivateDependencyModuleNames.AddRange(
new string[]
{
"Projects",
"UnrealEd",
"Engine",
"Slate",
"SlateCore",
"RenderCore"
// ... add private dependencies that you statically link with here ...
}
);
}
}
补充
烘培整个(UE4Editor.exe)
查看MainFrameActions.cpp::CookContent函数
D:\UE4\UE_4.25\Engine\Binaries\Win64\UE4Editor-Cmd.exe D:\unrealEngin4WorkStation\TestPython\TestPython.uproject -run=Cook -TargetPlatform=WindowsNoEditor
烘培地图
UE4Editor.exe <GameName or uproject> -run=cook -targetplatform=<Plat1>+<Plat2> [-cookonthefly] [-iterate] [-map=<Map1>+<Map2>]
or
UE4Editor-Cmd.exe <GameName> -run=cook -targetplatform=<Plat1>+<Plat2> [-cookonthefly] [-iterate] [-map=<Map1>+<Map2>]

简化单个资源烘培
bool SaveToCookAsset(UPackage * InPackage, const FString & InSavePath)
{
ITargetPlatformManagerModule& TPM = GetTargetPlatformManagerRef();
const TArray<ITargetPlatform*>& TargetPlatforms = TPM.GetTargetPlatforms();
ITargetPlatform* Target = nullptr;
for (ITargetPlatform *TargetPlatform : TargetPlatforms)
{
if (TargetPlatform->PlatformName() == "WindowsNoEditor")
{
Target = TargetPlatform;
break;
}
}
InPackage->SetPackageFlags(PKG_FilterEditorOnly);
uint32 saveFlags = SAVE_KeepGUID | SAVE_Async | SAVE_Unversioned;
FSavePackageResultStruct Result = GEditor->Save(InPackage, nullptr, RF_Public, *InSavePath, GError, NULL, false, false, saveFlags, Target, FDateTime::MinValue(), false);
return Result == ESavePackageResult::Success;
简单使用GEditor->Save()函数来烘培资源
总结
这次烘培仅仅只能在Editor打开的时候才能使用。
参考资料
版权声明:本文为weixin_44200074原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。