文章

UnLua相关

UnLua相关

C++ 怎么导出类给Lua

不导出能调用吗

  • 反射系统动态导出

怎么编写自己的C++业务封装给Lua

  • 工具函数和常规写c库的的方式一样
  • 库函数注册流程 EXPORT_UNTYPED_CLASSIMPLEMENT_EXPORTED_CLASS
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
namespace {
	template<typename ViewType>
	int32 UI_GetWidgetFromName(lua_State* L)
	{
		int32 NumParams = lua_gettop(L);
		if (NumParams != 2)
		{
			UNLUA_LOGERROR(L, LogUnLua, Log, TEXT("%s: Invalid parameters!"), ANSI_TO_TCHAR(__FUNCTION__));
			return 0;
		}

		ViewType* View = Cast<ViewType>(UnLua::GetUObject(L, 1));
		const char* WidgetName = lua_tostring(L, 2);

		UWidget* Widget = View->GetWidgetFromName(WidgetName);

		UnLua::PushUObject(L, Widget);
		return 1;
	}
}
class MiscUtils {};
static const luaL_Reg MiscUtilsLib[] =
{
	{ "GetWidgetFromName",      UI_GetWidgetFromName<UUserWidget> },
	{ nullptr, nullptr }
};

EXPORT_UNTYPED_CLASS(MiscUtils, false, MiscUtilsLib)
IMPLEMENT_EXPORTED_CLASS(MiscUtils)

初始化流程

Lua_State

  • 依托于 IModuleInterface 的生命周期初始化

  • 依托于 引擎的事件回调 执行 env 清理

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
    class FUnLuaModule : public IModuleInterface
    {
    public:
        virtual void StartupModule() override
        {
        	FLuaContext::Create();
            GLuaCxt->RegisterDelegates();
        }
    
        virtual void ShutdownModule() override
        {
        }
    };
    

Lua脚本静态绑定

  • 监听 UObject的引擎事件

    • FUObjectArray::FUObjectCreateListener

    • FUObjectArray::FUObjectDeleteListener

  • 执行 Lua脚本的绑定

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    
    bool FLuaEnv::TryBind(UObject* Object)
    {
    	// ...
        UFunction* Func = Class->FindFunctionByName(FName("GetModuleName"));
        // ...
        FString ModuleName;
        UObject* CDO = bIsCDO ? Object : Class->GetDefaultObject();
        CDO->ProcessEvent(Func, &ModuleName);
        // ...
        
        return GetManager()->Bind(Object, *ModuleName, GLuaDynamicBinding.InitializerTableRef);
    }
    
    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
    50
    51
    52
    53
    54
    55
    56
    57
    
    bool UUnLuaManager::Bind(UObject *Object, const TCHAR *InModuleName, int32 InitializerTableRef)
    {
        // ...
        UnLua::FLuaRetValues RetValues = UnLua::Call(L, "require", TCHAR_TO_UTF8(InModuleName));    // require Lua module
        // ...
        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(Class, InModuleName, bMultipleLuaBind, Error);                             // bind!!!
        }
        
        if (bSuccess)
        {   
            FString RealModuleName = *ModuleNames.Find(Class);
    
            // create a Lua instance for this UObject
            Env->GetObjectRegistry()->Bind(Object, TCHAR_TO_UTF8(*RealModuleName));
    
            // try call user first user function handler
            int32 FunctionRef = PushFunction(L, Object, "Initialize");                  // push hard coded Lua function 'Initialize'
            if (FunctionRef != LUA_NOREF)
            {
                if (InitializerTableRef != LUA_NOREF)
                {
                    lua_rawgeti(L, LUA_REGISTRYINDEX, InitializerTableRef);             // push a initializer table if necessary
                }
                else
                {
                    lua_pushnil(L);
                }
                bool 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);
        }
    
        return bSuccess;
    }
    

Lua文件的加载是如何控制的

  • Unlua_2.2.2 - LuaEnv.cpp
1
2
3
4
5
6
7
8
9
10
    FLuaEnv::FLuaEnv()
    {
    	// ...
    	
        AddSearcher(LoadFromCustomLoader, 2);
        AddSearcher(LoadFromFileSystem, 3);
        AddSearcher(LoadFromBuiltinLibs, 4);
        
        // ...
}

NewObject的Lua扩展

  • LoadObject
  • LoadClasss
  • NewObject

https://github.com/Tencent/UnLua/blob/master/Plugins/UnLua/Source/UnLua/Private/UELib.cpp

本文由作者按照 CC BY 4.0 进行授权