5 UE5 C++ 编码规范

2024-02-21  本文已影响0人  游戏开发程序员

UE必须的版权声明,否则CIS会生成失败!

// Copyright XXX Games, Inc. All Rights Reserved.

命名规范:每个单词需大写首字母,单词间通常无下划线。

模板类的前缀为 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;
上一篇 下一篇

猜你喜欢

热点阅读