UEC++ 创建自定义类型案例

虚幻引擎UFUNCTION宏参数详解与最佳实践 在游戏开发中,反射系统是实现代码与可视化脚本交互的关键技术。UFUNCTION宏作为虚幻引擎的核心反射机制,通过元数据标记将C++函数暴露给蓝图系统。其底层原理涉及函数签名分析、元数据处理和运行时绑定等复杂过程。合理使用UFUNCTION参数能显著提升开发效率,特别是在处理网络同步、多线程安全和编辑器集成等场景时。本文以BlueprintCallable和BlueprintPure等高频参数为例,解析如何避免常见陷阱并优化性能。针对动画系统和网络游戏等特定需求,还介绍了Server/Client标记和Blu 阅读详情

目录

一,子弹类型

二,带有增强输入的角色类型

三,Interface 接口

四,TimerHandle 定时器

五,3D Widget

六,Timeline

七,射线检查

八,软引用

九,共享指针

十,蓝图函数库

十一,文件

文件路径操作

文件I/O操作

十二,Delay节点的C++实现

十三,JSON

十四,Runnable

十五,编码

十六,时间戳


一,子弹类型

// MyActor.h文件
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Components/SphereComponent.h"
#include "GameFrameWork/ProjectileMovementComponent.h"
#include "MyActor.generated.h"

// ...
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TObjectPtr<UStaticMeshComponent> Sphere;

UPROPERTY(EditAnywhere, BlueprintReadWrite)
TObjectPtr<USphereComponent> SphereCollision;

UPROPERTY(EditAnywhere, BlueprintReadWrite)
TObjectPtr<UProjectileMovementComponent> ProjectileMovement;
// MyActor.cpp文件
AMyActor::AMyActor() {
	PrimaryActorTick.bCanEverTick = true;

	Sphere = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Sphere"));
	SphereCollision = CreateDefaultSubobject<USphereComponent>(TEXT("SphereCollision"));
	ProjectileMovement = CreateDefaultSubobject<UProjectileMovementComponent>(TEXT("ProjectileMovement"));
	static ConstructorHelpers::FObjectFinder<UStaticMesh> StaticSphereMesh(TEXT("/Engine/BasicShapes/Sphere.Sphere"));

	RootComponent = Sphere;
	SphereCollision->SetupAttachment(RootComponent);
	ProjectileMovement->SetUpdatedComponent(RootComponent);
	Sphere->SetStaticMesh(StaticSphereMesh.Object);
	ProjectileMovement->InitialSpeed = 500.0;
	ProjectileMovement->MaxSpeed = 2000.0;		
}

二,带有增强输入的角色类型

// MyCharacter.h文件
#include "CoreMinimal.h"
#include "InputActionValue.h"
#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"
#include "GameFramework/Controller.h"	
#include "GameFramework/SpringArmComponent.h"
#include "Camera/CameraComponent.h"	
#include "GameFramework/Character.h"	
#include "GameFramework/CharacterMovementComponent.h"
#include "MyCharacter.generated.h"

class UInputAction;
class UInputMappingContext;

// ...
public:
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	TObjectPtr<USpringArmComponent> SpringArm;
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	TObjectPtr<UCameraComponent> Camera;

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	TObjectPtr<UInputMappingContext> DefaultMappingContext;
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	TObjectPtr<UInputAction> MoveAction;
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	TObjectPtr<UInputAction> LookAction;

public:
	void Move(const FInputActionValue& value);
	void Look(const FInputActionValue& value);
// ...
// MyCharacter.cpp文件
AMyCharacter::AMyCharacter()
{
	PrimaryActorTick.bCanEverTick = true;

	SpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("SpringArm"));
	Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera"));

	SpringArm->TargetArmLength = 400.0f;
	SpringArm->bUsePawnControlRotation = true;  // 控制其附加的相机是否随控制器旋转
	SpringArm->SetupAttachment(RootComponent);
	Camera->SetupAttachment(SpringArm);

	// 控制器只影响相机不影响角色
	bUseControllerRotationPitch = false;
	bUseControllerRotationRoll = false; 
	bUseControllerRotationYaw = false;

    // 控制角色的身体(Yaw轴)平滑地转向其移动方向
	GetCharacterMovement()->bOrientRotationToMovement = true;
}

void AMyCharacter::BeginPlay()
{
	Super::BeginPlay();

	if (auto PlayerController = Cast<APlayerController>(Controller))
		if (auto Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer()))
			Subsystem->AddMappingContext(DefaultMappingContext, 0);	
}

void AMyCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);

	if (auto EnhancedInputComponent = CastChecked<UEnhancedInputComponent>(PlayerInputComponent))
	{
		EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Triggered, this, &AMyCharacter::Move);
		EnhancedInputComponent->BindAction(LookAction, ETriggerEvent::Triggered, this, &AMyCharacter::Look);
	}
}

void AMyCharacter::Move(const FInputActionValue& value)
{
	FVector2D MovementVector = value.Get<FVector2D>();
	if (Controller)
	{
		const FRotator Rotation = Controller->GetControlRotation();
		const FRotator YawRotation(0, Rotation.Yaw, 0);
		const FVector FowardDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
		const FVector RightDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);

		AddMovementInput(FowardDirection, MovementVector.Y);
		AddMovementInput(RightDirection, MovementVector.X);
	}
}

// 5.7使用IMC_Default 和 IMC_MouseLook 将按键和鼠标操作分开
// IMC_Default    →  按键操作(移动、跳跃、冲刺、交互等)
// IMC_MouseLook  →  鼠标操作(视角旋转)
void AMyCharacter::Look(const FInputActionValue& value) 
{
	FVector2D LookAxisVector = value.Get<FVector2D>();
	if (Controller)
	{
		AddControllerYawInput(LookAxisVector.X);
		AddControllerPitchInput(LookAxisVector.Y);
	}
}

三,Interface 接口

// MyInterface.h文件
#include "CoreMinimal.h"
#include "UObject/Interface.h"
#include "MyInterface.generated.h"

// 空壳类,仅供虚幻反射系统(UHT)识别,使其在蓝图中可见,不包含函数实现;
// 如使用该类多重继承,会造成菱形继承问题,所以使用IMyInterface;
UINTERFACE(MinimalAPI)
class UMyInterface : public UInterface
{
	GENERATED_BODY()
};

class MYCPP_API IMyInterface
{
	GENERATED_BODY()
public:
    // 接口函数,继承自该接口的类会重写这些函数;
	virtual void func1() = 0;

	UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "MyInterface")
	void Func2();
};
// MyActor.h
#include "CoreMinimal.h" 
#include "GameFramework/Actor.h"
#include "MyInterface.h"
#include "MyActor.generated.h"
 
UCLASS() 
class CPP_API AMyActor : public AActor, public IMyInterface // UE的UObject体系禁止多重继承
{
	GENERATED_BODY()
	
public:	
	AMyActor();
 
protected:
	virtual void BeginPlay() override;
 
public:	
	virtual void Tick(float DeltaTime) override;

	virtual void func1() override;
	UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "MyInterface")
	void func2(); 
};
// MyActor.cpp
void AMyActor::func1()
{
	GEngine->AddOnScreenDebugMessage(-1, 3.0f, FColor::Green, TEXT("AMyActor::func1"));
}

void AMyActor::Func2_Implementation()
{
	GEngine->AddOnScreenDebugMessage(-1, 3.0f, FColor::Green, TEXT("IMyInterface::func2_Implementation"));
}
// 判断是否拥有该接口,蓝图则使用Does Object Implement Interface节点
// 通过反射的Uclass来判断
if (IsValid(myActor))
{
	if (IMyInterface* MyInterfaceActor = Cast<IMyInterface>(myActor))
	{
		MyInterfaceActor->func1();
		IMyInterface::Execute_func2(myActor);
	}
}

四,TimerHandle 定时器

// MyTimerHandle.h
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "TimerManager.h"
#include "MyTimerHandle.generated.h"

UCLASS()
class MYCPP_API AMyTimerHandle : public AActor
{
	GENERATED_BODY()
	
public:	
	AMyTimerHandle();

protected:
	virtual void BeginPlay() override;
	virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;

public:	
	virtual void Tick(float DeltaTime) override;

	// FTimerHandle 是定时器的唯一标识符,通过它来操纵和控制定时器;
	// 同一个 FTimerHandle 同时只能绑定一个定时器;
	FTimerHandle MyTimerHandle; 

	// 回调函数
	inline void func1() { UE_LOG(LogTemp, Warning, TEXT("func1()")); };
	inline void func2() { UE_LOG(LogTemp, Warning, TEXT("func2()")); };
};
// MyTimerHandle.cpp
void AMyTimerHandle::BeginPlay()
{
	Super::BeginPlay();

    GetWorldTimerManager().SetTimer(
        MyTimerHandle,                 // 传入句柄引用(绑定具体定时器)
        this,                          // 执行回调的对象
        &AMyTimerHandle::func1,              // 回调函数
        2.0f,                          // 每2秒执行一次
        true,                          // true=循环,false=只执行一次
        1.0f                           // (可选)延迟1秒后首次执行
    );
    // 第二绑定会覆盖第一个
    GetWorld()->GetTimerManager().SetTimer(MyTimerHandle, this, &AMyTimerHandle::func2, 2.0f, true, 1.0f);
}

void AMyTimerHandle::EndPlay(const EEndPlayReason::Type EndPlayReason) {   
    if (GetWorld())
        GetWorld()->GetTimerManager().ClearAllTimersForObject(this);
    Super::EndPlay(EndPlayReason);
}

//void AMyTimerHandle::EndPlay(const EEndPlayReason::Type EndPlayReason) {
//    if (GetWorld() && MyTimerHandle.IsValid())
//        GetWorld()->GetTimerManager().ClearTimer(MyTimerHandle);  
//    Super::EndPlay(EndPlayReason);
//}

//void AMyTimerHandle::EndPlay(const EEndPlayReason::Type EndPlayReason) {
//    GetWorldTimerManager().ClearTimer(MyTimerHandle);
//    Super::EndPlay(EndPlayReason);
//}

五,3D Widget

// 注意添加依赖的模块
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "UMG" });
// MyActor.h
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Components/WidgetComponent.h"
#include "MyActor.generated.h"

UCLASS()
class MYCPP_API AMyActor : public AActor
{
	GENERATED_BODY()
...	
public:	
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	TObjectPtr<UWidgetComponent> MyWidget;
};
// MyActor.cpp
#include "MyActor.h"

AMyActor::AMyActor()
{
    ...
	MyWidget = CreateDefaultSubobject<UWidgetComponent>(TEXT("MyWidget"));
	if (MyWidget)
		MyWidget->SetupAttachment(RootComponent);

	ConstructorHelpers::FClassFinder<UUserWidget> MyWidgetObject(TEXT("/Game/BP_Widget.BP_Widget_C"));
	if (MyWidgetObject.Succeeded())
	{
		MyWidget->SetWidgetClass(MyWidgetObject.Class);
		MyWidget->SetWidgetSpace(EWidgetSpace::Screen);
		MyWidget->SetDrawSize(FVector2D(400, 20));
		MyWidget->SetRelativeLocation(FVector(0,0,100));
	}		
}

