目录
一,Adding Classes
二,Class Headers
Class Declaration
Class Specifiers
Metadata Specifiers
三,Class Implementation
Class Constructor
引擎中每个游戏类都由一个头文件(.h)和一个源文件(.cpp)构成;头文件包含类和类成员(如变量和函数)的声明,而源文件是类功能的实现;类通过首字母或前缀来标准化的命名;
一,Adding Classes
主菜单Tools > New C++ Class...是一种快速简便的方法,将本机C++代码类添加到项目中,以便根据需要扩展功能;这会将纯内容项目转换为代码项目;确保安装了兼容版本的Visual Studio;
二,Class Headers
引擎中的类通常拥有单独且唯一的头文件,通常这些文件的命名与其中定义的类相匹配(减去A或U前缀,并使用.h文件扩展名),如AActor类的类头文件命名为Actor.h;
头文件使用标准 C++ 语法,并结合专门的宏,以简化类、变量和函数的声明过程;
// 在每个头文件都包含自动生成的头文件; #include "ClassName.generated.h"Class Declaration
类声明定义类的名称、其继承的类,以及继承的函数和变量;通过类说明符和元数据,类声明还定义了引擎和编辑器的特定行为;
UCLASS([specifier, specifier, ...], [meta(key = value, key = value, ...)]) class ClassName : public ParentName { GENERATED_BODY() // 必须被放置在类体的最前方 }Class Specifiers
在类声明中添加类说明符,以控制类在引擎和编辑器的各个方面如何表现;
| Class Specifier | Effect |
| Abstract | 声明为"抽象基类",不可向关卡中添加此类的Actor,如 |
| Blueprintable | 将此类公开为用于创建蓝图的可接受基类,默认为NotBlueprintable,子类会继承; |
| NotBlueprintable | 指定此类不是可用于创建蓝图的可接受基类,此为默认说明符,子类会继承; |
| BlueprintType | 将此类公开为可用于蓝图变量的类型; |
| Const | 此类中的所有属性和函数都是const并且导出为const,子类会继承; |
| ConversionRoot | 根转换,将子类限制为仅可沿层级向上转换为第一个根类的子类; |
| CustomConstructor | 阻止构造函数声明自动生成; |
| DefaultToInstanced | 此类的所有实例都被认为是"实例化的",实例化的类(组件)将在构造时被复制,子类会继承; |
| EditInlineNew | 可从编辑器"Property"窗口创建此类的对象,而非从现有资源引用;默认仅可通过"Property"窗口指定对现有对象的引用; 此说明符会传播到所有子类,子类可通过 NotEditInlineNew说明符覆盖它; |
| Intrinsic | 此类直接在C++中声明,无Unreal Header Tool生成的样板,请勿在新类上使用此说明符; |
| MinimalAPI | 仅导出类的类型信息供其他模块使用; 该类可被类型转换,但无法调用该类的函数(内联方法除外); 对于不需要在其他模块中访问所有函数的类,通过不导出所有内容,可以缩短编译时间; |
| NoExport | 此类的声明不应包含在自动生成的C++头文件中,必须在单独的头文件中手动定义该C++类声明; 仅对本地类有效,请勿对新类使用此说明符; |
| Transient | 属于此类别的对象将永远不会保存到磁盘,与非持久性的本地类别(如玩家或窗口)结合使用时非常有用; 此说明符会传递给子类,但可以被非瞬态说明符覆盖; |
| NonTransient | 使继承自基类的Transient说明符无效; |
| Placeable | 可在编辑器中创建此类,且可将此类放置到关卡、UI场景或蓝图(取决于类类型)中; 此标志会传播到所有子类,子类可使用 NotPlaceable说明符覆盖此标志; |
| NotPlaceable | 使继承自基类的Placeable说明符无效,不可以在编辑器中将此类的对象放置到关卡、UI场景或蓝图中; |
| PerObjectConfig | 此类别的配置信息将按对象存储,其中每个对象在.ini文件中都有一个以对象名称命名的部分,格式为[ObjectName ClassName],此说明符会传递给子类; |
| Within=OuterClassName | 此类的对象无法在 要创建此类的对象,需要提供 |
| AdvancedClassDisplay | 强制类的所有属性仅显示在细节面板的"Advanced"部分; |
| ClassGroup=GroupName | 在编辑器的Actor浏览器中启用Group View时,Actor Browser应在指定的GroupName中包含此类及其所有子类; |
| Config=ConfigName | 此类可在配置文件( 会传播到所有子类并且无法使其无效,但是子类可通过重新声明 |
| DependsOn=(ClassName1, ClassName2, ...) | 所列的类将先于此类被编译,提供的类名必须指明是同一(或之前)包中的类; 可使用单个 当某个类使用在另一个类中声明的结构体或枚举时,这非常重要,因为编译器只知道它已经编译过的类中的内容; |
| Deprecated | 此类已弃用,序列化时将不保存此类的对象,子类会继承; |
| AutoCollapseCategories=(Category1, Category2, ...) | 使父类上的AutoExpandCategories说明符列出类别的效果无效; |
| AutoExpandCategories=(Category1, Category2, ...) | 指定应自动在编辑器属性窗口中展开的一个或多个类别; 要自动展开未使用类别变量,使用类名称; |
| DontAutoCollapseCategories=(Category, Category, ...) | 使列出类别的继承自父类的AutoCollapseCategories说明符无效; |
| CollapseCategories | 此类的属性不应划分到编辑器属性窗口的类别中,会传播到子类,可由DontCollapseCategories说明符覆盖; |
| DontCollapseCategories | 使继承自基类的CollapseCatogories说明符无效; |
| ShowCategories=(Category1, Category2, ...) | 使继承自基类的HideCategories说明符无效; |
| HideCategories=(Category1, Category2, ...) | 列出对用户完全隐藏的类别,此说明符会传播到子类; 要隐藏未使用类别声明的属性,使用声明变量的类的名称; |
| ShowFunctions=(Category1, Category2, ...) | 在属性查看器中显示列出的类别中的所有函数; |
| HideFunctions=(Category1, Category2, ...) | 让指定分类中的所有函数都对用户完全隐藏; |
| ShowFunctions=FunctionName | 在属性查看器中显示指定的函数; |
| HideFunctions=FunctionName | 将提到的函数对用户完全隐藏; |
| HideDropdown | 阻止此类在属性窗口组合框中显示; |
Metadata Specifiers
在声明类、接口、结构体、枚举/枚举值、函数或属性时,可以添加元数据说明符来控制其与引擎和编辑器各个方面的交互方式;每种数据结构或成员类型都有其自己的元数据说明符列表,元数据仅存在于编辑器中,请勿编写访问元数据的游戏逻辑;
| Class Meta Tag | Effect |
| BlueprintSpawnableComponent | 如其存在,组件类可由蓝图生成; |
| BlueprintThreadSafe | 只在蓝图函数库上有效,将把此类中的函数在动画蓝图中的非游戏线程上标记为可调用; |
| ChildCannotTick | 用于Actor和组件类,如本地类无法tick,那么基于此Actor或组件的蓝图生成类则无法tick,即使bCanBlueprintsTickByDefault为true也同样如此; |
| ChildCanTick | 用于Actor和组件类,如本地类无法tick,那么可以覆盖基于此Actor或组件的蓝图生成类的bCanEverTick标签,即使bCanBlueprintsTickByDefault为false也同样如此; |
| DeprecatedNode | 用于行为树节点,说明类已废弃,编译时将显示一条警告; |
| DeprecationMessage="Message Text" | 如类已废弃,尝试编译使用此类的蓝图时,会添加到标准废弃警告; |
| DisplayName="Blueprint Node Name" | 此节点在蓝图中的命名将被此处提供的值所取代,而非代码生成的命名; |
| DontUseGenericSpawnObject | 不使用蓝图中的通用Create Object节点来生成类的一个对象,此说明符只有在用于既非Actor又非ActorComponent的BluprintType类时才有意义; |
| ExposedAsyncProxy | 在Async Task节点中公开此类的一个代理对象; |
| IgnoreCategoryKeywordsInSubclasses | 用于让一个类的首个子类忽略所有继承的ShowCategories和HideCategories说明符; |
| IsBlueprintBase="true/false" | 说明此类是否为创建蓝图的一个可接受基类,与UCLASS说明符Blueprintable或NotBlueprintable相似; |
| KismetHideOverrides="Event1, Event2, .." | 不允许被覆盖的蓝图事件的列表; |
| ProhibitedInterfaces="Interface1, Interface2, .." | 列出与类不兼容的接口; |
| RestrictedToClasses="Class1, Class2, .." | 由蓝图函数库类使用,用于限制列表中命名类的用法; |
| ToolTip="Hand-written tooltip" | 覆盖从代码注释自动生成的提示文本; |
| ShortToolTip="Short tooltip" | 完整提示文本过长时使用的简短提示文本,如父类选取器对话; |
| ShowWorldContextPin | 说明放置在此类拥有的图表中的蓝图节点必须显式其World情景引脚(即使其通常状态下为隐藏也同样如此),因为此类的对象无法被用作World情景; |
| UsesHierarchy | 表示类使用层级数据,用于实例化"细节"面板中的层级编辑功能; |
| ScriptName="DisplayName" | 在将此类、属性或函数导出到脚本语言时使用的名称,可以包含已弃用的名称,作为额外的以分号分隔的条目; |
三,Class Implementation
所有类必须使用GENERATED_BODY宏才能正确实现,这是在定义类及其所有变量和函数的类头(.h)文件中完成的;最佳方法是使类源和头文件的命名与实现的类相匹配,减去A或U前缀,如AActor类的源文件命名为Actor.cpp,其头文件命名为Actor.h(对编辑器中"Add C++ Class"而言是自动进行);
源文件(.cpp)必须包含 C++ 类声明的头文件(.h),通常为自动生成,但也可手动生成(如有必要),如AActor类的 C++ 声明在EngineClasses.h头文件中自动生成,Actor.cpp文件必须包括EngineClasses.h文件或包含它的另一个文件;
Class Constructor
UObjects使用Constructors设置属性和其他变量的默认值,并执行其他必要的初值设定;类构造函数通常放置在类实现文件中,如AActor::AActor构造函数位于Actor.cpp中;
注,部分构造函数可能以每个模块为基础放置在一个特殊的"constructors"文件中;
也可以将构造函数内联放置在类头文件中;然而,如果构造函数在类头文件中,UClass 必须结合CustomConstructor说明符进行声明,因为这阻止了自动代码生成器在标头中创建构造函数声明;
Constructor Format
// UObject 构建函数最基本的形式 UMyObject::UMyObject() { // 在此处初始化 Class Default Object 属性 }// 另一个辅助构造函数,支持一个特殊的属性调整结构; UMyObject::UMyObject(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { // Initialize CDO properties here. }尽管上述两个构造函数实际上都没有执行任何初始化操作,但引擎已经将所有字段初始化为零、NULL或其默认构造函数所实现的任何值;任何在构造函数中的初始化代码都将应用于CDO,也会被复制到正确创建的对象的任何新实例中,就像使用CreateNewObject或SpawnActor一样;
传递给构造函数的FObjectInitializer参数,尽管被标记为const,但可以通过内置的可变函数进行配置,以覆盖属性和子对象;正在创建的UObject将受到这些更改的影响,并且这可用于更改已注册属性或组件的值;
AUDKEmitterPool::AUDKEmitterPool(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer.DoNotCreateDefaultSubobject(TEXT("SomeComponent")).DoNotCreateDefaultSubobject(TEXT("SomeOtherComponent"))) { // Initialize CDO properties here. }在上例中,超类将在其构建函数中创建名为"SomeComponent"和"SomeOtherComponent"的子对象,但由于 FObjectInitializer 的原因,该操作将不会执行;在下例中,SomeProperty在 CDO 中将默认为 26,因此在 AUTDemoHUD 的每个新实例中均为如此;
AUTDemoHUD::AUTDemoHUD() { // 在此处初始化 CDO 属性 SomeProperty = 26; }Constructor Statics and Helpers
为更复杂的数据类型(尤其是类引用、名称和资源引用)设置值时,需在构造函数中定义并实例化一个 ConstructorStatics 结构体来保存所需的各种属性值;该结构体仅在构造函数首次运行时创建,在后续运行中,它只需复制一个指针,因此速度极快;在创建 ConstructorStatics 结构体时,会将值分配给该结构体的成员,以便在构造函数后续将值分配给实际属性时进行访问;
ContructorHelpers是在ObjectBase.h中定义的一个特殊命名空间,其中包含用于执行特定于构造函数的常见操作的辅助模板,如存在用于查找资源或类引用以及创建和查找组件的辅助模板;
Asset References
理想情况下,类中不应存在资源引用,硬编码的资源引用易出错,更推荐使用蓝图来配置资源属性;不希望每次构造对象时都搜索资源,通过一个静态结构体,确保只进行一次资源搜索;
ConstructorHelpers::FObjectFinder通过 StaticLoadObject 函数查找指定 UObject 的引用,通常用于引用存储在内容包中的资源,如未找到对象,则会报告失败;
ATimelineTestActor::ATimelineTestActor() { // Structure to hold one-time initialization struct FConstructorStatics { ConstructorHelpers::FObjectFinder<UStaticMesh> Object0; FConstructorStatics() : Object0(TEXT("StaticMesh'/Game/UT3/Pickups/Pickups/Health_Large/Mesh/S_Pickups_Base_Health_Large.S_Pickups_Base_Health_Large'")) { } }; static FConstructorStatics ConstructorStatics; // Property initialization StaticMesh = ConstructorStatics.Object0.Object; }Class References
ConstructorHelpers::FClassFinder为特定的UClass寻找引用,如类未找到,则报告失败;
APylon::APylon(const class FObjectInitializer& ObjectInitializer) :Super(ObjectInitializer) { // 进行一次性初始化的结构 static FClassFinder<UNavigationMeshBase> ClassFinder(TEXT("class'Engine.NavigationMeshBase'")); if (ClassFinder.Succeeded()) NavMeshClass = ClassFinder.Class; else NavMeshClass = nullptr; }在许多情况下,可只使用USomeClass::StaticClass(),绕开复杂的全部 ClassFinder;对跨模块的引用而言,使用 ClassFinder 法较好;
NavMeshClass = UNavigationMeshBase::StaticClass();Components and Sub-Objects
在构造函数中也可创建组件子对象并将其附加到角色的层次结构中;当生成一个角色时,其组件将从CDO中克隆;为了确保组件始终被创建、销毁并正确地进行垃圾回收,应在所属类的UPROPERTY中存储一个指向构造函数中创建的每个组件的指针;
UCLASS() class AWindPointSource : public AActor { GENERATED_BODY() public: UPROPERTY() UWindPointSourceComponent* WindPointSource; UPROPERTY() UDrawSphereComponent* DisplaySphere; }; AWindPointSource::AWindPointSource() { // Create a new component and give it a name. WindPointSource = CreateDefaultSubobject<UWindPointSourceComponent>(TEXT("WindPointSourceComponent0")); // Set our new component as the RootComponent of this actor, or attach it to the root if one already exists. if (RootComponent == nullptr) RootComponent = WindPointSource; else WindPointSource->AttachTo(RootComponent); // Create a second component. This will be attached to the component we just created. DisplaySphere = CreateDefaultSubobject<UDrawSphereComponent>(TEXT("DrawSphereComponent0")); DisplaySphere->AttachTo(RootComponent); // Set some properties on the new component. DisplaySphere->ShapeColor.R = 173; DisplaySphere->ShapeColor.G = 239; DisplaySphere->ShapeColor.B = 231; DisplaySphere->ShapeColor.A = 255; DisplaySphere->AlwaysLoadOnClient = false; DisplaySphere->AlwaysLoadOnServer = false; DisplaySphere->bAbsoluteScale = true; }