UE4 C++ 基础
1. 反射与垃圾回收
反射是指程序在运行时可以访问、检测和修改它本身状态或行为的一种能力(应用在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
:一般用作显示和本地文本化。
3. 属性和函数
属性Property
UPROPERTY([specifier, specifier, ...], [meta(key=value, key=value, ...)])
Type VariableName;
想要反射系统识别到你创建的属性变量,就必须要加上UPROPERTY()
这个宏,通过宏来定义属性元数据和变量说明符,来与蓝图进行交流。
specifier
:变量说明符,用来控制属性与引擎和编辑器的相处方式。
常用属性说明符:
属性标签 | 效果 |
---|---|
VisibleAnywhere | 此属性在所有属性窗口可见,但无法被编辑 |
BlueprintReadOnly | 此属性可由蓝图读取,但不能被修改。 |
BlueprintReadWrite | 此属性可从蓝图读取或写入此属性。 |
EditAnywhere | 此属性可通过属性窗口在原型和实例上进行编辑。 |
EditDefaultsOnly | 此属性可通过属性窗口进行编辑,但只能在原型上进行。 |
EditInstanceOnly | 此属性可通过属性窗口进行编辑,但只能在实例上进行,不能在原型上进行。 |
meta
:元数据说明符,同样用来控制其与引擎和编辑器各方面的相处方式。
关于元数据说明符的使用可以查阅官方文档,这里不予说明。Metadata Specifiers | Unreal Engine Documentation
函数Function
UFUNCTION([specifier1=setting1, specifier2, ...], [meta(key1="value1", key2, ...)])
ReturnType FunctionName(...)
想要反射系统识别到你创建的函数,就必须要加上UFUNCTION()
这个宏,通过宏来定义属性元数据和函数说明符,来与蓝图进行交流。
常用的函数说明符:
函数说明符 | 效果 |
---|---|
BlueprintCallable | 此函数可在蓝图或关卡蓝图图表中执行。 |
BlueprintImplementableEvent | 此函数可在蓝图或关卡蓝图图表中实现。 |
BlueprintNativeEvent | 此函数旨在被蓝图覆盖掉,但是也具有默认原生实现。用于声明名称与主函数相同的附加函数,但是末尾添加了_Implementation ,是写入代码的位置。如果未找到任何蓝图覆盖,该自动生成的代码将调用 _Implementation 方法。 |
BlueprintPure | 此函数在蓝图中的表示为纯函数 |
Category | 此函数在蓝图中函数的分类 |
4. 通信方法
以下实现方法均是基于官方文档中给出的案例,只会给出其中实现的关键步骤,具体实现可以查阅官方文档Actor Communication | Unreal Engine Documentation
类型转换通信
实现效果:人物角色靠近正方体,正方体发送旋转。
实现思路:在角色外围添加一个用于碰撞检测的胶囊体,添加碰撞检测函数,判断发生碰撞的物体是不是目标正方体,是的话就进行类型转换,调用正方体内的旋转函数。
- 碰撞胶囊体的添加
UPROPERTY(BlueprintReadOnly)
USphereComponent* SphereDetector;
// detectorSphere
SphereDetector = CreateDefaultSubobject<USphereComponent>(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<ARotatingActor>(OtherActor))
{
CheckActor->ToggleRotating(true);
}
}
void ACommunicateCharactor::NotifyActorEndOverlap(AActor* OtherActor)
{
Super::NotifyActorEndOverlap(OtherActor);
if(ARotatingActor* CheckActor = Cast<ARotatingActor>(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的委托可以查看我的这篇博文UE4委托 CSDN UExplorer
实现效果:玩家碰撞到图示碰撞盒子时,门自动开启
实现思路:使用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<IMyInterface>(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++,没有任何继承 |
(4条消息) UE4C++基础(二)_WLSTLA-CSDN博客