以源码版本slua 2.1.4
- LuaActor的构造函数是如何进入到LuaOverrider::ClassConstructor的
LuaOverrider监听UObject创建的事件
UObject创建后调用所有FUObjectArray中的NotifyUObjectCreated。
这个时机在UObjectBase构造函数中的AddObject中,此时最终的LuaActor的构造函数还没被调用,因此能Hook。
这就涉及到NewObject的构造原理了,UObjectBase的构造在第一次分配完内存后首先被调用。
[UE4 UObject] UObject 的创建流程 - 知乎
LuaOverrider如何知道哪个UObject是我需要被重载掉的?
tyyHook之前isHookable通过UClass判断实现了ULuaOverriderInterface接口的类就是要被Hook的对象。
bool LuaOverrider::isHookable(const UObjectBaseUtility* obj)
{
UClass* cls = obj->GetClass();
if (cls->IsChildOf<UPackage>() || cls->IsChildOf<UClass>())
{
return false;
}
static UClass* interfaceClass = ULuaOverriderInterface::StaticClass();
if (cls->ImplementsInterface(interfaceClass))
{
return true;
}
return false;
}
在hook的时候每次修改一个cls的constructor都保存在一个map中,这样之后可以用到。
在CustomClassConstructor中要调用cls实际的构造函数,这时hook时收集的constructor就有用了,从中取出真实的constructor然后调用。在一切都结束后最重要的绑定重载函数bindOverrideFuncs。
- LuaActor的BeginPlay等函数是如何被Lua脚本重载的
LuaActor的构造过程中(CustomClassConstructor),会将对应的Lua脚本Require进来并获取其中所有的函数,然后利用UClass在其中查找是否有同名的,如果有的话那就调用hookBpScript来进行hook。注意这里Lua只能Override蓝图Event方法。因此这里重载的ReceiveBeginPlay而不是BeginPlay。
(hookBpScript的原理大概是修改了原本BP函数的字节码。。。)
如果蓝图中已经实现了某些方法比如EventTick,Lua中的ReceiveTick就会覆盖掉实际的ReceiveTick函数,就需要通过self.Super:ReceiveTick调用到BP中的方法。逻辑上 self->C++类(ALuaActor),self.Super->BP类
关于Tick
在Actor的Tick函数中,似乎ReceiveTick必须是蓝图类才会调用,所以要想在Lua中执行Tick就必须先创建蓝图再绑定LuaFile,直接创建C++类的实例并不能在Lua中使用ReceiveTick。
当然直接在Actor的Tick、BeginPlay等方法中直接调用CallFunction来调用Lua中的函数也是可行的。