void AMyActor::test()
{
	UUserWidget* UserWidgetObject = MyWidget->GetUserWidgetObject();
	if(UserWidgetObject)
	{
		UClass* UserWidgetClass = UserWidgetObject->GetClass();

		// 获取和设置蓝图内的变量CurHealth
		FProperty* CurHealthProperty = UserWidgetClass->FindPropertyByName(TEXT("CurHealth"));
		if (CurHealthProperty)
		{
			if (FFloatProperty* FloatProperty = CastField<FFloatProperty>(CurHealthProperty))
			{
				float Value = FloatProperty->GetPropertyValue_InContainer(UserWidgetObject);
				GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Blue, FString::Printf(TEXT("CurHealth: %f"), Value));				
			}	
            // 虽然在蓝图内设置的是浮点,实际存储的却不是
			else if (FNumericProperty* NumericProperty = CastField<FNumericProperty>(CurHealthProperty))
			{
				void* valueptr = NumericProperty->ContainerPtrToValuePtr<void>(UserWidgetObject);
				float Value = NumericProperty->GetFloatingPointPropertyValue(valueptr);
				NumericProperty->SetFloatingPointPropertyValue(valueptr, Value - 10);
				GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Blue, FString::Printf(TEXT("CurHealth: %.2f"), Value - 10));
			}
		}
	}		
}

六,Timeline

// MyTimelineActor.h
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Components/TimelineComponent.h"
#include "MyTimelineActor.generated.h"

UCLASS()
class MYCPP_API AMyTimelineActor : public AActor
{
	GENERATED_BODY()	
...
public:
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MyTimeline")
	TObjectPtr<UTimelineComponent> MyTimeline; // 相当于蓝图 Timeline 节点

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MyTimeline")
	TObjectPtr<UCurveFloat> MyCurveFloat;
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MyTimeline")
	TObjectPtr<UCurveVector> MyCurveVector;
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MyTimeline")
	TObjectPtr<UCurveLinearColor> MyCurveLinearColor;

	FOnTimelineFloat MyTimelineFloatDelegate;
	FOnTimelineVector MyTimelineVectorDelegate;
	FOnTimelineLinearColor MyTimelineLinearColorDelegate;
	FOnTimelineEvent MyTimelineEventDelegate;
public:
	UFUNCTION()
	void FunUpdate_Float(float value);
	UFUNCTION()
	void FunFinishEvent();
};
// MyTimelineActor.cpp
...
AMyTimelineActor::AMyTimelineActor()
{
	PrimaryActorTick.bCanEverTick = true;

	ConstructorHelpers::FObjectFinder<UCurveFloat> CurveFloatAsset(TEXT("/Game/_TEST/MyCurveFloat.MyCurveFloat")); 
	MyCurveFloat = CurveFloatAsset.Object;
	MyTimeline = CreateDefaultSubobject<UTimelineComponent>(TEXT("MyTimeLine"));
	MyTimeline->SetTimelineLengthMode(TL_TimelineLength);
	MyTimeline->SetTimelineLength(2);
}

void AMyTimelineActor::BeginPlay()
{
	Super::BeginPlay();

	if (MyCurveFloat && MyTimeline)
	{
		MyTimelineFloatDelegate.BindUFunction(this, FName("FunUpdate_Float"));
		MyTimeline->AddInterpFloat(MyCurveFloat, MyTimelineFloatDelegate); // 根据当前的播放进度计算对应Curve值,然后通过委托广播出去;

		MyTimelineEventDelegate.BindUFunction(this, FName("FunFinishEvent"));
		MyTimeline->SetTimelineFinishedFunc(MyTimelineEventDelegate);
        
        // 可在指定的地方播放
        MyTimeline->PlayFromStart();
	}
}

// 回调函数
void AMyTimelineActor::FunUpdate_Float(float value)
{
	UE_LOG(LogTemp, Warning, TEXT("Fun_Float: %f"), value);
}

void AMyTimelineActor::FunFinishEvent()
{
	UE_LOG(LogTemp, Warning, TEXT("FunFinishEvent"));
}

七,射线检查

// MyCharacter.h文件
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "MyCharacter.generated.h"

UCLASS()
class MYCPP_API AMyCharacter : public ACharacter
{
...
public:	
...
	virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;

	UPROPERTY(EditAnywhere)
	TSubclassOf<AActor> MyClass;

	void test();
};
// MyCharacter.cpp文件
#include "MyCharacter.h"

AMyCharacter::AMyCharacter()
{
	PrimaryActorTick.bCanEverTick = true;

	static ConstructorHelpers::FClassFinder<AActor> MyActorAsset(TEXT("/Game/_TEST/BP_TEST.BP_TEST_C"));
	if (MyActorAsset.Succeeded())
		MyClass = MyActorAsset.Class;
}

void AMyCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);

	if(PlayerInputComponent)
		PlayerInputComponent->BindKey(EKeys::One, IE_Pressed, this, &AMyCharacter::test);
}

void AMyCharacter::test()
{
	FHitResult OutHit;
	FVector Start = GetActorLocation();
	FVector End = Start + GetActorForwardVector() * 200;

	if (GetWorld()->LineTraceSingleByChannel(OutHit, Start, End, ECC_Visibility))
		DrawDebugSphere(GetWorld(), OutHit.Location, 10.0f, 12, FColor::Green, false, 5.0f);
	DrawDebugLine(GetWorld(), Start, End, FColor::Red, false, 5.0f, 0, 1.0f);
}

void AMyCharacter::test()
{
	TArray<FHitResult> OutHits;
	FVector Start = GetActorLocation();
	FVector End = Start + GetActorForwardVector() * 500;

    //多通道检测时,击中时即停止检测,输出在其射线路径上交叉的对象
	if (GetWorld()->LineTraceMultiByChannel(OutHits, Start, End, ECC_Visibility))
		for(FHitResult& OutHit : OutHits)
			DrawDebugSphere(GetWorld(), OutHit.Location, 10.0f, 12, FColor::Green, false, 5.0f);		
	DrawDebugLine(GetWorld(), Start, End, FColor::Red, false, 5.0f, 0, 1.0f);
}

八,软引用

// .h
...
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FSoftObjectPath A;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TSoftObjectPtr<UObject> B;  //如为AActor类型,则在Detail面板内选择大纲内的对象

UPROPERTY(EditAnywhere, BlueprintReadWrite)
FSoftClassPath C;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TSoftClassPtr<UObject> D;
...
// .cpp
#include "MyActor.h"
#include "Engine/AssetManager.h"

void AMyActor::BeginPlay()
{
...
	// 同步加载(当前线程会一直等待,直到资源完全加载进内存,如还有LoadObject/LoadClass/StaticLoadObject)
	FSoftObjectPath ObjectPath(TEXT("/Game/T_Image.T_Image"));
	TSharedPtr<FStreamableHandle> SyncHandle = UAssetManager::GetStreamableManager().RequestSyncLoad(ObjectPath);
	if (SyncHandle)
	{
		UTexture2D* Image = Cast<UTexture2D>(SyncHandle->GetLoadedAsset());
		if (Image)
			GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, FString::Printf(TEXT("Image Name: %s"), *Image->GetName()));
	}

	// 异步加载(不阻塞当前线程,资源在后台线程加载,完成后通过回调(Delegate) 通知)
	FSoftObjectPath ObjectPath1(TEXT("/Game/T_Image1.T_Image1"));
	TSharedPtr<FStreamableHandle> AsyncHandle = UAssetManager::GetStreamableManager().RequestAsyncLoad(ObjectPath1);
	if (AsyncHandle)
	{
		UTexture2D* Image1 = Cast<UTexture2D>(AsyncHandle->GetLoadedAsset());
		if (Image1)
			GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, FString::Printf(TEXT("Image1 Name: %s"), *Image1->GetName()));
	}
}

九,共享指针

class TestClass
{
public:
	TestClass(int a = 0):_a(a){}
	~TestClass(){UE_LOG(LogTemp, Warning, TEXT("~TestClass()"));}
	int32 _a = 0;
};
TestClass* ptr = new TestClass(1);		// 原生指针
TSharedPtr<TestClass> SharedPtr1(ptr);  // 不推荐
ptr = nullptr;

TSharedPtr<TestClass> SharedPtr2(new TestClass(2));
TSharedPtr<TestClass> SharedPtr3(SharedPtr2);

TSharedPtr<TestClass> SharedPtr4 = nullptr;
SharedPtr4 = MakeShareable(new TestClass(4));

TSharedPtr<TestClass, ESPMode::ThreadSafe> SharedPtr5(new TestClass(5));

if (SharedPtr4.IsValid())
{
	auto val4 = SharedPtr4->_a;
	auto Count4_1 = SharedPtr4.GetSharedReferenceCount();
	UE_LOG(LogTemp, Warning, TEXT("SharedPtr4 val: %d, ReferenceCount: %d!"), val4, Count4_1);

	if (SharedPtr4.IsUnique())
		UE_LOG(LogTemp, Warning, TEXT("SharedPtr4 Is Unique!"));

	SharedPtr4.Reset();
	auto Count4_2 = SharedPtr4.GetSharedReferenceCount();
	UE_LOG(LogTemp, Warning, TEXT("SharedPtr4 Reset and ReferenceCount: %d!"), Count4_2);
}

TSharedRef<TestClass> SharedRef(new TestClass(40)); // 共享引用必须指向有效对象
if (SharedRef.IsUnique())
{
	auto val = SharedRef->_a; // SharedRef.Get()._a;
	UE_LOG(LogTemp, Warning, TEXT("SharedRef Is Unique, val: %d!"), val);

	SharedRef = SharedPtr2.ToSharedRef(); // 共享指针转化为共享引用,引用计数+1
	auto Count = SharedRef.GetSharedReferenceCount();
	UE_LOG(LogTemp, Warning, TEXT("SharedRef ReferenceCount: %d!"), Count);

	SharedPtr3 = SharedRef; //共享引用可直接转化为共享指针
	auto val3 = SharedPtr3->_a;
	UE_LOG(LogTemp, Warning, TEXT("SharedPtr3 val: %d!, ReferenceCount: %d!"), val3, Count);
}
TSharedPtr<TestClass> SharedPtr6 = MakeShareable(new TestClass(6));
TSharedRef<TestClass> SharedRef6(new TestClass(60));
TWeakPtr<TestClass> WeakPtr1(SharedPtr6);
TWeakPtr<TestClass> WeakPtr2(SharedRef6);
if (WeakPtr1.IsValid())
{
	TSharedPtr<TestClass> SharedPtr7 = WeakPtr1.Pin();
	if (SharedPtr7.IsValid()) {
		UE_LOG(LogTemp, Warning, TEXT("SharedPtr7 val: %d!"), SharedPtr7->_a);
		UE_LOG(LogTemp, Warning, TEXT("SharedPtr7 ReferenceCount: %d!"), SharedPtr7.GetSharedReferenceCount());
	}
}

