UE4自动打包工具编写
在UE的开发中,有些项目需要针对不同版本出不同的包,并有一个对应的UI界面方便大家使用。
本文就来实现一下这个功能(UE4.27)。
1.插件编写
先使用UE4自己的插件模板进行创建,做成插件形式
然后注册Slate UI,编写打开逻辑。并在按钮点击函数PluginButtonClicked内触发。
.cpp部分代码如下:
#include "PackageHelper.h" #include "PackageHelperStyle.h" #include "PackageHelperCommands.h" #include "Misc/MessageDialog.h" #include "ToolMenus.h" #include "GameMapsSettings.h" #include "Misc/FileHelper.h" #include "FileHelpers.h" static const FName PackageHelperTabName("PackageHelper"); #define LOCTEXT_NAMESPACE "FPackageHelperModule" TSharedRef<SDockTab> FPackageHelperModule::WindowBody(const FSpawnTabArgs& SpawnTabArgs) { return SNew(SDockTab) .TabRole(ETabRole::NomadTab) [ SNew(SVerticalBox) + SVerticalBox::Slot() [ SNew(SButton) .Content() [ SNew(STextBlock) .Justification(ETextJustify::Center) .Text(LOCTEXT("Build Test Package1", "Build Test Package1")) ] .OnClicked_Lambda([this]() { //Clicked Test Package1. return FReply::Handled(); }) ] + SVerticalBox::Slot() [ SNew(SButton) .Content() [ SNew(STextBlock) .Justification(ETextJustify::Center) .Text(LOCTEXT("Build Test Package2", "Build Test Package2")) ] .OnClicked_Lambda([this]() { //Clicked Test Package2. return FReply::Handled(); }) ] + SVerticalBox::Slot() [ SNew(SButton) .Content() [ SNew(STextBlock) .Justification(ETextJustify::Center) .Text(LOCTEXT("Build Test Package3", "Build Test Package3")) ] .OnClicked_Lambda([this]() { //Clicked Test Package3. return FReply::Handled(); }) ] ]; } void FPackageHelperModule::StartupModule() { FPackageHelperStyle::Initialize(); FPackageHelperStyle::ReloadTextures(); FPackageHelperCommands::Register(); PluginCommands = MakeShareable(new FUICommandList); PluginCommands->MapAction( FPackageHelperCommands::Get().PluginAction, FExecuteAction::CreateRaw(this, &FPackageHelperModule::PluginButtonClicked), FCanExecuteAction()); UToolMenus::RegisterStartupCallback(FSimpleMulticastDelegate::FDelegate::CreateRaw(this, &FPackageHelperModule::RegisterMenus)); FGlobalTabmanager::Get()->RegisterNomadTabSpawner(PackageHelperTabName , FOnSpawnTab::CreateRaw(this, &FPackageHelperModule::WindowBody)) .SetDisplayName(LOCTEXT("PackageHelper", "PackageHelper")) .SetMenuType(ETabSpawnerMenuType::Hidden); } void FPackageHelperModule::ShutdownModule() { UToolMenus::UnRegisterStartupCallback(this); UToolMenus::UnregisterOwner(this); FPackageHelperStyle::Shutdown(); FPackageHelperCommands::Unregister(); FGlobalTabmanager::Get()->UnregisterNomadTabSpawner(PackageHelperTabName); } void FPackageHelperModule::PluginButtonClicked() { FGlobalTabmanager::Get()->TryInvokeTab(PackageHelperTabName); } void FPackageHelperModule::RegisterMenus() { FToolMenuOwnerScoped OwnerScoped(this); { UToolMenu* Menu = UToolMenus::Get()->ExtendMenu("LevelEditor.MainMenu.Window"); { FToolMenuSection& Section = Menu->FindOrAddSection("WindowLayout"); Section.AddMenuEntryWithCommandList(FPackageHelperCommands::Get().PluginAction, PluginCommands); } } { UToolMenu* ToolbarMenu = UToolMenus::Get()->ExtendMenu("LevelEditor.LevelEditorToolBar"); { FToolMenuSection& Section = ToolbarMenu->FindOrAddSection("Settings"); { FToolMenuEntry& Entry = Section.AddEntry(FToolMenuEntry::InitToolBarButton(FPackageHelperCommands::Get().PluginAction)); Entry.SetCommandList(PluginCommands); } } } } #undef LOCTEXT_NAMESPACE IMPLEMENT_MODULE(FPackageHelperModule, PackageHelper)
2.DefaultEngine.ini配置文件修改
因为打包不同内容加载的地图也不一样,所以在打包前先在配置文件里更新GameDefaultMap,
虽然RunUAT.bat也可以传入打包地图,但那样灵活性稍差一些。
此处使用GConfig写入配置文件:
FString MapPath;//要用UE路径格式,例如:Game/Maps/ThirdPersonExampleMap.ThirdPersonExampleMap FString Path = FPaths::ConvertRelativePathToFull(FPaths::ProjectConfigDir() + TEXT("DefaultEngine.ini")); GConfig->SetString(TEXT("/Script/EngineSettings.GameMapsSettings"), TEXT("GameDefaultMap"), *MapPath, *Path); GConfig->Flush(false, *Path);//Read参数为false是针对写入Flush,true是针对读取
3.RunUAT.bat打包
与Unity直接封装好了打包函数不同,UE中需要用RunUAT.bat打包,位于引擎目录:
Engine/Build/BatchFiles/RunUAT.bat
打包时,需要阻塞UE自身线程,这样可以在打包完成后做一些事情,
这里使用FPlatformProcess运行RunUAT.bat,具体代码如下:
//打包函数,参数BuildPath为打包输出目录(注意路径结尾不带斜杠,否则打包失败) void BuildPackage(FString BuildPath) { FString RunUATPath = FPaths::ConvertRelativePathToFull(FPaths::EngineDir()) + FString("Build/BatchFiles/RunUAT.bat"); FString UE4EditorCmdPath = FPaths::ConvertRelativePathToFull(FPaths::EngineDir()) + FString("Binaries/Win64/UE4Editor-Cmd.exe"); FString UProjectPath = FPaths::ConvertRelativePathToFull(FPaths::GameSourceDir() + TEXT("../")); TArray<FString> AssetName; IFileManager::Get().FindFiles(AssetName, *UProjectPath, TEXT(".uproject")); UProjectPath += AssetName[0]; FString Arg = FString("-ScriptsForProject=\"" + UProjectPath + "\" BuildCookRun -project=\"" + UProjectPath +"\" -targetplatform=Win64 -clientconfig=Development -ue4exe=\"" + UE4EditorCmdPath + "\" -noP4 -iterate -cook -pak -package -stage -archive -archivedirectory=\"" + BuildPath + "\" -nocompileeditor -prereqs -nodebuginfo -build -CrashReporter -utf8output -compressed"); FProcHandle Handle = FPlatformProcess::CreateProc(*RunUATPath, *Arg , false, false, false, nullptr , 2, nullptr, nullptr); //创建进程打包,PriorityModifer可以填2,进程优先级会高一些 FPlatformProcess::WaitForProc(Handle); //堵塞,等待新进程打包完成 UE_LOG(LogTemp, Log, TEXT("Package Successful!")); //这里可以加一些打包完的后续操作 }
这样重启UE之后即可使用打包工具,提升开发效率。
最后Build.cs中还需要添加一些模块依赖,参考如下:
PrivateDependencyModuleNames.AddRange( new string[] { "Projects", "InputCore", "UnrealEd", "ToolMenus", "CoreUObject", "Engine", "Slate", "SlateCore", "EngineSettings" } );