393 lines
20 KiB
Markdown
393 lines
20 KiB
Markdown
|
---
|
|||
|
title: Ue4 c++ UProperty反射 PostEditChangeProperty
|
|||
|
date: 2022-12-09 13:40:28
|
|||
|
excerpt:
|
|||
|
tags: UObject
|
|||
|
rating: ⭐⭐⭐
|
|||
|
---
|
|||
|
## 反射系统
|
|||
|
https://ikrima.dev/ue4guide/engine-programming/uobject-reflection/uobject-reflection/
|
|||
|
|
|||
|
## Property类型判断
|
|||
|
- UStructProperty:结构体
|
|||
|
- UMapProperty:TMap
|
|||
|
- UArrayProperty:TArray
|
|||
|
属性判断是否是数组或是Map
|
|||
|
```c++
|
|||
|
FProperty* PropertyThatChanged = PropertyChangedEvent.Property;
|
|||
|
if ( PropertyThatChanged != nullptr )
|
|||
|
{
|
|||
|
if (PropertyThatChanged->IsA(FArrayProperty::StaticClass()))
|
|||
|
{
|
|||
|
FArrayProperty* ArrayProp = CastField<FArrayProperty>(PropertyThatChanged);
|
|||
|
FProperty* InnerProp = ArrayProp->Inner;
|
|||
|
if (InnerProp->IsA(FStructProperty::StaticClass()))
|
|||
|
{
|
|||
|
const FToonRampData* ToonRampData = ArrayProp->ContainerPtrToValuePtr<FToonRampData>(InnerProp, 0);
|
|||
|
if(ToonRampData)
|
|||
|
{
|
|||
|
}
|
|||
|
}
|
|||
|
}else if(PropertyThatChanged->IsA(FMapProperty::StaticClass()))
|
|||
|
{
|
|||
|
FArrayProperty* ArrayProp = CastField<FArrayProperty>(PropertyThatChanged);
|
|||
|
FProperty* InnerProp = ArrayProp->Inner;
|
|||
|
if (InnerProp->IsA(FStructProperty::StaticClass()))
|
|||
|
{
|
|||
|
const FToonRampData* ToonRampData = ArrayProp->ContainerPtrToValuePtr<FToonRampData>(InnerProp, 0);
|
|||
|
if(ToonRampData)
|
|||
|
{
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
```
|
|||
|
## 属性变化回调函数
|
|||
|
```c#
|
|||
|
virtual void EditorApplyTranslation(const FVector& DeltaTranslation, bool bAltDown, bool bShiftDown, bool bCtrlDown) override;
|
|||
|
virtual void EditorApplyRotation(const FRotator& DeltaRotation, bool bAltDown, bool bShiftDown, bool bCtrlDown) override;
|
|||
|
virtual void EditorApplyScale(const FVector& DeltaScale, const FVector* PivotLocation, bool bAltDown, bool bShiftDown, bool bCtrlDown) override;
|
|||
|
virtual void PostEditMove(bool bFinished) override;
|
|||
|
virtual void PostEditComponentMove(bool bFinished) override;
|
|||
|
```
|
|||
|
|
|||
|
# 没有Struct()却可以在蓝图找到类型问题笔记
|
|||
|
## Traits
|
|||
|
大概率是这个
|
|||
|
```c++
|
|||
|
template<>
|
|||
|
struct TStructOpsTypeTraits<FBox2D> : public TStructOpsTypeTraitsBase2<FBox2D>
|
|||
|
{
|
|||
|
enum
|
|||
|
{
|
|||
|
WithIdenticalViaEquality = true,
|
|||
|
WithNoInitConstructor = true,
|
|||
|
WithZeroConstructor = true,
|
|||
|
};
|
|||
|
};
|
|||
|
IMPLEMENT_STRUCT(Box2D);
|
|||
|
```
|
|||
|
## BlueprintCompilerCppBackendValueHelper.cpp
|
|||
|
```c++
|
|||
|
bool FEmitDefaultValueHelper::SpecialStructureConstructor(const UStruct* Struct, const uint8* ValuePtr, /*out*/ FString* OutResult)
|
|||
|
{
|
|||
|
...
|
|||
|
if (TBaseStructure<FBox2D>::Get() == Struct)
|
|||
|
{
|
|||
|
if (OutResult)
|
|||
|
{
|
|||
|
const FBox2D* Box2D = reinterpret_cast<const FBox2D*>(ValuePtr);
|
|||
|
*OutResult = FString::Printf(TEXT("CreateFBox2D(FVector2D(%s, %s), FVector2D(%s, %s), %s)")
|
|||
|
, *FEmitHelper::FloatToString(Box2D->Min.X)
|
|||
|
, *FEmitHelper::FloatToString(Box2D->Min.Y)
|
|||
|
, *FEmitHelper::FloatToString(Box2D->Max.X)
|
|||
|
, *FEmitHelper::FloatToString(Box2D->Max.Y)
|
|||
|
, Box2D->bIsValid ? TEXT("true") : TEXT("false"));
|
|||
|
}
|
|||
|
return true;
|
|||
|
}
|
|||
|
...
|
|||
|
}
|
|||
|
struct FStructAccessHelper_StaticData
|
|||
|
{
|
|||
|
TMap<const UScriptStruct*, FString> BaseStructureAccessorsMap;
|
|||
|
TMap<const UScriptStruct*, bool> SupportsDirectNativeAccessMap;
|
|||
|
TArray<FSoftClassPath> NoExportTypesWithDirectNativeFieldAccess;
|
|||
|
static FStructAccessHelper_StaticData& Get()
|
|||
|
{
|
|||
|
static FStructAccessHelper_StaticData StaticInstance;
|
|||
|
return StaticInstance;
|
|||
|
}
|
|||
|
private:
|
|||
|
FStructAccessHelper_StaticData()
|
|||
|
{
|
|||
|
// These are declared in Class.h; it's more efficient to access these native struct types at runtime using the specialized template functions, so we list them here.
|
|||
|
MAP_BASE_STRUCTURE_ACCESS(TBaseStructure<FRotator>::Get());
|
|||
|
MAP_BASE_STRUCTURE_ACCESS(TBaseStructure<FTransform>::Get());
|
|||
|
MAP_BASE_STRUCTURE_ACCESS(TBaseStructure<FLinearColor>::Get());
|
|||
|
MAP_BASE_STRUCTURE_ACCESS(TBaseStructure<FColor>::Get());
|
|||
|
MAP_BASE_STRUCTURE_ACCESS(TBaseStructure<FVector>::Get());
|
|||
|
MAP_BASE_STRUCTURE_ACCESS(TBaseStructure<FVector2D>::Get());
|
|||
|
MAP_BASE_STRUCTURE_ACCESS(TBaseStructure<FRandomStream>::Get());
|
|||
|
MAP_BASE_STRUCTURE_ACCESS(TBaseStructure<FGuid>::Get());
|
|||
|
MAP_BASE_STRUCTURE_ACCESS(TBaseStructure<FTransform>::Get());
|
|||
|
MAP_BASE_STRUCTURE_ACCESS(TBaseStructure<FBox2D>::Get());
|
|||
|
MAP_BASE_STRUCTURE_ACCESS(TBaseStructure<FFallbackStruct>::Get());
|
|||
|
MAP_BASE_STRUCTURE_ACCESS(TBaseStructure<FFloatRangeBound>::Get());
|
|||
|
MAP_BASE_STRUCTURE_ACCESS(TBaseStructure<FFloatRange>::Get());
|
|||
|
MAP_BASE_STRUCTURE_ACCESS(TBaseStructure<FInt32RangeBound>::Get());
|
|||
|
MAP_BASE_STRUCTURE_ACCESS(TBaseStructure<FInt32Range>::Get());
|
|||
|
MAP_BASE_STRUCTURE_ACCESS(TBaseStructure<FFloatInterval>::Get());
|
|||
|
MAP_BASE_STRUCTURE_ACCESS(TBaseStructure<FInt32Interval>::Get());
|
|||
|
MAP_BASE_STRUCTURE_ACCESS(TBaseStructure<FFrameNumber>::Get());
|
|||
|
MAP_BASE_STRUCTURE_ACCESS(TBaseStructure<FFrameTime>::Get());
|
|||
|
{
|
|||
|
// Cache the known set of noexport types that are known to be compatible with emitting native code to access fields directly.
|
|||
|
TArray<FString> Paths;
|
|||
|
GConfig->GetArray(TEXT("BlueprintNativizationSettings"), TEXT("NoExportTypesWithDirectNativeFieldAccess"), Paths, GEditorIni);
|
|||
|
for (FString& Path : Paths)
|
|||
|
{
|
|||
|
NoExportTypesWithDirectNativeFieldAccess.Add(FSoftClassPath(Path));
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
## 大钊的文章中有相似的代码
|
|||
|
https://zhuanlan.zhihu.com/p/26019216
|
|||
|
|
|||
|
Struct的收集
|
|||
|
对于Struct,我们先来看上篇里生成的代码:
|
|||
|
```c++
|
|||
|
static FCompiledInDeferStruct Z_CompiledInDeferStruct_UScriptStruct_FMyStruct(FMyStruct::StaticStruct, TEXT("/Script/Hello"), TEXT("MyStruct"), false, nullptr, nullptr); //延迟注册
|
|||
|
static struct FScriptStruct_Hello_StaticRegisterNativesFMyStruct
|
|||
|
{
|
|||
|
FScriptStruct_Hello_StaticRegisterNativesFMyStruct()
|
|||
|
{
|
|||
|
UScriptStruct::DeferCppStructOps(FName(TEXT("MyStruct")),new UScriptStruct::TCppStructOps<FMyStruct>);
|
|||
|
}
|
|||
|
} ScriptStruct_Hello_StaticRegisterNativesFMyStruct;
|
|||
|
```
|
|||
|
https://zhuanlan.zhihu.com/p/59553490
|
|||
|
|
|||
|
ICppStructOps的作用
|
|||
|
很多朋友在看源码的时候,可能会对UScriptStruct里定义的ICppStructOps类以及模板子类`TCppStructOps<CPPSTRUCT>`感到疑惑。其实它们是C++的一种常见的架构模式,用一个虚函数基类定义一些公共操作,再用一个具体模板子类来实现,从而既可以保存类型,又可以有公共操作接口。
|
|||
|
针对于UE4这里来说,ICppStructOps就定义了这个结构的一些公共操作。而探测这个C++结构的一些特性就交给了`TCppStructOps<CPPSTRUCT>`类里的`TStructOpsTypeTraits<CPPSTRUCT>`。一些C++结构的信息不能通过模板探测出来的,就需要我们手动标记提供了,所以具体的代码是:
|
|||
|
|
|||
|
```c++
|
|||
|
template <class CPPSTRUCT>
|
|||
|
struct TStructOpsTypeTraitsBase2
|
|||
|
{
|
|||
|
enum
|
|||
|
{
|
|||
|
WithZeroConstructor = false, // 0构造,内存清零后就可以了,说明这个结构的默认值就是0
|
|||
|
WithNoInitConstructor = false, // 有个ForceInit的参数的构造,用来专门构造出0值结构来
|
|||
|
WithNoDestructor = false, // 是否没有结构有自定义的析构函数, 如果没有析构的话,DestroyStruct里面就可以省略调用析构函数了。默认是有的。结构如果是pod类型,则肯定没有析构。
|
|||
|
WithCopy = !TIsPODType<CPPSTRUCT>::Value, // 是否结构有自定义的=赋值函数。如果没有的话,在CopyScriptStruct的时候就只需要拷贝内存就可以了
|
|||
|
WithIdenticalViaEquality = false, // 用==来比较结构
|
|||
|
WithIdentical = false, // 有一个自定义的Identical函数来专门用来比较,和WithIdenticalViaEquality互斥
|
|||
|
WithExportTextItem = false, // 有一个ExportTextItem函数来把结构值导出为字符串
|
|||
|
WithImportTextItem = false, // 有一个ImportTextItem函数把字符串导进结构值
|
|||
|
WithAddStructReferencedObjects = false, // 有一个AddStructReferencedObjects函数用来添加结构额外的引用对象
|
|||
|
WithSerializer = false, // 有一个Serialize函数用来序列化
|
|||
|
WithStructuredSerializer = false, // 有一个结构结构Serialize函数用来序列化
|
|||
|
WithPostSerialize = false, // 有一个PostSerialize回调用来在序列化后调用
|
|||
|
WithNetSerializer = false, // 有一个NetSerialize函数用来在网络复制中序列化
|
|||
|
WithNetDeltaSerializer = false, // 有一个NetDeltaSerialize函数用来在之前NetSerialize的基础上只序列化出差异来,一般用在TArray属性上进行优化
|
|||
|
WithSerializeFromMismatchedTag = false, // 有一个SerializeFromMismatchedTag函数用来处理属性tag未匹配到的属性值,一般是在结构进行升级后,但值还是原来的值,这个时候用来把旧值升级到新结构时使用
|
|||
|
WithStructuredSerializeFromMismatchedTag = false, // SerializeFromMismatchedTag的结构版本
|
|||
|
WithPostScriptConstruct = false,// 有一个PostScriptConstruct函数用在蓝图构造脚本后调用
|
|||
|
WithNetSharedSerialization = false, // 指明结构的NetSerialize函数不需要用到UPackageMap
|
|||
|
};
|
|||
|
};
|
|||
|
template<class CPPSTRUCT>
|
|||
|
struct TStructOpsTypeTraits : public TStructOpsTypeTraitsBase2<CPPSTRUCT>
|
|||
|
{
|
|||
|
};
|
|||
|
```
|
|||
|
|
|||
|
举个小例子,假如你看到编辑器里某个属性,想在C++里去修改它的值,结果发现它不是public的,甚至有可能连头文件都是private的,这个时候如果对类型系统结构理解不深的人可能就放弃了,但懂的人就知道可以通过这个对象遍历UProperty来查找到这个属性从而修改它。
|
|||
|
还有一个例子是如果你做了一个插件,调用了引擎编辑器本身的Details面板属性,但又想隐藏其中的一些字段,这个时候如果不修改引擎往往是难以办到的,但是如果知道了属性面板里的属性其实也都是一个个UProperty来的,这样你就可以通过对象路径获得这个属性,然后开启关闭它的某些Flags来达成效果。这也算是一种常规的Hack方式。
|
|||
|
|
|||
|
## 《InsideUE4》UObject(十三)类型系统-反射实战
|
|||
|
https://zhuanlan.zhihu.com/p/61042237
|
|||
|
|
|||
|
#### 获取类型对象
|
|||
|
如果想获取到程序里定义的所有的class,方便的方法是:
|
|||
|
```c++
|
|||
|
TArray<UObject*> result;
|
|||
|
GetObjectsOfClass(UClass::StaticClass(), result); //获取所有的class和interface
|
|||
|
GetObjectsOfClass(UEnum::StaticClass(), result); //获取所有的enum
|
|||
|
GetObjectsOfClass(UScriptStruct::StaticClass(), result); //获取所有的struct
|
|||
|
```
|
|||
|
GetObjectsOfClass是UE4已经写好的一个很方便的方法,可以获取到属于某个UClass*下面的所有对象。因此如果用UClass::StaticClass()本身,就可以获得程序里定义的所有class。值得注意的是,UE4里的接口是有一个配套的UInterface对象来存储元数据信息,它的类型也是用UClass*表示的,所以也会获得interface。根据前文,enum会生成UEnum,struct会生成UScriptStruct,所以把参数换成UEnum::StaticClass()就可以获得所有的UEnum*对象了,UScriptStruct::StaticClass()就是所有的UScriptStruct*了,最后就可以根据这些类型对象来反射获取类型信息了。
|
|||
|
而如果要精确的根据一个名字来查找某个类型对象,就可以用UE4里另一个方法:
|
|||
|
```c++
|
|||
|
template< class T >
|
|||
|
inline T* FindObject( UObject* Outer, const TCHAR* Name, bool ExactClass=false )
|
|||
|
{
|
|||
|
return (T*)StaticFindObject( T::StaticClass(), Outer, Name, ExactClass );
|
|||
|
}
|
|||
|
UClass* classObj=FindObject<UClass>(ANY_PACKAGE,"MyClass"); //获得表示MyClass的UClass*
|
|||
|
```
|
|||
|
#### 遍历字段
|
|||
|
在获取到了一个类型对象后,就可以用各种方式去遍历查找内部的字段了。为此,UE4提供了一个方便的迭代器`TFieldIterator<T>`,可以通过它筛选遍历字段。
|
|||
|
```c++
|
|||
|
const UStruct* structClass; //任何复合类型都可以
|
|||
|
//遍历属性
|
|||
|
for (TFieldIterator<UProperty> i(structClass); i; ++i)
|
|||
|
{
|
|||
|
UProperty* prop=*i;
|
|||
|
}
|
|||
|
//遍历函数
|
|||
|
for (TFieldIterator<UFunction> i(structClass); i; ++i)
|
|||
|
{
|
|||
|
UFunction* func=*i;
|
|||
|
//遍历函数的参数
|
|||
|
for (TFieldIterator<UProperty> i(func); i; ++i)
|
|||
|
{
|
|||
|
UProperty* param=*i;
|
|||
|
if( param->PropertyFlags & CPF_ReturnParm ) //这是返回值
|
|||
|
{
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
//遍历接口
|
|||
|
const UClass* classObj; //只有UClass才有接口
|
|||
|
for (const FImplementedInterface& ii : classObj->Interfaces)
|
|||
|
{
|
|||
|
UClass* interfaceClass = ii.Class;
|
|||
|
}
|
|||
|
//遍历枚举
|
|||
|
const UEnum* enumClass;
|
|||
|
for (int i = 0; i < enumClass->NumEnums(); ++i)
|
|||
|
{
|
|||
|
FName name = enumClass->GetNameByIndex(i);
|
|||
|
int value = enumClass->GetValueByIndex(i);
|
|||
|
}
|
|||
|
//遍历元数据
|
|||
|
#if WITH_METADATA
|
|||
|
const UObject* obj;//可以是任何对象,但一般是UField才有值
|
|||
|
UMetaData* metaData = obj->GetOutermost()->GetMetaData();
|
|||
|
TMap<FName, FString>* keyValues = metaData->GetMapForObject(obj);
|
|||
|
if (keyValues != nullptr&&keyValues->Num() > 0)
|
|||
|
{
|
|||
|
for (const auto& i : *keyValues)
|
|||
|
{
|
|||
|
FName key=i.Key;
|
|||
|
FString value=i.Value;
|
|||
|
}
|
|||
|
}
|
|||
|
#endif
|
|||
|
//查找属性
|
|||
|
UProperty* UStruct::FindPropertyByName(FName InName) const
|
|||
|
{
|
|||
|
for (UProperty* Property = PropertyLink; Property != NULL; Property = Property->PropertyLinkNext)
|
|||
|
{
|
|||
|
if (Property->GetFName() == InName)
|
|||
|
{
|
|||
|
return Property;
|
|||
|
}
|
|||
|
}
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
//查找函数
|
|||
|
UFunction* UClass::FindFunctionByName(FName InName, EIncludeSuperFlag::Type IncludeSuper) const;
|
|||
|
```
|
|||
|
|
|||
|
#### 查看继承
|
|||
|
```c++
|
|||
|
//得到类型对象后,也可以遍历查看它的继承关系。 遍历继承链条:
|
|||
|
const UStruct* structClass; //结构和类
|
|||
|
TArray<FString> classNames;
|
|||
|
classNames.Add(structClass->GetName());
|
|||
|
UStruct* superClass = structClass->GetSuperStruct();
|
|||
|
while (superClass)
|
|||
|
{
|
|||
|
classNames.Add(superClass->GetName());
|
|||
|
superClass = superClass->GetSuperStruct();
|
|||
|
}
|
|||
|
FString str= FString::Join(classNames, TEXT("->")); //会输出MyClass->UObject
|
|||
|
//那反过来,如果想获得一个类下面的所有子类,可以这样:
|
|||
|
const UClass* classObj; //结构和类
|
|||
|
TArray<UClass*> result;
|
|||
|
GetDerivedClasses(classObj, result, false);
|
|||
|
//函数原型是
|
|||
|
void GetDerivedClasses(UClass* ClassToLookFor, TArray<UClass *>& Results, bool bRecursive);
|
|||
|
//那么怎么获取实现了某个接口的所有子类呢?
|
|||
|
TArray<UObject*> result;
|
|||
|
GetObjectsOfClass(UClass::StaticClass(), result);
|
|||
|
TArray<UClass*> classes;
|
|||
|
for (UObject* obj : result)
|
|||
|
{
|
|||
|
UClass* classObj = Cast<UClass>(obj);
|
|||
|
if (classObj->ImplementsInterface(interfaceClass))//判断实现了某个接口
|
|||
|
{
|
|||
|
classes.Add(classObj);
|
|||
|
}
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
#### 获取设置属性值
|
|||
|
```c++
|
|||
|
template<typename ValueType>
|
|||
|
ValueType* UProperty::ContainerPtrToValuePtr(void* ContainerPtr, int32 ArrayIndex = 0) const
|
|||
|
{
|
|||
|
return (ValueType*)ContainerVoidPtrToValuePtrInternal(ContainerPtr, ArrayIndex);
|
|||
|
}
|
|||
|
template<typename ValueType>
|
|||
|
ValueType* UProperty::ContainerPtrToValuePtr(UObject* ContainerPtr, int32 ArrayIndex = 0) const
|
|||
|
{
|
|||
|
return (ValueType*)ContainerVoidPtrToValuePtrInternal(ContainerPtr, ArrayIndex);
|
|||
|
}
|
|||
|
void* UProperty::ContainerVoidPtrToValuePtrInternal(void* ContainerPtr, int32 ArrayIndex) const
|
|||
|
{
|
|||
|
//check...
|
|||
|
return (uint8*)ContainerPtr + Offset_Internal + ElementSize * ArrayIndex;
|
|||
|
}
|
|||
|
void* UProperty::ContainerUObjectPtrToValuePtrInternal(UObject* ContainerPtr, int32 ArrayIndex) const
|
|||
|
{
|
|||
|
//check...
|
|||
|
return (uint8*)ContainerPtr + Offset_Internal + ElementSize * ArrayIndex;
|
|||
|
}
|
|||
|
//获取对象或结构里的属性值地址,需要自己转换成具体类型
|
|||
|
void* propertyValuePtr = property->ContainerPtrToValuePtr<void*>(object);
|
|||
|
//包含对象引用的属性可以获得对象
|
|||
|
UObject* subObject = objectProperty->GetObjectPropertyValue_InContainer(object);
|
|||
|
//也因为获取到的是存放属性值的指针地址,所以其实也就可以*propertyValuePtr=xxx;方便的设置值了。当然如果是从字符串导入设置进去,UE4也提供了两个方法来导出导入:
|
|||
|
//导出值
|
|||
|
virtual void ExportTextItem( FString& ValueStr, const void* PropertyValue, const void* DefaultValue, UObject* Parent, int32 PortFlags, UObject* ExportRootScope = NULL ) const;
|
|||
|
//使用
|
|||
|
FString outPropertyValueString;
|
|||
|
property->ExportTextItem(outPropertyValueString, property->ContainerPtrToValuePtr<void*>(object), nullptr, (UObject*)object, PPF_None);
|
|||
|
//导入值
|
|||
|
const TCHAR* UProperty::ImportText( const TCHAR* Buffer, void* Data, int32 PortFlags, UObject* OwnerObject, FOutputDevice* ErrorText = (FOutputDevice*)GWarn ) const;
|
|||
|
//使用
|
|||
|
FString valueStr;
|
|||
|
prop->ImportText(*valueStr, prop->ContainerPtrToValuePtr<void*>(obj), PPF_None, obj);
|
|||
|
```
|
|||
|
|
|||
|
#### 反射调用函数
|
|||
|
```c++
|
|||
|
//方法原型
|
|||
|
int32 UMyClass::Func(float param1);
|
|||
|
UFUNCTION(BlueprintCallable)
|
|||
|
int32 InvokeFunction(UObject* obj, FName functionName,float param1)
|
|||
|
{
|
|||
|
struct MyClass_Func_Parms //定义一个结构用来包装参数和返回值,就像在gen.cpp里那样
|
|||
|
{
|
|||
|
float param1;
|
|||
|
int32 ReturnValue;
|
|||
|
};
|
|||
|
UFunction* func = obj->FindFunctionChecked(functionName);
|
|||
|
MyClass_Func_Parms params;
|
|||
|
params.param1=param1;
|
|||
|
obj->ProcessEvent(func, ¶ms);
|
|||
|
return params.ReturnValue;
|
|||
|
}
|
|||
|
//使用
|
|||
|
int r=InvokeFunction(obj,"Func",123.f);
|
|||
|
```
|
|||
|
ProcessEvent也是UE4里事先定义好的非常方便的函数,内部会自动的处理蓝图VM的问题。当然,更底层的方法也可以是:
|
|||
|
```c++
|
|||
|
//调用1
|
|||
|
obj->ProcessEvent(func, ¶ms);
|
|||
|
//调用2
|
|||
|
FFrame frame(nullptr, func, ¶ms, nullptr, func->Children);
|
|||
|
obj->CallFunction(frame, ¶ms + func->ReturnValueOffset, func);
|
|||
|
//调用3
|
|||
|
FFrame frame(nullptr, func, ¶ms, nullptr, func->Children);
|
|||
|
func->Invoke(obj, frame, ¶ms + func->ReturnValueOffset);
|
|||
|
```
|
|||
|
|
|||
|
#### 运行时修改类型
|
|||
|
让我们继续扩宽一下思路,之前已经详细讲解过了各大类型对象的构造过程,最后常常都是到UE4CodeGen_Private里的调用。既然我们已经知道了它运行的逻辑,那我们也可以仿照着来啊!我们也可以在常规的类型系统注册流程执行完之后,在游戏运行的半途过程中,动态的去修改类型甚至注册类型,因为说到底UE4编辑器也就是一个特殊点的游戏而已啊!这种方式有点类似C#的emit的方式,用代码去生成代码然后再编译。这些方式理论上都是可以通的,我来提供一些思路用法,有兴趣的朋友可以自己去实现下,代码贴出来就太长了。
|
|||
|
1. 修改UField的MetaData信息,其实可以改变字段在编辑器中的显示信息。MetaData里有哪些字段,可以在ObjectMacros.h中自己查看。
|
|||
|
2. 动态修改UField的相应的各种Flags数据,比如PropertyFlags,StructFlags,ClassFlags等,可以达成在编辑器里动态改变其显示行为的效果。
|
|||
|
3. 动态添加删除UEnum对象里面的Names字段,就可以动态给enum添加删除枚举项了。
|
|||
|
4. 动态地给结构或类添加反射属性字段,就可以在蓝图内创建具有不定字段的结构了。当然前提是在结构里预留好属性存放的内存,这样UProperty的Offset才有值可指向。这么做现在想来好像也不知道能用来干嘛。
|
|||
|
5. 同属性一样,其实参照对了流程,也可以动态的给蓝图里暴露函数。有时候这可以达成某种加密保护的奇效。
|
|||
|
6. 可以动态的注册新结构,动态的构造出来相应的UScriptStruct其实就可以了。
|
|||
|
7. 动态注册新类其实也是可以的,只不过UClass的构造稍微要麻烦点,不过也没麻烦到哪去,有需求了就自然能照着源码里的流程自己实现一个流程出来。
|
|||
|
8. 再甚至,其实某种程度上的用代码动态创建蓝图节点,填充蓝图VM指令其实也是可行的。只不过想了想好像一般用不着上这种大手术。
|