UE4C++代码编写过程中常用的方法代码片段(方便小编做个CV程序员)

UE4C++代码编写过程中常用的方法代码片段

前言

UE4有些代码块与方法容易遗忘或者有些块比较懒得写,此篇章用于记录常见的一些东西,便于自己查找拷贝使用,毕竟高端的程序员都是CV程序员,小编正在努力中(Ctrl+C Ctrl+V)

注:此篇章会随时增加,有些可能不完善,可能是因为懒得写

正文

日志

屏幕上的日志打印

#include "Engine.h"
GEngine->AddOnScreenDebugMessage(-1, 20, FColor::Red, FString(TEXT("GO!")));
#include "Kismet/KismetSystemLibrary.h"
UKismetSystemLibrary::PrintString(this, TEXT("Successed!"));
#include "Kismet/KismetSystemLibrary.h"
int num;
UKismetSystemLibrary::PrintString(this, TEXT("GOGOGO :%d"), num);

普通日志(Output Logs)

UE_LOG宏输出Log,第一个参数为Log的分类(需要预先定义)。第二个参数为类型,有Log、Warning、Error三种类型。Log为灰色,Warning为黄色,Error为红色。TEXT内容可以根据需要自行构造。

UE_LOG(LogTemp, Warning, TEXT("GO GO  %s"), *(FString("GO!")));

%s字符串(FString)
%d整型数据(int32)
%f浮点形(float)

自定义LogCategory

在使用DEFINE_LOG_CATEGORY_STATIC自定义Log分类的时候,我们可以将此宏放在你需要输出Log的源文件顶部。为了更方便地使用,可以将它放到PCH文件里,或者模块的头文件里(原则上是将Log分类定义放在被多数源文件include的文件里)。

DEFINE_LOG_CATEGORY_STATIC(LogMyCategory,Warning,All);

在这里插入图片描述
在这里插入图片描述

查看日志

Game模式
在Game(打包)模式下,记录Log需要在启动参数后加-Log。
编辑器模式
在编辑器下,需要打开Log窗口(Window->DeveloperTools->OutputLog

字符串转换

FString from int

int a=10;
FString::FromInt(a)

FString to float

FString TestString = "121.56";
float myFloat = FCString::Atof(*TestString);

FString to FText

FString TestString = TEXT("str");  
FText TestFText = FText::FromString(TestString );

FString to FName

FName TestFName = FName(FString("Text"));

FString to std::string

#include <string>
std::string TestString = "Text"; 
FString TextString(TestString.c_str())

std::string to FString

#include <string>
FString TestFString = "Text";
std::string myString (TCHAR_TO_UTF8(*TestFString));

FText to FString

FText TestFtext;
FString TestFString = TestFtext.ToString();

FString to const char*

*FString ()
const char* testChar = TCHAR_TO_ANSI(*FString("Text"));

值转std::to_string

std::string to_string( long value );
std::string to_string( long long value );
std::string to_string( unsigned value );
std::string to_string( unsigned long value );
std::string to_string( unsigned long long value );
std::string to_string( float value );
std::string to_string( double value );
std::string to_string( long double value );

生成与实例

如果你的类是一个纯C++类型(F开头),你可以通过new来产生对象。
如果你的类继承自UObject但不继承自Actor,你需要通过NewObject函数来产
生出对象。
如果你的类继承自AActor,你需要通过SpawnActor函数来产生出对象。
生成

NewObject<T>()
GetWorld()->SpawnActor<T>(UClass, FTransform);

创建相机:

 UCameraComponent* Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera0"));

根据路径加载生成人物基础模型

USkeletalMesh* Sk_Mesh = Cast<USkeletalMesh>(StaticLoadObject(USkeletalMesh::StaticClass(),
NULL,
TEXT("/Game/Mannequin/Character/Mesh/SK_Mannequin"))
);

基于路径创建对应的动画实例

static ConstructorHelpers::FClassFinder<UAnimInstance> meshAnima(
		TEXT("/Game/Mannequin/Animations/ThirdPerson_AnimBP")
	);

查找与遍历

调用此方法需要给需要遍历的Actor设置好Tag

#include "Kismet/GameplayStatics.h"
TArray<AActor*> Actors;
UGameplayStatics::GetAllActorsWithTag(GetWorld(), TEXT("ActorTag"), Actors);
for (AActor* Actor: Actors)
{
}

在这里插入图片描述
获取所有的Actor

#include "Kismet/GameplayStatics.h"
TArray<AActor*> Actors;
UGameplayStatics::GetAllActorsOfClass(GetWorld(), AActor::StaticClass(), Actors); 
for (AActor* Actor : Actors)
{ 
}

注:以上俩种方式只能用于Actor

对继承自Object的物体进行遍历,这种遍历方式会多出一倍,因为它会把所有引用的地方都遍历出来,而PIE运行时候是PIE的world里运行,这时候,你编辑器的world加PIE的world就是俩倍,如果只想要PIE的,就应该采用下方的方法

for (TObjectIterator<USkeletalMeshComponent> It; It; ++It)
	{
		USkeletalMeshComponent* SkelComp = *It;
		if( SkelComp->GetScene() == InWorld->Scene )
		{
			SkelComp->bShowPrePhysBones = bShowPrePhysSkelBones;
			SkelComp->MarkRenderStateDirty();
		}
	}
for (TActorIterator<AStaticMeshActor> ActorItr(GetWorld()); ActorItr; ++ActorItr)
	{
    
    
		AStaticMeshActor* Mesh = *ActorItr;
		FString str1 = ActorItr->GetName();
		GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Green, FString::Printf(TEXT("%s"), *str1));
		FString str2 = ActorItr->GetActorLocation().ToString();
		GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Green, FString::Printf(TEXT("%s"), *str2));
	}

