在UnLua插件中可以直接使用协程编写蓝图中LatentAction逻辑,实现延迟执行线性逻辑。
典型用例:在蓝图中可以调用delay函数,不过,仅能在事件图表使用,因为整个事件图标是作为一个蓝图函数处理的,在Delay或者说是LatentAction位置就记录节点ID,待计时器触发,继续执行图表函数的对于节点。
在LUA中可以新建一个LUA线程执行LatentAction及其后续逻辑,虽然不如蓝图表达上那么自然,但也不太复杂。
主要实现如下:
static FName LatentPropName = FName("LatentInfo");
int32 FastLuaHelper::CallUnrealFunction(lua_State* InL)
{
//SCOPE_CYCLE_COUNTER(STAT_LuaCallBP);
UFunction* Func = (UFunction*)lua_touserdata(InL, lua_upvalueindex(1));
FLuaObjectWrapper* Wrapper = (FLuaObjectWrapper*)lua_touserdata(InL, 1);
UObject* Obj = nullptr;
if (Wrapper && Wrapper->WrapperType == ELuaWrapperType::Object)
{
Obj = Wrapper->GetObject();
}
int32 StackTop = 2;
if (Obj == nullptr)
{
lua_pushnil(InL);
return 1;
}
if (Func->NumParms < 1)
{
Obj->ProcessEvent(Func, nullptr);
return 0;
}
else
{
FStructOnScope FuncParam(Func);
FProperty* ReturnProp = nullptr;
FStructProperty* LatentProp = nullptr;
for (TFieldIterator<FProperty> It(Func); It; ++It)
{
FProperty* Prop = *It;
if (Prop->HasAnyPropertyFlags(CPF_ReturnParm))
{
ReturnProp = Prop;
}
else
{
FastLuaHelper::FetchProperty(InL, Prop, FuncParam.GetStructMemory(), StackTop++);
if (Prop->GetFName() == LatentPropName)
{
LatentProp = (FStructProperty*)Prop;
}
}
}
//重新纠正latent参数
if (LatentProp)
{
if (lua_pushthread(InL) == 1)
{
UE_LOG(LogTemp, Warning, TEXT("never use latent in main thread!"));
return 0;
}
FLatentActionInfo LatentInfo;
//新建一个代理,等触发以后再删除
ULuaLatentActionWrapper* LatentWrapper = NewObject<ULuaLatentActionWrapper>(GetTransientPackage());
LatentWrapper->AddToRoot();
LatentInfo.CallbackTarget = LatentWrapper;
LatentWrapper->MainThread = InL->l_G->mainthread;
LatentWrapper->WorkerThread = InL;
LatentInfo.ExecutionFunction = LatentWrapper->GetWrapperFunctionName();
//记录当前线程到注册表,触发以后再移除
LatentInfo.Linkage = luaL_ref(InL, LUA_REGISTRYINDEX);
LatentInfo.UUID = GetTypeHash(FGuid::NewGuid());
LatentProp->CopySingleValue(LatentProp->ContainerPtrToValuePtr<void>(FuncParam.GetStructMemory()), &LatentInfo);
}
Obj->ProcessEvent(Func, FuncParam.GetStructMemory());
int32 ReturnNum = 0;
if (ReturnProp)
{
FastLuaHelper::PushProperty(InL, ReturnProp, FuncParam.GetStructMemory());
++ReturnNum;
}
if (Func->HasAnyFunctionFlags(FUNC_HasOutParms))
{
for (TFieldIterator<FProperty> It(Func); It; ++It)
{
FProperty* Prop = *It;
if (Prop->HasAnyPropertyFlags(CPF_OutParm) && !Prop->HasAnyPropertyFlags(CPF_ConstParm))
{
FastLuaHelper::PushProperty(InL, *It, FuncParam.GetStructMemory());
++ReturnNum;
}
}
}
if (LatentProp == nullptr)
{
return ReturnNum;
}
else
{
//目前是在子线程工作,调用完UFunction要让出,回到主线程
return lua_yield(InL, ReturnNum);
}
}
}
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "LuaLatentActionWrapper.generated.h"
struct lua_State;
class FastLuaUnrealWrapper;
/**
*
*/
UCLASS()
class FASTLUASCRIPT_API ULuaLatentActionWrapper : public UObject
{
GENERATED_BODY()
public:
UFUNCTION()
void TestFunction(int32 InParam);
static FName GetWrapperFunctionName() { return FName(TEXT("TestFunction")); }
lua_State* MainThread = nullptr;
lua_State* WorkerThread = nullptr;
};
// Fill out your copyright notice in the Description page of Project Settings.
#include "LuaLatentActionWrapper.h"
#include "lua/lua.hpp"
#include "FastLuaUnrealWrapper.h"
void ULuaLatentActionWrapper::TestFunction(int32 InParam)
{
int32 nres = 0;
//从主线程切换到工作线程继续执行
int32 Result = lua_resume(WorkerThread, MainThread, 0, &nres);
//工作线程结束了就移除,实际使用中要处理异常
if (Result == LUA_OK)
{
luaL_unref(MainThread, LUA_REGISTRYINDEX, InParam);
}
this->RemoveFromRoot();
this->MarkPendingKill();
}
--用法示例
function Main()
print = Unreal.PrintLog
print(("----Lua Ram: %.2fMB----"):format(collectgarbage("count") / 1024))
G_Timer:SetTimer('MainDelayInit', 1, 0.1, DelayInit, nil)
end
function DelayInit()
local co = coroutine.create(
function()
KismetSystemLibrary:Delay(GameInstance, 2.0)
print(222)
end
)
coroutine.resume(co)
print(111)
--编辑器日志界面先打印了111,2秒后打印了222
end
|
请发表评论