UE4 Slate独立引用程序(摘抄大象无形)

UE4 Slate独立引用程序(摘抄大象无形)

  • 简介
  • 如何开始
  • BlankProgram
  • 走的更远
    • 预先准备
    • 增加模块引用
    • 添加头文件应用
    • 修改Main函数为WinMain
    • 添加LOCTEXT_NAMESPACE 定义
    • 添加SlateStandaloneApplication
    • 链接CoreUObject
    • 添加一个Window
    • 最终代码
  • 剥离引擎的独立应用程序

简介

什么时引擎独立应用程序?让我们首先看一下虚化引擎的Launcher运行器。不知道大家是否曾经好奇,虚幻引擎的Luancher运行器时用什么写的呢?如果你曾经仔细观察过虚幻引擎的运行器的文件结构,你会惊讶的发现,这个文件结构非常类似于虚幻引擎本身的文件结构,而非虚幻引擎编译打包形成的游戏文件结构。这意味着,虚幻引擎的运行器是一个微型虚幻引擎,而不是一个打包形成的游戏!

如何开发这样的东西呢?如果我们希望在一个小型的、不是游戏的应用程序中继续使用虚幻引擎的一部分API,那么我们就需要学习UE4 Luancher(虚幻运行器)这样的技术。

如何开始

关于如何撰写这样的应用程序的介绍,虚幻官方文档语焉不详,几乎没有说明什么。但是虚幻引擎提供了几个案例性的程序,如BlankProgram 和SlateViewer。我们可以从几个应用程序着手进行分析和学习。

  1. 建立文件结构
  2. 配置.Target.cs 和.build.cs
  3. 撰写代码
  4. 剥离应用程序

由于没有了虚幻引擎提供的打包机制,我们需要自己完成应用程序的剥离和发布。这个发布过程并不是一个简单地做加法的过程,而是一个做减法的过程。我们不是选择出哪些文件时我们需要的,并添加到程序中,而是选择出哪些文件时我们不需要的从程序中删除。

那么,我们首先把目光投向虚幻引擎的源码目录ixa的一个小文件夹,就是Source\Programs\BlankProgram。

BlankProgram

这个文件夹包含了以下的文件结构:

BlankProgram
| BlankProgarm.Build.cs
| BlankProgram.Target.cs
|
|_Private
BlankProgram.cpp
BlankProgram.h

如何编译呢?你只需要打开源码版本的虚幻引擎的工程文件,然后运行解决方案窗口中的BlankProgram项目,你会看到弹出一个命令行窗口,显示出一行 “Hellow World”。

在对源码进行分析前,我们必须先关注如何配置这个工程。怎么告诉UHT(类似API库)和UBT(类似编译器),我们需要编译出一个可以运行的.exe文件,而不时虚幻引擎的插件、模块或者游戏?

答案是:我们通过.Target.cs 指定。由于虚幻引擎对源码引用的限制,笔者将会逐函数地分析.Target.cs的内容。你可以队长对应的文件,然后阅读比记者的讲解。比较重要的函数主要有SetupBinares和SetupGlobalEnvironment。

Public ouverride void SetupBinaries(
	TargetInfo Target,
	ref List
		OutBuildBinaryConfigurations,
	ref List OutExtraModuleNames
)
{
	OutBuildBinaryConfigurations.Add(
		new UEBuildBinaryConfiguration( InType:
			UEBuildBinaryType.Eecuteable,
							InmoduleNames:new List(){
											"BlankProram"
							}
		)
	);
}

在SetupBinaries中,我们指定了两样信息:

需要被引入的模块名,在这里叫BlankProgram,指向的是.build.cs中定义的BlankProgram模块。
需要指定引入模块的编译类型,这里编译的类型是UEBuildBinaryType.Executable。

第二个编译类型指定非常重要,意味着我们的模块将不再被编译为二进制数据模块,用于编译链接,而是编译为可执行的exe应用程序。换句话说,对SetupBinaries函数的重载,完成了对编译为exe应用程序的指定。

public onverride void SetupGlobaleEnvionment(
	TargetInfo Target,
	ref LinkEnvironmentConfiguration
		OutLinkEnvironmentConfiguration,
	ref CPPEnvironmentConfiguration
		OutCPPEnvironmentCOnfiguration
)
{
	UEBuildConfiguration.bCompileLeanAndMeanUE = true;
	BuildCOnfiguration.bUseMallcProfiler = false
	UEBuildConfiguration.bBuildEditor = false
	UEBuildConfiguration.bBuildWithEditorOnlyData = true;
	UEBuildConfiguration.bCompileAgainstEngine = false;
	UEBuildConfiguration.bCompileAgainsetCoreUOBject = false
	OutLinkEnvironmentConfiguration.bIsBuildingCOnsoleApplication = true;
}