各种说明符

注:此处建议看官方文档
类说明符
关键词,用于在声明UClasses时指定类相对于引擎和编辑器的各个方面的行为。
https://docs.unrealengine.com/4.27/zh-CN/ProgrammingAndScripting/GameplayArchitecture/Classes/Specifiers/

UFunction声明
UFunction 是虚幻引擎4(UE4)反射系统可识别的C++函数。UObject 或蓝图函数库可将成员函数声明为UFunction,方法是将 UFUNCTION 宏放在头文件中函数声明上方的行中。宏将支持 函数说明符 更改UE4解译和使用函数的方式。
https://docs.unrealengine.com/4.27/zh-CN/ProgrammingAndScripting/GameplayArchitecture/Functions/

属性UPROPERTY
https://docs.unrealengine.com/4.27/zh-CN/ProgrammingAndScripting/GameplayArchitecture/Properties/

结构体

USTRUCT([Specifier, Specifier, ...])
struct FStructName
{
    GENERATED_BODY()
};

https://docs.unrealengine.com/4.27/zh-CN/ProgrammingAndScripting/GameplayArchitecture/Structs/

杂项知识点

在.h中声明你的类:如果你的类继承自UObject,你的类名上方需要加入
UCLASS()宏。同时,你需要在类体的第一行添加GENERATED_UCLASS_BODY()
宏,或者GENERATED_BODY()宏。前者需要手动实现一个带有const FObject Initializer&参数的构造函数。后者需要手动实现一个无参数构造函数。注意
笔者说的是“实现”而非声明。

UBlueprintAsyncActionBase异步蓝图节点
IInputProcessor输入类(不阻塞不吞噬输入类型)

