• 运行时创建UField[叁]


    此一回主要研究虚幻的反射类。
    虚幻的反射类跟C++的静态编译类不同,可以在Runtime增删改,其概念更接近LUA的table。

    UClass实质功能就是描述一段内存的内容,使用FProperty描述成员变量,UFunction描述反射函数。
    FProperty描述了成员变量在Container(即所属的类UClass)偏移量和大小,使用链表记录所有成员变量。
    成员变量的FProperty需要注意POD(简单理解,不涉及内部内存分配和虚函数的结构体/类,int32/FVector等),
    对于POD比较简单,变量直接复制到对应地址即可,但是对于非POD必须调用专用赋值函数,FProperty::SetPropertyValue_InContainer(),
    该函数同时适用POD和非POD,尽量避免直接赋值操作。

    UFunction描述了蓝图函数,UFunction内部使用FProperty记录函数参数以及返回值,使用TMap记录。
    UClass的UFunction列表包含了蓝图文件中定义的函数和C++编写的反射函数,对于UFunction的调用一般是通过UObject::ProcessEvent()函数触发的。
    其中蓝图编写的函数通过虚幻脚本虚拟机执行;C++编写的反射函数间接调用一个FNativeFuncPtr类型的全局函数最终调用C++函数本体,
    具体是在给UFUnction加标记FUNC_Native,同时设置一个全局函数:NewFunc->SetNativeFunc(CallLuaFunction)。

    // Fill out your copyright notice in the Description page of Project Settings.
    
    
    #include "LuaGeneratedClass.h"
    #include "lua/lua.hpp"
    #include "FastLuaHelper.h"
    #include "LuaFunction.h"
    #include "LuaObjectWrapper.h"
    
    static int32 FillPropertyIntoField_Class(UField* InOutField, TMap<FString, int32>& InKV)
    {
    	int32 PropCount = 0;
    	int32 PropOffset = 0;
    	EObjectFlags ObjectFlags = EObjectFlags::RF_Standalone;
    	EPropertyFlags PropertyFlags = EPropertyFlags::CPF_None;
    	bool bIsPOD = true;
    	for (const TPair<FString, int32>& It : InKV)
    	{
    		FProperty* NewProp = nullptr;
    		UE4CodeGen_Private::EPropertyGenFlags PropertyType = (UE4CodeGen_Private::EPropertyGenFlags)(It.Value);
    		switch (PropertyType)
    		{
    		case UE4CodeGen_Private::EPropertyGenFlags::Int8:
    			NewProp = new FInt8Property(InOutField, FName(*It.Key), ObjectFlags, PropOffset, PropertyFlags);
    			NewProp->SetPropertyFlags(EPropertyFlags::CPF_IsPlainOldData);
    			break;
    
    		case UE4CodeGen_Private::EPropertyGenFlags::Byte:
    			NewProp = new FByteProperty(InOutField, FName(*It.Key), ObjectFlags, PropOffset, PropertyFlags, nullptr);
    			NewProp->SetPropertyFlags(EPropertyFlags::CPF_IsPlainOldData);
    			break;
    
    		case UE4CodeGen_Private::EPropertyGenFlags::Int16:
    			NewProp = new FInt16Property(InOutField, FName(*It.Key), ObjectFlags, PropOffset, PropertyFlags);
    			NewProp->SetPropertyFlags(EPropertyFlags::CPF_IsPlainOldData);
    			break;
    
    		case UE4CodeGen_Private::EPropertyGenFlags::UInt16:
    			NewProp = new FUInt16Property(InOutField, FName(*It.Key), ObjectFlags, PropOffset, PropertyFlags);
    			NewProp->SetPropertyFlags(EPropertyFlags::CPF_IsPlainOldData);
    			break;
    
    		case UE4CodeGen_Private::EPropertyGenFlags::Int:
    			NewProp = new FIntProperty(InOutField, FName(*It.Key), ObjectFlags, PropOffset, PropertyFlags);
    			NewProp->SetPropertyFlags(EPropertyFlags::CPF_IsPlainOldData);
    			break;
    
    		case UE4CodeGen_Private::EPropertyGenFlags::UInt32:
    			NewProp = new FUInt32Property(InOutField, FName(*It.Key), ObjectFlags, PropOffset, PropertyFlags);
    			NewProp->SetPropertyFlags(EPropertyFlags::CPF_IsPlainOldData);
    			break;
    
    		case UE4CodeGen_Private::EPropertyGenFlags::Int64:
    			NewProp = new FInt64Property(InOutField, FName(*It.Key), ObjectFlags, PropOffset, PropertyFlags);
    			NewProp->SetPropertyFlags(EPropertyFlags::CPF_IsPlainOldData);
    			break;
    
    		case UE4CodeGen_Private::EPropertyGenFlags::UInt64:
    			NewProp = new FUInt64Property(InOutField, FName(*It.Key), ObjectFlags, PropOffset, PropertyFlags);
    			NewProp->SetPropertyFlags(EPropertyFlags::CPF_IsPlainOldData);
    			break;
    
    		case UE4CodeGen_Private::EPropertyGenFlags::Float:
    			NewProp = new FFloatProperty(InOutField, FName(*It.Key), ObjectFlags, PropOffset, PropertyFlags);
    			NewProp->SetPropertyFlags(EPropertyFlags::CPF_IsPlainOldData);
    			break;
    
    		case UE4CodeGen_Private::EPropertyGenFlags::Str:
    			NewProp = new FStrProperty(InOutField, FName(*It.Key), ObjectFlags, PropOffset, PropertyFlags);
    			bIsPOD = false;
    			break;
    
    		case UE4CodeGen_Private::EPropertyGenFlags::Array:
    			NewProp = new FArrayProperty(InOutField, FName(*It.Key), ObjectFlags, PropOffset, PropertyFlags, EArrayPropertyFlags::None);
    			bIsPOD = false;
    			break;
    
    		case UE4CodeGen_Private::EPropertyGenFlags::Map:
    			NewProp = new FMapProperty(InOutField, FName(*It.Key), ObjectFlags, PropOffset, PropertyFlags, EMapPropertyFlags::None);
    			bIsPOD = false;
    			break;
    		}
    		++PropCount;
    		PropOffset += NewProp->GetSize();
    	}
    
    	return PropCount;
    }
    
    static void CallLuaFunction(UObject* Context, FFrame& TheStack, RESULT_DECL)
    {
    	UClass* TmpClass = Context->GetClass();
    	if (ULuaGeneratedClass* LuaClass = Cast<ULuaGeneratedClass>(TmpClass))
    	{
    		lua_State* LuaState = LuaClass->GetLuaState();
    		int32 LuaTableIndex = LuaClass->GetLuaTableIndex();
    		FString FuncName = TheStack.Node->GetName();
    		lua_rawgeti(LuaState, LUA_REGISTRYINDEX, LuaTableIndex);
    		int32 LuaType = lua_type(LuaState, -1);
    		lua_getfield(LuaState, -1, TCHAR_TO_UTF8(*FuncName));
    		if (lua_isfunction(LuaState, -1))
    		{
    			int32 ParamsNum = 0;
    			FProperty* ReturnParam = nullptr;
    			//store param from UE script VM stack
    			FStructOnScope FuncTmpMem(TheStack.Node);
    			//push self
    			FLuaObjectWrapper::PushObject(LuaState, Context);
    			++ParamsNum;
    
    			for (TFieldIterator<FProperty> It(TheStack.Node); It; ++It)
    			{
    				//get function return Param
    				FProperty* CurrentParam = *It;
    				void* LocalValue = CurrentParam->ContainerPtrToValuePtr<void>(FuncTmpMem.GetStructMemory());
    				TheStack.StepCompiledIn<FProperty>(LocalValue);
    				if (CurrentParam->HasAnyPropertyFlags(CPF_ReturnParm))
    				{
    					ReturnParam = CurrentParam;
    				}
    				else
    				{
    					//set params for lua function
    					FastLuaHelper::PushProperty(LuaState, CurrentParam, FuncTmpMem.GetStructMemory(), 0);
    					++ParamsNum;
    				}
    			}
    
    			//call lua function
    			int32 CallRet = lua_pcall(LuaState, ParamsNum, ReturnParam ? 1 : 0, 0);
    			if (CallRet)
    			{
    				UE_LOG(LogTemp, Warning, TEXT("%s"), UTF8_TO_TCHAR(lua_tostring(LuaState, -1)));
    			}
    
    			if (ReturnParam)
    			{
    				//get function return Value, in common
    				FastLuaHelper::FetchProperty(LuaState, ReturnParam, FuncTmpMem.GetStructMemory(), -1);
    			}
    		}
    	}
    }
    
    int32 ULuaGeneratedClass::GenerateClass(struct lua_State* InL)
    {
    	FString InClassName = FString(UTF8_TO_TCHAR(lua_tostring(InL, 1)));
    	FString SuperClassName = FString(UTF8_TO_TCHAR(lua_tostring(InL, 2)));
    	UClass* SuperClass = FindObject<UClass>(ANY_PACKAGE, *SuperClassName);
    	if (!SuperClass)
    	{
    		SuperClass = UObject::StaticClass();
    	}
    
    	ULuaGeneratedClass* NewClass = FindObject<ULuaGeneratedClass>(ANY_PACKAGE, *InClassName);
    	if (NewClass)
    	{
    		NewClass->RemoveFromRoot();
    		NewClass->MarkPendingKill();
    		NewClass = nullptr;
    	}
    
    	NewClass = NewObject<ULuaGeneratedClass>(GetTransientPackage(), *InClassName, RF_Public | RF_Standalone | RF_Transient);
    	NewClass->SetSuperStruct(SuperClass);
    	NewClass->ClassFlags |= CLASS_Native;
    	NewClass->AddToRoot();
    
    	TMap<FString, int32> TmpKV;
    	if (lua_istable(InL, 3))
    	{
    		{
    			lua_pushvalue(InL, 3);
    			NewClass->RegistryIndex = luaL_ref(InL, LUA_REGISTRYINDEX);
    			NewClass->LuaState = InL;
    		}
    
    		lua_pushnil(InL);
    		while (lua_next(InL, 3))
    		{
    			int32 KeyType = lua_type(InL, -2);
    			int32 ValueType = lua_type(InL, -1);
    
    			if (KeyType == LUA_TSTRING && ValueType == LUA_TFUNCTION)
    			{
    				FString TmpFunctionName = UTF8_TO_TCHAR(lua_tostring(InL, -2));
    				UFunction* NewFunc = nullptr;
    				UFunction* SuperFunction = SuperClass->FindFunctionByName(*TmpFunctionName);
    				if (SuperFunction)
    				{
    					NewFunc = Cast<UFunction>(StaticDuplicateObject(SuperFunction, NewClass, *TmpFunctionName));
    					if (NewFunc)
    					{
    						NewFunc->ParmsSize = SuperFunction->ParmsSize;
    						NewFunc->PropertiesSize = SuperFunction->PropertiesSize;
    						NewFunc->SetNativeFunc(CallLuaFunction);
    						NewFunc->FunctionFlags |= FUNC_Native;
    						NewClass->AddFunctionToFunctionMap(NewFunc, *TmpFunctionName);
    					}
    				}
    
    			}
    			lua_pop(InL, 1);
    		}
    
    		lua_getfield(InL, 3, "VariableList");
    		if (lua_istable(InL, -1))
    		{
    			lua_pushnil(InL);
    			while (lua_next(InL, -2))
    			{
    				TPair<FString, int32> TmpNew;
    				TmpNew.Key = FString(UTF8_TO_TCHAR(lua_tostring(InL, -2)));
    				TmpNew.Value = lua_tointeger(InL, -1);
    				TmpKV.Add(TmpNew);
    
    				lua_pop(InL, 1);
    			}
    		}
    		lua_pop(InL, 1);
    		FillPropertyIntoField_Class(NewClass, TmpKV);	
    	}
    
    
    	NewClass->Bind();
    	NewClass->StaticLink(true);
    	NewClass->AssembleReferenceTokenStream();
    	// Ensure the CDO exists
    	NewClass->GetDefaultObject();
    
    	FLuaObjectWrapper::PushObject(InL, NewClass);
    	return 1;
    }
    
    bool ULuaGeneratedClass::IsFunctionImplementedInScript(FName InFunctionName) const
    {
    	if (RegistryIndex > 0)
    	{
    		lua_rawgeti(LuaState, LUA_REGISTRYINDEX, RegistryIndex);
    		lua_getfield(LuaState, -1, TCHAR_TO_UTF8(*InFunctionName.ToString()));
    		return lua_isfunction(LuaState, -1);
    	}
    	return Super::IsFunctionImplementedInScript(InFunctionName);
    }
    
    

    在LUA中使用

    --不要在MyActor表内部记录SuperClass名字
    --方便修改父类
    local MyActor = MyActor or {}
    
    MyActor.VariableList = 
    {
    	['MyID'] = UnrealPropertyType.Int,
    	['MyName'] = UnrealPropertyType.Str,
    }
    
    function MyActor:ReceiveBeginPlay()
    	self.MyName = 'MyGame'
    	print('ReceiveBeginPlay')
    
    	local TmpMesh = Unreal.LuaLoadObject(self, '/Game/ParagonShinbi/Characters/Heroes/Shinbi/Meshes/Shinbi.Shinbi')
    	self.Mesh:SetSkeletalMesh(TmpMesh)--可以看见场景中Actor的模型变了
    	local AnimBP = Unreal.LuaLoadObject(self, '/Game/ParagonShinbi/Characters/Heroes/Shinbi/Shinbi_AnimBlueprint.Shinbi_AnimBlueprint')
            self.Mesh:SetAnimClass(AnimBP)
    end
    
    function MyActor:ReceiveEndPlay(InReason)
    	print('ReceiveEndPlay:' .. tostring(InReason))
    	print(self.MyName)
    end
    
    function MyActor:ReceiveActorBeginOverlap(InActor)
    	print(tostring(InActor))
    end
    
    function DelayInit()
        
    	local PlayerCtrl = GameplayStatics:GetPlayerController(G_GameInstance, 0)
    
    	G_LocalPlayer = GenericGameAPI:GetLocalPlayer(PlayerCtrl)
    
    	local WorldName = GameplayStatics:GetCurrentLevelName(G_GameInstance, true)
            --新建一个反射类MyActor,继承自ACharacter
    	local MyCls = Unreal.LuaGenerateClass('MyActor', 'Character', MyActor)
    
    	local TmpVec = {X = 5250, Y = 8740, Z = 170}	
    	local TmpTrans = Unreal.LuaNewStruct('Transform')
    	TmpTrans.Translation = Unreal.LuaNewStruct('Vector', TmpVec)
    	local ActorInst = GameplayStatics:BeginSpawningActorFromClass(G_GameInstance, MyCls, TmpTrans)
    	GameplayStatics:FinishSpawningActor(ActorInst, TmpTrans)
    
    end
    
    

    续:
    虚幻引擎内部仅存在一个脚本虚拟机实例,而LUA中可能需要同时存在多个虚拟机,
    执行创建反射类的代码可能会被多个LUA虚拟机反复执行,导致虚幻脚本虚拟机中只会保留一个反射类,
    并且关联到最后一次执行创建到LUA虚拟机,我想这大概是UnLua插件暂不支持多状态的一个重要原因。
    在最终游戏客户端中一个LUA虚拟机也许够用,但在开发阶段以及服务器端非常依赖多个LUA虚拟机。
    临时方案:
    如果能保持创建反射类的LUA代码跟其他逻辑代码分离,那那么可以专门使用一个静态成员LUA状态机处理反射枚举/结构体/类的创建,
    不过这个应该很难做到。

  • 相关阅读:
    使用contentProvider
    创建Sqlite数据库(一)
    AIDL实现进程间通信
    Messenger实现进程间通信(IPC)
    Serializable使用
    Parcelable使用(二)
    STAR法则
    Python系列-------基本语法
    前端随心记---------面试题集
    前端随心记---------惟客科技面试
  • 原文地址:https://www.cnblogs.com/rpg3d/p/12627460.html
Copyright © 2020-2023  润新知