5 UE5 C++ 编码规范
UE必须的版权声明,否则CIS会生成失败!
// Copyright XXX Games, Inc. All Rights Reserved.
命名规范:每个单词需大写首字母,单词间通常无下划线。
- 若In或Out参数同样为布尔,以b作为In/Out的前缀,如 bOutResult。
模板类的前缀为 T。
继承自 UObject 的类前缀为 U。
继承自 AActor 的类前缀为 A。
继承自 SWidget 的类前缀为 S。
抽象界面类的前缀为 I。
枚举的前缀为 E。
布尔变量必须以 b为前缀
其他多数类均以 F为前缀,而部分子系统则以其他字母为前缀。
TArray<FMytype> FArrayOfMyTypes;
float TeaWeight;
int32 TeaCount;
bool bIsTeaStink;
FName TeaName;
FString TeaFriendlyName;
UClass* TeaClass;
USoundCue* TeaSound;
UTexture* TeaTexture;
TArray<FMytype> FArrayOfMyTypes;
可移植的C++代码
[涉及序列化或复制类型的格式时,必须使用长度明确的类型]
bool 代表布尔值(不会假定布尔大小)。BOOL 不会进行编译。
TCHAR 代表字符(不会假定TCHAR大小)。
uint8 代表无符号字节(1字节)。
int8 代表带符号字节(1字节)。
uint16 代表无符号"短"字符(2字节)。
int16 代表带符号"短"字符(2字节)。
uint32 代表无符号整数(4字节)。
int32 代表带符号整数(4字节)。
uint64 代表无符号"四字"(8字节)。
int64 代表带符号"四字"(8字节)。
float 代表单精确浮点(4字节)。
double 代表双精确浮点(8字节)。
PTRINT 代表可能含有指针的整数(不会假定PTRINT尺寸)。
标准库的使用
避免使用标准容器和字符串。
<atomic> 推荐使用,原子性在支持平台上高效。
<limits>: std::numeric_limits 可以完整使用。
<regex>:可以直接使用,但其使用应封装在仅和编辑器有关的代码中。
<cmath>: 这个头文件中只有浮点比较函数可以使用。
nullptr 所用情况下均使用 nullptr,而非C-style NULL 宏。
常量const的使用:
1) 不修改参数,常量指针或引用将传递函数参数
void SomeMutatingOperation(FThing& OutResult, const TArray<Int32>& InArray)
2)方法不修改对象,将方法标记为常量
void FThing::SomeNonMutatingOperation() const
3)循环不修改容器,则在容器上使用常量迭代
for (const FString& :StringArray)
auto 关键字
什么时候可以使用 自动 模式?
需要将匿名函数与变量绑定时。因为代码中无法表达匿名函数类型。
仅迭代器类型冗长且会损坏可读性时,适用于迭代函数。
无法清楚识别表达式的模板代码中适用。
强类型化枚举:支持 UPROPERTY可为任何大小,公开到蓝图的须基于 uint8。
UPROPERTY()
EThing MyProperty;
UENUM()
enum class EThing : uint8
{
Thing1,
Thing2
}
基于范围的遍历
TMap使用键值 TPair 的 键 和 值
// 新样式
for (TPair<FString, int32>& Kvp :MyMap)
{
UE_LOG(LogCategory, Log, TEXT("Key:%s, Value:%d"), *Kvp.Key, Kvp.Value);
}
移动语意(Move Semantics)
TArray、TMap、TSet、FString 等所有主要容器类型含有移动构造函数与移动赋值运算符。
默认成员初始器
根据经验而言,默认成员初始器更适用于游戏代码而非引擎代码。同样也可考虑使用默认值的配置文件。
UCLASS()
class UTeaOptions : public UObject
{
GENERATED_BODY()
public:
UPROPERTY()
int32 MaximumNumberOfCupsPerDay = 10;
UPROPERTY()
float CupWidth = 11.5f;
UPROPERTY()
FString TeaType = TEXT("Earl Grey");
UPROPERTY()
EDrinkingStyle DrinkingStyle = EDrinkingStyle::PinkyExtended;
};
优势:
无需跨多个构造函数重复初始器。
不会打乱初始化顺序和声明顺序。
成员类型、属性标志和默认值均在一处,便于阅读及维护。
劣势:
需重新编译依赖文件才能修改默认设置。
引擎的补丁中无法修改头文件,此格式将限制修复的类型。
部分对象无法以此方式格式化,例如基类、UObject 子对象、前置声明类型的指针、构造函数参数的推断值、多步骤初始化成员等。
如将部分初始器放入头文件,而其余在.cpp文件的构造函数中,可能会影响可读性和可维护性。
避免在函数调用中使用匿名文字。建议使用描述其含义的命名常量
// 旧样式
Trigger(TEXT("Soldier"), 5, true);.
// 新样式
const FName ObjectName = TEXT("Soldier");
const float CooldownInSeconds = 5;
const bool bVulnerableDuringCooldown = true;
Trigger(ObjectName, CooldownInSeconds, bVulnerableDuringCooldown);
不允许隐藏变量。C++可在外部作用域隐藏变量,但此操作会导致语意不清。
int32 Count;
for (int32 Count = 0; Count != 10; ++Count) // error!!
指针与引用应仅含一个空格,该空格位于指针/引用右侧。
FShaderType* Ptr
在字符串固定使用 TEXT() 宏。
若未使用,在文字中构建 FStrings 的代码将导致不理想的字符转换过程。
依赖性
文件名应尽量不添加前缀。例如使用 Scene.cpp
使用 #pragma once
避免包含来自其他头文件的标准库头文件。
若可使用前置声明,而非头文件,请使用前置声明。
包含时尽量细粒化。例如,勿包含Core.h,而包含特定头文件。
请勿依赖被包含的其他头文件间接包含的头文件。
模块含有私有和公开源目录。其他模块所需定义必须在公开目录的头文件中。
API设计指导
应避免使用 布尔 函数参数,应优先使用枚举。
避免过长的函数参数列表。如函数使用多个参数,则选择传递专属结构体
避免使用 布尔 和 `Fstring`` 重载函数,此操作可能导致意外行为
声明覆盖方法时应使用 virtual 和 override关键字。
protected:
virtual void BeginPlay() override;