十,蓝图函数库

// #include "Kismet/BlueprintFunctionLibrary.h"
// 所有暴露给蓝图函数库的抽象基类,其子类的函数或方法应为静态
UCLASS(Abstract, MinimalAPI)
class UBlueprintFunctionLibrary : public UObject
{
    // 会自动把访问权限重置为 public,并要求你手动写构造函数
	GENERATED_UCLASS_BODY()
	// 决定了当前该函数库函数在调用时应该运行在哪个“空间”
    // 返回值为:FUNCTIONCALLSPACE_Server/FUNCTIONCALLSPACE_Client/FUNCTIONCALLSPACE_Anywhere
	ENGINE_API virtual int32 GetFunctionCallspace(UFunction* Function, FFrame* Stack) override;
};
// UGameplayStatics 是UE提供的一个蓝图函数库,有很多静态辅助函数;
UCLASS(MinimalAPI)
class UGameplayStatics : public UBlueprintFunctionLibrary
{
    GENERATED_UCLASS_BODY()
// --- Create Object
	UFUNCTION(BlueprintCallable, Category = "Spawning", meta = (BlueprintInternalUseOnly = "true", DefaultToSelf = "Outer"))
	static ENGINE_API UObject* SpawnObject(TSubclassOf<UObject> ObjectClass, UObject* Outer);

// --- Spawning functions ------------------------------
	UFUNCTION(BlueprintCallable, Category = "Spawning", meta = (WorldContext = "WorldContextObject", UnsafeDuringActorConstruction = "true", BlueprintInternalUseOnly = "true", DeprecatedFunction = "Note", DeprecationMessage = "Use BeginSpawningActorFromClass"))
	static ENGINE_API class AActor* BeginSpawningActorFromBlueprint(const UObject * WorldContextObject, const class UBlueprint* Blueprint, const FTransform & SpawnTransform, bool bNoCollisionFail);
	UFUNCTION(BlueprintCallable, Category = "Spawning", meta = (WorldContext = "WorldContextObject", UnsafeDuringActorConstruction = "true", BlueprintInternalUseOnly = "true"))
	static ENGINE_API class AActor* BeginDeferredActorSpawnFromClass(const UObject * WorldContextObject, TSubclassOf<AActor> ActorClass, const FTransform & SpawnTransform, ESpawnActorCollisionHandlingMethod CollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::Undefined, AActor * Owner = nullptr, ESpawnActorScaleMethod TransformScaleMethod = ESpawnActorScaleMethod::MultiplyWithRoot);
	UFUNCTION(BlueprintCallable, Category = "Spawning", meta = (UnsafeDuringActorConstruction = "true", BlueprintInternalUseOnly = "true"))
	static ENGINE_API class AActor* FinishSpawningActor(class AActor* Actor, const FTransform & SpawnTransform, ESpawnActorScaleMethod TransformScaleMethod = ESpawnActorScaleMethod::MultiplyWithRoot);

// --- Actor functions ------------------------------
	UFUNCTION(BlueprintCallable, Category="Transformation")
	static ENGINE_API FVector GetActorArrayAverageLocation(const TArray<AActor*>& Actors);
	UFUNCTION(BlueprintCallable, Category="Collision")
	static ENGINE_API void GetActorArrayBounds(const TArray<AActor*>& Actors, bool bOnlyCollidingComponents, FVector& Center, FVector& BoxExtent);
	UFUNCTION(BlueprintCallable, Category="Actor", meta=(WorldContext="WorldContextObject", DeterminesOutputType="ActorClass"))
	static ENGINE_API class AActor* GetActorOfClass(const UObject* WorldContextObject, TSubclassOf<AActor> ActorClass);
	UFUNCTION(BlueprintCallable, Category="Actor",  meta=(WorldContext="WorldContextObject", DeterminesOutputType="ActorClass", DynamicOutputParam="OutActors"))
	static ENGINE_API void GetAllActorsOfClass(const UObject* WorldContextObject, TSubclassOf<AActor> ActorClass, TArray<AActor*>& OutActors);
	UFUNCTION(BlueprintCallable, Category="Actor",  meta=(WorldContext="WorldContextObject", DeterminesOutputType="Interface", DynamicOutputParam="OutActors"))
	static ENGINE_API void GetAllActorsWithInterface(const UObject* WorldContextObject, TSubclassOf<UInterface> Interface, TArray<AActor*>& OutActors);
	UFUNCTION(BlueprintCallable, Category="Actor",  meta=(WorldContext="WorldContextObject"))
	static ENGINE_API void GetAllActorsWithTag(const UObject* WorldContextObject, FName Tag, TArray<AActor*>& OutActors);
	UFUNCTION(BlueprintCallable, Category = "Actor", meta = (WorldContext="WorldContextObject", DeterminesOutputType="ActorClass", DynamicOutputParam="OutActors"))
	static ENGINE_API void GetAllActorsOfClassWithTag(const UObject* WorldContextObject, TSubclassOf<AActor> ActorClass, FName Tag, TArray<AActor*>& OutActors);
	UFUNCTION(BlueprintPure, Category = "Actor")
	static ENGINE_API AActor* FindNearestActor(FVector Origin, const TArray<AActor*>& ActorsToCheck, float& Distance);

// --- Player functions ------------------------------
	UFUNCTION(BlueprintPure, Category = "Game", meta = (WorldContext = "WorldContextObject"))
	static ENGINE_API class UGameInstance* GetGameInstance(const UObject * WorldContextObject);
	UFUNCTION(BlueprintPure, Category = "Game", meta = (WorldContext = "WorldContextObject", UnsafeDuringActorConstruction = "true"))
	static ENGINE_API int32 GetNumPlayerStates(const UObject * WorldContextObject);
	UFUNCTION(BlueprintPure, Category = "Game", meta = (WorldContext = "WorldContextObject", UnsafeDuringActorConstruction = "true"))
	static ENGINE_API class APlayerState* GetPlayerState(const UObject * WorldContextObject, int32 PlayerStateIndex);
	UFUNCTION(BlueprintPure, Category = "Game", meta = (WorldContext = "WorldContextObject", UnsafeDuringActorConstruction = "true"))
	static ENGINE_API class APlayerState* GetPlayerStateFromUniqueNetId(const UObject * WorldContextObject, const FUniqueNetIdRepl & UniqueId);
	UFUNCTION(BlueprintPure, Category = "Game", meta = (WorldContext = "WorldContextObject", UnsafeDuringActorConstruction = "true"))
	static ENGINE_API int32 GetNumPlayerControllers(const UObject * WorldContextObject);
	UFUNCTION(BlueprintPure, Category = "Game", meta = (WorldContext = "WorldContextObject", UnsafeDuringActorConstruction = "true"))
	static ENGINE_API int32 GetNumLocalPlayerControllers(const UObject * WorldContextObject);
	UFUNCTION(BlueprintPure, Category = "Game", meta = (WorldContext = "WorldContextObject", UnsafeDuringActorConstruction = "true"))
	static ENGINE_API class APlayerController* GetPlayerController(const UObject * WorldContextObject, int32 PlayerIndex);
	UFUNCTION(BlueprintPure, Category = "Game", meta = (WorldContext = "WorldContextObject", UnsafeDuringActorConstruction = "true"))
	static ENGINE_API class APawn* GetPlayerPawn(const UObject * WorldContextObject, int32 PlayerIndex);
	UFUNCTION(BlueprintPure, Category = "Game", meta = (WorldContext = "WorldContextObject", UnsafeDuringActorConstruction = "true"))
	static ENGINE_API class ACharacter* GetPlayerCharacter(const UObject * WorldContextObject, int32 PlayerIndex);
	UFUNCTION(BlueprintPure, Category = "Game", meta = (WorldContext = "WorldContextObject", UnsafeDuringActorConstruction = "true"))
	static ENGINE_API class APlayerCameraManager* GetPlayerCameraManager(const UObject * WorldContextObject, int32 PlayerIndex);
	UFUNCTION(BlueprintCallable, Category = "Game", meta = (DisplayName = "Create Local Player", WorldContext = "WorldContextObject", AdvancedDisplay = "2", UnsafeDuringActorConstruction = "true"))
	static ENGINE_API class APlayerController* CreatePlayer(const UObject * WorldContextObject, int32 ControllerId = -1, bool bSpawnPlayerController = true);
	UFUNCTION(BlueprintCallable, Category = "Game", meta = (DisplayName = "Remove Local Player", UnsafeDuringActorConstruction = "true"))
	static ENGINE_API void RemovePlayer(APlayerController * Player, bool bDestroyPawn);

// --- Level Streaming functions ------------------------
	UFUNCTION(BlueprintCallable, meta = (WorldContext = "WorldContextObject", Latent = "", LatentInfo = "LatentInfo", DisplayName = "Load Stream Level (by Name)"), Category = "Game")
	static ENGINE_API void LoadStreamLevel(const UObject * WorldContextObject, FName LevelName, bool bMakeVisibleAfterLoad, bool bShouldBlockOnLoad, FLatentActionInfo LatentInfo);
	UFUNCTION(BlueprintCallable, meta = (WorldContext = "WorldContextObject", Latent = "", LatentInfo = "LatentInfo", DisplayName = "Load Stream Level (by Object Reference)"), Category = "Game")
	static ENGINE_API void LoadStreamLevelBySoftObjectPtr(const UObject * WorldContextObject, const TSoftObjectPtr<UWorld> Level, bool bMakeVisibleAfterLoad, bool bShouldBlockOnLoad, FLatentActionInfo LatentInfo);
	UFUNCTION(BlueprintCallable, meta = (WorldContext = "WorldContextObject", Latent = "", LatentInfo = "LatentInfo", DisplayName = "Unload Stream Level (by Name)"), Category = "Game")
	static ENGINE_API void UnloadStreamLevel(const UObject * WorldContextObject, FName LevelName, FLatentActionInfo LatentInfo, bool bShouldBlockOnUnload);
	UFUNCTION(BlueprintCallable, meta = (WorldContext = "WorldContextObject", Latent = "", LatentInfo = "LatentInfo", DisplayName = "Unload Stream Level (by Object Reference)"), Category = "Game")
	static ENGINE_API void UnloadStreamLevelBySoftObjectPtr(const UObject * WorldContextObject, const TSoftObjectPtr<UWorld> Level, FLatentActionInfo LatentInfo, bool bShouldBlockOnUnload);
	UFUNCTION(BlueprintPure, meta = (WorldContext = "WorldContextObject"), Category = "Game")
	static ENGINE_API class ULevelStreaming* GetStreamingLevel(const UObject * WorldContextObject, FName PackageName);
	UFUNCTION(BlueprintCallable, meta = (WorldContext = "WorldContextObject"), Category = "Game")
	static ENGINE_API void FlushLevelStreaming(const UObject * WorldContextObject);
	UFUNCTION(BlueprintCallable, Category = "Game")
	static ENGINE_API void CancelAsyncLoading();
	UFUNCTION(BlueprintCallable, meta = (WorldContext = "WorldContextObject", AdvancedDisplay = "2", DisplayName = "Open Level (by Name)"), Category = "Game")
	static ENGINE_API void OpenLevel(const UObject * WorldContextObject, FName LevelName, bool bAbsolute = true, FString Options = FString(TEXT("")));
	UFUNCTION(BlueprintCallable, meta = (WorldContext = "WorldContextObject", AdvancedDisplay = "2", DisplayName = "Open Level (by Object Reference)"), Category = "Game")
	static ENGINE_API void OpenLevelBySoftObjectPtr(const UObject * WorldContextObject, const TSoftObjectPtr<UWorld> Level, bool bAbsolute = true, FString Options = FString(TEXT("")));
	UFUNCTION(BlueprintCallable, meta = (WorldContext = "WorldContextObject", AdvancedDisplay = "1"), Category = "Game")
	static ENGINE_API FString GetCurrentLevelName(const UObject * WorldContextObject, bool bRemovePrefixString = true);

// --- Global functions ------------------------------
	UFUNCTION(BlueprintPure, Category = "Game", meta = (WorldContext = "WorldContextObject"))
	static ENGINE_API class AGameModeBase* GetGameMode(const UObject * WorldContextObject);
	UFUNCTION(BlueprintPure, Category = "Game", meta = (WorldContext = "WorldContextObject"))
	static ENGINE_API class AGameStateBase* GetGameState(const UObject * WorldContextObject);
	UFUNCTION(BlueprintPure, meta = (DisplayName = "Get Class", DeterminesOutputType = "Object"), Category = "Utilities")
	static ENGINE_API class UClass* GetObjectClass(const UObject * Object);
	UFUNCTION(BlueprintPure, meta = (DisplayName = "IsA"), Category = "Utilities")
	static ENGINE_API bool ObjectIsA(const UObject * Object, TSubclassOf<UObject> ObjectClass);
	UFUNCTION(BlueprintPure, Category = "Utilities|Time", meta = (WorldContext = "WorldContextObject"))
	static ENGINE_API float GetGlobalTimeDilation(const UObject * WorldContextObject);
	UFUNCTION(BlueprintCallable, Category = "Utilities|Time", meta = (WorldContext = "WorldContextObject"))
	static ENGINE_API void SetGlobalTimeDilation(const UObject * WorldContextObject, float TimeDilation);
	UFUNCTION(BlueprintCallable, Category = "Game", meta = (WorldContext = "WorldContextObject"))
	static ENGINE_API bool SetGamePaused(const UObject * WorldContextObject, bool bPaused);
	UFUNCTION(BlueprintPure, Category = "Game", meta = (WorldContext = "WorldContextObject"))
	static ENGINE_API bool IsGamePaused(const UObject * WorldContextObject);
	UFUNCTION(BlueprintCallable, Category = "Viewport", meta = (WorldContext = "WorldContextObject"))
	static ENGINE_API void SetForceDisableSplitscreen(const UObject * WorldContextObject, bool bDisable);
	UFUNCTION(BlueprintPure, Category = "Viewport", meta = (WorldContext = "WorldContextObject"))
	static ENGINE_API bool IsSplitscreenForceDisabled(const UObject * WorldContextObject);
	UFUNCTION(BlueprintCallable, Category = "Rendering", meta = (WorldContext = "WorldContextObject"))
	static ENGINE_API void SetEnableWorldRendering(const UObject * WorldContextObject, bool bEnable);
	UFUNCTION(BlueprintCallable, Category = "Rendering", meta = (WorldContext = "WorldContextObject"))
	static ENGINE_API bool GetEnableWorldRendering(const UObject * WorldContextObject);
	UFUNCTION(BlueprintPure, Category = "Viewport", meta = (WorldContext = "WorldContextObject"))
	static ENGINE_API EMouseCaptureMode GetViewportMouseCaptureMode(const UObject * WorldContextObject);
	UFUNCTION(BlueprintCallable, Category = "Viewport", meta = (WorldContext = "WorldContextObject"))
	static ENGINE_API void SetViewportMouseCaptureMode(const UObject * WorldContextObject, const EMouseCaptureMode MouseCaptureMode);
	UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category = "Game|Damage", meta = (WorldContext = "WorldContextObject", AutoCreateRefTerm = "IgnoreActors"))
	static ENGINE_API bool ApplyRadialDamage(const UObject * WorldContextObject, float BaseDamage, const FVector & Origin, float DamageRadius, TSubclassOf<class UDamageType> DamageTypeClass, const TArray<AActor*>&IgnoreActors, AActor * DamageCauser = NULL, AController * InstigatedByController = NULL, bool bDoFullDamage = false, ECollisionChannel DamagePreventionChannel = ECC_Visibility);
	UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category = "Game|Damage", meta = (WorldContext = "WorldContextObject", AutoCreateRefTerm = "IgnoreActors"))
	static ENGINE_API bool ApplyRadialDamageWithFalloff(const UObject * WorldContextObject, float BaseDamage, float MinimumDamage, const FVector & Origin, float DamageInnerRadius, float DamageOuterRadius, float DamageFalloff, TSubclassOf<class UDamageType> DamageTypeClass, const TArray<AActor*>&IgnoreActors, AActor * DamageCauser = NULL, AController * InstigatedByController = NULL, ECollisionChannel DamagePreventionChannel = ECC_Visibility);
	UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category = "Game|Damage")
	static ENGINE_API float ApplyPointDamage(AActor * DamagedActor, float BaseDamage, const FVector & HitFromDirection, const FHitResult & HitInfo, AController * EventInstigator, AActor * DamageCauser, TSubclassOf<class UDamageType> DamageTypeClass);
	UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category = "Game|Damage")
	static ENGINE_API float ApplyDamage(AActor * DamagedActor, float BaseDamage, AController * EventInstigator, AActor * DamageCauser, TSubclassOf<class UDamageType> DamageTypeClass);

