VR技术开发-UE5(Unreal Engine)
虚幻引擎UE5入门教程第一课:安装虚幻引擎
UE5官方文档
UE社区文章
UE bilibili官方主页
【UFSH2023】总有一个你不知道的虚幻引擎调试技巧 | 陈拓 Epic Games
【bilibili】UE5 C++零基础全网全流程开发从入门到进阶教程合集(持续跟新中)
【bilibili】UE5 3A级独立游戏开发全流程
https://ue5wiki.com/
UE4,UE5虚幻引擎源码版下载
【官方文档】从GitHub下载虚幻引擎源代码
【知乎】凌泽-UE系列文章
虚幻C++课程笔记(系列文章)
Unreal Engine 5 开发 — C++编程基础
UE开发(系列文章)
Matt 的UE探索站(系列文章-好)
1、概述
VR(Virtual Reality)虚拟现实,是一种通过计算机生成的三维图像和声音等多种感官反馈手段,使用户获得身临其境的仿真体验的技术。目前主要应用于游戏、教育、医疗等领域,并逐渐扩展到虚拟旅游、设计模拟、军事训练等方面。
开发引擎:Unity、Unreal Engine
2D设计工具:PS、AI
3D建模工具:3dmax、C4D、blender
常用的VR SDK:steam VR plugin、vive input utility
2、UE5
UE4 C++编程基础记录
虚幻引擎为您提供在构建过程中生成代码的工具。这些工具拥有一些类命名规则。如命名与规则不符,将触发警告或错误。下方的类前缀列表说明了命名的规则。
派生自 Actor 的类前缀为 A,如 AController。
派生自 对象 的类前缀为 U,如 UComponent。
枚举 的前缀为E,如 EFortificationType。
接口 类的前缀通常为 I,如 IAbilitySystemInterface。
模板类的前缀为 T,如 TArray。
派生自 SWidget(Slate UI)的类前缀为 S,如 SButton。
其余类的前缀均为 字母 F ,如 FVector。
| 前缀 | 说明 |
|---|---|
| A | 继承自Actor的类 |
| U | 继承自UObject,但不继承自Actor的类 |
| E | 枚举 的前缀 |
| I | 接口 类的前缀 |
| T | 模板类的前缀 |
| S | 派生自 SWidget(Slate UI)的类前缀 |
| H | HitResult相关类前缀 |
| F | 其余类,纯c++类 |
基础标记:都是UE5使用其自身的反射实现,来启动动态功能,比如,垃圾回收,序列化,网络复制和蓝图/C++通信。这些功能你可以根据需求选择的加入,只要为相应的类型添加正确的标记就可生成反射数据。
| 标记 | 说明 |
|---|---|
| UCLASS() | 告知虚幻引擎生成类的反射数据。类必须派生自 UObject。 |
| USTRUCT() | 告知虚幻引擎生成结构体的反射数据。 |
| GENERATED_BODY() | UE4 使用它替代为类型生成的所有必需样板文件代码。就是说这个类不直接使用父类的声明。但是,你必须得去实现,自己去声明,否则就会报错。更多信息 |
| UPROPERTY() | 使 UCLASS 或 USTRUCT 的成员变量可用作 UPROPERTY。UPROPERTY 用途广泛。它允许变量被复制、被序列化,并可从蓝图中进行访问。垃圾回收器还使用它们来追踪对 UObject 的引用数。 |
| UFUNCTION() | 使 UCLASS 或 USTRUCT 的类方法可用作 UFUNCTION。UFUNCTION 允许类方法从蓝图中被调用,并在其他资源中用作 RPC |
2.1 数据类型
UE_C++ —— 数据类型
(1)基本类型
bool、TCHAR、short(2个字节)int(32位4字节、64位8字节)、uint8(1字节)、uint16(2字节)、uint32(4字节)、uint64(8字节)、int8(1字节)、int16(2字节)、int32(4字节)、int64(8字节)、long(4字节)、long long(8字节)、float(4字节)、double(8字节)。
(2)字符串
UE4_最全FString字符串与各格式转换 输出
FString,动态字符数组;
FName,资源命名字符串(不可变);
FText,支持本地化的文本字符串(不可变)
引擎字符串(TCHAR*):TEXT()宏将字符串转换成Unicode的TCHAR*。
//FString类
//构造与赋值
FString str1; // 创建一个空的FString对象
FString str2(TEXT("Hello")); // 使用TEXT宏创建一个包含"Hello"的FString对象
FString str3 = str2; // 通过拷贝构造函数赋值
FString str4 = TEXT("World"); // 直接使用TEXT宏进行赋值
//追加
FString str = TEXT("Hello, ");
str.Append(TEXT("World!")); // 使用Append函数追加字符串,结果str为"Hello, World!"
str += TEXT(" Again!"); // 使用+=运算符追加字符串,结果str为"Hello, World! Again!"
//插入
FString str = TEXT("Hello");
str.InsertAt(5, TEXT(" World")); // 在索引5的位置插入字符串" World",结果str为"Hello World"
//替换
FString str = TEXT("Hello World");
str = str.Replace(TEXT("World"), TEXT("UE")); // 将"World"替换为"UE",结果str为"Hello UE"
//查询
FString str = TEXT("Hello World");
bool containsHello = str.Contains(TEXT("Hello")); // 判断是否包含子字符串"Hello",结果为true
int32 index = str.Find(TEXT("World")); // 查找子字符串"World"的索引,结果为6
bool startsWithHello = str.StartsWith(TEXT("Hello")); // 判断是否以"Hello"开头,结果为true
bool endsWithWorld = str.EndsWith(TEXT("World")); // 判断是否以"World"结尾,结果为true
//格式化
FString playerName = TEXT("John");
FString greeting = FString::Printf(TEXT("Hello, %s!"), *playerName); // 使用Printf函数格式化字符串,结果greeting为"Hello, John!"
//路径拼接
FString basePath = TEXT("C:/Users/Documents/");
FString fileName = TEXT("example.txt");
FString fullPath = basePath / fileName; // 使用/运算符进行路径拼接,结果fullPath为"C:/Users/Documents/example.txt"
FString fullPath;
fullPath.PathAppend(fileName, TEXT("example.txt").Length()); // 将文件名追加到基路径上,注意需要传递文件名的长度
//FString转FName
FString TheString = "UE4_C++_IS_Awesome";
FName ConvertedFString = FName(*TheString);
//FString To FText
FString TheString = "Hello,world";
FText ConvertedFSting=FText::FromSting(TheString);
//std::string 互转 FString
#include <string>
std::string TestString = "Happy";
FString HappyString(TestString.c_str());
FString UE4Str = "Flowers";
std::string MyStdString(TCHAR_TO_UTF8(*UE4Str));
//数值转std::string
std::string std::to_string(value);
//数值与FString
FString TheString = "123.021";
int32 MyShinyNewInt = FCString::Atoi(*TheString);
FString TheString = "123.021";
int32 MyShinyNewInt = FCString::Atof(*TheString);
FString NewString = FString::FromInt(YourInt);
FString VeryCleanString = FString::SanitizeFloat(YourFloat);
#include "CString.h"
#include "UnrealString.h"
#include "NameTypes.h"
#include "StringConv.h"
//Converting FArrayReaderPtr to FString
uint8 data[512];
FMemory::Memzero(data, 512);
FMemory::Memcpy(data, ArrayReaderPtr->GetData(), ArrayReaderPtr->Num());
FString str = ((const char*)data);
//Converting Array to FString
TArray<uint8> content;
...
const std::string cstr(reinterpret_cast<const char*>(content.GetData()), content.Num());
FString frameAsFString = cstr.c_str();
UE_LOG(VRSLog, Warning, TEXT("%s"), *frameAsFString);
//Converting FString to Array
FString JsonStr;
TArray<uint8> content;
content.SetNum(JsonStr.Len());
memcpy(content.GetData(), TCHAR_TO_ANSI(*JsonStr), JsonStr.Len());
FString StrData;
const TCHAR* StrPtr = *StrData;
FTCHARToUTF8 UTF8String(StrPtr);
int32 CTXSize = UTF8String.Length();
TArray<uint8> URLData;
URLData.SetNum(CTXSize);
memcpy(URLData.GetData(), UTF8String.Get(), CTXSize);
//Converting FString to char *
///1
FString str
string t = TCHAR_TO_UTF8(*str);
char * returnvalue = (char *)malloc(sizeof(char) * (t.length() + 1));
strncpy_s(returnvalue, t.length() , t.c_str(), t.length());
///2
int ARPG_Database::BP_GetColumnIndex(int resultSet, FString columnName)
{
return GetColumnIndex(resultSet, TCHAR_TO_ANSI(*columnName));
}
int ARPG_Database::GetColumnIndex(int iResult, const char* columnName)
{
}
//Converting FString to const char *
const char *plainText= TCHAR_TO_ANSI(*aes_content);
//Converting FString to TCHAR *
int ARPG_Database::BP_GetColumnIndex(int resultSet, FString columnName)
{
return GetColumnIndex(resultSet, *columnName);
}
int ARPG_Database::GetColumnIndex(int iResult, const TCHAR* columnName)
{
}
//Converting FString to FText
FString Str = TEXT("str");
FText Text = FText::FromString(Str);
//Converting FText to FString
FString Name = NameDesc->GetText().ToString();
FString str = FString::Printf(TEXT("Name: %s, Age: %d"), *Name, Age);
UE_LOG(LogTemp, Warning, TEXT("Your String: %s" ), *str); //FString 的变量,在UE_LOG中使用时,需要解引用符 * 。
TArray<FStringFormatArg> args;
args.Add(FStringFormatArg(Name));
args.Add(FStringFormatArg(Age));
FString Str = FString::Format(TEXT("Name: {0}, Age: {1}"),args);
UE_LOG(LogTemp, Warning, TEXT("Your String: %s"), *Str);
(3)容器
TArray、TSet、TMap<k, T>
【官方文档】TArray
【官方文档】TMap
TArray<int32> IntArray;
IntArray.Init(10, 5); // IntArray == [10,10,10,10,10]
//TArray<FString>中,Add将用字符串文字创建临时FString,然后将该临时FString的内容移至容器内的新FString中;
//而Emplace将用字符串文字直接新建FString。
//最终结果相同,但Emplace可避免创建临时文件。对于FString等非浅显数值类型而言,临时文件通常有害无益。
//总体而言,Emplace优于Add,因此其可避免在调用点创建无需临时变量,并将此类变量复制或移动到容器中。
TArray<FString> StrArr;
StrArr.Add (TEXT("Hello"));
StrArr.Emplace(TEXT("World")); // StrArr == ["Hello","World"]
StrArr.AddUnique(TEXT("!"));
StrArr.Append(Arr, ARRAY_COUNT(Arr));
StrArr.Insert(TEXT("Brave"), 1);
StrArr.SetNum(8);
int32 Count = StrArr.Num();
FString* StrPtr = StrArr.GetData();
uint32 ElementSize = StrArr.GetTypeSize(); //元素类型大小 ElementSize == sizeof(FString)
FString Elem1 = StrArr[1];
ValArr.Remove(20); //移除所有值为20的元素
ValArr.RemoveSingle(30); //移除第一个值为20的元素
ValArr.RemoveAt(2); // 移除索引位置的元素
//RemoveAll也可用于移除与谓词匹配的元素。 例如,移除为3倍数的所有数值:
ValArr.RemoveAll([](int32 Val) {
return Val % 3 == 0;
});
ValArr2.Empty(); //清空,同STL的clear()
//基于索引
for (int32 Index = 0; Index != StrArr.Num(); ++Index)
{
JoinedStr += StrArr[Index];
JoinedStr += TEXT(" ");
}
//基于范围
FString JoinedStr;
for (auto& Str : StrArr)
{
JoinedStr += Str;
JoinedStr += TEXT(" ");
}
//基于迭代器
for (auto It = StrArr.CreateConstIterator(); It; ++It)
{
JoinedStr += *It;
JoinedStr += TEXT(" ");
}
TMap<int32, FString> FruitMap;
FruitMap.Add(5, TEXT("Banana"));
FruitMap.Emplace(3, TEXT("Orange")); //防止创建临时对象
TMap<int32, FString> FruitMap2;
FruitMap2.Emplace(4, TEXT("Kiwi"));
FruitMap2.Emplace(9, TEXT("Melon"));
FruitMap2.Emplace(5, TEXT("Mango"));
FruitMap.Append(FruitMap2);
FString Val7 = FruitMap[7];
int32 Count = FruitMap.Num();
bool bHas7 = FruitMap.Contains(7);
FString* Ptr7 = FruitMap.Find(7); //Find将返回指向元素数值的指针
FruitMap.Remove(8);
FruitMapCopy.Empty(); //清空
FruitMapCopy.Reset(); //清空
for (auto& Elem : FruitMap)
{
FPlatformMisc::LocalPrint(*FString::Printf(TEXT("(%d, \"%s\")\n"), Elem.Key, *Elem.Value));
}
for (auto It = FruitMap.CreateConstIterator(); It; ++It)
{
FPlatformMisc::LocalPrint(
*FString::Printf(
TEXT("(%d, \"%s\")\n"),
It.Key(), // same as It->Key
*It.Value() // same as *It->Value
)
);
}
(4)向量及变换类型
FVector,表示3D空间点、向量等的三维向量类型,包含三个浮点数分量:X、Y和Z;
常用于表示对象的位置、方向、速度等;
支持向量的基本运算,如加法、减法、点积、叉积等;
可以方便地转换为其他数学类型,如四元数(FQuat)和矩阵(FMatrix);
FRotator,表示3D旋转的三维向量类型,包含三个浮点数Pitch、Yaw、Roll(以角度为单位存储);
Pitch表示绕右轴(Y轴)旋转的角度,上下(0=正前方,+向上,-向下)的旋转;
Yaw表示绕上轴(Z轴)旋转的角度,绕圈(0=东,+北,-南)
Roll表示绕前向轴(X轴)旋转的角度,歪着头(0=正直,+顺时针旋转,-逆时针旋转);
可以转换为四元数(FQuat)以进行更高效的旋转计算;
FTransform,更高级别的矩阵类型,包含三个矩阵:Rotation(旋转)、Translation(平移)和Scale3D(缩放);
广泛用于表示对象在3D空间中的位置和变换;
支持矩阵的基本运算,如乘法、逆运算等;
可以方便地转换为其他数学类型,如四元数(FQuat)和向量(FVector);
FQuat(四元数)、FMatrix(矩阵)
(5)日期时间
阿赵UE引擎C++编程学习笔记——时间日期相关(系列文章)
FDateTime,用于表示日期和时间;
#include "Misc/DateTime.h" // /Engine/Source/Runtime/Core/Public/Misc/DateTime.h
FDateTime ( int32 Year,
int32 Month,
int32 Day,
int32 Hour,
int32 Minute,
int32 Second,
int32 Millisecond
)
FDateTime ( int64 InTicks);
FDateTime GetDate ();
int32 GetYear ();
int32 GetMonth ();
int32 GetDay ();
int32 GetHour ();
int32 GetHour12 ();
int32 GetMinute ();
int32 GetSecond ();
int32 GetMillisecond ();
int64 GetTicks();
FTimespan GetTimeOfDay ();
FDateTime::UtcNow(); //UTC时间,实际上就是时区为0的时间,全球统一时间
FDateTime::Now(); //获得本机的时间,带时区
//时间戳是指从1970年1月1日凌晨开始算起到现在的秒数,或者毫秒数。
(FDateTime::Now() - FDateTime(1970, 1, 1)).GetTotalMilliseconds(); //毫秒
FDateTime::ToUnixTimestamp(); //1970之后的秒数。提供10位的时间戳,不能13位,且只精确到秒。
FDateTime FromUnixTimestamp ( int64 UnixTime);
FDateTime::Ticks(); //Holds the ticks in 100 nanoseconds resolution since January 1, 0001 A.D.
FDateTime::Now().ToString(TEXT("%Y-%m-%d %H:%M:%S"));
ToString(TEXT("%h:%m:%s.%t"));
ToString(TEXT("%Y%m%d")); //格式:20190101
FString FormattedTimeStamp = FString::Printf(TEXT("%04i-%02i-%02i %02i:%02i:%02i.%03i"), CurrentDateAndTime.GetYear(), CurrentDateAndTime.GetMonth(), CurrentDateAndTime.GetDay(), CurrentDateAndTime.GetHour(), CurrentDateAndTime.GetMinute(), CurrentDateAndTime.GetSecond(), CurrentDateAndTime.GetMillisecond());
FTimespan,表示时间间隔;
(6) Enum 枚举
UENUM(BlueprintType) //这个标记是为了在蓝图中使用。
enum class ECharacterState : uint8
{
Idle UMETA(DisplayName = "Idle"), // 在编辑器中显示为 "Idle"
Running UMETA(DisplayName = "Running"), // 在编辑器中显示为 "Running"
Jumping UMETA(DisplayName = "Jumping"), // 在编辑器中显示为 "Jumping"
Attacking UMETA(DisplayName = "Attacking") // 在编辑器中显示为 "Attacking"
};
//使用方法如下
ECharacterState currenState=ECharacterState::Running; //获取枚举状态
if(currenState==ECharacterState::Running)
{
}
3、问题解决
3.1 Cannot find file ‘CoreMinimal.h’ in serch paths:

