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使用原理

UE4 Unlua源码解析1 - 读源码的前置知识

UE4 Unlua源码解析1 - 读源码的前置知识

UE4 Unlua源码解析12 - Lua与UE4的混合GC

UE4 Unlua源码解析5 - FLuaContext内重要方法逐行解释