// --- Camera functions ------------------------------
	UFUNCTION(BlueprintCallable, Category = "Camera", meta = (WorldContext = "WorldContextObject", UnsafeDuringActorConstruction = "true"))
	static ENGINE_API void PlayWorldCameraShake(const UObject * WorldContextObject, TSubclassOf<class UCameraShakeBase> Shake, FVector Epicenter, float InnerRadius, float OuterRadius, float Falloff = 1.f, bool bOrientShakeTowardsEpicenter = false);

// --- Particle functions ------------------------------
	UFUNCTION(BlueprintCallable, Category = "Effects", meta = (Keywords = "particle system", WorldContext = "WorldContextObject", UnsafeDuringActorConstruction = "true"))
	static ENGINE_API UParticleSystemComponent* SpawnEmitterAtLocation(const UObject * WorldContextObject, UParticleSystem * EmitterTemplate, FVector Location, FRotator Rotation = FRotator::ZeroRotator, FVector Scale = FVector(1.f), bool bAutoDestroy = true, EPSCPoolMethod PoolingMethod = EPSCPoolMethod::None, bool bAutoActivateSystem = true);
	static ENGINE_API UParticleSystemComponent* SpawnEmitterAtLocation(const UObject * WorldContextObject, UParticleSystem * EmitterTemplate, FVector Location, FRotator Rotation, bool bAutoDestroy, EPSCPoolMethod PoolingMethod = EPSCPoolMethod::None, bool bAutoActivateSystem = true);
	static ENGINE_API UParticleSystemComponent* SpawnEmitterAtLocation(UWorld * World, UParticleSystem * EmitterTemplate, const FTransform & SpawnTransform, bool bAutoDestroy = true, EPSCPoolMethod PoolingMethod = EPSCPoolMethod::None, bool bAutoActivate = true);

}
UCLASS(meta=(BlueprintThreadSafe, ScriptName="StringLibrary"), MinimalAPI)
class UKismetStringLibrary : public UBlueprintFunctionLibrary
{
    GENERATED_UCLASS_BODY()
	UFUNCTION(BlueprintPure, Category="Utilities|String", meta=(Keywords = "empty"))
	static ENGINE_API bool IsEmpty(const FString& InString);
	UFUNCTION(BlueprintPure, Category = "Utilities|String")
	static ENGINE_API bool IsNumeric(const FString& SourceString);
	UFUNCTION(BlueprintPure, Category="Utilities|String", meta=(CompactNodeTitle = "LEN", Keywords = "length"))
	static ENGINE_API int32 Len(const FString& S);
	UFUNCTION(BlueprintPure, Category = "Utilities|String")
	static ENGINE_API bool Contains(const FString& SearchIn, const FString& Substring, bool bUseCase = false, bool bSearchFromEnd = false);
	UFUNCTION(BlueprintPure, Category="Utilities|String")
	static ENGINE_API FString GetSubstring(const FString& SourceString, int32 StartIndex = 0, int32 Length = 1);
	UFUNCTION(BlueprintPure, Category = "Utilities|String")
	static ENGINE_API int32 FindSubstring(const FString& SearchIn, const FString& Substring, bool bUseCase = false, bool bSearchFromEnd = false, int32 StartPosition = -1);
	UFUNCTION(BlueprintPure, Category="Utilities|String")
	static ENGINE_API int32 GetCharacterAsNumber(const FString& SourceString, int32 Index = 0);
	UFUNCTION(BlueprintPure, Category = "Utilities|String")
	static ENGINE_API TArray<FString> GetCharacterArrayFromString(const FString& SourceString);
	UFUNCTION(BlueprintPure, Category="Utilities|String")
	static ENGINE_API FString ToUpper(const FString& SourceString);
	UFUNCTION(BlueprintPure, Category = "Utilities|String")
	static ENGINE_API FString ToLower(const FString& SourceString);
	UFUNCTION(BlueprintPure, Category = "Utilities|String")
	static ENGINE_API FString LeftPad(const FString& SourceString, int32 ChCount);
	UFUNCTION(BlueprintPure, Category = "Utilities|String")
	static ENGINE_API FString RightPad(const FString& SourceString, int32 ChCount);
	UFUNCTION(BlueprintPure, Category = "Utilities|String")
	static ENGINE_API bool StartsWith(const FString& SourceString, const FString& InPrefix, ESearchCase::Type SearchCase = ESearchCase::IgnoreCase);
	UFUNCTION(BlueprintPure, Category = "Utilities|String")
	static ENGINE_API bool EndsWith(const FString& SourceString, const FString& InSuffix, ESearchCase::Type SearchCase = ESearchCase::IgnoreCase);
	UFUNCTION(BlueprintPure, Category = "Utilities|String")
	static ENGINE_API FString Replace(const FString& SourceString, const FString& From, const FString& To, ESearchCase::Type SearchCase = ESearchCase::IgnoreCase);
	UFUNCTION(BlueprintPure, Category = "Utilities|String")
	static ENGINE_API FString Left(const FString& SourceString, int32 Count);
	UFUNCTION(BlueprintPure, Category = "Utilities|String")
	static ENGINE_API FString Right(const FString& SourceString, int32 Count);
	UFUNCTION(BlueprintPure, Category = "Utilities|String")
	static ENGINE_API FString LeftChop(const FString& SourceString, int32 Count);
	UFUNCTION(BlueprintPure, Category = "Utilities|String")
	static ENGINE_API FString RightChop(const FString& SourceString, int32 Count);
	UFUNCTION(BlueprintPure, Category = "Utilities|String")
	static ENGINE_API FString Mid(const FString& SourceString, int32 Start, int32 Count);
}
UCLASS(meta=(ScriptName="SystemLibrary"), MinimalAPI)
class UKismetSystemLibrary : public UBlueprintFunctionLibrary
{
    GENERATED_UCLASS_BODY()
...
    UFUNCTION(BlueprintCallable, Category="Utilities|FlowControl", meta=(Latent, WorldContext="WorldContextObject", LatentInfo="LatentInfo", Duration="0.2", Keywords="sleep"))
	static ENGINE_API void Delay(const UObject* WorldContextObject, float Duration, struct FLatentActionInfo LatentInfo );
    UFUNCTION(BlueprintCallable, meta=(Latent, LatentInfo="LatentInfo", WorldContext="WorldContextObject", Duration="0.2", Keywords="sleep"), Category="Utilities|FlowControl")
    static ENGINE_API void RetriggerableDelay(const UObject* WorldContextObject, float Duration, FLatentActionInfo LatentInfo);
    UFUNCTION(BlueprintCallable, meta=(Latent, LatentInfo="LatentInfo", WorldContext="WorldContextObject", ExpandEnumAsExecs="MoveAction", OverTime="0.2"), Category="Components")
    static ENGINE_API void MoveComponentTo(USceneComponent* Component, FVector TargetRelativeLocation, FRotator TargetRelativeRotation, bool bEaseOut, bool bEaseIn, float OverTime, bool bForceShortestRotationPath, TEnumAsByte<EMoveComponentAction::Type> MoveAction, FLatentActionInfo LatentInfo);
...
}