I have fixed the problem adding this to the *Target.cs files:
DefaultBuildSettings = BuildSettingsVersion.V5;
IncludeOrderVersion = EngineIncludeOrderVersion.Unreal5_4;
3.2 崩溃解析
UE4/UE5的崩溃,卡死等问题处理
死循环有个土办法,保存fulldump看
3.3 打不了断点
断点变成白色带个斜杠,鼠标放上面提示:The breakpoint will not be hit, no executable code is associated with this line.
原因:编译完ue启动时,打印提示 该组件 error:Property FSample::BeginTime is not initialized properly,主要是暴露给蓝图的struct里的property项没有初始化造成的,必须加入构造函数并以初始化列表的形式进行初始化,注意在构造函数中赋值不能解决问题。但为什么蓝图struct里有那么多项,只那几个报没有正确初始化呢?(有int64、double)。从而导致该组件没有按最新的代码执行,还是执行以前的代码,所以没有可执行的代码,断点是白色的。
解决:要将本组件运行时报的所有error都解决掉。
3.4 MSB3073错误
Microsft.Makefile.Targets(44,5): error MSB3073: 命令c:\Progam files\Epic Games\UE_5.4\Engine\Build\BatchFiles\Build.bat已退出,代码为-1。
原因是编译过程中有红色的error必须解决。只要有error就不要放过。
3.5 每一次都从头编译(共529actions)
原因是由于Rider试用期过了,小朋友把日期调成以前的,但文件时期是之后的,造成每次都从头编译。对比之下还是VS2022好用。
3.6 调试时代码跳过中间的很多代码
Unreal Engine 各种编译运行模式的区别和应用场景
可能是因为编译优化导致的,将vs2022解决方案配置(Solution Configurations)的编译模式由Development Editor改为DebugGame Editor,还是会跳过,最终是代码稍做修改(敲几下空格),保存再编译就可以了。
4、启动流程-main()
【搬运】【中文字幕】UE4游戏框架:从int main()到BeginPlay
UE基础—GamePlay启动流程
【UE4】虚幻引擎运行流程
【搬运】【中文字幕】蓝图 vs C++ 它们如何一起工作 为什么你应该一起使用
【youtobe】蓝图 vs C++ 它们如何一起工作 为什么你应该一起使用
【译文】UE基础—蓝图 VS. C++
该作者文章(Blueprints vs. C++):http://awforsythe.com/unreal/blueprints_vs_cpp/
该作者所有视频:http://awforsythe.com/unreal/
该作者Github:https://github.com/alexwforsythe
该作者UE资源:https://unrealdirective.com/community/resources/alex-forsythes-channel
视频中的动图由Adobe After Effects(简称 AE)制作
Launch模块:
5、智能指针
- TObjectPtr:UObject
- TSharedPtr: 非UObject
TObjectPtr(UE5 推荐的 UObject 指针包装器)
TWeakObjectPtr(弱引用,自动置空)
TLazyObjectPtr(延迟/跨关卡对象引用)
TSoftObjectPtr / FSoftObjectPath(软引用 / 以路径引用 Asset,可异步加载)
TScriptInterface(接口引用,存 UObject* + 接口指针)
TSubclassOf(类类型选择器:保存 UClass 子类)
TSharedPtr / TSharedRef /TWeakPtr(引用计数智能指针,用于非UObject 的普通 C++ 对象)
TUniquePtr(独占所有权的智能指针,用于非UObject 对象)
// 创建一个空白的共享指针
TSharedPtr<FMyObjectType> EmptyPointer;
// 为新对象创建一个共享指针
TSharedPtr<FMyObjectType> NewPointer(new FMyObjectType());
// 从共享引用创建一个共享指针
TSharedRef<FMyObjectType> NewReference(new FMyObjectType());
TSharedPtr<FMyObjectType> PointerFromReference = NewReference;
// 创建一个线程安全的共享指针
TSharedPtr<FMyObjectType, ESPMode::ThreadSafe> NewThreadsafePointer = MakeShared<FMyObjectType, ESPMode::ThreadSafe>(MyArgs);
// 使用MakeShareable(推荐) TSharedPtr<TArray<uint8>>
auto MyArrayPtr = MakeShareable(new TArray<uint8>());
// 或使用MakeShared(更高效,单次内存分配)
auto EfficientPtr = MakeShared<TArray<uint8>>();
void ExampleUsage()
{
// 1. 创建MyClass的智能指针(如问题所示)
TSharedPtr<MyClass> MyPtr = MakeShareable(new MyClass());
MyPtr->SetValue(42);
// 2. 创建包含TArray<uint8>的智能指针
TSharedPtr<TArray<uint8>> ImageData = MakeShared<TArray<uint8>>();
// 模拟加载图像数据
const int32 ImageSize = 1024 * 768; // 假设的图像大小
ImageData->SetNumUninitialized(ImageSize);
// 填充数据(示例)
for (int32 i = 0; i < ImageData->Num(); i++)
{
(*ImageData)[i] = static_cast<uint8>(i % 256);
}
// 3. 在函数间传递
ProcessImageData(ImageData);
// 4. 使用Lambda捕获智能指针
auto ProcessLambda = [ImageData]()
{
if (ImageData.IsValid())
{
UE_LOG(LogTemp, Log, TEXT("Processing %d bytes"), ImageData->Num());
}
};
ProcessLambda();
// 5. 重置智能指针
ImageData.Reset(); // 引用计数减1,如果为0则释放内存
}
void ProcessImageData(const TSharedPtr<TArray<uint8>>& Data)
{
if (!Data.IsValid())
{
return;
}
// 处理数据
int32 Total = 0;
for (uint8 Value : *Data)
{
Total += Value;
}
UE_LOG(LogTemp, Log, TEXT("Total value: %d"), Total);
}
6、UObject
#pragma once
#include 'Object.h'
#include 'MyObject.generated.h'
UCLASS()
class MYPROJECT_API UMyObject : public UObject
{
GENERATED_BODY()
};
UObject实例创建
NewObject、NewNamedObject、ConstructObject
NewObject 用于在运行时动态创建任何继承自 UObject 的对象。
动态创建:NewObject 在运行时创建对象。
适用于任意 UObject:可以创建任何 UObject 派生类的实例。
参数灵活:可以指定外部对象(Outer)、对象名字(Name)、对象标志(Flags)等。
//创建UObject类
UMyObject* MyObject = NewObject<UMyObject>(GetTransientPackage());
//持有UObject
UPROPERTY()
UMyObject *MyObjcet; //UE自动初始化为空了,UPROPERTY赋值后引用计数加1,或加入容器中引用计数也加1
MyObjcet = NewObject<UMyObject>(this); //UE自动初始化为空了,赋值后引用计数加1,或加入容器中引用计数也加1
//GC回收
MyObjcet=nullptr; //释放UPROPERTY引用,引用次数-1;从容器中移除,引用次数-1
CreateDefaultSubobject 用于在构造函数中创建默认子对象,通常用于 AActor 的子组件创建。特点
构造函数内使用:通常在 AActor 或 UActorComponent 的构造函数中调用。
默认子对象: 创建的对象会被注册为拥有者(如 AActor)的默认子对象。
自动内存管理: 创建的子对象会自动成为拥有者的一部分,并在拥有者销毁时自动销毁。
UStaticMeshComponent* MeshComponent = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("MyMeshComponent"));

