unreal-c++教程-第十章:c++和Unlua的交互

1. 背景

我们希望频繁变动的脚本是通过lua来完成的,因此我们引入了tx的UnLua(开源库)
我们会在c++中完成socket层面的处理,然后过滤出proto的字节流,这个字节流会从
c++传递给lua,然后在lua中完成proto消息的处理

2. 基本思路

1. 定义c++的静态函数
2. 在lua中访问该静态函数

2.1 (GlobalTable函数的交互)在c++定义暴露给UnLua的静态函数

	UFUNCTION(BlueprintCallable, meta = (DisplayName = "CallLuaByGlobalTable", Category = "UnLua Tutorial"))
	static void CallLuaByGlobalTable();

在这里插入图片描述
然后我们就可以在lua中调用该c++的函数了

在这里插入图片描述
在这里需要进修UnLua的入门教程

2.1.1 该函数的基本需求:

1. 我们在TestReceiveCpp.lua中定义了一个函数叫做:CallMe的函数
function TestReceiveCpp.CallMe(a, b)
    local ret = a + b
    local msg = string.format("c++ call a = %f,b = %f,ans = %f", a, b, ret)
    Screen.Print(msg)
    return
2. 我们希望在c++中去调用这个CallMe函数

2.1.2 该函数的基本实现思路

1. 拿到对应lua脚本的闭包,并用变量TRC保留该闭包的引用
	UnLua::FLuaEnv Env;
	const auto bSuccess = Env.DoString("TRC = require 'TestReceiveCpp'");
	check(bSuccess);
显然,到这里,我们其实就是将定义的TestReceiveCpp的lua脚本加载到当前Env的内存当中

在这里插入图片描述

2. 调用该闭包的CallMe函数
const auto RetValues = UnLua::CallTableFunc(Env.GetMainState(), "TRC", "CallMe", 1.1f, 2.2f);
	check(RetValues.Num() == 1);
3. 完整的函数
void AClient::CallLuaByGlobalTable()
{	
	UnLua::FLuaEnv Env;
	const auto bSuccess = Env.DoString("TRC = require 'TestReceiveCpp'");
	check(bSuccess);
	const auto RetValues = UnLua::CallTableFunc(Env.GetMainState(), "TRC", "CallMe", 1.1f, 2.2f);
	check(RetValues.Num() == 1);
}

2.2 (FLuaTable的交互)在c++定义暴露给UnLua的静态函数

2.2.1 该函数的基本需求

见2.1.1

2.2.2 该函数的实现思路

1. 利用UnLua自带的FLuaFuntion去加载require函数
const auto Require = UnLua::FLuaFunction(&Env, "_G", "require");
2. 利用requre函数获取TestReceiveCpp的闭包(加载到内存并返回引用)
const auto RetValues1 = Require.Call("TestReceiveCpp");
check(RetValues1.Num() == 2);
3. 获取函数Table
	const auto RetValue = RetValues1[0];
	const auto LuaTable = UnLua::FLuaTable(&Env, RetValue);
4. 调用CallMe函数
	const auto RetValues2 = LuaTable.Call("CallMe", 3.3f, 4.4f);
	check(RetValues2.Num() == 1);
5. 从lua函数的返回中解析目标值
	const auto Msg = FString::Printf(TEXT("[C++]收到来自Lua的返回,结果=%f"), RetValues2[0].Value<float>());
	PrintScreen(Msg);

3. 完整的c++代码

.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Client.generated.h"

UCLASS()
class LUAX_API AClient : public AActor
{
	GENERATED_BODY()
	
public:	
	// Sets default values for this actor's properties
	AClient();

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;

	UFUNCTION(BlueprintCallable, meta = (DisplayName = "CallLuaByGlobalTable",
	Category = "UnLua Tutorial"))
	static void CallLuaByGlobalTable();

	UFUNCTION(BlueprintCallable, meta = (DisplayName = "CallLuaByFLuaTable", Category = "UnLua Tutorial"))
	static void CallLuaByFLuaTable();

	UFUNCTION(BlueprintCallable, meta = (DisplayName = "SetupCustomLoader", Category = "UnLua Tutorial"))
	static void SetupCustomLoader(int Index);
	
	
	UFUNCTION(BlueprintNativeEvent, Category = "Switch Functions")
	void OnReceiveMsg(float val);

	void OnReceiveMsg_Implementation(float val);
};

.cpp

// Fill out your copyright notice in the Description page of Project Settings.


#include "Client.h"
#include "UnLua.h"
#include "Kismet/KismetSystemLibrary.h"

//#include "UnLua/"
//#include "UnLua/"
// Sets default values
AClient::AClient()
{
 	// 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

// Called every frame
void AClient::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

	OnReceiveMsg(DeltaTime);

}




void AClient::OnReceiveMsg_Implementation(float val)
{

}


