# Objects

Unreal有一个完备的系统来处理游戏对象,在Unreal中的所有对象的基类都是UObject。UCLASS宏可被用来标记从UObject继承的类,所以这个类能被UObject处理系统处理。

# UCLASS宏

UClASS宏让UObject能引用一个UCLASS,UCLASS是用来描述Unreal基类型的类,每个UCLASS都有一个称为"Class Default Object"的对象,简称为CDO。CDO是很重要的一个"模板"对象,由构造函数生成之后就不会改变了。UCLASS和CDO都可以恢复一个对象实例,但是它们一般被认为是只读的。一个对象实例的UCLASS可在任何时候通过GetClass()获取。

一个UCLASS包含了定义一个类的所需的属性和函数。可以使用原生C++正常函数和变量,但是如果使用Unreal指定元信息标记的,就可以在对象系统中控制它们的行为。

注意一个UObject类可以包括在UCLASS中并没有对应的原生属性。

# 属性和函数

UObject可以有很多成员变量(比如属性)或任意类型的函数。但是,为了让Unreal识别和操作这些变量或函数,必须使用特殊宏来标记它们,而且得遵从某些类型标准。

# 创建对象

有几种方法来创建UObject实例:

方法 描述
NewObject<Class> 从所有可用的选项中使用可选参数来创建实例,提供更大的灵活性。
new 在一个较低的环境中构建对象,比如当构造器中需要参数时。

# UObjects提供的函数

并不是在所有的场景中都需要或者合适使用这个系统,但还是能做很多有用的事,包括:

  • 垃圾回收
  • 更新引用
  • 反射
  • 序列化
  • 默认属性的自动更新
  • 属性自动序列化
  • 自动集成编辑器
  • 运行时的类型信息
  • 网络复制

# Unreal头文件工具

为了使用由UObject派生的类所继承的函数,需要在头文件处添加一步预处理,以核对所需信息,这步是通过UnrealHeaderTool(UHT)执行的。

UObject派生的类型有一种结构体,后面再说这个。

# 头文件格式

尽管一个UObject会在cpp文件中像其他C++类一样实现,它在头文件中的定义必须依附某个结构体来让它在Unreal中工作正常,使用编辑器的"New C++ Class"命令是最容易建立正确头文件格式的方法。一个派生于UObject类的基本头文件可能像下面这样,假设这个类被命名为UMyObject,它所在的工程是MyProject:

#pragma once

#include 'Object.h'
#include 'MyObject.generated.h'

/**
 * 
 */
UCLASS()
class MYPROJECT_API UMyObject : public UObject
{
    GENERATED_BODY()

};
1
2
3
4
5
6
7
8
9
10
11
12
13
14

Unreal相关的部分是:

#include "MyObject.generated.h"
1

这行需要是头文件中#include指令里的最后一条,如果这个头文件需要知道其他类,它们可在文件中任何地方向前声明,或在包含MyObject.generated.h之前包含该文件。

UCLASS()
1

UCLASS宏让UMyObject类对Unreal是可见的,这个宏支持很多类说明符,能开启或关闭一些特性:

class MYPROJECT_API UMyObject : public UObject
1

如果MyProject工程希望把UMyObject类暴露给其他模块,指定MYPROJECT_API是很有必要的。这对于模块或插件是很有用的,因为它们会被游戏工程包含,需要故意暴露类在跨多个项目时来提供可解耦的工具函数库。

GENERATED_BODY()
1

这个宏不接收参数,但可以让类支持引擎要求的基础架构,所有的UCLASS都需要它。

# 更新对象

在Unreal中Ticking涉及到对象的更新,所有的对象有能力在每一帧tick,这让你来执行更新计算以及必要的动作。

对象并没有任何内置的更新能力,但是,这个能力可在需要的时候使用inherits类说明符继承自FTickableGameObject类。然后他们可以实现Tick()函数,这会被引擎在每一帧调用。注意游戏中的大部分对象都是Actors,它可在用户设置的最小间隔而不是非得在每一帧时Tick。

# 销毁对象

注意无论对象是否被垃圾回收,弱指针对它都没有影响。

在对象不再被变量引用时,对象会被垃圾回收系统自动销毁,这意味着没有UPROPERTY指针,或者引擎容器,或者智能指针实例有该对象的引用。当垃圾回收运行时,没有被引用的对象会被删除,另外,可在对象上直接调用MarkPendingKill()函数,这会让所有指向该对象的指针都设置为NULL,也会从全局搜索中移除对象,这样,对象会在下一次垃圾回收时被完全删除。