UE4 Unlua源码解析6 - UUnLuaManager内重要方法逐行解释
Posted 珞珈大胖强TURBO
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了UE4 Unlua源码解析6 - UUnLuaManager内重要方法逐行解释相关的知识,希望对你有一定的参考价值。
UUnLuaManager内重要方法逐行解释
1 Bind
总结:绑定Lua模块到UObject
当新的UObject实例被创建时,创建Lua对象,然后把Lua对象和C++对象的映射保存了起来,C++存在AttachedObjects中,Lua存在ObjectMap中
bool UUnLuaManager::Bind(UObjectBaseUtility *Object, UClass *Class, const TCHAR *InModuleName, int32 InitializerTableRef)
###1
if (!Object || !Class)
UE_LOG(LogUnLua, Warning, TEXT("Invalid target object!"));
return false;
#if UNLUA_ENABLE_DEBUG != 0
UE_LOG(LogUnLua, Log, TEXT("UUnLuaManager::Bind : %p,%s,%s"), Object, *Object->GetName(),InModuleName);
#endif
bool bSuccess = true;
lua_State *L = *GLuaCxt;
bool bMutipleLuaBind = false;
UClass** BindedClass = Classes.Find(InModuleName);
if ((BindedClass)
&&(*BindedClass != Object->GetClass()))
bMutipleLuaBind = true;
###1
###2
if (!RegisterClass(L, Class)) // register class first
return false;
###2
###3
// try bind lua if not bind or use a copyed table
UnLua::FLuaRetValues RetValues = UnLua::Call(L, "require", TCHAR_TO_UTF8(InModuleName)); // require Lua module
FString Error;
if (!RetValues.IsValid() || RetValues.Num() == 0)
Error = "invalid return value of require()";
bSuccess = false;
else if (RetValues[0].GetType() != LUA_TTABLE)
Error = FString("table needed but got ");
if(RetValues[0].GetType() == LUA_TSTRING)
Error += UTF8_TO_TCHAR(RetValues[0].Value<const char*>());
else
Error += UTF8_TO_TCHAR(lua_typename(L, RetValues[0].GetType()));
bSuccess = false;
else
bSuccess = BindInternal(Object, Class, InModuleName, true, bMutipleLuaBind, Error); // bind!!!
###3
if (bSuccess)
###4
bool bDerivedClassBinded = false;
if (Object->GetClass() != Class)
bDerivedClassBinded = true;
OnDerivedClassBinded(Object->GetClass(), Class);
###4
###5
FString RealModuleName = *ModuleNames.Find(Class);
GLuaCxt->AddModuleName(*RealModuleName); // record this required module
###5
###6
// create a Lua instance for this UObject
int32 ObjectRef = NewLuaObject(L, Object, bDerivedClassBinded ? Class : nullptr, TCHAR_TO_UTF8(*RealModuleName));
###6
AddAttachedObject(Object, ObjectRef); // record this binded UObject
###7
// try call user first user function handler
bool bResult = false;
int32 FunctionRef = PushFunction(L, Object, "Initialize"); // push hard coded Lua function 'Initialize'
if (FunctionRef != INDEX_NONE)
if (InitializerTableRef != INDEX_NONE)
lua_rawgeti(L, LUA_REGISTRYINDEX, InitializerTableRef); // push a initializer table if necessary
else
lua_pushnil(L);
bResult = ::CallFunction(L, 2, 0); // call 'Initialize'
if (!bResult)
UE_LOG(LogUnLua, Warning, TEXT("Failed to call 'Initialize' function!"));
luaL_unref(L, LUA_REGISTRYINDEX, FunctionRef);
else
UE_LOG(LogUnLua, Warning, TEXT("Failed to attach %s module for object %s,%p!\\n%s"), InModuleName, *Object->GetName(), Object, *Error);
###7
return bSuccess;
###1 容错判断
###2 调用RegisterClass,知道结果是
1 记录反射信息
根据传入的类型名,通过UE反射得到它的类型信息,然后记录这个类型信息,以FClassDesc(UnLua自己的数据结构)的形式存入UnLua的反射库GReflectionRegistry中,方便后续Lua、C++交互调用
2 创建元表 为传入的类型,以及它所有的父类,依次创建一个元表,然后设置一些元方法,最后记录在Lua的G表中,这样下次在Lua里调这个类型名,获得的就是创建出的G表。调这个类型里的方法,就会索引到当初注册时赋予的元方法
###3 加载lua模块,如果是存在的表,则调用到BindInternal方法
###4 Object继承自Class情况处理
###5 缓存Module的名字
###6 最重要,在此之前根本还没有Lua对象的出现,在这里才开始创建Lua对象,并且让Lua对象可以和UObjcet一一对应,可以说是Unlua的重点了。
###7 对Lua对象,push一个Initialize方法进去并执行
2 BindInternal
总结:Bind的重要实现函数,还是实现Lua绑定UObject
1 Lua模块名和C++对象的UClass记录在ModuleNames、Classes表中
2 如果Lua模块的方法中,有和C++函数同名的方法,则直接拿这个C++函数的反射信息进行改写,改写为执行Lua方法
bool UUnLuaManager::BindInternal(UObjectBaseUtility* Object, UClass* Class, const FString& InModuleName, bool bNewCreated, bool bMutipleLuaBind, FString& Error)
###1
if (!Object || !Class)
return false;
// module may be already loaded for other class,etc muti bp bind to same lua
FString RealModuleName = InModuleName;
if (bMutipleLuaBind)
lua_State* L = UnLua::GetState();
const int32 Type = GetLoadedModule(L, TCHAR_TO_UTF8(*InModuleName));
if (Type != LUA_TTABLE)
Error = FString::Printf(TEXT("table needed got %s"), UTF8_TO_TCHAR(lua_typename(L, Type)));
return false;
// generate new module for this module
int16* NameIdx = RealModuleNames.Find(InModuleName);
if (!NameIdx)
RealModuleNames.Add(InModuleName, 1);
RealModuleName = FString::Printf(TEXT("%s_#%d"), *InModuleName, 1);
else
*NameIdx = *NameIdx + 1;
RealModuleName = FString::Printf(TEXT("%s_#%d"), *InModuleName, *NameIdx);
// make a copy of lua module
lua_newtable(L);
lua_pushnil(L);
while (lua_next(L, -2) != 0)
lua_pushvalue(L, -2);
lua_insert(L, -2);
lua_settable(L, -5);
lua_pop(L, 1);
lua_getglobal(L, "package");
lua_getfield(L, -1, "loaded");
lua_pushvalue(L, -3);
lua_setfield(L, -2, TCHAR_TO_UTF8(*RealModuleName));
lua_pop(L, 3);
###1
###2
ModuleNames.Add(Class, RealModuleName);
Classes.Add(RealModuleName, Class);
###2
###3
TSet<FName> &LuaFunctions = ModuleFunctions.Add(RealModuleName);
GetFunctionList(UnLua::GetState(), TCHAR_TO_UTF8(*RealModuleName), LuaFunctions); // get all functions defined in the Lua module
TMap<FName, UFunction*> &UEFunctions = OverridableFunctions.Add(Class);
GetOverridableFunctions(Class, UEFunctions); // get all overridable UFunctions
OverrideFunctions(LuaFunctions, UEFunctions, Class, bNewCreated); // try to override UFunctions
###3
###4
return ConditionalUpdateClass(Class, LuaFunctions, UEFunctions);
###4
###1 多个UObject绑定一个Lua的处理
###2 Class和Lua Name关系绑定
###3 覆盖函数的重要实现,拿到所有Lua函数,和UObject函数,如果重名,就指向Lua,将原Function的执行代码保存在一个新的函数中,新的函数名为函数原名+“Copy”
###4 动画AnimNotify_ Lua覆盖
以上是关于UE4 Unlua源码解析6 - UUnLuaManager内重要方法逐行解释的主要内容,如果未能解决你的问题,请参考以下文章
UE4 Unlua源码解析6 - UUnLuaManager内重要方法逐行解释
UE4 Unlua源码解析11 - 非UE4反射支持的静态类导出给Lua使用原理