笔者删除了注释以符合虚幻引擎的源码应用协议。这里我们能够看到7个控制变量的设定,具体含义如下:

  1. bComplieLeanAndMeanUE:这个控制变量表示编译为“部分”虚幻引擎,也就是非完整的虚幻引擎。
  2. bUseMallocProfiler:这个控制变量表示是否使用内存分配剖析器。BlankProgram关闭了内存分配婆媳。虚幻引擎给出的理由是,UHT有可能会在虚幻引擎本身编译之前被编译(实际商经常就是这样)。如果先于引擎本身编译,同时又打开内存剖析,则UHT会出现异常工作。同样地,BlankProgram也是一样
  3. 接下来是两个BuildEditor控制。我们不需要一个编辑器,但是我们需要编辑器相关的数据。
  4. bCompiledAgainstEngine和bCompileAgainstCoreUObject:这两个标志控制了是直接静态链接还是动态连接的方式来与Engine模块和CoreUObject模块链接。这里BlankApp采用动态链接的方式以降低体积。
  5. bIsBuildingConsoleApplication:是否编译为控制台命令程序。这实质是指程序入口点。比如对于控制台应用程序,入口点将会是Mian函数。

在此基础上,我们的Hello World程序就会显得比较简单,核心代码如下:

#include "BlankProgram.h"
#include "RequiredProgramMainCPPInclude,h"
DEFINE_LOG_CATEGORY_STATIC(LogBlankProgram,Log,All);
IMPLEMENT_APPLICATION(BlankProgram,"BlankProgram") ;
INT32_MAIN_INT32_ARGC_TCHAR_ARGV()
{
	GEnginloop.PreInit(ArgC,ArgV);
	UE_LOG(LogBlankProgram,Display,TEXT("Hello World"));
	return 0;
}

整个代码中包含的内容不多,相对来说,Main函数中的内容理解起来比较简单,即预初始化引擎来完成日志系统初始化,从而支持UE_LOG宏,输出Hello World。为了配和UE_LOG宏,我们需要定义log category,所以需要在最前方书写DEFINE_LOG_CATEGORY_STATIC宏。

此时相对来说陌生的只有两个部分:

  1. RequiredProgramMainCPPInclude头文件的内容
  2. IMEPLEMENT APPLICATION宏的含义

对于前者,这个头我呢见包含了:
ModuleManager.h 模块管理器头文件,提供注册模块需要的辅助类,这是提供给IMPLEMENT_APPLICAION宏使用的。
LaunchEngineLoop.h 提供我们用的GEngineLoop定义。
LaunchEngineLoop.cpp 头文件包含cpp显得非常奇怪,根据官方注释,cpp提供了一些需要的定义。
对于后者,实际上可以理解为和之前介绍的模块实现宏IMPLEMENT_MODULE一样,提供了模块的注册实现。

走的更远

整个程序就打印了一行日志输出,逗我玩?莫慌,先看看我们已经拥有了什么:

  1. 可以引入虚幻引擎已有的运行时模块。
  2. 可以包含虚幻引擎的类定义,从而使用虚幻引擎提供的公共API

在这个基础上,我们能走的更远:我们可以开发出一个引擎独立的Slate应用程序。若读者有兴趣,请按照以下步骤操作。

预先准备

请读者尽可能准备一个源码版的。可以在github上获取。

增加模块引用

在.Build.cs文件的PrivateDependencyModuleNames中,添加到Slate\SlateCore和StandaloneRenderer模块的引用。前两者时为了提供Slate访问的接口,很好理解,后者是为了让Slate能够独立于整个引擎渲染(可以看作一个轻量级的Slate窗口渲染器)。当你修改完之后的代码应该是这样:

PrivateDependencyModuleNames.AddRange(
	new string[]{
		"Core"
		"Slate",
		"SlateCore",
		"StandaloneRenderer"
	}
)

添加头文件应用

在BlankProgram.h头文件中加入:

#include "SlateBasics.h"
#include "StandaloneRenderer.h"
#include "SlateApplication.h"

修改Main函数为WinMain

