反射是指程序在运行时可以访问、检测和修改它本身状态或行为的一种能力(应用在UE4中的话,就是根据类命获取类中的信息),但原生C++并没有提供反射系统,在UE4中则有专门的反射系统负责采集数据。该系统同时也负责垃圾回收。垃圾回收GC(GarbageCollector)简单地说,只要被垃圾回收系统追踪到的变量,该变量就无需我们人为的进行Delete,由垃圾回收系统进行删除。
类参与垃圾回收系统,需要指针特定的宏。
UCLASS([specifier,specifier...],[meta(key=value,key=value...)])
class ClassName : public ParentName
{
GENERATED_BODY()
public:
...
};
其中的UCLASS()便是用于GC进行回收的宏标志,代码中的GENERATED_BODY()是UHT(Unreal Head Tool)用于为类生成额外代码,从而让反射系统进行工作,收集此类中的信息。
同时在类.Cpp的头文件中,有以下一段头文件的定义。
#include "ClassName.generated.h"
.generated.h文件的类中包含所有从类为反射系统收集数据相关的代码,有了它才可以进行反射采集数据以及垃圾回收。需要注意的是,之后的头文件都要保证在该文件的上方进行定义。
2. 基本类型| 类型 | 描述 |
|---|---|
| bool | 大小:1byte 取值范围: true或者false |
| uint8 | 大小:1byte 取值范围: 0~255 |
| int32 | 大小: 4byte 取值范围: -2^32 ~ 2^32-1 |
| int64 | 大小: 8byte 取值范围: -2^64 ~ 2^64-1 |
| float | 大小: 4byte 取值范围: -2^32 ~ 2^32-1 |
| FName | 大小: 12byte |
| FString | 大小: 12byte |
| FText | 大小: 24byte |
| FVector | 三维向量 |
| FVector2D | 二维向量 |
| FRotator | 旋转向量 |
| FTransform | 包含了旋转向量比例缩放向量和位置向量 |
- FName:FName不区分大小写。他们为不可变,无法被操作,FName的存储系统和静态特性决定了通过键进行FName的查找和访问速度较快。FName子系统的另一个功能是使用散列表为FName转换提供快速字符串。
- FString:与FName和FText不同,FString可以搜索、修改并且与其他字符串比较。不过,这些操作会导致FString的开销比不可变字符串类更大。这是因为FString对象保存自己的字符数组,而FName和FText对象保存共享字符数组的指针,并且可以完全根据索引值建立相等性。
- FText:一般用作显示和本地文本化。
UPROPERTY([specifier, specifier, ...], [meta(key=value, key=value, ...)]) Type VariableName;
想要反射系统识别到你创建的属性变量,就必须要加上UPROPERTY()这个宏,通过宏来定义属性元数据和变量说明符,来与蓝图进行交流。
- specifier:变量说明符,用来控制属性与引擎和编辑器的相处方式。
常用属性说明符:
| 属性标签 | 效果 |
|---|---|
| VisibleAnywhere | 此属性在所有属性窗口可见,但无法被编辑 |
| BlueprintReadOnly | 此属性可由蓝图读取,但不能被修改。 |
| BlueprintReadWrite | 此属性可从蓝图读取或写入此属性。 |
| EditAnywhere | 此属性可通过属性窗口在原型和实例上进行编辑。 |
| EditDefaultsOnly | 此属性可通过属性窗口进行编辑,但只能在原型上进行。 |
| EditInstanceOnly | 此属性可通过属性窗口进行编辑,但只能在实例上进行,不能在原型上进行。 |
- meta:元数据说明符,同样用来控制其与引擎和编辑器各方面的相处方式。
关于元数据说明符的使用可以查阅官方文档,这里不予说明。metadata Specifiers | Unreal Engine documentation
函数FunctionUFUNCTION([specifier1=setting1, specifier2, ...], [meta(key1="value1", key2, ...)]) ReturnType FunctionName(...)
想要反射系统识别到你创建的函数,就必须要加上UFUNCTION()这个宏,通过宏来定义属性元数据和函数说明符,来与蓝图进行交流。
常用的函数说明符:
| 函数说明符 | 效果 |
|---|---|
| BlueprintCallable | 此函数可在蓝图或关卡蓝图图表中执行。 |
| BlueprintImplementableEvent | 此函数可在蓝图或关卡蓝图图表中实现。 |
| BlueprintNativeEvent | 此函数旨在被蓝图覆盖掉,但是也具有默认原生实现。用于声明名称与主函数相同的附加函数,但是末尾添加了_Implementation,是写入代码的位置。如果未找到任何蓝图覆盖,该自动生成的代码将调用 _Implementation 方法。 |
| BlueprintPure | 此函数在蓝图中的表示为纯函数 |
| Category | 此函数在蓝图中函数的分类 |
以下实现方法均是基于官方文档中给出的案例,只会给出其中实现的关键步骤,具体实现可以查阅官方文档Actor Communication | Unreal Engine documentation
类型转换通信实现效果:人物角色靠近正方体,正方体发送旋转。
实现思路:在角色外围添加一个用于碰撞检测的胶囊体,添加碰撞检测函数,判断发生碰撞的物体是不是目标正方体,是的话就进行类型转换,调用正方体内的旋转函数。
- 碰撞胶囊体的添加
UPROPERTY(BlueprintReadOnly) USphereComponent* SphereDetector;
// detectorSphere SphereDetector = CreateDefaultSubobject(TEXT("Detector")); SphereDetector->SetupAttachment(RootComponent); SphereDetector->SetSphereRadius(200.0f);
- 添加正方体Actor中的旋转函数,在Tick事件中检测
// Called every frame
void ARotatingActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
if(bCanRotate)
{
AddActorLocalRotation(FRotator(0.0f,10.0f,0.0f));
}
}
- 碰撞检测函数的添加
//在.h文件中重载以下函数 virtual void NotifyActorBeginOverlap(AActor* OtherActor) override; virtual void NotifyActorEndOverlap(AActor* OtherActor) override;
void ACommunicateCharactor::NotifyActorBeginOverlap(AActor* OtherActor)
{
Super::NotifyActorBeginOverlap(OtherActor);
if(ARotatingActor* CheckActor = Cast(OtherActor))
{
CheckActor->ToggleRotating(true);
}
}
void ACommunicateCharactor::NotifyActorEndOverlap(AActor* OtherActor)
{
Super::NotifyActorEndOverlap(OtherActor);
if(ARotatingActor* CheckActor = Cast(OtherActor))
{
CheckActor->ToggleRotating(false);
}
}
直接通信
实现效果:玩家按E控制灯的开关
实现思路:添加灯类Actor,在类中实现开关灯的函数,在角色类中添加灯类Actor的变量,在蓝图中赋值,随后添加按键E的函数。随后绑定按键E的事件函数。
- 添加灯类Actor,添加ToggleLightOn()
UCLASS()
class RIDERTEST_API AToggleLighting : public AActor
{
GENERATED_BODY()
...
void ToggleLightOn();
private:
UStaticMeshComponent* LightMesh;
UPointLightComponent* PointLight;
bool bTurnOn;
};
void AToggleLighting::ToggleLightOn()
{
if(this->bTurnOn)
{
PointLight->SetVisibility(false);
}
PointLight->SetVisibility(true);
}
- 角色类中添加灯类的变量,并可以在蓝图中为该变量赋值,同时添加按键E的事件函数,ToggleLightVisibility
UPROPERTY(EditInstanceOnly,BlueprintReadWrite,Category=ToggleLight,meta=(AllowPrivateAccess="true")) class AToggleLighting* LightActor; void ToggleLightVisibility();
void ACommunicateCharactor::ToggleLightVisibility()
{
if(LightActor)
{
LightActor->ToggleLightOn();
}
}
- 在蓝图中为该变量赋值
实现效果:玩家碰撞到图示碰撞盒子时,门自动开启
实现思路:使用UE4委托实现,绑定对应函数,在指定时机进行调用。
-
建立委托类,声明Delegate,实例化Delegate。声明函数HandleActorDieEvent(该函数便是使门开启委托需要绑定的函数)。重载函数NotifyActorBeginOverlap,用于触发HandleActorDieEvent。当有Actor与之重叠,调用绑定的函数。
DECLARE_DELEGATE(FOnDeleActorDieDelegate) UCLASS() class RIDERTEST_API ADelegateActor : public AActor { GENERATED_BODY() ... void HandleActorDieEvent(); UPROPERTY(EditInstanceOnly,BlueprintReadWrite) UBoxComponent* BoxComponent; virtual void NotifyActorBeginOverlap(AActor* OtherActor) override; FonDeleActorDieDelegate OnActorDie; };void ADelegateActor::HandleActorDieEvent() { OnActorDie.ExecuteIfBound(); } void ADelegateActor::NotifyActorBeginOverlap(AActor* OtherActor) { Super::NotifyActorBeginOverlap(OtherActor); HandleActorDieEvent(); }- 建立与代理相关的门的类,声明代理绑定的函数DelegateEventFunction。
UCLASS() class RIDERTEST_API ADoorActor : public AActor { GENERATED_BODY() ... UPROPERTY(EditInstanceOnly,BlueprintReadWrite) ADelegateActor* delegateActorRef; void DelegateEventFunction(); };- 在Beginplay事件中进行函数的绑定
void ADoorActor::BeginPlay() { Super::BeginPlay(); ... if(delegateActorRef) { delegateActorRef->OnActorDie.BindUObject(this,&ADoorActor::DelegateEventFunction); } }
实现效果:使用接口来实现灯泡的开关
实现思路:创建接口类,声明一个OnInteract纯虚函数,灯泡类继承接口类,实现该函数。角色类调用该函数。
- 创建接口类
UINTERFACE()
class UMyInterface : public UInterface
{
GENERATED_BODY()
};
class RIDERTEST_API IMyInterface
{
GENERATED_BODY()
public:
UFUNCTION()
virtual void OnInteract()=0; // 需要实现的接口
};
- 灯泡类继承接口,并实现OnInteract函数
class RIDERTEST_API AToggleLighting : public AActor,public IMyInterface
{
GENERATED_BODY()
public:
AToggleLighting();
virtual void OnInteract() override;
....
}
void AToggleLighting::OnInteract()
{
ToggleLightOn();
}
- 角色类调用该接口
void ACommunicateCharactor::NotifyActorEndOverlap(AActor* OtherActor)
{
if(IMyInterface* Interface = Cast(OtherActor))
{
Interface->OnInteract();
}
}
5. 常用类型转换
void AMyActor::TestConversion()
{
{
// std::string to FString
std::string str1 = "TestString";
FString MyStr(str1.c_str());
// FString to char*
char* c = TCHAR_TO_UTF8(*MyStr);
// FString to std::String
std::string str2(TCHAR_TO_UTF8(*MyStr));
}
{
//FString to int32
FString str1= TEXT("testString1");
int32 i = FCString::Atoi(*str1);
i = std::atoi(TCHAR_TO_UTF8((*str1)));
//FString to float
FString str2= TEXT("testString2");
float f = FCString::Atof(*str2);
f = std::atof(TCHAR_TO_UTF8(*str2));
//FString to bool
FString str3= TEXT("testString3");
bool b = str3.ToBool();
//int32 to FString
FString str4 = FString::FromInt(123);
//float to FString
FString str5 = FString::SanitizeFloat(30.0f);
//bool to FString
bool bNewBoolen = true;
FString str6 = bNewBoolen?TEXT("true"):TEXT("false");
}
{
//FString to TChar*
FString str1(TEXT("TestString"));
const TCHAR* ch1 = *str1;
//FString to FText
FText Text1 = FText::FromString(str1);
//FString to FName
FName Name1 = FName(*Text1.ToString());
//FName to FText
FText Text2 = FText::FromName(Name1);
}
}
6. 创建结构体、枚举
结构体
结构体和Class一样需要添加宏USTRUCT()来供反射系统和GC使用,需要注意的是UE4中的Struct和原生C++不同的是,不能在Struct中写函数方法。
USTRUCT(BlueprintType)
struct FPlayerStruct
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere,BlueprintReadWrite,Category="PlayerInfo")
int32 PlayerAge;
UPROPERTY(EditAnywhere,BlueprintReadWrite,Category="PlayerInfo")
FName PlayerName;
UPROPERTY(EditAnywhere,BlueprintReadWrite,Category="PlayerInfo")
EPlayerState curPlayerState = EPlayerState::Idle;
};
蓝图中调用
枚举类:
使用ENUM()宏进行包裹。
UENUM(BlueprintType)
enum class EPlayerState:uint8 //设置uint8类型
{
Walk,
Run,
Idle,
};
蓝图中调用
7. 命名规范| 前缀 | 描述 |
|---|---|
| T | 模板类 |
| U | 继承自UObject |
| A | 继承自AActor |
| S | 继承自SWiget |
| I | 接口类 |
| E | 枚举类 |
| F | 原生C++,没有任何继承 |



