UE4C++基础(一)

12 篇文章 2 订阅

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包含了旋转向量比例缩放向量和位置向量
  • FNameFName不区分大小写。他们为不可变,无法被操作,FName的存储系统和静态特性决定了通过键进行FName的查找和访问速度较快。FName子系统的另一个功能是使用散列表为FName转换提供快速字符串。
  • FString:与FNameFText不同,FString可以搜索、修改并且与其他字符串比较。不过,这些操作会导致FString的开销比不可变字符串类更大。这是因为FString对象保存自己的字符数组,而FNameFText对象保存共享字符数组的指针,并且可以完全根据索引值建立相等性。
  • 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

类型转换通信

请添加图片描述

实现效果:人物角色靠近正方体,正方体发送旋转。

实现思路:在角色外围添加一个用于碰撞检测的胶囊体,添加碰撞检测函数,判断发生碰撞的物体是不是目标正方体,是的话就进行类型转换,调用正方体内的旋转函数。

  1. 碰撞胶囊体的添加
UPROPERTY(BlueprintReadOnly)
USphereComponent* SphereDetector;
// detectorSphere
SphereDetector = CreateDefaultSubobject<USphereComponent>(TEXT("Detector"));
SphereDetector->SetupAttachment(RootComponent);
SphereDetector->SetSphereRadius(200.0f);
  1. 添加正方体Actor中的旋转函数,在Tick事件中检测
// Called every frame
void ARotatingActor::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);
	if(bCanRotate)
	{
		AddActorLocalRotation(FRotator(0.0f,10.0f,0.0f));
	}
}
  1. 碰撞检测函数的添加
//在.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的事件函数。

  1. 添加灯类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);
}
  1. 角色类中添加灯类的变量,并可以在蓝图中为该变量赋值,同时添加按键E的事件函数,ToggleLightVisibility
UPROPERTY(EditInstanceOnly,BlueprintReadWrite,Category=ToggleLight,meta=(AllowPrivateAccess="true"))
class AToggleLighting* LightActor;
void ToggleLightVisibility();
void ACommunicateCharactor::ToggleLightVisibility()
{
	if(LightActor)
	{
		LightActor->ToggleLightOn();
	}
}
  1. 在蓝图中为该变量赋值
    请添加图片描述

委托通信

请添加图片描述
关于UE4的委托可以查看我的这篇博文UE4委托 CSDN UExplorer
实现效果:玩家碰撞到图示碰撞盒子时,门自动开启
实现思路:使用UE4委托实现,绑定对应函数,在指定时机进行调用。

  1. 建立委托类,声明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();
    }
    
    1. 建立与代理相关的门的类,声明代理绑定的函数DelegateEventFunction
    UCLASS()
    class RIDERTEST_API ADoorActor : public AActor
    {
    	GENERATED_BODY()
    ...
    	UPROPERTY(EditInstanceOnly,BlueprintReadWrite)
    	ADelegateActor* delegateActorRef;
    	
    	void DelegateEventFunction();
    };
    
    1. Beginplay事件中进行函数的绑定
    void ADoorActor::BeginPlay()
    {
       Super::BeginPlay();
    ...
       if(delegateActorRef)
       {
          delegateActorRef->OnActorDie.BindUObject(this,&ADoorActor::DelegateEventFunction);
       }
    }
    

接口通信

实现效果:使用接口来实现灯泡的开关

实现思路:创建接口类,声明一个OnInteract纯虚函数,灯泡类继承接口类,实现该函数。角色类调用该函数。

  1. 创建接口类
UINTERFACE()
class UMyInterface : public UInterface
{
   GENERATED_BODY()
};

class RIDERTEST_API IMyInterface
{
   GENERATED_BODY()
public:
   UFUNCTION()
   virtual void OnInteract()=0;  // 需要实现的接口
};
  1. 灯泡类继承接口,并实现OnInteract函数
class RIDERTEST_API AToggleLighting : public AActor,public IMyInterface
{
	GENERATED_BODY()
	
public:	
	AToggleLighting();
	virtual void OnInteract() override;
	....
}
void AToggleLighting::OnInteract()
{
	ToggleLightOn();
}
  1. 角色类调用该接口
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博客

码字不易,如果觉得对你有帮助的话,点个免费的赞支持下博主把 😆

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值