[翻译]Integrating Lua

一篇名为Integrating Lua的Wiki页面翻译。

原文链接:https://wiki.unrealengine.com/Integrating_Lua

Integrating Lua

Overview

欢迎来到我的第三个Wiki页面,这个页面是关于那个很棒的游戏引擎的——虚幻引擎!这篇文章包含了人们最近问我数次的那个问题,我认为这将对游戏逻辑编程和一个高水平的mod-abilty(模块化能力?):整合Lua很有用。

Requirements

Starting off : code in our project

Aquiring:

现在,我们需要向项目中引入Lua,以便于其与我们的项目一起编译并包含在最终游戏中。但是首先,我们需要获取到Lua的二进制文件。有些项目会为您提供预编译的二进制文件,或者您也可以自行编译!因为编译的过程耗费时间,而且这个过程是必要的,所以我们将使用预编译的版本。LuaBinaries是一个很棒的项目,它始终是最新的,并且具有适用于每个平台的二进制文件(在本文中,我们将只涉及Windows)。选择最新版本,然后选择Windows库,接着选择静态(Static),因为我们会将Lua嵌入到游戏的可执行文件中,然后根据需要搜索软件包。在我们下载过这些包后,我们应该在它们的结构中找到如下文件:

  • Include/(essential includes for working with Lua)
    • luaxlib.h
    • lua.h
    • lua.hpp
    • luaconf.h
    • lualib.h
  • luaXX.lib(XX代表Lua的当前版本,例如Lua 5.3.3就为lu a53.lib)

现在,我们拥有了二进制文件和包含文件,我们可以开始我们的集成工作了!

Copying the files

在虚幻引擎中链接Lua很容易。首先,我们需要在项目目录中创建一个新文件夹,将Lua内容放入其中。我个人更喜欢在项目的主目录(.uproject所在的位置)中创建一个新文件夹,并将其命名为“ThirdParty”。然后在这个文件夹中,我创建了一个名为“Lua”的文件夹,并将其分为“includes”和“libraries”。结构如下:

  • Project Home/
    • ThirdParty/
      • Lua/
        • includes/(我们只需要复制一次,因为它们与体系结构没有区别,最后请注意“s”)
        • libraries/
          • luaXX.x64.lib(这是您在Win64包中下载的库文件,重命名来做区分)
          • luaXX.x32.lib(这是您在Win32包中下载的库文件,重命名来做区分)

Linking

在Visual Studio中,打开YourProjectName.Build.cs,这个文件位于 Source/YourProjectName/ 文件夹中。默认情况下,此文件仅链接默认引擎模块。要对此链接Lua,我们需要向其中添加一些代码。由于展示每一个细节实在是太多了,所以我只向你展示我对Build.cs文件的改动:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
using System.IO;
using UnrealBuildTool;

public class ProjectName : ModuleRules
{
private string ThirdPartyPath
{
get { return Path.GetFullPath(Path.Combine(ModuleDirectory, "../../ThirdParty/")); }
}

public ProjectName(TargetInfo Target)
{
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" });

PrivateDependencyModuleNames.AddRange(new string[] { });

// 如果使用Slate UI,则取消注释
// PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });

// 如果使用在线功能,则取消注释
// PrivateDependencyModuleNames.Add("OnlineSubsystem");

// 要包含 OnlineSubsystemSteam, 请将其添加到uproject文件的plugins部分中,并将Enabled属性设置为true

LoadLua(TargetRules); // 此函数加载Lua
}

private bool LoadLua(ReadOnlyTargetRules TargetRules)
{
bool isLibSupported = false;

// 检查是否使用的是Windows
if ((Target.Platform == UnrealTargetPlatform.Win64) || (Target.Platform == UnrealTargetPlatform.Win32))
{
isLibSupported = true;

string PlatformString = (Target.Platform == UnrealTargetPlatform.Win64) ? "x64" : "x86"; // 此字符串是“x64”或“x86”,因此我们可以将其附加到lib文件名
string LibrariesPath = Path.Combine(ThirdPartyPath, "Lua", "libraries");

PublicAdditionalLibraries.Add(Path.Combine(LibrariesPath, "lua53." + PlatformString + ".lib"));

PublicIncludePaths.Add(Path.Combine(ThirdPartyPath, "Lua", "includes"));
}

Definitions.Add(string.Format("WITH_LUA_BINDING={0}", isLibSupported ? 1 : 0));

return isLibSupported;
}
}

“LoadLua”方法检查我们是否在Windows上,如果是,则根据编译的体系结构创建一个包含“x64”或“x86”的字符串,因此我们可以轻松的找到lib文件。然后,我们创建库路径,该路径就是(在这个例子中为“ThirdParty/”)/Lua/libraries。从那里,我们将平台字符串附加到文件名的前缀和后缀,因此最后应该是lua53.xxx.lib。此路径已添加到PublicAdditionalLibraries中,这会强制编译器使用我们放在其中的lib文件。我们还将includes目录添加到PublicIncludePaths,以便编译器找到我们的includes。最后也很重要的一点是,我们向已编译的游戏中添加了一个定义:WITH_LUA_BINDING = x,其中x为0(不支持Lua)或1(支持Lua)。

First steps

现在,我们已经将Lua二进制文件集成到我们的游戏中,让我们通过在Visual Studio中右键单击该项目并选择“Build”来进行尝试。它应该会成功!下一步:编写一个蓝图节点,该节点从我们输入的字符串中执行Lua代码。打开您创建的蓝图函数库的头文件(.h)。空文件如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
#pragma once

#include "Kismet/BlueprintFunctionLibrary.h"
#include "LuaBlueprints.generated.h"

/**
*
*/
UCLASS()
class YOURPROJECT_API ULuaBlueprints : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
};

首先,在顶部添加Lua包含项,以便编译器知道声明函数的位置。为此,请在其他包含项的头部添加#include "lua.hpp"。接下来,我们向其中添加一个新的UFUNCTION,将其解析为带有以下声明的蓝图节点:

1
2
3
public:
UFUNCTION(BlueprintCallable, Category = "Lua")
static void RunLua(const FString& code);

这将创建一个可调用的蓝图节点,该节点带有一个字符串(FString),我们可以在其中输入将要运行的Lua代码。请注意,这是静态的,因为我们没有要在上下文中调用此对象的对象,换言之,我们想在没有任何目标的情况下从任何地方调用它。让我们进入LuaBlueprints.cpp文件,该文件应该为空(include文件除外)。我们为RunLua函数添加定义:

1
2
3
4
5
6
7
8
9
10
11
void ULuaBlueprints::RunLua(const FString& code)
{
lua_State* L = luaL_newstate();
luaL_openlibs(L);

int result = luaL_dostring(L, TCHAR_TO_ANSI(*code));
if (result != 0)
{
UE_LOG(LogTemp, Error, TEXT("Lua Script error: %s"), ANSI_TO_TCHAR(lua_tostring(L, -1)));
}
}

这很容易。它使用Lua C API创建一个新的Lua状态,将其分配给名为L的变量。然后,打开标准的lua库(例如math),然后通过执行luaL_dostring()运行代码。这将Lua状态L作为一个参数,并将要运行的代码作为另一个参数。该函数返回一个整数,如果一切正常,则返回0。如果不为0,则显然出了问题。在这种情况下,我使用UE_LOG将其打印到日志中。注意,我们通过lua_tostring(L,-1)得到Lua错误消息。