UE4 Program 类型工程的限制和解决方法

目前如果你想在 UE4 里创建一个 Program 工程的话,存在两点限制,第一需要将工程源文件都放在 Engine/Source/Programs 目录下,第二是必须使用源码版本的引擎来编译。一般来说也没什么问题,但是有些时候会带来一些问题,比如你的 Program 有着特定的用途只和特定的项目相关,放在引擎目录下稍显不合适,会污染引擎代码结构。怎么样解决这两点限制,构建更加干净的项目?

本文的示例工程地址:MyProject ,测试的引擎版本为:4.24

在引擎目录外创建 Program 工程

首先随便创建一个游戏工程,如果 MyProject,目录结构如下

MyProject
|---MyProject.uproject
|---Config
|---Content
|---Source
    |---MyProject.Target.cs
    |---MyProjectEditor.Target.cs
    |---MyProject
        |---MyProject.Build.cs
        |---MyProject.cpp
        |---MyProject.h
        |---MyProjectGameModeBase.cpp
        |---MyProjectGameModeBase.h

类似引擎目录结构,在 Source 目录下新建两个子目录:Programs 用来放所有的 Program 工程模块;Runtime 用来放游戏相关的工程模块。然后将原来 Source 目录下的文件全部复制的 Runtime 目录中,并复制引擎的空工程示例 BlankProgram 工程到 Programs 目录中,并改个新名字 MyBlankProgram(UE4中各个Target名字全局不能冲突),新的结构如下

MyProject
|---MyProject.uproject
|---Config
|---Content
|---Source
    |---Runtime // 新建的目录,将之前的 Source 目录下的内容直接平移进来
    |   |---MyProject.Target.cs
    |   |---MyProjectEditor.Target.cs
    |   |---MyProject
    |       |---MyProject.Build.cs
    |       |---MyProject.cpp
    |       |---MyProject.h
    |       |---MyProjectGameModeBase.cpp
    |       |---MyProjectGameModeBase.h
    |---Programs // 新建的目录,用来放各种工具项目
        |---MyBlankProgram
            |---MyBlankProgram.Build.cs
            |---MyBlankProgram.Target.cs
            |---Private
            |   |---MyBlankProgram.cpp
            |   |---MyBlankProgram.h
            |---Resources
                |---Windows
                    |---BlankProgram.ico

右键 MyProject.uproject 文件,选择 Generate Visual Studio project files,生成工程文件,此时就会发现 MyBlankProgram 工程正确生成到 Solution 中的 Programs 目录下,如图

myblankprogram.png

如果想自定义 MyBlankProgram 在 Solution 中的目录,可以打开 MyBlankProgram.target.cs 文件,在构造函数在给 SolutionDirectory 赋个自定义值,如

[SupportedPlatforms(UnrealPlatformClass.Desktop)]
public class MyBlankProgramTarget : TargetRules
{
    public MyBlankProgramTarget(TargetInfo Target) : base(Target)
    {
        // other codes...

        SolutionDirectory = "MyPrograms";

        // other codes...
    }
}

重新生成 Solution,此时 MyBlankProgram 就在 MyPrograms 目录下了,如图

myblankprogram_new.png

注意点

在引擎的目录结构中,Engine/Source 目录下是有几个 Target.cs 文件的,如图

engine_source.png

所以在我们的工程中,是否一样也可以把 MyProject.Target.cs 也放到 Source 根目录下呢?

答案是不行的,游戏工程搜索 Target.cs 文件和引擎是有些区别的,代码见 Source/Programs/UnrealBuildTool/System/NativeProjects.cs 中的 FindTargetFiles 函数。生成工程的时候,会搜索 uproject 文件所在目录下的所有 Target.cs 文件,但是如果在某一级目录搜索到了 Target.cs 文件,那就不会继续搜索这一级目录中的子目录了。所以如果将 MyProject.Target.cs 也放到 Source 下,那么搜索就会停在 Source 目录,所以 Source/Programs 目录中的 Target.cs 文件就会被忽略了

findtargetfiles.png

编译

此时编译 MyBlankProgram,会出现编译错误,如下

1>------ Build started: Project: MyBlankProgram, Configuration: Development_Program x64 ------
1>UnrealBuildTool : error : Unable to find project 'E:\Workspace\MyProject\MyBlankProgram.uproject'.

这个错误的原因是编译时调用 UBT 的命令行参数中有一个 -project 参数,参数值是有问题的,如图

nmake.png

有以下几种解决方法

使用 4.25 版本的引擎

4.25 中已经没有这个问题了

直接修改参数

在命令行中将 #(ProjectName) 直接改为 MyProject,如图

nmake_new.png

这种方法缺点是每次重新生成工程后都得改一遍