十一,文件

文件路径操作
// BlueprintPathsLibrary.h 内有内置的关于path的蓝图库函数,其调用的就是FPaths内的函数
class UBlueprintPathsLibrary : public UBlueprintFunctionLibrary
// 头文件为 #include "Misc/Paths.h",一般默认该头文件可不包括
// 用于跨平台地处理文件和目录路径,避免手动拼接字符串时出现的平台差异问题
class FPaths {
public:
    static CORE_API bool CanGetProjectDir();
    static CORE_API bool IsStaged();
    static CORE_API bool ShouldSaveToUserDir();
    static CORE_API bool IsRestrictedPath(const FString& InPath);
    static CORE_API bool IsProjectFilePathSet();
    static CORE_API bool FileExists(const FString& InPath);
    static CORE_API bool DirectoryExists(const FString& InPath);
    static CORE_API bool IsDrive(const FString& InPath);
    static CORE_API bool IsRelative(const FString& InPath);
    static CORE_API bool IsSamePath(const FString& PathA, const FString& PathB);
    static CORE_API bool IsUnderDirectory(const FString& InPath, const FString& InDirectory);
    static CORE_API bool HasProjectPersistentDownloadDir();
    static CORE_API bool MakePathRelativeTo( FString& InPath, const TCHAR* InRelativeTo );
    static CORE_API bool ValidatePath( const FString& InPath, FText* OutReason = nullptr ); 
    static CORE_API bool CollapseRelativeDirectories(FString& InPath, bool bCollapseAllPossible = false);
    
    static CORE_API void Split( const FString& InPath, FString& PathPart, FString& FilenamePart, FString& ExtensionPart );
    static CORE_API FString ConvertRelativePathToFull(const FString& InPath);
    static CORE_API FString ConvertRelativePathToFull(FString&& InPath);
    static CORE_API FString ConvertRelativePathToFull(const FString& BasePath, const FString& InPath);
    static CORE_API FString ConvertRelativePathToFull(const FString& BasePath, FString&& InPath);
    static CORE_API FString ConvertRelativePathToFull(FString&& BasePath, const FString& InPath);
    static CORE_API FString ConvertRelativePathToFull(FString&& BasePath, FString&& InPath);
    static CORE_API FString ConvertToSandboxPath( const FString& InPath, const TCHAR* InSandboxName );
    static CORE_API FString ConvertFromSandboxPath( const FString& InPath, const TCHAR* InSandboxName );

    static CORE_API FString FindCorrectCase(const FString& Path);
    static CORE_API const FString& GetRelativePathToRoot();
    static CORE_API FString GetProjectFilePath();
    static CORE_API void SetProjectFilePath( const FString& NewGameProjectFilePath );
    static CORE_API FString GetPath(FString&& InPath);
    static CORE_API FString GetPathLeaf(const FString& InPath);
    static CORE_API FString ConvertPath(const FString& Path, EPathConversion Method, const TCHAR* ExtraData=nullptr, const TCHAR* OverrideProjectDir=nullptr);
    static CORE_API const TArray<FString>& GetEngineLocalizationPaths();
    static CORE_API const TArray<FString>& GetEditorLocalizationPaths();
    static CORE_API const TArray<FString>& GetPropertyNameLocalizationPaths();
    static CORE_API const TArray<FString>& GetToolTipLocalizationPaths();
    static CORE_API const TArray<FString>& GetGameLocalizationPaths();

    static CORE_API void NormalizeFilename(FString& InPath);
    static CORE_API void NormalizeDirectoryName(FString& InPath);
    static CORE_API FString GetCleanFilename(const FString& InPath);
    static CORE_API FString GetBaseFilename(const FString& InPath, bool bRemovePath=true );    
    static CORE_API FString GetPlatformLocalizationFolderName();
    static CORE_API const TArray<FString>& GetRestrictedFolderNames();
    static CORE_API FString CreateStandardFilename(const FString& InPath);
    static CORE_API void MakeStandardFilename(FString& InPath);
    static CORE_API void MakePlatformFilename(FString& InPath);
    static CORE_API FString MakeValidFileName(const FString& InString, const TCHAR InReplacementChar = TEXT('\0'));
    static CORE_API FString CreateTempFilename( const TCHAR* Path, const TCHAR* Prefix = TEXT(""), const TCHAR* Extension = TEXT(".tmp") );
    static CORE_API FString GetExtension( const FString& InPath, bool bIncludeDot=false );
    static CORE_API FString SetExtension(const FString& InPath, const FString& InNewExtension);

    static CORE_API FString RootDir();
    static CORE_API FString LaunchDir();
    static CORE_API FString EngineDir();
    static CORE_API FString EngineUserDir();
    static CORE_API FString EngineContentDir();
    static CORE_API FString EngineConfigDir();
    static CORE_API FString EngineEditorSettingsDir();
    static CORE_API FString EngineIntermediateDir();
    static CORE_API FString EngineSavedDir();
    static CORE_API FString EnginePluginsDir();
    static CORE_API FString EngineDefaultLayoutDir();
    static CORE_API FString EngineProjectLayoutDir();
    static CORE_API FString EngineUserLayoutDir();   
    static CORE_API FString EnterpriseDir();
	static CORE_API FString EnterprisePluginsDir();
	static CORE_API FString EnterpriseFeaturePackDir();
    static CORE_API TArray<FString> GetExtensionDirs(const FString& BaseDir, const FString& SubDir, bool bCheckValid);
    static CORE_API FString EngineVersionAgnosticUserDir();
    static FString EnginePlatformExtensionDir(const TCHAR* Platform);
    static FString ProjectPlatformExtensionDir(const TCHAR* Platform);
    
    static CORE_API FString ProjectDir();
    static CORE_API FString ProjectContentDir();
    static CORE_API FString ProjectUserDir();
    static CORE_API FString ProjectConfigDir();
    static CORE_API const FString& ProjectSavedDir();
    static CORE_API FString ProjectIntermediateDir();
    static CORE_API FString ProjectPluginsDir();
    static CORE_API FString ProjectModsDir();
    static CORE_API FString ProjectPersistentDownloadDir();
    static CORE_API FString ProjectLogDir();
    
    static CORE_API FString ShaderWorkingDir();
    static CORE_API FString SourceConfigDir();
    static CORE_API FString GeneratedConfigDir();
    static CORE_API FString SandboxesDir();
    static CORE_API FString ProfilingDir();
    static CORE_API FString ScreenShotDir();
    static CORE_API FString BugItDir();
    static CORE_API FString VideoCaptureDir();
    static CORE_API FString AudioCaptureDir();

    static CORE_API FString AutomationDir();
    static CORE_API FString AutomationTransientDir();
    static CORE_API FString AutomationReportsDir();
    static CORE_API FString AutomationLogDir();
    static CORE_API FString CloudDir();
    static CORE_API FString GameDevelopersDir();
    static CORE_API FStringView DevelopersFolderName();
    static CORE_API FString GameUserDeveloperFolderName();
    static CORE_API FString GameUserDeveloperDir();
    static CORE_API FString DiffDir();
    static CORE_API FString GameAgnosticSavedDir();
    static CORE_API FString EngineSourceDir();
    static CORE_API FString GameSourceDir();
    static CORE_API FString FeaturePackDir();
    static CORE_API void RemoveDuplicateSlashes(FString& InPath);
    static CORE_API FString GetInvalidFileSystemChars();
    static CORE_API void TearDown();
}
文件I/O操作
// 路径字符串层
FPaths                          
├── Combine / Normalize / ConvertRelativePathToFull
├── ProjectDir / EngineDir / LaunchDir
└── FileExists()  // 委托 IFileManager::Get()
  
// 内容读写便利层(内部调用 IFileManager::Get())                  
FFileHelper                   
├── LoadFileToString / SaveStringToFile
└── LoadFileToArray / SaveArrayToFile

// 上层文件管理工具(文件管理抽象层,持有 IPlatformFile* 委托底层操作)                             
IFileManager                  
└── FFileManagerGeneric  // 通用实现,持有并委托 IPlatformFile*
    ├── FFileManagerGeneric::Get() 获取对应单例 
    ├── FileExists / Delete / Move / Copy
    ├── FindFiles / IterateDirectory
    └── CreateFileReader / CreateFileWriter


// 底层平台文件管理器(负责管理 IPlatformFile 链)
FPlatformFileManager          
├── FPlatformFileManager::Get() 获取对应单例 
└── GetPlatformFile()  // 返回低层 IPlatformFile