反射方式获取类属性设置属性

	UYourClassName* YourClassName = NewObject<UYourClassName>();
	UClass* YourClassNameClass = YourClassName->GetClass();
	//获取类名称
	FString ClassName = YourClassNameClass->GetName();
	UE_LOG(LogTemp, Warning, TEXT("你的类名是 %s."),*ClassName);
	//获取类标记
	bool bHasMinimalAPI = YourClassNameClass->HasAnyClassFlags(EClassFlags::CLASS_MinimalAPI);

	//类属性遍历
	for (FProperty* Property = YourClassNameClass->PropertyLink; Property; Property = Property->PropertyLinkNext)
	{
    
    
		//获取属性名称
		FString PropertyName = Property->GetName();
		//获取属性类型
		FString PropertyType = Property->GetCPPType();
		//检测类里面是否存在FString类型
		if (PropertyType == "FString")
		{
    
    
			FStrProperty* StringProperty = CastField<FStrProperty>(Property);
			void* Addr = StringProperty->ContainerPtrToValuePtr<void>(YourClassName);
			FString PropertyValue = StringProperty->GetPropertyValue(Addr);
			//获取属性元数据
			FString CategoryName = StringProperty->GetMetaData(TEXT("Category"));

			StringProperty->SetPropertyValue(Addr,"SettingValue");
			FString PropertyValueAfterSetting = StringProperty->GetPropertyValue(Addr);

		}

	}

	//类方法遍历
	for (TFieldIterator<UFunction> IteratorOfFunction(YourClassNameClass); IteratorOfFunction; ++IteratorOfFunction)
	{
    
    
		UFunction* Function = *IteratorOfFunction;
		//获取方法名称
		FString FunctionName = Function->GetName();
		//获取方法标记
		EFunctionFlags FunctionFlags = Function->FunctionFlags;

		for (TFieldIterator<FProperty> IteratorOfParam(Function); IteratorOfParam; ++IteratorOfParam)
		{
    
    
			FProperty* Param = *IteratorOfParam;
			FString ParamName = Param->GetName();
			FString ParamType = Param->GetCPPType();
			EPropertyFlags ParamFlags =  Param->GetPropertyFlags();
		}
	}

	//获取父类的名称
	UChildClassName* MiddleYourClassName = NewObject<UChildClassName>();
	UClass* ParentClass = MiddleYourClassName->GetClass()->GetSuperClass();
	FString ParentClassName = ParentClass->GetName();
	//判断一个类是否是另一个类子类
	UClass* Class1 = UChildClassName::StaticClass();

	if (Class1->IsChildOf(Class2))
	{
    
    
		UE_LOG(LogTemp, Warning, TEXT("Class1 is Class2's child."));
	}


	//查找特定类的所有子类
	TArray<UClass*> ClassResults;
	GetDerivedClasses(UYourClassName::StaticClass(), ClassResults, true);
	for (int32 Index = 0; Index < ClassResults.Num(); Index++)
	{
    
    
		UClass* ClassResult = ClassResults[Index];
		FString ClassResultName = ClassResult->GetName();
	}

	//查找由特定类对象
	UYourClassSpecial* GMS = NewObject<UYourClassSpecial>(this, FName("GSM1"));
	TArray<UObject*> ObjectResults;
	GetObjectsOfClass(UYourClassSpecial::StaticClass(), ObjectResults, false);
	for (UObject* ObjectResult : ObjectResults)
	{
    
    
		FString ObjectResultName = ObjectResult->GetName();
	}
	//根据字符串查找相应的类
	UClass* FindedClass = FindObject<UClass>(ANY_PACKAGE, *FString("UYourClass"), true);
	if (FindedClass)
	{
    
    
		UE_LOG(LogTemp, Warning, TEXT("已找到UYourClass类"));
	}

	//根据字符串查找枚举
	UEnum* Enum = FindObject<UEnum>(ANY_PACKAGE, *FString("EnumYourType"), true);
	if (Enum)
	{
    
    
		UE_LOG(LogTemp, Warning, TEXT("找到EnumYourType枚举"));

		//遍历枚举项名称
		for (int32 Index = 0; Index < FindedEnum->NumEnums(); Index++)
		{
    
    
			FString EnumItemName = FindedEnum->GetNameStringByIndex(Index);
			//undo
		}
	}

	//通过名称查找蓝图类

	UBlueprint* Blueprint = FindObject<UBlueprint>(ANY_PACKAGE, *FString("BP_BlueprintClass"));
	if (Blueprint)
	{
    
    
		//判断该类是蓝图类,还是Native类
		if (FindedBlueprint->IsNative())
		{
    
    
			UE_LOG(LogTemp, Warning, TEXT("BP_BlueprintClass是原生类"));
		}
		else
		{
    
    
			UE_LOG(LogTemp, Warning, TEXT("BP_BlueprintClass是蓝图类"));
		}
	}

	//遍历所有类
	FString AllClassNames = "";
	for (TObjectIterator<UClass> ClassIt; ClassIt; ++ClassIt)
	{
    
    
		FString InterClassName = ClassIt->GetName();
		AllClassNames += InterClassName;
	}
	//根据名称获取类方法
	UGoodMiddleYourClassName* GoodMiddleYourClassName = NewObject<UGoodMiddleYourClassName>();
	UClass* GoodMiddleYourClassNameClass = GoodMiddleYourClassName->GetClass();
	if (GoodMiddleYourClassNameClass)
	{
    
    
		UFunction* PlayFunction = GoodMiddleYourClassNameClass->FindFunctionByName(TEXT("Play"), EIncludeSuperFlag::ExcludeSuper);
		if (PlayFunction)
		{
    
    
			FString PlayFunctionName = PlayFunction->GetName();
			UE_LOG(LogTemp, Warning, TEXT("Function with name:%s has finded in GoodMiddleYourClassNameClass"),*PlayFunctionName);

			//通过反射调用类方法
			//1.给所有方法参数分配空间并初始化为0
			uint8* AllFunctionParam = static_cast<uint8*>(FMemory_Alloca(PlayFunction->ParmsSize));
			FMemory::Memzero(AllFunctionParam, PlayFunction->ParmsSize);

			//2.给所有方法参数赋值
			for (TFieldIterator<FProperty> IteratorOfFunctionParam(PlayFunction); IteratorOfFunctionParam; ++IteratorOfFunctionParam)
			{
    
    
				FProperty* FunctionParam = *IteratorOfFunctionParam;
				FString FunctionParamName = FunctionParam->GetName();
				if (FunctionParamName == FString("InGame"))
				{
    
    
					*FunctionParam->ContainerPtrToValuePtr<FString>(AllFunctionParam) = "Ball";
				}
			}

			//3.调用方法
			GoodMiddleYourClassName->ProcessEvent(PlayFunction, AllFunctionParam);
		}

		//Function.invoke
		UFunction* GoHomeFunction = GoodMiddleYourClassNameClass->FindFunctionByName(TEXT("YourFunction"), EIncludeSuperFlag::ExcludeSuper);
		if (GoHomeFunction)
		{
    
    
			//1.给所有方法参数分配空间并初始化为0
			uint8* AllFunctionParam = static_cast<uint8*>(FMemory_Alloca(GoHomeFunction->ParmsSize));
			FMemory::Memzero(AllFunctionParam, GoHomeFunction->ParmsSize);

			//2.创建FFrame
			FFrame Frame(nullptr, GoHomeFunction, &AllFunctionParam);

			//3.调用Invoke
			GoHomeFunction->Invoke(GoodMiddleYourClassName, Frame, &AllFunctionParam + GoHomeFunction->ReturnValueOffset);

			//4.获取返回值
			int* ResultValue = (int*)(&AllFunctionParam + GoHomeFunction->ReturnValueOffset);
			UE_LOG(LogTemp, Warning, TEXT("YourFunction方法的返回值是 %d"), *ResultValue);
		}
	}