将工程新建到引擎目录下

第二种方法是直接将工程新建到引擎目录下,见之前的文章 UE4 关于工程目录结构的一个小技巧

将工程建到引擎目录下,生成工程时就不会在编译命令行中增加 -project 参数,此时会根据目录的层级关系在上级目录中搜索对应的 uproject

修改 UBT

最直接的方式就是改 UBT 源码,打开 Source/Programs/UnrealBuildTool/ProjectFiles/VisualStudio/VCProject.cs 文件,在 WriteConfiguration 函数中,新增一段代码,当是 IsForeignProject 的时候,优先使用命令行中传入的 uproject 文件路径,如图

ubt.png

其它编译错误

Program 的模块需要依赖于 LaunchEngineLoop.cpp 文件,由于现在将 Program 移到了引擎目录之外,所以在模块中添加 include 路径的地方也需要修改,打开 MyBlankProgram.Build.cs,将 PublicIncludePathsPrivateIncludePaths 添加的路径拼上引擎路径,如图

module.png

结果

通过以上修改后,编译 MyBlankProgram,就会在 MyProject/Binaries/Win64 下看到生成的可执行文件,如图

result.png

运行结果如图

result1.png

不过有一点不完美的就是,运行后日志,配置等文件的生成路径是在这里

result2.png

这里的初始化代码见 FGenericPlatformMisc::ProjectDir,在 Program 下写死的使用 Engine 目录,如图

projectdir.png

如果想要修改,只能在自己的 Program 代码中,一开始初始化的时候调用 FPlatformMisc::SetOverrideProjectDir 改一下,如图

projectdir_new.png

这样,Program 运行时生成的日志配置等就生成到正确的目录下,如图

result3.png

非源码引擎编译 Program 工程

首先将上文的示例工程 MyProject 的引擎切换成安装版本的引擎,如图

switchengine.png

此时生成工程会报错,报错信息如下

ERROR: Targets with a unique build environment cannot be built an installed engine

解决这个错误需要修改 MyBlankProgram.Target.cs,将 BuildEnvironment 改成 TargetBuildEnvironment.Shared,如下

[SupportedPlatforms(UnrealPlatformClass.Desktop)]
public class MyBlankProgramTarget : TargetRules
{
    public MyBlankProgramTarget(TargetInfo Target) : base(Target)
    {
        // other codes...

        BuildEnvironment = TargetBuildEnvironment.Shared;

        // other codes...
    }
}

重新生成解决方案,修改后可以正常生成,如图

installedsolution.png

现在编译 MyBlankProgram,会遇到上文中同样的错误,如下

1>------ Build started: Project: MyBlankProgram, Configuration: Development_Program x64 ------
1>UnrealBuildTool : error : Unable to find project 'E:\Workspace\MyProject\MyBlankProgram.uproject'.

解决方法同上

继续编译,遇到新的错误

1>Creating makefile for MyBlankProgram (.uproject file is newer)
1>UnrealBuildTool : error : Program targets are not currently supported from this engine distribution.

这个错误的原因的安装版本的引擎能编译的配置是固定好的,并配置在 Engine/Config/BaseEngine.ini[InstalledPlatforms]

解决这个问题的方法是将 Engine\Build\InstalledBuild.txt 文件重命名一下,UBT 是根据这个文件是否存在来判断是否是安装版本的引擎

installedbuild.png

此时再编译,就会正确编译 MyBlankProgram,可执行文件的输出目录是引擎目录 Engine/Binaries/Win64,如图

result4.png

以上的步骤可以通过脚本来进行整合

生成工程

@ECHO OFF

SET EnginePath=E:\Epic Games\UE_4.24\Engine

CALL "%EnginePath%\Build\BatchFiles\Build.bat" -ProjectFiles -Project="%~dp0MyProject.uproject" -game -progress

PAUSE

编译工程

@ECHO OFF

SET EnginePath=E:\Epic Games\UE_4.24\Engine

REM 重命名 InstalledBuild.txt
IF EXIST "%EnginePath%\Build\InstalledBuild.txt" (
    REN "%EnginePath%\Build\InstalledBuild.txt" InstalledBuild1.txt
)

CALL "%EnginePath%\Build\BatchFiles\Build.bat" MyBlankProgram Win64 Development -Project="%~dp0MyProject.uproject" -WaitMutex

REM 重命名 InstalledBuild.txt
IF EXIST "%EnginePath%\Build\InstalledBuild1.txt" (
    REN "%EnginePath%\Build\InstalledBuild1.txt" InstalledBuild.txt
)

PAUSE

总结

在上文中解决这两个限制的方法,其实并不完美,但是目前好像也没有什么其它的解决方法