// 平台文件抽象层                             
IPlatformFile                 
├── FileExists / FileSize / DeleteFile
├── OpenRead / OpenWrite
├── IterateDirectory / FindFiles
└── 派生实现
    ├── 平台具体实现
    │   ├── FWindowsPlatformFile
    │   └── FLinuxPlatformFile
    └── 包装/装饰层
        ├── FPakPlatformFile
        └── FLoggedPlatformFile
// 头文件为 #include "Misc/FileHelper.h",一般默认该头文件可不包括
// FFileHelper 是提供静态文件读写功能的工具结构体,非常适合用于处理文本、二进制文件以及序列化数据(如 JSON);
struct FFileHelper
{
...
    // 底层为 IFileManager::Get().CreateFileReader()
    static CORE_API bool LoadFileToString(FString& Result, const TCHAR* Filename, EHashOptions VerifyFlags = EHashOptions::None, uint32 ReadFlags = 0 );
    static CORE_API bool LoadFileToStringArray( TArray<FString>& Result, const TCHAR* Filename );
    static CORE_API bool LoadFileToArray( TArray<uint8>& Result, const TCHAR* Filename, uint32 Flags = 0 );

    // 底层为 FileManager->CreateFileWriter(Filename, WriteFlags)
    static CORE_API bool SaveStringToFile( FStringView String, const TCHAR* Filename, EEncodingOptions EncodingOptions = EEncodingOptions::AutoDetect, IFileManager* FileManager = &IFileManager::Get(), uint32 WriteFlags = 0 );
    static CORE_API bool SaveStringArrayToFile( const TArray<FString>& Lines, const TCHAR* Filename, EEncodingOptions EncodingOptions = EEncodingOptions::AutoDetect, IFileManager* FileManager = &IFileManager::Get(), uint32 WriteFlags = 0 );
    static CORE_API bool SaveArrayToFile(TArrayView64<const uint8> Array, const TCHAR* Filename, IFileManager* FileManager = &IFileManager::Get(), uint32 WriteFlags = 0);
...
}
#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "MyBlueprintFunctionLibrary.generated.h"

UCLASS()
class MYCPP_API UMyBlueprintFunctionLibrary : public UBlueprintFunctionLibrary
{
	GENERATED_BODY()
public:
// 可在蓝图中调用这些函数,Filename为带路径的完整名字(可使用ProjectContentDir获取内容文件夹路径)
	UFUNCTION(BlueprintCallable, Category = "File")
	static bool LoadFileToString(FString& Ret, FString Filename);
	UFUNCTION(BlueprintCallable, Category = "File")
	static bool LoadFileToStringArray(TArray<FString>& Ret, FString Filename);

	UFUNCTION(BlueprintCallable, Category = "File")
	static bool SaveStringToFile(FString String, FString Filename);
	UFUNCTION(BlueprintCallable, Category = "File")
	static bool SaveStringArrayToFile(TArray<FString> Strings, FString Filename);
};
#include "MyBlueprintFunctionLibrary.h"
bool UMyBlueprintFunctionLibrary::LoadFileToString(FString& Ret, FString Filename) {	
	return FFileHelper::LoadFileToString(Ret, *Filename);
}
bool UMyBlueprintFunctionLibrary::LoadFileToStringArray(TArray<FString>& Ret, FString Filename) {
	return FFileHelper::LoadFileToStringArray(Ret, *Filename);
}
bool UMyBlueprintFunctionLibrary::SaveStringToFile(FString String, FString Filename) {
	return FFileHelper::SaveStringToFile(String, *Filename);
}
bool UMyBlueprintFunctionLibrary::SaveStringArrayToFile(TArray<FString> Strings, FString Filename) {
	return FFileHelper::SaveStringArrayToFile(Strings, *Filename);
}
// #include "GenericPlatform/GenericPlatformFile.h" 
// UE文件I/O体系的底层抽象接口,定义引擎与操作系统文件系统交互的标准协议,主要定义了核心接口类:IFileHandle/IPlatformFile
// 用于实现平台文件系统、Pak包、缓存、日志等底层功能,需要直接控制文件句柄时使用;
class IPlatformFile // 提供对文件和目录的整体操作
{
...
    // 纯虚函数会在具体派生平台实现
	virtual const TCHAR* GetName() const = 0;
	virtual bool FileExists(const TCHAR* Filename) = 0;
	virtual int64 FileSize(const TCHAR* Filename) = 0;
	virtual bool DeleteFile(const TCHAR* Filename) = 0;
	virtual bool DeleteFiles(const TArrayView<const TCHAR*>& Filenames)

    virtual IFileHandle* OpenRead(const TCHAR* Filename, bool bAllowWrite = false) = 0;
    virtual IFileHandle* OpenWrite(const TCHAR* Filename, bool bAppend = false, bool bAllowRead = false) = 0;

    virtual bool DirectoryExists(const TCHAR* Directory) = 0;
	virtual bool CreateDirectory(const TCHAR* Directory) = 0;
	virtual bool DeleteDirectory(const TCHAR* Directory) = 0;

    CORE_API virtual bool CreateDirectoryTree(const TCHAR* Directory);
    CORE_API virtual bool CopyDirectoryTree(const TCHAR* DestinationDirectory, const TCHAR* Source, bool bOverwriteAllExisting);
    CORE_API virtual bool CopyFile(const TCHAR* To, const TCHAR* From, EPlatformFileRead ReadFlags = EPlatformFileRead::None, EPlatformFileWrite WriteFlags = EPlatformFileWrite::None);
    CORE_API virtual bool DeleteDirectoryRecursively(const TCHAR* Directory);
    CORE_API virtual void FindFiles(TArray<FString>& FoundFiles, const TCHAR* Directory, const TCHAR* FileExtension);
    CORE_API virtual void FindFilesRecursively(TArray<FString>& FoundFiles, const TCHAR* Directory, const TCHAR* FileExtension);
...
}

class IFileHandle // 单个文件的具体读写操作
{
public:
    virtual int64 Tell() = 0;
    virtual bool Seek(int64 NewPosition) = 0;
    virtual bool SeekFromEnd(int64 NewPositionRelativeToEnd = 0) = 0;
    virtual bool Read(uint8* Destination, int64 BytesToRead) = 0;
    virtual bool ReadAt(uint8* Destination, int64 BytesToRead, int64 Offset) = 0;
    virtual bool Write(const uint8* Source, int64 BytesToWrite) = 0;
    virtual bool Flush(const bool bFullFlush = false) = 0;
    virtual bool Truncate(int64 NewSize) = 0;
...
}
// IPlatformFile 应用
IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile(); // 获取平台文件系统实例(获取具体派生类的实例)
PlatformFile.FileExists(*FilePath); // 判断文件是否存在
PlatformFile.DeleteFile(*FilePath); // 删除文件

// 打开文件进行读取
FString FilePath = TEXT("D:/MyProject/Data.bin");
TUniquePtr<IFileHandle> FileHandle(PlatformFile.OpenRead(*FilePath));
if (FileHandle)
{
    int64 FileSize = FileHandle->Size();
    uint8* Buffer = new uint8[FileSize];
    if (FileHandle->Read(Buffer, FileSize))
    {
        // 处理读取的数据
    }
    delete[] Buffer;
    // FileHandle 析构时自动关闭文件
}
// #include "HAL/FileManagerGeneric.h" 是IPlatformFile上层封装
// 实现了 IFileManager 接口,提供了跨平台的文件读写、目录操作等底层功能;
// 用于游戏逻辑中常见的文件读写、查询、目录管理等,API更友好;
class FFileManagerGeneric : public IFileManager
{
	UE_FORCEINLINE_HINT IPlatformFile& GetLowLevel() const
	{
		return FPlatformFileManager::Get().GetPlatformFile();
	}
public:
    FArchive* CreateFileReader( const TCHAR* Filename, uint32 ReadFlags=0 ) override;
    FArchive* CreateFileWriter( const TCHAR* Filename, uint32 WriteFlags=0 ) override;

    CORE_API bool Delete( const TCHAR* Filename, bool RequireExists=0, bool EvenReadOnly=0, bool Quiet=0 ) override;
	CORE_API bool IsReadOnly( const TCHAR* Filename ) override;
	CORE_API bool Move( const TCHAR* Dest, const TCHAR* Src, bool Replace=1, bool EvenIfReadOnly=0, bool Attributes=0, bool bDoNotRetryOrError=0 ) override;
	CORE_API bool FileExists( const TCHAR* Filename ) override;
	CORE_API bool DirectoryExists(const TCHAR* InDirectory) override;
	CORE_API void FindFiles( TArray<FString>& Result, const TCHAR* Filename, bool Files, bool Directories ) override;
	CORE_API void FindFilesRecursive( TArray<FString>& FileNames, const TCHAR* StartDirectory, const TCHAR* Filename, bool Files, bool Directories, bool bClearFileNames=true) override;
	CORE_API virtual uint32	Copy( const TCHAR* Dest, const TCHAR* Src, bool Replace = 1, bool EvenIfReadOnly = 0, bool Attributes = 0, FCopyProgress* Progress = nullptr, EFileRead ReadFlags = FILEREAD_None, EFileWrite WriteFlags = FILEWRITE_None ) override;
	CORE_API virtual bool	MakeDirectory( const TCHAR* Path, bool Tree=0 ) override;
	CORE_API virtual bool	DeleteDirectory( const TCHAR* Path, bool RequireExists=0, bool Tree=0 ) override;
...	
}
// FFileManagerGeneric 应用
FFileManagerGeneric::Get().FileExists(*FilePath) // 判断文件是否存在
FFileManagerGeneric::Get().FindFiles(FoundFiles, *Directory, TEXT(".txt")); // 查找目录下的所有 .txt 文件
FFileManagerGeneric::Get().DeleteDirectory(*FolderToDelete, true, true); // 删除一个目录及其所有内容

FString FileName = TEXT("D:/MyProject/Output.txt");
TUniquePtr<FArchive> Ar(FFileManagerGeneric::Get().CreateFileWriter(*FileName));
if (Ar)
{
    FString Data = TEXT("Hello, Unreal!");
    *Ar << Data;
    // FArchive 析构时会自动关闭文件
}

十二,Delay节点的C++实现

// LatentActionManager.h
// 蓝图里的 Delay、Retriggerable Delay、Move Component To 等节点底层依赖它实现;
USTRUCT(BlueprintInternalUseOnly)
struct FLatentActionInfo
{
	GENERATED_USTRUCT_BODY()

	UPROPERTY(meta=(NeedsLatentFixup = true))
	int32 Linkage;
	UPROPERTY()
	int32 UUID;
	UPROPERTY()
	FName ExecutionFunction;
	UPROPERTY(meta=(LatentCallbackTarget = true))
	TObjectPtr<UObject> CallbackTarget;

	FLatentActionInfo()
		: Linkage(INDEX_NONE)
		, UUID(INDEX_NONE)
		, ExecutionFunction(NAME_None)
		, CallbackTarget(NULL)
	{}

