UE4自动打包工具编写

在UE的开发中,有些项目需要针对不同版本出不同的包,并有一个对应的GUI界面,供大家使用。

 

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)

PackageHelper.cpp

 

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"
    }
);

Build.cs依赖模块参考