LiteralliJeff 发表于 2023-2-11 09:37

[UE4]无插件源码编译和打包工程:为多个有依赖关系的 ...

封面来源:顽皮狗美术总监John Sweeney个人作品https://gumroad.com/l/VqjIjt
无插件源码编译打包工程的意义:比如,既能将UE4插件给客户正常使用,又能保护代码不被泄漏(曾经我的一个下属把我在公司写的代码和工具放到公网上当作自己的个人作品去光子面试);或者你开发了一个UE4插件放在商城中售卖,希望将有源码版本和无源码版本分开销售(UE4商城有不少牛鼻的插件,但是价格动辄200刀,让一些不需要源码的用户望而却步)。

常规的C++工程只需要编一个链接库就可以达到以上目的,但是UE4 plugin的编译规则有点麻烦,而且UE4的plugin系统有些设计缺陷导致编译步骤有不少坑(比如:不同原因导致的链接错误,UE4控制台只会报同一个错误,且只有简单文本提示);另外对于多个有依赖关系的plugin如何实现无源码编译打包,官方也没相关文档。
1,新建两个插件 (Edit -> Plugins -> New Plugin) 并命令为P1和P2.


2,为 P1 和 P2添加测试代码(这些代码有跨越插件的依赖关系)


我们假设 Actor1 在 P1中, Actor2 在 P2中, P2 依赖于 P1, 但是 P1 并不依赖于P2。
Actor1.h
#include "Actor1.h"
#include "Engine.h"

// Sets default values
AActor1::AActor1()
{
    // Set this actor to call Tick() every frame.You can turn this off to improve performance if you don't need it.
    PrimaryActorTick.bCanEverTick = true;

}

// Called when the game starts or when spawned
void AActor1::BeginPlay()
{
    Super::BeginPlay();
   
    GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Yellow, FString("Actor1 BeginPlay"));
}
Actor2.h
#include "Actor2.h"
#include "Engine/Engine.h"
#include "Engine/World.h"
#include "Actor1.h"

// Sets default values
AActor2::AActor2()
{
    // Set this actor to call Tick() every frame.You can turn this off to improve performance if you don't need it.
    PrimaryActorTick.bCanEverTick = true;

}

// Called when the game starts or when spawned
void AActor2::BeginPlay()
{
    Super::BeginPlay();
   
    GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Green, FString("Actor2 BeginPlay"));

    FTransform Trans;
    AActor1* A1 = GetWorld()->SpawnActor<AActor1>(AActor1::StaticClass(), Trans);
}
3,将 P1 添加到P2的Build.cs中的 PrivateDependencyModuleNames 下:
PrivateDependencyModuleNames.AddRange(
    new string[]
    {
      "CoreUObject",
      "Engine",
      "Slate",
      "SlateCore",
      "P1"
      // ... add private dependencies that you statically link with here ...       
    }
    );
4,在 P1.uplugin和P2.uplugin中添加白名单WhitelistPlatforms ,并将 P1添加到P2.uplugin中的依赖选项中:
P1.uplugin
{
    "FileVersion": 3,
    "Version": 1,
    "VersionName": "1.0",
    "FriendlyName": "P1",
    "Description": "",
    "Category": "Other",
    "CreatedBy": "",
    "CreatedByURL": "",
    "DocsURL": "",
    "MarketplaceURL": "",
    "SupportURL": "",
    "CanContainContent": true,
    "IsBetaVersion": false,
    "IsExperimentalVersion": false,
    "Installed": false,
    "Modules": [
      {
            "Name": "P1",
            "Type": "Runtime",
            "LoadingPhase": "Default",
            "WhitelistPlatforms": [
                "Win64"
            ]
      }
    ]
}P2.uplugin
{
    "FileVersion": 3,
    "Version": 1,
    "VersionName": "1.0",
    "FriendlyName": "P2",
    "Description": "",
    "Category": "Other",
    "CreatedBy": "",
    "CreatedByURL": "",
    "DocsURL": "",
    "MarketplaceURL": "",
    "SupportURL": "",
    "CanContainContent": true,
    "IsBetaVersion": false,
    "IsExperimentalVersion": false,
    "Installed": false,
    "Modules": [
      {
            "Name": "P2",
            "Type": "Runtime",
            "LoadingPhase": "Default",
            "WhitelistPlatforms": [
                "Win64"
            ]
      }
    ],
    "Plugins": [
      {
            "Name": "P1",
            "Enabled": true
      }
    ]
}5, 关掉编辑器,重新编译工程代码,再重启编辑器。
6,,打包插件P1: Edit -> Plugins -> Project -> P1 -> Package:


然后关掉编辑器,删除TestProj\Plugins\P1,然后拷贝刚刚打包完成的版本P1到引擎的plugins目录下。


然后你就可以删除所有cpp文件


然后重启编辑器,你就会看到plugins的Installed标签下多了一个P1插件:


7,打包插件P2: Edit -> Plugins -> Project -> P2 -> Package:


然后关掉编辑器,并删除TestProj\Plugins\P2,然后拷贝刚刚打包的版本P2放到引擎的plugins目录下:


然后你就可以删除P2的所有cpp文件


8,将P2 添加到TestProj.Build.cs中的PublicDependencyModuleNames下 :
// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.

using UnrealBuildTool;

public class TestProj : ModuleRules
{
    public TestProj(ReadOnlyTargetRules Target) : base(Target)
    {
      PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;

      PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "NavigationSystem", "AIModule" });

      PublicDependencyModuleNames.AddRange(new string[] { "P2" });
    }
}
9, 将P1和P2 添加到TestProj.uproject中
{
    "FileVersion": 3,
    "EngineAssociation": "4.24",
    "Category": "",
    "Description": "",
    "Modules": [
      {
            "Name": "TestProj",
            "Type": "Runtime",
            "LoadingPhase": "Default"
      }
    ],
    "Plugins": [
      {
            "Name": "P1",
            "Enabled": true
      },
      {
            "Name": "P2",
            "Enabled": true
      }
    ]
}否则在启动编辑器时会出现以下错误:
The game module `TestProj` could not be loaded. There may be an operating system error or the module may not be properly set up.9, 此时你就可以在你自己的游戏工程中添加对插件P2 的头文件引用了
#include "TestProjGameMode.h"
#include "TestProjPlayerController.h"
#include "TestProjCharacter.h"
#include "UObject/ConstructorHelpers.h"
#include "Actor2.h"

ATestProjGameMode::ATestProjGameMode()
{
    // use our custom PlayerController class
    PlayerControllerClass = ATestProjPlayerController::StaticClass();

    // set default pawn class to our Blueprinted character
    static ConstructorHelpers::FClassFinder<APawn> PlayerPawnBPClass(TEXT("/Game/TopDownCPP/Blueprints/TopDownCharacter"));
    if (PlayerPawnBPClass.Class != NULL)
    {
      DefaultPawnClass = PlayerPawnBPClass.Class;
    }
}

void ATestProjGameMode::StartPlay()
{
    Super::StartPlay();

    FTransform Trans;
    AActor2* Pawn = GetWorld()->SpawnActor<AActor2>(AActor2::StaticClass(), Trans);
}
然后重新编译工程并启动编辑器,此时你就可以实现在无插件源码的情况下,不收约束的编译和打包工程。

七彩极 发表于 2023-2-11 09:42

感谢,相当实用

mypro334 发表于 2023-2-11 09:44

[干杯]

rustum 发表于 2023-2-11 09:49

感谢分享

mypro334 发表于 2023-2-11 09:53

[爱心]

Ilingis 发表于 2023-2-11 09:55

工程一编译的时候Dll就会被清理掉,然后就会报错了,这个方法不行吧?

Arzie100 发表于 2023-2-11 10:03

不会,因为这些预编译文件是放在引擎的plugin目录,不是工程的plugin目录。

XGundam05 发表于 2023-2-11 10:13

实用性很好啊,感谢大佬分享

JoshWindsor 发表于 2023-2-11 10:14

[干杯]

mastertravels77 发表于 2023-2-11 10:22

4.25版本测试的时候删除源代码打包的时候就报错
页: [1] 2
查看完整版本: [UE4]无插件源码编译和打包工程:为多个有依赖关系的 ...