	FORCENOINLINE FLatentActionInfo(int32 InLinkage, int32 InUUID, const TCHAR* InFunctionName, UObject* InCallbackTarget)
		: Linkage(InLinkage)
		, UUID(InUUID)
		, ExecutionFunction(InFunctionName)
		, CallbackTarget(InCallbackTarget)
	{}
};
void UKismetSystemLibrary::Delay(const UObject* WorldContextObject, float Duration, FLatentActionInfo LatentInfo )
{
	if (UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull))
	{
		FLatentActionManager& LatentActionManager = World->GetLatentActionManager();
		if (LatentActionManager.FindExistingAction<FDelayAction>(LatentInfo.CallbackTarget, LatentInfo.UUID) == NULL)
		{
			LatentActionManager.AddNewAction(LatentInfo.CallbackTarget, LatentInfo.UUID, new FDelayAction(Duration, LatentInfo));
		}
	}
}

十三,JSON

// 注意添加依赖的模块
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "Json", "JsonUtilities" });
// Json文件,注意为英文标点符号
{ 
	"Name": "Tom", 
	"Age": 25,
	"IsReady": true,
	"Data":
	{
		"UserName": "ABC",
		"UserID": 123
	 }
 }
void AMyJSONActor::JsonRead(FString JsonString)
{
    TSharedRef<TJsonReader<TCHAR>> JsonReader = TJsonReaderFactory<TCHAR>::Create(JsonString);
    TSharedPtr<FJsonObject> JsonObject;

    if (FJsonSerializer::Deserialize(JsonReader, JsonObject) && JsonObject.IsValid())
    {
        FString Name;
        if (JsonObject->TryGetStringField(TEXT("Name"), Name))
            UE_LOG(LogTemp, Warning, TEXT("Name: %s"), *Name);

        int32 Age;
        if (JsonObject->TryGetNumberField(TEXT("Age"), Age))
            UE_LOG(LogTemp, Warning, TEXT("Age: %d"), Age);

        double Score;
        if (JsonObject->TryGetNumberField(TEXT("Score"), Score))
            UE_LOG(LogTemp, Warning, TEXT("Score: %f"), Score);

        bool bIsReady = false;
        if (JsonObject->TryGetBoolField(TEXT("IsReady"), bIsReady))
            UE_LOG(LogTemp, Warning, TEXT("IsReady: %s"), bIsReady ? TEXT("true") : TEXT("false"));

        const TSharedPtr<FJsonObject>* OutObject = nullptr;
        if (JsonObject->TryGetObjectField(TEXT("Data"), OutObject) && OutObject && (*OutObject).IsValid())
        {
            FString UserName;
            if ((*OutObject)->TryGetStringField(TEXT("UserName"), UserName))
                UE_LOG(LogTemp, Warning, TEXT("UserName: %s"), *UserName);

            int32 UserID;
            if ((*OutObject)->TryGetNumberField(TEXT("UserID"), UserID))
                UE_LOG(LogTemp, Warning, TEXT("UserID: %d"), UserID);
        }
    }
    else
    {
        UE_LOG(LogTemp, Error, TEXT("Failed to parse JSON: %s"), *JsonString);
    }
}
FString AMyJSONActor::JsonRead(FString InJsonString)
{
    TSharedRef<TJsonReader<>> JsonReader = TJsonReaderFactory<>::Create(InJsonString);
    TSharedPtr<FJsonObject> JsonObject;
    FString OutString;

    if (FJsonSerializer::Deserialize(JsonReader, JsonObject) && JsonObject.IsValid())
    {
        TSharedRef<TJsonWriter<>> JsonWriter = TJsonWriterFactory<>::Create(&OutString);
        FJsonSerializer::Serialize(JsonObject, JsonWriter);
    }

    return OutString;
}
FString AMyJSONActor::JsonWrite(const TMap<FString, FString>& InMap)
{
    FString JsonString;
    TSharedRef<TJsonWriter<>> JsonWriter = TJsonWriterFactory<>::Create(&JsonString);

    JsonWriter->WriteObjectStart();
    for (const TPair<FString, FString>& Pair : InMap)
    {
        JsonWriter->WriteValue(Pair.Key, Pair.Value);
    }
    JsonWriter->WriteObjectEnd();
    JsonWriter->Close();

    return JsonString;
}

十四,Runnable

// MyRunnable.h
#include "CoreMinimal.h"
class MYCPP_API FMyRunnable : public FRunnable
{
public:
	FMyRunnable();
	~FMyRunnable();

	virtual bool Init() override;
	virtual uint32 Run() override;
	virtual void Stop() override;
	virtual void Exit() override;
private:
	FThreadSafeBool bStopRequested;
};
// MyRunnable.CPP
#include "MyRunnable.h"
FMyRunnable::FMyRunnable()
{
	UE_LOG(LogTemp, Warning, TEXT("MyRunnable FMyRunnable()!"));
}

FMyRunnable::~FMyRunnable()
{
	UE_LOG(LogTemp, Warning, TEXT("MyRunnable ~FMyRunnable()!"));
}

bool FMyRunnable::Init()
{
	UE_LOG(LogTemp, Warning, TEXT("MyRunnable Init()!"));
	return true;
}

uint32 FMyRunnable::Run()
{
	while (!bStopRequested)
	{
		FPlatformProcess::Sleep(0.1f);
		UE_LOG(LogTemp, Warning, TEXT("MyRunnable Run() ... !"));
	}
	return uint32();
}

void FMyRunnable::Stop()
{
	bStopRequested = true;
	UE_LOG(LogTemp, Warning, TEXT("MyRunnable Stop()!"));
}

void FMyRunnable::Exit()
{
	UE_LOG(LogTemp, Warning, TEXT("MyRunnable Exit()!"));
}
// MyActor.h
UCLASS()
class MYCPP_API AMyActor : public AActor
{
	GENERATED_BODY()
...	
public:	
	UFUNCTION(BlueprintCallable)
	void StartThread();
	UFUNCTION(BlueprintCallable)
	void StopThread();

	FMyRunnable* MyRunnable = nullptr;
	FRunnableThread* MyRunnableThread = nullptr;
};
// MyActor.cpp
void AMyGameplayStaticsActor::StartThread()
{
	if(!MyRunnableThread)
	{
		MyRunnable = new FMyRunnable();
		MyRunnableThread = FRunnableThread::Create(MyRunnable, TEXT("MyRunnable"));
		if (!MyRunnableThread)
		{
			UE_LOG(LogTemp, Error, TEXT("Failed to create MyRunnableThread!"));
			delete MyRunnable;
			MyRunnable = nullptr;
		}
	}
	else
	{
		UE_LOG(LogTemp, Warning, TEXT("MyRunnableThread already running!"));
	}
}

void AMyGameplayStaticsActor::StopThread()
{
	// 先请求停止
	if (MyRunnable)
	{
		MyRunnable->Stop();
	}

	// 再等待线程退出
	if (MyRunnableThread)
	{
		MyRunnableThread->WaitForCompletion();
		delete MyRunnableThread;
		MyRunnableThread = nullptr;
	}

	// 最后删除 Runnable
	delete MyRunnable;
	MyRunnable = nullptr;
}

十五,编码

// MyCPP.Build.cs
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "EnhancedInput", "Json", "JsonUtilities", "HTTP" });
// MyBlueprintFunctionLibrary.h
#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "MyBlueprintFunctionLibrary.generated.h"

UCLASS()
class MYPROJECT_API UMyBlueprintFunctionLibrary : public UBlueprintFunctionLibrary
{
    GENERATED_BODY()

public:
    /** 文本 -> Base64(内部按 UTF-8 转字节) */
    UFUNCTION(BlueprintPure, Category="MyLib|Base64")
    static FString Base64EncodeString(const FString& Source);
    /** 原始字节 -> Base64 */
    UFUNCTION(BlueprintPure, Category="MyLib|Base64")
    static FString Base64EncodeBytes(const TArray<uint8>& Source);
    /** Base64 -> 文本(按 UTF-8 解码) */
    UFUNCTION(BlueprintPure, Category="MyLib|Base64")
    static bool Base64DecodeToString(const FString& Source, FString& OutDest);
    /** Base64 -> 原始字节 */
    UFUNCTION(BlueprintPure, Category="MyLib|Base64")
    static bool Base64DecodeToBytes(const FString& Source, TArray<uint8>& OutDest);

    UFUNCTION(BlueprintPure, Category="MyLib|Url")
    static FString UrlEncode(const FString& Source);
    UFUNCTION(BlueprintPure, Category="MyLib|Url")
    static FString UrlDecode(const FString& Source);

    UFUNCTION(BlueprintPure, Category = "MyLib|MD5")
    static FString MD5Encode(const FString& Source);
};
// MyBlueprintFunctionLibrary.cpp
#include "MyBlueprintFunctionLibrary.h"
#include "Misc/Base64.h"
#include "GenericPlatform/GenericPlatformHttp.h"

FString UMyBlueprintFunctionLibrary::Base64EncodeString(const FString& Source)
{
    return FBase64::Encode(Source);
}
FString UMyBlueprintFunctionLibrary::Base64EncodeBytes(const TArray<uint8>& Source)
{
    return FBase64::Encode(Source);
}
bool UMyBlueprintFunctionLibrary::Base64DecodeToString(const FString& Source, FString& OutDest)
{
    return FBase64::Decode(Source, OutDest);
}
bool UMyBlueprintFunctionLibrary::Base64DecodeToBytes(const FString& Source, TArray<uint8>& OutDest)
{
	return FBase64::Decode(Source, OutDest);
}

FString UMyBlueprintFunctionLibrary::UrlEncode(const FString& Source)
{
    return FGenericPlatformHttp::UrlEncode(Source);
}
FString UMyBlueprintFunctionLibrary::UrlDecode(const FString& Source)
{
    return FGenericPlatformHttp::UrlDecode(Source);
}

FString UMyBlueprintFunctionLibrary::MD5Encode(const FString& Source)
{
	return FMD5::HashAnsiString(*Source);
}

十六,时间戳