文件读写访问

虚幻引擎提供了与平台无关的文件读写与访问接口,即FPlatformFileManager,该类在PlatformFilemanager.h中,Core模块下

 FPlatformFileManager::Get()->GetPlatformFile();

文件拷贝

提供文件目录和文件的拷贝操作:
CopyDirectoryTree 递归拷贝某个目录;
CopyFile 拷贝当前文件。

文件创建

提供创建文件和目录的操作,目录创建成功或者目录已经存在
都会返回真:
CreateDirectory 创建目录;
CreateDirectoryTree 创建一个目录树,即给定一个路径字符串,如果对应路径的父目录不存在,也会被创建出来。

文件删除

删除指定目录或文件,成功删除返回真,否则失败:
DeleteDirectory 删除指定目录;
DeleteDirectoryRecursively递归删除指定目录;
DeleteFile 删除指定文件。

文件移动

MoveFile移动文件

获取文件所对应的属性

提供对文件、目录的属性访问操作:
DirectoryExists 检查目录是否存在;
FileExists 检查文件是否存在;
GetStateData 获得文件状态信息,返回FFileStatData类型对象,这个对象其实包含了足够的状态信息,
GetAccessTimeStamp 获得当前文件上一次访问的时间;
SetTimeStamp 设置文件的修改时间;
FileSize获得文件大小,如果文件不存在返回-1;
IsReadOnly文件是否只读。

文件遍历函数