为了能够创建一个Windows窗口,我们必须要让引擎的入口点设置为WinMain,修改Main函数定义为:

int
WINAPI WinMain(
	_In_ HINSTANCE hInIstance,
	_In_opt_ HINSTANCE hPrevInstance,
	_In_ LPSTR,
	_In_ int
	nCmdShow
)

添加LOCTEXT_NAMESPACE 定义

在BlankProgram.cpp 最开头添加上一行LOCTEXT_NAMESPACE定义,内容随意,这是为了适配虚幻引擎的本地化机制。

添加SlateStandaloneApplication

此时GEngineLoop的初始化会缺少参数,因此需要略微修改,之后添加以下代码以启动SlateApplication,并进入消息循环:

GEngineLoop.PreInit(GetCommandLineW());
FSlateApplication::InitializAsStandlongApplication(
	GetStandaloneRenderer()
);
while(!GIsRequestingExit)
{
	FSlateApplication::Get().Tick();
	FSlateApplication::Get().PumpMessages();
}

链接CoreUObject

由于Slate需要CoreUObject模块中的一些定义,因此需要在.Target.cs文件中把链接选项设置为true,同时顺便也把编译为命令行应用的设置为false,即:

UEBuildConfiguration.bCompileAgainstCoreUObject = true;
OutLinkEnvironmentConfiguration.bIsBuildingConsoleApplication = false;

添加一个Window

为了让我们的Slate系统打开一个窗口,我们需要手动增加一个Window。在FSlateApplication初始化后,添加一下代码:

TSharedPtrMainWindow=
	SNew(SWindow)
	.ClientSize(FVector2D(800,200));
FSlateApplication::Get().AddWindow(MainWindow,ToShareRef());

最终代码

如果一切顺利,最终完成的代码应该是这样的:

#pragma once
#inlcude "Core.h"
#include "SlateBasics.h"
#include "StandaloneRenderer.h"
#include "SlateApplication.h"
#include "BlankProgram.h"
#include "RequiredProgramMainCPPInclude.h"
DEFINE_LOG_CATEGORY_STATIC(LogBlankProgram,Log,All);
IMPLEMENT_APPLICATION(BlankProgram,"BlankProgram");
int
WINAPI WinMain(_In_ HINSTANCE hInIstance,_In_opt_ HINSTANCE hPrevInstance,_in_ LPSTR,In_ int nCmdShow)
{
	GEngineLoop.PreInit(GetCommandLineW());
	FSlateApplication::InitializeAstandaloneApplication(
		GetStandardStandaloneRenderer()
	);
	TSharedPtr MainWindow=SNew(SWindow)
					.ClientSize(FVector2D(800,200));
	FSlateApplication::Get().AddWindow(MainWindow.ToSharedRef());
	while(!GIsRequestingExit)
	{
				FSlateApplication::Get().Tick();
				FSlateApplication::Get().PumpMessages();
	}
	return 0;
}

退出VisualStudio,然后进入你的源码版引擎目录,运行里面的GenerateProjectFiles批处理文件,以调用UBT更新工程文件的设置与依赖。笔者目前不确定这一步是否必须执行,但笔者每次都用这样的方式刷新了工程文件。

在BlankProgram(或者你自己复制出来的项目)上单击鼠标右键,设置为启动项目,然后单击鼠标右键,构建当前项目。构建完成后,直接运行。不出意外,你会看到一个弹出的Slate窗口。

剥离引擎的独立应用程序

此时就有读者想得更远,希望能够不依赖虚幻引擎运行自己写好的引擎独立应用程序。同时也为了验证笔者刚刚说的是不是真的——真的不需要引擎来运行吗?

一个最简单的方式就是去\Engine\Binaries\Win64 文件夹下找到BlankProgram.exe,拷贝到你的Epic运行器文件夹对应的目录。没错,就是虚幻引擎哪个登录账号的运行器的目录。在笔者电脑上,它位于Epic Games\Launcher\Engine\Binaries\Win64。双击一下,你依然会发现程序能正常工作,验证了笔者的说法:你的程序不需要依赖一个引擎也能运行。

需要注意的是,这种应用程序依赖特定文件结构和Shader着色器,所以如果你真的希望把你的程序交给没有虚幻引擎的人使用,笔者由衷建议直接打包运行器,作为已经配置完成的一栏,会大幅度简化你的工作

你可能感兴趣的:(UE4&UE5,c++)