// 构造一个具体的日期时间:2026年11月6日 14点30分0秒
FDateTime SpecificDate(2026, 11, 6, 14, 30, 0);
// 通过 Ticks 构造(通常用于从序列化数据中恢复)
FDateTime FromTicks(638000000000000000LL); 
FDateTime Now = FDateTime::Now();
int32 Year = Now.GetYear();
int32 Month = Now.GetMonth(); // 1-12
int32 Day = Now.GetDay();
int32 Hour = Now.GetHour(); // 0-23
int32 Minute = Now.GetMinute();
int32 Second = Now.GetSecond();
int32 Millisecond = Now.GetMillisecond(); // 0-999
// 当前时间点
FDateTime NowLocal = FDateTime::Now();
FDateTime NowUtc   = FDateTime::UtcNow();
FString HttpDate = NowUtc.ToHttpDate(); // RFC 1123 格式,如Fri, 06 Nov 2026 06:30:00 GMT
// 构造时间段
FTimespan FiveMinutes = FTimespan::FromMinutes(5);
FTimespan TwoHours    = FTimespan::FromHours(2);
// 获取总时长
double TotalSeconds = FiveMinutes.GetTotalSeconds();
double TotalHours   = TwoHours.GetTotalHours();
// 与FTimespan的算术运算
FDateTime Start = FDateTime::Now();
FDateTime End = Start + FTimespan::FromMinutes(5); 
// 格式化为字符串
FString TimeString = FDateTime::Now().ToString();
// 从字符串解析
FString TimeStr = TEXT("2026.11.06-14.30.00.000");
FDateTime ParsedTime;
FDateTime::Parse(TimeStr, ParsedTime);
// 与Unix时间戳互转
int64 UnixSec = Now.ToUnixTimestamp(); // 转换为秒级时间戳
FDateTime FromUnix = FDateTime::FromUnixTimestamp(UnixSec); // 从时间戳还原

UEC++创建并绑定Widget控件 使用 UEC++ 创建 Widget 控件,并将控件绑定到 WidgetTree 中。 阅读详情

相关推荐

【UE】UEC++ DataTable数据表

DataTableComponent上有一个DataTablePath的属性,这里存储了DataTable的路径,在不同情况下设置不同的路径,就可以对不同的DataTable进行操作。声明一个组件,就可以在需要的类上挂载组件,进行对DataTable的处理。

Hank_W 2351

10.25UEC++/小试牛刀(笨鸟先飞案例)

角色可能是每个游戏的最重要的部分,所以一般可以先从角色入手,如果游戏很复杂,可以进行拆分设计。引入Paper2D模块:会加载FlipBook,类似连续帧数图片;选取Sprite类型的资产(但是图片加载进入时,是纹理类型)UEC++中编写BirdPawn代码,首先创建根组件(能力)将我们创建的BirdPawn设置为默认Pawn。怎么知道一个资源需要什么样的组件呢?创建渲染组件:并且设置为根节点。创建一个FlipBook组件。创建飞行动画:(三帧)

qq_44160901的博客 1496

UEC++ 虚幻5第三人称射击游戏(一)

动画IK绑定,动画重定向,创建武器追踪线,调整追踪线位置,伤害效果,射击特效

qq_44924388的博客 1939

11UEC++吃豆人[UI、背景音乐、打包游戏]

UI、背景音乐、打包游戏

zhang2362167998的博客 414

UEC++ 虚幻5第三人称射击游戏(二)

为了区别头部与身体部分的受到伤害不同,我们需要将测试人偶的物理材质表面重新区分一下在项目的物理界面新建两个物理表面,一个是普通伤害,一个是高伤害表示创建两个物理材质作为到时候的区分,然后将其表面类型换成我们刚才新建的类型然后在测试人偶的物理资产里面将头部物理材质改为易受伤害的,其它为默认的。

qq_44924388的博客 1598

UEC++接口开发指南:从基础到高级应用

在游戏开发中,接口是实现多态和模块化设计的关键技术。UEC++接口通过特殊的宏系统实现C++与蓝图的互操作,使用UINTERFACE和IInterface结构定义契约式编程规范。相比标准C++抽象类,UEC++接口必须遵循反射系统要求,所有函数需使用UFUNCTION说明符标记。这种设计在虚幻引擎中广泛应用于交互系统、AI行为、UI控制等场景,支持BlueprintCallable和BlueprintImplementableEvent等特性,既保证了类型安全又提供了蓝图灵活性。掌握接口的Blueprint

weixin_32147929的博客 316

UE4 C++ 小项目汇总

**项目实现:**一个旋转上下的立方体 项目地址:传送门 **操作流程:**创建一个c++c++c++类,以ActorActorActor为父类,然后编写代码,.h.h.h中可以写点属性,材质,.cpp.cpp.cpp中实现构造函数和Tick()Tick()Tick()函数 函数精华: FloatingActor.hFloatingActor.hFloatingActor.h //声明的是 StaticMeshComponent,它将担当对象的视觉表示。 public: UPROPERTY(Visible

肘子的博客 713

UE知识点记录5-UEC++获取轴向量

蓝图API中获取向前向量、获取向右向量、获取向上向量对应的UEC++API

weixin_45295931的博客 1754

UE-CPP游戏客户端开发面筋1

本文系统梳理了C++核心知识点,涵盖基础语法、面向对象、内存管理、模板泛型、C++11特性及UE引擎相关内容。重点包括:值传递与引用传递区别、智能指针原理、多态实现机制、内存对齐优化、STL容器特性、移动语义与完美转发等现代C++特性。同时详解了面向对象设计中的虚函数、继承、重载等核心概念,以及内存管理中的new/delete、RAII等技术。最后介绍了UE引擎特有的Gameplay框架、UObject系统和反射机制。文章内容全面深入,既包含基础概念解析,也涉及性能优化实践,是C++开发者系统掌握语言特性的

m0_72340664的博客 421

关于UEC++一些问题的解决方案

本人在UE5、UE4学习中的一些问题和解决方案

qq_17409753的博客 842

实测iOS和安卓大不同!用NFC触发小程序跳转的完整避坑指南(含NFC Tools配置)

本文深入解析iOS与安卓在NFC触发小程序跳转时的核心差异,提供微信与支付宝小程序的NFC实现方案及优化技巧。涵盖跨平台兼容性测试方法论、性能优化策略及异常处理方案,帮助开发者避开常见陷阱,提升跳转成功率。特别针对NFC Tools配置和微信Universal Link使用进行详细指导。

asdfghjkl978564的博客 433

从零开始使用UEC++创建一个简易的AI

基于UE4.27使用c++创建一个简易的AI

yuemuc的博客 954

UE5.4像素流送实战:GPU驱动、WebRTC与系统级调优全解析

像素流送是将实时3D渲染画面通过WebRTC编码推流至浏览器的关键技术,其本质是GPU硬件编码(如NVENC)与低延迟网络传输(UDP+BBR)的深度协同。理解其原理需跨越图形API(Vulkan/DX12)、实时音视频(H.264/H.265)、WebRTC信令与NAT穿透四大技术层。技术价值在于突破终端算力限制,支撑远程工业仿真、云VR看房等高交互场景;但落地难点集中于基础设施层——如NVIDIA驱动与UE5.4.3的CUDA Toolkit 12.2兼容性、Linux内核TCP/UDP参数调优、GPU

weixin_32943857的博客 614

UMG在UEC++的使用

本文介绍了在虚幻引擎中使用UMG实现UI按钮事件的方法。首先需要在C++中创建继承UserWidget的类,并通过BindWidget属性绑定蓝图中的按钮控件。然后在NativeConstruct函数中添加按钮点击事件的动态绑定,并实现对应的点击方法逻辑。文章还展示了如何在关卡蓝图中设置UMG的初始显示,以及通过定时器处理延迟操作。最后实现了UI按钮点击后切换视角、控制角色等功能,并提供了退出游戏的功能实现。

streams_star的博客 785

wsl + vscode 离线配置 ERROR: Faild to download https://update.code.visualstudio.com

很多情况下是没有现成的VS Code Server程序的,需要手动下载。记住上面的Commit ID,可以通过下面的链接下载 vscode-server-linux-x64.tar.gz。然后将vscode-server-linux-x64.tar.gz上传在服务器上的$HOME/.vscode-server/bin文件夹中,解压。在wsl中下载不了 vscode 的核心。运行下面两行命令,建立空的$HOME/.vscode-server/bin文件夹。重新运行 wsl与vs的链接即可。

点云算法工程师 3789

OpenClaw Manager Pro - 可视化管理工具

OpenClaw Manager Pro 是一个现代化的 Web 管理界面,用于管理多个 OpenClaw Gateway 实例。🎨美观的界面:深色主题,渐变设计,现代化 UI📡实时监控:WebSocket 实时推送状态和日志🔧完整管理:创建、编辑、删除、启动、停止 Gateway📊多渠道支持:飞书、Telegram、Discord、Slack、微信、钉钉🧩技能管理:可视化配置 61+ 个技能🌍跨平台:支持 macOS、Linux、Windows使用自定义模型系统会自动在中创建},

Maynor的博客 3085

UEC++实现人物按照A星路线移动

这篇博客摘要介绍了在Unreal Engine 5中实现A*寻路算法的关键实现细节。

streams_star的博客 1293

《时序分析、时序约束和时序收敛》专栏的说明与导航

《时序分析、时序约束和时序收敛》专栏的说明与导航(建议阅读)。

孤独的单刀 14万+

UEC++ | spawn 、 模板

这是一个系列视频的第二天,期待60天后的蜕变

wunan233的博客 529

第6篇:ESP32连接无源喇叭播放音乐《涛声依旧》

相邻两个音之间的倍数是2^(1/12),约等于1.0594630943592952645618252949463)。这样一来,高八度的do频率是低八度do的两倍。我们把频率440赫兹作为中央C ,C调的歌就是把频率440赫兹当作do,乐谱中写作:1=C。一个八度分成12份,八个八度的十二平均音率对应的频率如下(从33Hz到7902Hz)。相邻的两个音之间的差一个半音,12345671之间的间隔分别是“全全半全全全半”。D调的歌就是把从440后面第二个音494赫兹当作do,乐谱中写作1=D。

Txwtech 1421

风控域——知识图谱相关原理

本文全面介绍了知识图谱在风控领域的应用原理。从基础知识入手,阐述了知识图谱的定义、概念及应用场景,重点聚焦于金融风控。进一步讲解了知识图谱的技术架构,涵盖计算、融合与构建等方面。深入探讨了知识表示、建模、抽取、挖掘、存储、融合、检索和推理等关键技术原理,并结合金融风控实际需求,详细说明了各技术环节的具体应用方法与流程,为构建高效风控知识图谱提供了理论与实践指导。

庄小焱 606

UEC++第四天(补充)|FString、日志

你可以自定义日志类型并且讲它在代码中使用。如果需要有多个文件以及框架需要进行不同的分类,这个功能会非常的实用声明_日志_类别 扩展(<日志_类别>,<冗余等级>,全部)

wunan233的博客 967

MIKE水动力笔记5_建立水动力模型

前文已经讲了如何制作网格文件(.mdf文件),这一博文就讲如何建立水动力模型。

晏长街的博客 3万+

【UE】UEC++委托代理

UEC++委托代理

Hank_W 1243

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

UEC++第13天| 字体集、UEC中的 -> :: .

在做的案例中,

wunan233的博客 393

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

UEC++第10天|UEC++获取对象、RTTI是C++

最近在写UEC项目,这里写几个案例里的问题,还在学习阶段。

wunan233的博客 429

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

上一篇: UEC++ 常用成员函数
下一篇: Niagara 知识点
挨代码
博客等级 码龄5年 1696粉丝 384原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值