该类函数都需要传入一个FDirectoryVisitorFDirectoryStatVisitor对象作为参数。你可以创造一个类继承自该类,然后重写Visit函数。每当遍历到一个文件或者目录时,遍历函数会调用Visitor对象的Visit函数以通知执行自定义的逻辑:IterateDirectory 遍历某个目录;
IterateDirectoryRecursively 递归遍历某个目录;IterateDirectoryStat 遍历文件目录状态,Visit函数参数为状态对象而非路径字符串;IterateDirectoryStatRecursively 同上,递归遍历。

文件读写

OpenRead打开一个文件用于读取,返回IFileHandle类型的句柄用于读取;
OpenWrite打开一个文件用于写入,返回IFileHandle类型的句柄。同时,针对一些极其普遍的需求,虚幻引擎提供了一套更简单的方式用于读写文件内容,即FFileHelper类,位于CoreMisc头文件中,提供以下静态函数:LoadFileToArray 直接将路径指定的文件读取到一个TArray类型的二进制数组中;LoadFileToString直接将路径指定的文本文件读取到一个FString类型的字符串中。请注意,字符串有长度限制,不要试图读取超大文本文件;
SaveArrayToFile保存一个二进制数组到文件中;
SaveStringToFile保存一个字符串到指定文件中;
CreateBitmap在硬盘中创建一个BMP文件;
LoadANSITextFileToStrings读取一个ANSI编码的文本文件到一个字
符串数组中,每行对应一个FString类型的对象。

配置文件

虚幻引擎提供的专门读写配置文件的类——GConfi

写配置

GConfig->SetString(
TEXT("MySection"),
TEXT("Name"),
TEXT("李白"),
FPaths::GameDir()/ "MyConfig.ini");

在这里插入图片描述

读配置

FString Result;
GConfig->GetString(
TEXT("MySection"),
TEXT("Name"),
Result,
FPaths::GameDir() / "MyConfig.ini");

在这里插入图片描述

Slate部分知识点

Slot获取

通过Expose的方式将其暴露出去,然后达到更改变量值去修改Slot属性的目的
在这里插入图片描述
在这里插入图片描述

ENum字符串与值直接的转行

通常情况下,网络上的文章都是以下面方式获取的,但是UE5会报以下警告ANY_PACKAGE has been deprecated. Either use full path name of objects (including classes) or provide a valid Outer for *FindObject* functions,因为,本着程序员的消出警告的开发原则,小编还是采用了新的方式来获取。

FString getBodyEnumAsString(EBodyPosition value)
{
    
    
	const UEnum* EnumPtr = FindObject<UEnum>(ANY_PACKAGE, TEXT("EBodyPosition"), true);
	if (!EnumPtr) return FString("Invalid");

	return EnumPtr->GetEnumName((int32)value);
}

无警告方式:

//通过枚举值获取对应字符串
template<typename TEnum>
FString GameDataHelper::GetEnumValueAsString(const TCHAR* PathName, TEnum Value)
{
	const UEnum* EnumPtr = FindObject<UEnum>(nullptr, PathName, true);
	if (!EnumPtr)
	{
		Print("UEnum is not InValid type. GetEnumValueAsString");
		return FString("Invalid");
	}
	return EnumPtr->GetNameStringByValue((int64)Value);
}
//通过对应字符串获取枚举值
template<typename TEnum>
TEnum GameDataHelper::GetEnumValueFromString(const TCHAR* PathName, FString Value)
{
	const UEnum* EnumPtr = FindObject<UEnum>(nullptr, PathName, true);
	if (!EnumPtr)
	{
		Print("UEnum is not InValid type . GetEnumValueFromString");
		return TEnum(0);
	}
	return (TEnum)EnumPtr->GetIndexByNameString(Value);
}

传参示例:

UENUM()
enum class ECultureTeam : uint8
{
    
    
	EN = 0,
	ZH = 1,
	MAX = 2
};
GetEnumValueAsString<ECultureTeam>(TEXT("/Script/Game.ECultureTeam"), CurrentCulture)
GetEnumValueFromString<ECultureTeam>(TEXT("/Script/Game.ECultureTeam")

TEXT("/Script/Game.ECultureTeam"传参中,Game对应枚举所在的模块,ECultureTeam则是自己的枚举类型

猜你喜欢

转载自blog.csdn.net/weixin_56946623/article/details/123572434