7、打印
UE_LOG(LogCategory, LogVerbosity, FormatString, Parameters...)
UE_LOG(LogTemp, Warning, TEXT("value is %d"), value);
UE_LOG(LogTemp, Warning, TEXT("An Actor's name is %s"), *ExampleActor->GetName());
UE_LOG(LogTemp, Warning, TEXT("The boolean value is %s"), ( bExampleBool ? TEXT("true"): TEXT("false") ));
UE_LOG(LogTemp, Warning, TEXT("Current values are: vector %s, float %f, and integer %d"), *ExampleVector.ToString(), ExampleFloat, ExampleInteger);
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, FString::Printf(TEXT("value is %f"), valueF));
对应蓝图中的Print String:
LogCategory:定义在CoreGlobals.h
CORE_API DECLARE_LOG_CATEGORY_EXTERN(LogHAL, Log, All);
CORE_API DECLARE_LOG_CATEGORY_EXTERN(LogSerialization, Log, All);
CORE_API DECLARE_LOG_CATEGORY_EXTERN(LogUnrealMath, Log, All);
CORE_API DECLARE_LOG_CATEGORY_EXTERN(LogUnrealMatrix, Log, All);
CORE_API DECLARE_LOG_CATEGORY_EXTERN(LogContentComparisonCommandlet, Log, All);
CORE_API DECLARE_LOG_CATEGORY_EXTERN(LogNetPackageMap, Warning, All);
CORE_API DECLARE_LOG_CATEGORY_EXTERN(LogNetSerialization, Warning, All);
CORE_API DECLARE_LOG_CATEGORY_EXTERN(LogMemory, Log, All);
CORE_API DECLARE_LOG_CATEGORY_EXTERN(LogProfilingDebugging, Log, All);
CORE_API DECLARE_LOG_CATEGORY_EXTERN(LogCore, Log, All);
CORE_API DECLARE_LOG_CATEGORY_EXTERN(LogOutputDevice, Log, All);
CORE_API DECLARE_LOG_CATEGORY_EXTERN(LogSHA, Warning, All);
CORE_API DECLARE_LOG_CATEGORY_EXTERN(LogStats, Log, All);
CORE_API DECLARE_LOG_CATEGORY_EXTERN(LogStreaming, Display, All);
CORE_API DECLARE_LOG_CATEGORY_EXTERN(LogInit, Log, All);
CORE_API DECLARE_LOG_CATEGORY_EXTERN(LogExit, Log, All);
CORE_API DECLARE_LOG_CATEGORY_EXTERN(LogExec, Warning, All);
CORE_API DECLARE_LOG_CATEGORY_EXTERN(LogScript, Warning, All);
CORE_API DECLARE_LOG_CATEGORY_EXTERN(LogLocalization, Log, All);
CORE_API DECLARE_LOG_CATEGORY_EXTERN(LogLongPackageNames, Log, All);
CORE_API DECLARE_LOG_CATEGORY_EXTERN(LogProcess, Log, All);
CORE_API DECLARE_LOG_CATEGORY_EXTERN(LogLoad, Log, All);
CORE_API DECLARE_LOG_CATEGORY_EXTERN(LogVirtualization, Log, All);
// Temporary log category, generally you should not check things in that use this
CORE_API DECLARE_LOG_CATEGORY_EXTERN(LogTemp, Log, All);
// Platform specific logs, set here to make it easier to use them from anywhere
// need another layer of macro to help using a define in a define
#define DECLARE_LOG_CATEGORY_EXTERN_HELPER(A,B,C) DECLARE_LOG_CATEGORY_EXTERN(A,B,C)
#ifdef PLATFORM_GLOBAL_LOG_CATEGORY
CORE_API DECLARE_LOG_CATEGORY_EXTERN_HELPER(PLATFORM_GLOBAL_LOG_CATEGORY, Log, All);
#endif
#ifdef PLATFORM_GLOBAL_LOG_CATEGORY_ALT
CORE_API DECLARE_LOG_CATEGORY_EXTERN_HELPER(PLATFORM_GLOBAL_LOG_CATEGORY_ALT, Log, All);
#endif
自定义日志类别:
DECLARE_LOG_CATEGORY_EXTERN(<LOG_CATEGORY>, <VERBOSITY_LEVEL>, All);
//先在头文件中声明:
DECLARE_LOG_CATEGORY_EXTERN(LogSpaceShooter, Log, All);
//在源文件中定义:
DEFINE_LOG_CATEGORY(LogSpaceShooter);
//使用:
UE_LOG(LogSpaceShooter, Display, TEXT("Enemy spawned at: %s"), *SpawnLocation.ToString());
LogVerbosity:

格式化:
详细归纳UE_LOG字符串占位符
//const TCHAR*: UE的TCHAR字符串(宽字符或多字节,由平台决定)
const TCHAR* Str = TEXT("Hello UE");
UE_LOG(LogTemp, Log, TEXT("CStr: %s"), Str);
//FString : 需通过*运算符转换为const TCHAR*
FString FStr = TEXT("FString");
UE_LOG(LogTemp, Log, TEXT("FString: %s"), *FStr);
//FText: 需先调用 ToString() 转换为 FString,再用 * 转换
FText FTxt = FText::FromString(TEXT("FText")); UE_LOG(LogTemp, Log, TEXT("FText: %s"), *FTxt.ToString());
8、多线程
优先选择 Async: 对简单的一次性任务,使用 Async 最便捷。
复用任务用 AsyncTask: 需要任务对象复用或取消时使用。
高并发用 AsyncPool: 批量处理任务时通过线程池提高效率。
特殊需求用 AsyncThread:仅在需要完全控制线程时使用。
8.1 std::thread
void ATestRunnableActor::BeginPlay()
{
Super::BeginPlay();
std::thread Thread(&ATestRunnableActor::HelloWorld);
Thread.join();
}
//静态方法
void ATestRunnableActor::HelloWorld()
{
SetThreadDescription(GetCurrentThread(), L"ThisIsMyThreadName");
UE_LOG(LogTemp, Warning, TEXT("Hellow,C++ Thread!"));
}
static std::mutex MutexLock;
void AMutiThreadActor::Test()
{
int32 count = 0;
auto Lambda = [&count]
{
for(int32 j =0; j< 500000; j++)
{
MutexLock.lock();
count++;
MutexLock.unlock();
}
};
auto Thread1 = std::thread(Lambda);
auto Thread2 = std::thread(Lambda);
Thread1.join(); Thread2.join();
UE_LOG(LogTemp, Warning, TEXT("TestCount: %d"),count);
}
FEvent有点类似 C++ 的 condition variable ,mutex 有些情况并不能满足,比如线程间的通知。
FEvent* ThreadEvent;
void AMutiThreadActor::TestFEvent()
{
ThreadEvent = FPlatformProcess::GetSynchEventFromPool();
std::vector<std::thread> Threads(2);
std::thread Thread1 = std::thread([this]
{
ThreadEvent->Wait(); //停止执行等待被唤醒
UE_LOG(LogTemp, Warning, TEXT("[FEvent]Thread wake up"));
});
std::thread Thread2 = std::thread([this]
{
UE_LOG(LogTemp, Warning, TEXT("[FEvent]Send message to wake up other thread"));
std::this_thread::sleep_for(std::chrono::seconds(1));
ThreadEvent->Trigger(); //唤醒wait()
});
Thread1.detach(); Thread2.detach();
}
8.2 Async
Async(EAsyncExecution::Thread, []() {
// 这里放置需要在单独线程中执行的任务逻辑,比如执行一些耗时计算
int sum = 0;
for (int i = 0; i < 1000; i++)
{
sum += i;
}
});
第一个参数:类型
/** Execute in Task Graph (for short running tasks). */
TaskGraph,
/** Execute in Task Graph on the main thread (for short running tasks). */
TaskGraphMainThread,
/** Execute in separate thread if supported (for long running tasks). */
Thread,
/** Execute in separate thread if supported or supported post fork (see FForkProcessHelper::CreateThreadIfForkSafe) (for long running tasks). */
ThreadIfForkSafe,
/** Execute in global queued thread pool. */
ThreadPool,
8.3 AsyncTask
【UE5 C++课程系列笔记】23——多线程基础——AsyncTask
AsyncTask 是一种简单易用的工具,用于在后台线程中异步执行任务,并将结果返回主线程。它的特点是无需手动管理线程的创建和销毁,适合处理轻量级任务。默认使用UE的FQueuedThreadPool线程池。
#include "Async/Async.h"
// 在后台线程执行任务
AsyncTask(ENamedThreads::AnyBackgroundThreadNormalTask, []() {
// 后台任务逻辑
int Result = 42;
// 返回主线程处理结果
AsyncTask(ENamedThreads::GameThread, [Result]() {
UE_LOG(LogTemp, Log, TEXT("任务结果: %d"), Result);
});
});
Async与AsyncTask嵌套:
#include "Async/Async.h"
void AYourActor::PerformHeavyCalculation()
{
// 将耗时计算放到后台线程中执行
Async(EAsyncExecution::ThreadPool, [this]()
{
// 模拟耗时计算
FPlatformProcess::Sleep(2.0f); // 模拟2秒计算
int32 Result = 0;
for (int32 i = 0; i < 1000000; i++)
{
Result += i;
}
// 计算完成后,将结果返回到主线程
AsyncTask(ENamedThreads::GameThread, [this, Result]()
{
UE_LOG(LogTemp, Warning, TEXT("计算结果:%d"), Result);
// 在主线程中更新 UI 或执行其他逻辑
});
});
}
定时器与AsyncTask结合:
void AYourActor::BeginPlay()
{
Super::BeginPlay();
// 每 5 秒执行一次后台任务
GetWorldTimerManager().SetTimer(BackgroundTaskTimerHandle, this, &AYourActor::PerformBackgroundTask, 5.0f, true);
}
void AYourActor::PerformBackgroundTask()
{
Async(EAsyncExecution::ThreadPool, [this]()
{
// 后台任务逻辑,例如检查游戏状态或统计数据
FPlatformProcess::Sleep(1.0f); // 模拟计算
int32 DataResult = FMath::Rand(); // 模拟计算结果
// 将结果返回主线程进行处理
AsyncTask(ENamedThreads::GameThread, [this, DataResult]()
{
UE_LOG(LogTemp, Warning, TEXT("后台任务结果:%d"), DataResult);
// 更新 UI 或其他主线程逻辑
});
});
}
void AYourActor::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
Super::EndPlay(EndPlayReason);
// 清除定时器,防止后台任务在角色销毁后继续执行
GetWorldTimerManager().ClearTimer(BackgroundTaskTimerHandle);
}
8.4 FRunnable 与 FRunnableThread
【UE5 C++课程系列笔记】22——多线程基础——FRunnable和FRunnableThread
FRunnable 定义了线程的接口,而 FRunnableThread 是线程的具体实现。通过继承 FRunnable,可以自定义线程的行为,并使用 FRunnableThread::Create 创建线程。
class FMyRunnable : public FRunnable
{
public:
virtual bool Init() override { return true; }
virtual uint32 Run() override {
// 线程任务逻辑
return 0;
}
virtual void Exit() override {}
};
// 创建线程
FRunnable* MyRunnable = new FMyRunnable();
FRunnableThread* MyThread = FRunnableThread::Create(MyRunnable, TEXT("MyThread"));
//1、继承 FRunnable:
class FMyRunnable : public FRunnable
{
public:
virtual uint32 Run() override {
while (!bStop) { /* 任务逻辑 */ }
return 0;
}
FThreadSafeBool bStop;
};
//2、创建并启动线程:
FRunnable* Runnable = new FMyRunnable();
FRunnableThread* Thread = FRunnableThread::Create(Runnable, TEXT("MyThread"));
//3、停止线程:
Runnable->bStop = true;
Thread->WaitForCompletion();
delete Thread;
delete Runnable;
8.5 线程池与任务队列
UE5 提供了线程池(FQueuedThreadPool)来管理线程资源。通过将任务添加到线程池,可以实现高效的任务调度。
class FMyTask : public FNonAbandonableTask {
public:
void DoWork() {
// 任务逻辑
}
FORCEINLINE TStatId GetStatId() const {
RETURN_QUICK_DECLARE_CYCLE_STAT(FMyTask, STATGROUP_ThreadPoolAsyncTasks);
}
};
// 添加任务到线程池
FAsyncTask<FMyTask>* Task = new FAsyncTask<FMyTask>();
Task->StartBackgroundTask();
8.6 FGraphEventRef
【UE5 C++课程系列笔记】25——多线程基础——FGraphEventRef的简单使用
void UThreadSubsystem::GraphEvent()
{
FGraphEventRef SimpleEvent = FFunctionGraphTask::CreateAndDispatchWhenReady([this]() {
PrintLogInThread(TEXT("SimpleEvent Start"));
FPlatformProcess::Sleep(3);
PrintLogInThread(TEXT("SimpleEvent End"));
});
check(!SimpleEvent->IsComplete());
SimpleEvent->Wait();
FGraphEventArray SimpleEventArray;
for (size_t i = 0; i < 20; i++)
{
SimpleEventArray.Add(FFunctionGraphTask::CreateAndDispatchWhenReady([this,i]() {
FString Info1 = FString::Printf(TEXT("SimpleEvent Start -- %d"), i);
PrintLogInThread(Info1);
FPlatformProcess::Sleep(i%3);
FString Info2 = FString::Printf(TEXT("SimpleEvent End -- %d"), i);
PrintLogInThread(Info2);
}));
}
FTaskGraphInterface::Get().WaitUntilTasksComplete(SimpleEventArray);
}
void UThreadSubsystem::PrintLogInThread(FString Info)
{
AsyncTask(ENamedThreads::GameThread, [Info]() {
UE_LOG(LogTemp, Warning, TEXT("ThreadLog:[%s]"), *Info);
});
}
8.7 ParallelFor
【UE5 C++课程系列笔记】26——多线程基础——ParallelFor的简单使用
ParallelFor 循环的代码执行时间约为 普通 for 的1/10,但是执行顺序是乱的。
由于 ParallelFor 是并行执行任务的,当 lambda 表达式中涉及到对共享资源(如全局变量、共享的游戏对象等)的访问时,必须要注意线程安全。如果多个线程同时读写同一个共享资源,没有合适的多线程同步机制保障,很容易导致数据不一致、程序崩溃等问题。可以通过对变量加锁来确保同一时刻只有一个线程能够对其进行修改,从而保证线程安全。
ParallelFor(100, [](int32 Index) {
// 这里可以根据 Index 来操作对应的元素,比如对数组元素进行赋值、计算等
MyArray[Index] = Index * 2;
});
8.8 ControlFlow插件
【UE5 C++课程系列笔记】27——多线程基础——ControlFlow插件的基本使用
9、蓝图
UE5 蓝图学习笔记(自用备忘)
构建蓝图中包含多输入多输出引脚的节点
9.1 蓝图基础知识
(1)组成
- 标题(Header):显示节点名称,颜色代表类型(如事件→红色,函数→蓝色,变量→绿色,流程控制节点→蓝色(菱形图标))。
- 输入/输出引脚(Pins):左侧:输入引脚(触发逻辑或接收数据)。右侧:输出引脚(执行后续逻辑或返回数据)。数据类型:引脚颜色区分(如红色=布尔,蓝色=整数,粉色=字符串)。
- 折叠按钮(Collapse Arrow):可展开/隐藏节点的详细参数(如函数的输入值)。
(2)引脚
执行引脚(Exec):白色箭头,表示执行顺序(从左到右)。
数据引脚:彩色圆形,传输变量(如浮点数、向量)。左侧输入,右侧输出
特殊引脚:①委托引脚:绑定事件(如OnCompleted)。②枚举扩展引脚:将枚举值拆分为多个执行引脚(如SetVisualFidelity)。
10、UMG
虚幻示意图形界面设计器(Unreal Motion Graphics UI Designer)(UMG)
【官方文档】UMG UI设计器快速入门
11、当前时间
FString GSystemStartTime = FDateTime::Now().ToString();
FDateTime FDateTime::Now()
{
int32 Year, Month, Day, DayOfWeek;
int32 Hour, Minute, Second, Millisecond;
FPlatformTime::SystemTime(Year, Month, DayOfWeek, Day, Hour, Minute, Second, Millisecond);
return FDateTime(Year, Month, Day, Hour, Minute, Second, Millisecond);
}
12、平台判断
#if PLATFORM_WINDOWS
// Windows平台代码
UE_LOG(LogTemp, Warning, TEXT("Running on Windows"));
#elif PLATFORM_ANDROID
// Android平台代码
UE_LOG(LogTemp, Warning, TEXT("Running on Android"));
#elif PLATFORM_IOS
// iOS平台代码
UE_LOG(LogTemp, Warning, TEXT("Running on iOS"));
#else
// 其他平台(如Mac、Linux等)
UE_LOG(LogTemp, Warning, TEXT("Running on other platform"));
#endif
#include "HAL/PlatformProperties.h"
// 示例代码:
if (FPlatformProperties::IsWindows())
{
// Windows平台逻辑
}
else if (FPlatformProperties::IsAndroid())
{
// Android平台逻辑
}
else if (FPlatformProperties::IsIOS())
{
// iOS平台逻辑
}
#include "Misc/App.h"
FString PlatformName = FPlatformMisc::GetPlatformName();
UE_LOG(LogTemp, Warning, TEXT("Current Platform: %s"), *PlatformName);
// 示例输出:
// Windows -> "Windows"
// Android -> "Android"
// iOS -> "IOS"
13、TCP连接
FString ServerIp;
int32 ServerPort;
float HeartbeatInterval;
FSocket* Socket;
FRunnableThread* Thread;
FThreadSafeBool bRunThread;
FThreadSafeBool bConnected;
FTimerHandle HeartbeatTimerHandle;
FSocket* Socket = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)
->CreateSocket(NAME_Stream, TEXT("TcpClientSocket"), false);
Socket->SetNonBlocking(true);
TSharedRef<FInternetAddr> Addr = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)
->CreateInternetAddr();
bool bOk = Socket->Connect(*Addr);
Socket->Recv(RecvBuffer.GetData(), RecvBuffer.Num(), BytesRead); //接收
Socket->Send(Data.GetData(), Data.Num(), BytesSent); //发送
Socket->Close();
ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->DestroySocket(Socket);
Socket = nullptr;
14、定时器
要访问定时器管理器,可以使用AActor函数GetWorldTimerManager,它会在UWorld中调用GetTimerManager函数。要访问全局定时器管理器,使用UGameInstance函数`GetTimerManager’。
AActor::GetWorldTimerManager()
UWorld::GetTimerManager()
UGameInstance::GetTimerManager()
FTimerHandle timerHandle;
GetWorldTimerManager().SetTimer(timerHandle, this, &OnTime, 5); //倒计时5秒:
GetWorldTimerManager().SetTimer(timerHandle, this, &OnTime, 2, true, 5); //每2秒钟执行一次,首次执行延时5秒:
FTimerHandle MemberTimerHandle;
void AMyActor::BeginPlay()
{
Super::BeginPlay();
// 从现在开始两秒后,每秒调用一次RepeatingFunction。
GetWorldTimerManager().SetTimer(MemberTimerHandle, this, &AMyActor::RepeatingFunction, 1.0f, true, 2.0f);
}
void AMyActor::RepeatingFunction()
{
// 调用该函数达到足够次数后,清空定时器。
if (--RepeatingCallsRemaining <= 0)
{
GetWorldTimerManager().ClearTimer(MemberTimerHandle);
// MemberTimerHandle现在可以复用于其他任意定时器。
}
// 在此进行一些操作...
}
//.h
#include "TimerManager.h"
FTimerHandle HeartbeatTimerHandle;
//.cpp
void FTcpClient::StartHeartbeat()
{
if (GWorld)
{
GWorld->GetTimerManager().SetTimer(HeartbeatTimerHandle, [this]()
{
SendHeartbeat();
}, HeartbeatInterval, true);
}
}
void FTcpClient::StopHeartbeat()
{
if (GWorld)
{
GWorld->GetTimerManager().ClearTimer(HeartbeatTimerHandle);
}
}
15、单例
UE5 C++项目实现单例
主要有两种思路:一种是传统C++静态单例,另一种是利用UE5提供的Subsystem体系(如GameInstanceSubsystem、UWorldSubsystem等)。
16、读取配置文件
【官方文档】UE5的配置文件
【UE4 C++】 Config Settings配置文件(.ini)
ini文件位置:在开发版中Game.ini文件在打包目录/项目名称/Saved/Config/Windows/Game.ini
ini配置文件语法:
[SECTION1]
<KEY1>=<VALUE1>
<KEY2>=<VALUE2>
[SECTION2]
<KEY3>=<VALUE3>
系统默认的配置文件:
FString GEngineIni; /* Engine ini filename */
/** Editor ini file locations - stored per engine version (shared across all projects). Migrated between versions on first run. */
FString GEditorIni; /* Editor ini filename */
FString GEditorKeyBindingsIni; /* Editor Key Bindings ini file */
FString GEditorLayoutIni; /* Editor UI Layout ini filename */
FString GEditorSettingsIni; /* Editor Settings ini filename */
/** Editor per-project ini files - stored per project. */
FString GEditorPerProjectIni; /* Editor User Settings ini filename */
FString GCompatIni;
FString GLightmassIni; /* Lightmass settings ini filename */
FString GScalabilityIni; /* Scalability settings ini filename */
FString GHardwareIni; /* Hardware ini filename */
FString GInputIni; /* Input ini filename */
FString GGameIni; /* Game ini filename */
FString GGameUserSettingsIni; /* User Game Settings ini filename */
FString GRuntimeOptionsIni; /* Runtime Options ini filename */
FString GInstallBundleIni; /* Install Bundle ini filename*/
FString GDeviceProfilesIni; /* Runtime DeviceProfiles ini filename - use LoadLocalIni for other platforms'
Config=Game 默认路径为 [ProjectName]\Saved\Config\Windows\Game.ini
注意配置文件的 section 设置 [/Script/ModuleName.ExampleClass]
SaveConfig(CPF_Config, *ConfPath); 可以用于将变量值存到配置文件中
ReloadConfig(NULL, *ConfPath) 可以从配置文件中读取值,实时
变量声明:
UCLASS(config=Game)
class AMyConfigActor : public UObject
{
GENERATED_BODY()
UPROPERTY(Config)
int32 MyConfigVariable;
}
读写综合:
void AINIActor::GetINIVariable()
{
//获取虚幻引擎项目中源配置目录的路径
const FString DefaultGamePath = FString::Printf(TEXT("%sTest.ini"), *FPaths::SourceConfigDir());
int32 MyConfigVariable;
GConfig->GetInt(TEXT("MyCustom"), TEXT("CustomInt"), MyConfigVariable, DefaultGamePath);
UE_LOG(LogTemp, Warning, TEXT("CustomInt:%d"), MyConfigVariable);
MyConfigVariable += 10;
GConfig->SetInt(TEXT("MyCustom"), TEXT("CustomInt"), MyConfigVariable, DefaultGamePath);
GConfig->Flush(false, DefaultGamePath); //写入
}
读:
int MyConfigVariable;
GConfig->GetInt(TEXT("MyCategoryName"), TEXT("MyVariable"), MyConfigVariable, GGameIni);
bool success = GConfig->GetString(TEXT("DarkSoul"), TEXT("Name"), Name, GGameIni);
success &= GConfig->GetInt(TEXT("DarkSoul"), TEXT("DeadTimes"), DeadTimes, GGameIni);
写:
if (GConfig)
{
FString Name = "Mike";
TArray<FString> Buff;
Buff.Add(TEXT("Fire"));
Buff.Add(TEXT("Strong"));
Buff.Add(TEXT("Speed"));
GConfig->SetString(TEXT("DarkSoul"), TEXT("Name"), *Name, GGameIni);
GConfig->SetArray(TEXT("DarkSoul"), TEXT("Buff"), Buff, GGameIni);
GConfig->SetVector(TEXT("DarkSoul"), TEXT("Name"), GetActorLocation(), GGameIni);
GConfig->Flush(false, GGameIni); //写入
}
GConfig 类型为 FConfigCacheIni* ,下面为 FConfigCacheIni 的一些方法:
void LoadFile( const FString& InFilename, const FConfigFile* Fallback = NULL, const TCHAR* PlatformString = NULL );
void SetFile( const FString& InFilename, const FConfigFile* NewConfigFile );
void UnloadFile( const FString& Filename );
void Detach( const FString& Filename );
bool GetString( const TCHAR* Section, const TCHAR* Key, FString& Value, const FString& Filename );
bool GetText( const TCHAR* Section, const TCHAR* Key, FText& Value, const FString& Filename );
bool GetSection( const TCHAR* Section, TArray<FString>& Result, const FString& Filename );
bool DoesSectionExist(const TCHAR* Section, const FString& Filename);
void SetString( const TCHAR* Section, const TCHAR* Key, const TCHAR* Value, const FString& Filename );
void SetText( const TCHAR* Section, const TCHAR* Key, const FText& Value, const FString& Filename );
bool RemoveKey( const TCHAR* Section, const TCHAR* Key, const FString& Filename );
bool EmptySection( const TCHAR* Section, const FString& Filename );
bool EmptySectionsMatchingString( const TCHAR* SectionString, const FString& Filename );
FString GetStr(const TCHAR * Section, const TCHAR * Key, const FString & Filename);
bool GetInt(const TCHAR * Section, const TCHAR * Key, int32 & Value, const FString & Filename);
bool GetFloat(const TCHAR * Section, const TCHAR * Key, float& Value, const FString & Filename);
bool GetDouble(const TCHAR * Section, const TCHAR * Key, double& Value, const FString & Filename);
bool GetBool(const TCHAR * Section, const TCHAR * Key, bool& Value, const FString & Filename);
int32 GetArray(const TCHAR * Section, const TCHAR * Key, TArray<FString>&out_Arr, const FString & Filename);
// Derived functions.
FString GetString
(
const TCHAR* Section,
const TCHAR* Key,
const FString& Filename
);
bool GetInt
(
const TCHAR* Section,
const TCHAR* Key,
int32& Value,
const FString& Filename
);
bool GetFloat
(
const TCHAR* Section,
const TCHAR* Key,
float& Value,
const FString& Filename
);
bool GetDouble
(
const TCHAR* Section,
const TCHAR* Key,
double& Value,
const FString& Filename
);
bool GetBool
(
const TCHAR* Section,
const TCHAR* Key,
bool& Value,
const FString& Filename
);
int32 GetArray
(
const TCHAR* Section,
const TCHAR* Key,
TArray<FString>& out_Arr,
const FString& Filename
);
/** Loads a "delimited" list of strings
* @param Section - Section of the ini file to load from
* @param Key - The key in the section of the ini file to load
* @param out_Arr - Array to load into
* @param Filename - Ini file to load from
*/
int32 GetSingleLineArray
(
const TCHAR* Section,
const TCHAR* Key,
TArray<FString>& out_Arr,
const FString& Filename
);
bool GetColor
(
const TCHAR* Section,
const TCHAR* Key,
FColor& Value,
const FString& Filename
);
bool GetVector2D(
const TCHAR* Section,
const TCHAR* Key,
FVector2D& Value,
const FString& Filename);
bool GetVector
(
const TCHAR* Section,
const TCHAR* Key,
FVector& Value,
const FString& Filename
);
bool GetVector4
(
const TCHAR* Section,
const TCHAR* Key,
FVector4& Value,
const FString& Filename
);
bool GetRotator
(
const TCHAR* Section,
const TCHAR* Key,
FRotator& Value,
const FString& Filename
);
void SetInt
(
const TCHAR* Section,
const TCHAR* Key,
int32 Value,
const FString& Filename
);
void SetFloat
(
const TCHAR* Section,
const TCHAR* Key,
float Value,
const FString& Filename
);
void SetDouble
(
const TCHAR* Section,
const TCHAR* Key,
double Value,
const FString& Filename
);
void SetBool
(
const TCHAR* Section,
const TCHAR* Key,
bool Value,
const FString& Filename
);
void SetArray
(
const TCHAR* Section,
const TCHAR* Key,
const TArray<FString>& Value,
const FString& Filename
);
/** Saves a "delimited" list of strings
* @param Section - Section of the ini file to save to
* @param Key - The key in the section of the ini file to save
* @param out_Arr - Array to save from
* @param Filename - Ini file to save to
*/
void SetSingleLineArray
(
const TCHAR* Section,
const TCHAR* Key,
const TArray<FString>& In_Arr,
const FString& Filename
);
void SetColor
(
const TCHAR* Section,
const TCHAR* Key,
FColor Value,
const FString& Filename
);
void SetVector2D(
const TCHAR* Section,
const TCHAR* Key,
FVector2D Value,
const FString& Filename);
void SetVector
(
const TCHAR* Section,
const TCHAR* Key,
FVector Value,
const FString& Filename
);
void SetVector4
(
const TCHAR* Section,
const TCHAR* Key,
const FVector4& Value,
const FString& Filename
);
void SetRotator
(
const TCHAR* Section,
const TCHAR* Key,
FRotator Value,
const FString& Filename
);
17、UTextRenderComponent
//.h
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Input, meta = (AllowPrivateAccess = "true"))
class UTextRenderComponent* textRenderComp;
//.cpp
#include "Components/TextRenderComponent.h"
textRenderComp = CreateDefaultSubobject<UTextRenderComponent>(TEXT("Text render commp"));
textRenderComp->SetupAttachment(GetRootComponent());
#include "Components/TextRenderComponent.h"
//声明
/** Text component for the score */
UPROPERTY(Category = Grid, VisibleDefaultsOnly, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"))
class UTextRenderComponent* ScoreText;
// Create static mesh component
ScoreText = CreateDefaultSubobject<UTextRenderComponent>(TEXT("ScoreText0"));
ScoreText->SetRelativeLocation(FVector(200.f,0.f,0.f));
ScoreText->SetRelativeRotation(FRotator(90.f,0.f,0.f));
ScoreText->SetText(FText::Format(LOCTEXT("ScoreFmt", "Score: {0}"), FText::AsNumber(0)));
ScoreText->SetHorizontalAlignment(EHTA_Center);
ScoreText->SetWorldSize(150);
mTextRender->SetTextRenderColor(FColor::Black);
ScoreText->SetupAttachment(DummyRoot);
// Update text
ScoreText->SetText(FText::Format(LOCTEXT("ScoreFmt", "Score: {0}"), FText::AsNumber(Score)));
ScoreText->SetText(FText::FromString(TEXT("GO")));
上面的CreateDefaultSubObject()只能用在构造函数中,下面是动态创建:
void ATopDownTestGameMode::BeginPlay()
{
Super::BeginPlay();
Text = GetWorld()->SpawnActor<ATextRenderActor>(ATextRenderActor::StaticClass(), FVector(0.f, 100, 170.f), FRotator(90.f, 180.f, 0.f));
Text->GetTextRender()->SetText(FText::FromString(TEXT("Dynamic Text")));
Text->GetTextRender()->SetTextRenderColor(FColor::Red);
Text->SetActorScale3D(FVector(5.f, 5.f, 5.f));
}
18、将ue程序嵌入Qt窗口
Qt窗口中嵌入UE程序在windows开发时的问题最新解决方案(附代码)
19、文件读写及目录
阿赵UE引擎C++编程学习笔记——文件夹操作和文件读写
获取到路径一般是相对路径,使用FPaths::ConvertRelativePathToFull转换后,可以获得完整的绝对路径。
FPaths和FFileHelper
(1)目录:
FString contentDir = FPaths::ProjectContentDir();
FString contentDirFull = FPaths::ConvertRelativePathToFull(contentDir);
FString projectDir = FPaths::ProjectDir();
FString projectDirFull = FPaths::ConvertRelativePathToFull(projectDir);
FString gameSourceDir = FPaths::GameSourceDir();
FString gameSourceDirFull = FPaths::ConvertRelativePathToFull(gameSourceDir);
FString gameSaveDir = FPaths::ProjectSavedDir();
FString gameSaveDirFull = FPaths::ConvertRelativePathToFull(gameSaveDir);
FString logDir = FPaths::ProjectLogDir();
FString logDirFull = FPaths::ConvertRelativePathToFull(logDir);
FString sandboxDir = FPaths::SandboxesDir();
FString sandboxDirFull = FPaths::ConvertRelativePathToFull(sandboxDir);
FString contentDirSandbox = FPaths::ConvertToSandboxPath("testPackage", TEXT("azhao"));
FString contentDirSandboxFull = FPaths::ConvertRelativePathToFull(contentDirSandbox);
FString contentDirNormal = FPaths::ConvertFromSandboxPath(contentDirSandbox, TEXT("azhao"));
FString testNormalPath = FPaths::ConvertToSandboxPath(contentDirNormal, TEXT("azhao"));
FString internalDir = FPaths::ProjectIntermediateDir();
FString internalDirFull = FPaths::ConvertRelativePathToFull(internalDir);
FString platformDir = FPaths::ProjectPlatformExtensionsDir();
FString platformDirFull = FPaths::ConvertRelativePathToFull(platformDir);
读:
TArray<FString> MyFileManager::ReadAllLines(FString path)
{
TArray<FString> textData;
FFileHelper::LoadFileToStringArray(textData, *path);
return textData;
}
FString MyFileManager::ReadFileText(FString path)
{
FString result;
FFileHelper::LoadFileToString(result, *path);
return result;
}
TArray<uint8> MyFileManager::ReadFileBytes(FString path)
{
TArray<uint8> result;
FFileHelper::LoadFileToArray(result, *path);
return result;
}
写:
void MyFileManager::SaveFile(FString path, FString content)
{
MyFileManager::CheckFileSavePath(path);
FFileHelper::SaveStringToFile(content, *path, FFileHelper::EEncodingOptions::ForceUTF8WithoutBOM);
}
void MyFileManager::SaveFileByCode(FString path, FString content, FFileHelper::EEncodingOptions code)
{
MyFileManager::CheckFileSavePath(path);
FFileHelper::SaveStringToFile(content, *path, code);
}
void MyFileManager::SaveFileAppend(FString path, FString content)
{
MyFileManager::CheckFileSavePath(path);
FFileHelper::SaveStringToFile(content, *path, FFileHelper::EEncodingOptions::ForceUTF8WithoutBOM, &IFileManager::Get(), EFileWrite::FILEWRITE_Append);
}
void MyFileManager::SaveFileAppendByCode(FString path, FString content, FFileHelper::EEncodingOptions code)
{
MyFileManager::CheckFileSavePath(path);
FFileHelper::SaveStringToFile(content, *path,code, &IFileManager::Get(), EFileWrite::FILEWRITE_Append);
}
void MyFileManager::SaveFileAllLines(FString path, TArray<FString> lines, bool isAppend)
{
MyFileManager::CheckFileSavePath(path);
if (isAppend == false)
{
FFileHelper::SaveStringArrayToFile(lines, *path, FFileHelper::EEncodingOptions::ForceUTF8WithoutBOM);
}
else
{
FFileHelper::SaveStringArrayToFile(lines, *path, FFileHelper::EEncodingOptions::ForceUTF8WithoutBOM,&IFileManager::Get(),EFileWrite::FILEWRITE_Append);
}
}
void MyFileManager::SaveFileAllLinesByCode(FString path, TArray<FString> lines, FFileHelper::EEncodingOptions code, bool isAppend)
{
MyFileManager::CheckFileSavePath(path);
if (isAppend == false)
{
FFileHelper::SaveStringArrayToFile(lines, *path, code);
}
else
{
FFileHelper::SaveStringArrayToFile(lines, *path, code, &IFileManager::Get(), EFileWrite::FILEWRITE_Append);
}
}
void MyFileManager::SaveBytes(FString path, TArray<uint8> bytes)
{
MyFileManager::CheckFileSavePath(path);
FFileHelper::SaveArrayToFile(bytes, *path);
}
更多推荐



所有评论(0)