static void PrintScreen(const FString& Msg)
{
	UKismetSystemLibrary::PrintString(nullptr, Msg, true, false, FLinearColor(0, 0.66, 1), 100);
}

void AClient::CallLuaByGlobalTable()
{	
	UnLua::FLuaEnv Env;
	const auto bSuccess = Env.DoString("TRC = require 'TestReceiveCpp'");
	check(bSuccess);
	const auto RetValues = UnLua::CallTableFunc(Env.GetMainState(), "TRC", "CallMe", 1.1f, 2.2f);
	check(RetValues.Num() == 1);
}

void AClient::CallLuaByFLuaTable()
{
	PrintScreen(TEXT("[C++]CallLuaByFLuaTable 开始"));
	UnLua::FLuaEnv Env;

	const auto Require = UnLua::FLuaFunction(&Env, "_G", "require");
	const auto RetValues1 = Require.Call("TestReceiveCpp");
	check(RetValues1.Num() == 2);

	const auto RetValue = RetValues1[0];
	const auto LuaTable = UnLua::FLuaTable(&Env, RetValue);
	const auto RetValues2 = LuaTable.Call("CallMe", 3.3f, 4.4f);
	check(RetValues2.Num() == 1);

	const auto Msg = FString::Printf(TEXT("[C++]收到来自Lua的返回,结果=%f"), RetValues2[0].Value<float>());
	PrintScreen(Msg);
	PrintScreen(TEXT("[C++]CallLuaByFLuaTable 结束"));
}

bool CustomLoader1(UnLua::FLuaEnv& Env, const FString& RelativePath, TArray<uint8>& Data, FString& FullPath)
{
	const auto SlashedRelativePath = RelativePath.Replace(TEXT("."), TEXT("/"));
	FullPath = FString::Printf(TEXT("%s%s.lua"), *GLuaSrcFullPath, *SlashedRelativePath);

	if (FFileHelper::LoadFileToArray(Data, *FullPath, FILEREAD_Silent))
		return true;

	FullPath.ReplaceInline(TEXT(".lua"), TEXT("/Index.lua"));
	if (FFileHelper::LoadFileToArray(Data, *FullPath, FILEREAD_Silent))
		return true;
		
	return false;
}

bool CustomLoader2(UnLua::FLuaEnv& Env, const FString& RelativePath, TArray<uint8>& Data, FString& FullPath)
{
	const auto SlashedRelativePath = RelativePath.Replace(TEXT("."), TEXT("/"));
	const auto L = Env.GetMainState();
	lua_getglobal(L, "package");
	lua_getfield(L, -1, "path");
	const char* Path = lua_tostring(L, -1);
	lua_pop(L, 2);
	if (!Path)
		return false;

	TArray<FString> Parts;
	FString(Path).ParseIntoArray(Parts, TEXT(";"), false);
	for (auto& Part : Parts)
	{
		Part.ReplaceInline(TEXT("?"), *SlashedRelativePath);
		FPaths::CollapseRelativeDirectories(Part);

		if (FPaths::IsRelative(Part))
			FullPath = FPaths::ConvertRelativePathToFull(GLuaSrcFullPath, Part);
		else
			FullPath = Part;

		if (FFileHelper::LoadFileToArray(Data, *FullPath, FILEREAD_Silent))
			return true;
	}

	return false;
}

void AClient::SetupCustomLoader(int Index)
{
	switch (Index)
	{
	case 0:
		FUnLuaDelegates::CustomLoadLuaFile.Unbind();
		break;
	case 1:
		FUnLuaDelegates::CustomLoadLuaFile.BindStatic(CustomLoader1);
		break;
	case 2:
		FUnLuaDelegates::CustomLoadLuaFile.BindStatic(CustomLoader2);
		break;
	}
}


void AClient::BeginPlay()
{
	Super::BeginPlay();
	SetupCustomLoader(1);
}


4. 注意

4.1 添加Lua的搜索路径

bool CustomLoader1(UnLua::FLuaEnv& Env, const FString& RelativePath, TArray<uint8>& Data, FString& FullPath)
{
	const auto SlashedRelativePath = RelativePath.Replace(TEXT("."), TEXT("/"));
	FullPath = FString::Printf(TEXT("%s%s.lua"), *GLuaSrcFullPath, *SlashedRelativePath);

	if (FFileHelper::LoadFileToArray(Data, *FullPath, FILEREAD_Silent))
		return true;

	FullPath.ReplaceInline(TEXT(".lua"), TEXT("/Index.lua"));
	if (FFileHelper::LoadFileToArray(Data, *FullPath, FILEREAD_Silent))
		return true;
		
	return false;
}

4.2 缺少Lua库

如果报以下错误:
在这里插入图片描述
这是因为缺少Lua库

可以在.Build.cs中
在这里插入图片描述
添加
在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值