赞
踩
本文聚焦如何在不改变指定类源码的前提下,利用UE反射机制为该类添加UPROPERTY和UFUNCTION。
首先要说明的是,这个指定的类,必须是UCLASS。因为只有UClass才支持反射。
我们都知道,蓝图编辑器可以添加变量也可以添加函数。每次编译,蓝图编辑器都会生成一个class,这就给我们带来突破口,去看蓝图编辑器代码不就成了?!
于是,翻看代码找到了两个有用的接口,它们是UClass::AddCppProperty(FProperty*)和UClass::AddFunctionToFunctionMap(UFunction*, FName)。有了这两个接口,我们就可以实现添加UPROPERTY和UFUNCTION了。
以CameraComponent为例,我们为它添加一个布尔值变量,并让蓝图可以访问。首先,我们要创建一个布尔类型的FProperty,即FBoolProperty。
- UClass* SuperClass = UCameraComponent::StaticClass();
- UClass* ClassToAddProperty = SuperClass;
-
- FBoolProperty* NewProperty = new FBoolProperty(
- ClassToAddProperty,
- TEXT("ShowDebug"),
- RF_Public
- );
新建出来变量后,我们可以像平时写UPROPERTY说明符那样对其进行属性设置,配置该变量的属性。
NewProperty->SetPropertyFlags(CPF_Edit | CPF_BlueprintVisible);
这里设置的是EditAnywhere、BlueprintReadWrite。更多属性Flag,可以阅读头文件ObjectMacros.h内的EPropertyFlags枚举。
最后,在添加到类之前,还需要调用FProperty的Link函数,对FProperty完成初始化。这一步非常重要,跳过会导致该变量不可用,且会导致UE崩溃。
- FArchive ArDummy;
- NewProperty->Link(ArDummy);
-
- ClassToAddProperty->AddCppProperty(NewProperty);
至此,我们已经成功添加了一个UPROPERTY变量到CameraComponent内。
Actor细节面板:
蓝图节点:
添加UFUNCTION稍微复杂一点点,本节将实现添加新的UFUNCTION,名为GetCameraName,并且这个函数可以获取到Camera Component的owner Actor的名称。
首先,UFunction对象是一个UObject,因此使用NewObject来创建对象。
新建完UFunction对象后,我们对其Flag进行设置,这里设置的是蓝图可调用、公开函数和原生函数等Flag。更多的flag请参考EFunctionFlags的定义。
设置完函数的flag后,再设置UFunction对象对应的原生函数,此函数必须是静态的,且需要使用DECLARE_FUNCTION宏进行声明。紧接着我们需要对UFunction进行初始化,调用StaticLink、手动接上UClass内的函数链表。
最后,调用AddFunctionToFunctionMap。
- UClass* ClassToAddFunction = SuperClass;
- const FName FunctionName = "GetCameraName";
-
- UFunction* NewFunc = NewObject<UFunction>(ClassToAddProperty, FunctionName, RF_Public);
- NewFunc->FunctionFlags |= FUNC_BlueprintCallable | FUNC_Public | FUNC_Native;
- NewFunc->SetNativeFunc(&UMyCameraFunctionLibrary::execGetCameraName);
- NewFunc->StaticLink(true);
-
- NewFunc->Next = ClassToAddProperty->Children;
- ClassToAddProperty->Children = NewFunc;
-
- ClassToAddProperty->AddFunctionToFunctionMap(NewFunc, FunctionName);
关于原生函数的内部实现,这里不多介绍,可以参考别的大佬所写文章CustomThunk。
- // 头文件
-
- #pragma once
-
- #include "CoreMinimal.h"
- #include "Kismet/BlueprintFunctionLibrary.h"
- #include "MyCameraFunctionLibrary.generated.h"
-
- UCLASS()
- class VI_PROJECT_API UMyCameraFunctionLibrary : public UBlueprintFunctionLibrary
- {
- GENERATED_BODY()
- public:
- DECLARE_FUNCTION(execGetCameraName);
- };
-
- // CPP
-
- #include "MyCameraFunctionLibrary.h"
- #include "Camera/CameraComponent.h"
-
- DEFINE_FUNCTION(UMyCameraFunctionLibrary::execGetCameraName)
- {
- P_FINISH;
-
- P_NATIVE_BEGIN;
- if (const UCameraComponent* CameraComponent = Cast<UCameraComponent>(Context))
- {
- if (const AActor* Owner = CameraComponent->GetOwner())
- {
- const FString ObjName = Owner->GetName();
- GEngine->AddOnScreenDebugMessage(-1, 5, FColor::Cyan, ObjName);
- }
- }
- P_NATIVE_END;
- }
至此,我们已经完成了反射函数的添加,以下是成果展示:
运行后能看到屏幕输出了CameraComponent所属Actor的名称:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。