其实文档都有,做个整理方便查询:
类类型(UObject、AActor、UActorComponent 和 UStruct):
UObject:(不常用)此类结合 UClass (包含UObject派生类实例的所有元数据)提供引擎中最重要的若干基础服务:
- 属性和方法反射
- 属性序列化
- 垃圾回收
- 按命名查找 UObject
- 可配置属性数值
- 属性和方法网络支持
AActor:拥有一系列事件,可在生命周期中进行调用:
- BeginPlay - 对象首次出现在游戏进程中时调用
- Tick - 每帧调用一次,在一段时间内执行操作
- EndPlay - 对象离开游戏进程时调用
UActorComponent :通常为 AActor 指定的是与其在游戏中全局作用相关的高级目标,而 UActorComponent 通常执行的是支持这些高级目标的单个任务。组件也可附着到其他组件,或为 Actor 的根组件。组件只能附着到一个父组件或 Actor,但可被多个子组件附着。
- RootComponent - 这是在 AActor 组件树中拥有顶层组件的 AActor 成员
- Ticking - 组件作为拥有 AActor Tick() 的部分被点击
UStruct:使用 UStruct 时不必从任意特定类进行延展,只需要使用 USTRUCT() 标记结构体,编译工具将执行基础工作。和 UObject 不同,UStruct 不会被垃圾回收。如创建其动态实例,则必须自行管理其生命周期。UStruct 为纯旧式数据类型。它们拥有 UObject 反射支持,以便在虚幻编辑器、蓝图操作、序列化和网络通信中进行编辑。
反射系统:
UE4 使用其自身的反射实现,可启用动态功能,如垃圾回收、序列化、网络复制和蓝图/C++ 通信。这些功能为选择加入,意味着您需要为类型添加正确的标记,否则引擎将无视类型,不生成反射数据。
UCLASS() - 告知虚幻引擎生成类的反射数据。类必须派生自 UObject。常用说明符如下:
UFUNCTION() - 使 UCLASS 或 USTRUCT 的类方法可用作 UFUNCTION。UFUNCTION 允许类方法从蓝图中被调用,并在其他资源中用作 RPC。UFUNCTION 说明符列表
UCLASS() 范例:
#include "MyObject.generated.h"
UCLASS(Blueprintable)
class UMyObject : public UObject
{
GENERATED_BODY()
public:
MyUObject();
UPROPERTY(BlueprintReadOnly, EditAnywhere)
float ExampleProperty;
UFUNCTION(BlueprintCallable)
void ExampleFunction();
};
首先注意 - “MyClass.generated.h”文件已包含。虚幻引擎将生成所有反射数据并将放入此文件。必须在声明类型的头文件中将此文件作为最后的 include 包含。
同时可以在标记上添加额外的说明符,通过说明符可对类型拥有的特定行为进行说明。
虚幻引擎为您提供在构建过程中生成代码的工具。这些工具拥有一些类命名规则。如命名与规则不符,将触发警告或错误。下方的类前缀列表说明了命名的规则。
因为不同平台基础类型的尺寸不同,如 short、int 和 long,UE4 提供了以下类型,可用作替代品:
int8/uint8 :8 位带符号/不带符号 整数
int16/uint16 :16 位带符号/不带符号 整数
int32/uint32 :32 位带符号/不带符号 整数
int64/uint64 :64 位带符号/不带符号整数
标准 浮点 (32-bit) 和 双倍(64-bit)类型也支持浮点数。
UE4有一个模板 TNumericLimits,用于找到数值类型支持的最小和最大范围。如需了解详情,请查阅此 链接 。
UE4 提供多个不同类使用字符串,可满足多种需求。
完整要点:字符串处理
FString 是一个可变字符串,类似于 std::string。FString 拥有许多方法,便于简单地使用字符串。使用 TEXT() 宏可新建一个 FString:
FString MyStr = TEXT("Hello, Unreal 4!").
完整要点:FString API
FText 与 FString 相似,但用于本地化文本。使用 NSLOCTEXT 宏可新建一个 FText。此宏拥有默认语言的命名空间、键和一个数值。
FText MyText = NSLOCTEXT("Game UI", "Health Warning Message", "Low Health!")
也可使用 LOCTEXT 宏,只需要在每个文件上定义一次命名空间。确保在文件底层取消它的定义
// 在 GameUI.cpp 中
#define LOCTEXT_NAMESPACE "Game UI"
//...
FText MyText = LOCTEXT("Health Warning Message", "Low Health!")
//...
#undef LOCTEXT_NAMESPACE
// 文件末端
FString 到 FText 的转换:
//FString到FText的转换示例:
FText WidgetText = FText::Format(
LOCTEXT("WindowWidgetText", "Add code to {0} in {1} to override this window's contents"),
FText::FromString(TEXT("FtestPluginModule::OnSpawnPluginTab")),
FText::FromString(TEXT("testPlugin.cpp"))
);
完整要点:FText API
FName 将经常反复出现的字符串保存为辨识符,以便在对比时节约内存和 CPU 时间。FName 不会在引用完整字符串的每个对象间对其进行多次保存,而是使用一个映射到给定字符串的较小存储空间 索引。这会单次保存字符串内容,在字符串用于多个对象之间时节约内存。检查 NameA.Index 是否等于 NameB.Index 可对两个字符串进行快速对比,避免对字符串中每个字符进行相等性检查。
完整要点:FName API
TCHARs 用于存储不受正在使用的字符集约束的字符。平台不同,它们也可能存在不同。UE4 字符串在后台使用 TCHAR 阵列将数据保存在 UTF-16 编码中。使用返回 TCHAR 的重载解引用运算符可以访问原始数据。
完整要点:角色编码
部分函数会需要它。如 FString::Printf,‘%s’ 字符串格式说明符需要 TCHAR,而非 FString。
FString Str1 = TEXT("World");
int32 Val1 = 123;
FString Str2 = FString::Printf(TEXT("Hello, %s! You have %i points."), *Str1, Val1);
FChar 类型提供一个静态效用函数集,以便使用单个 TCHAR。
TCHAR Upper('A');
TCHAR Lower = FChar::ToLower(Upper); // 'a'
FChar 类型被定义为 TChar(因其列在 API 中)。
完整要点:TChar API
容器也是类,它们的主要功能是存储数据集。常见的类有 TArray、TMap 和 TSet。它们的大小均为动态,因此可变为所需的任意大小。
完整要点:组件 API
在这三个容器中,虚幻引擎 4 使用的主要容器是 TArray。它的作用和 std::vector 相似,但却多出许多功能。以下是一些常规操作:
TArray<AActor*> ActorArray = GetActorArrayFromSomewhere();
// 告知当前 ActorArray 中保存的元素(AActors)数量。
int32 ArraySize = ActorArray.Num();
// TArrays 从零开始(第一个元素在索引 0 处)
int32 Index = 0;
// 尝试获取在给定索引处的元素
TArray* FirstActor = ActorArray[Index];
// 在阵列末端添加一个新元素
AActor* NewActor = GetNewActor();
ActorArray.Add(NewActor);
// 只有元素不在阵列中时,才在阵列末端添加元素
ActorArray.AddUnique(NewActor); // 不会改变阵列,因为 NewActor 已被添加
// 移除阵列中所有 NewActor 实例
ActorArray.Remove(NewActor);
// 移除特定索引处的元素
// 索引上的元素将被下调一格,以填充空出的位置
ActorArray.RemoveAt(Index);
// RemoveAt 的高效版,但无法保持元素的排序
ActorArray.RemoveAtSwap(Index);
// 移除阵列中的所有元素
ActorArray.Empty();
TArray 还有一个额外好处 - 可使其元素被垃圾回收。这将假定 TArray 被标记为 UPROPERTY,并存储 UObject 派生的指针。
UCLASS()
class UMyClass :UObject
{
GENERATED_BODY();
// ...
UPROPERTY()
TArray<AActor*> GarbageCollectedArray;
};
之后章节中我们将深度讨论垃圾回收。
完整要点:TArrays
完整要点:TArray API
TMap 是键值对的合集,与 std::map 相似。TMap 可基于元素的键快速寻找、添加、并移除元素。只要键拥有为其定义的 GetTypeHash函数(稍后对此进行了解),即可使用任意类型的键。
假设您创建了一个基于网格的桌面游戏,需要保存并询问每个方格上的块。通过 TMap 即可轻松完成。如棋盘尺寸较小且保持不变,还存在更加高效的处理方式。
enum class EPieceType
{
King,
Queen,
Rook,
Bishop,
Knight,
Pawn
};
struct FPiece
{
int32 PlayerId;
EPieceType Type;
FIntPoint Position;
FPiece(int32 InPlayerId, EPieceType InType, FIntVector InPosition) :
PlayerId(InPlayerId),
Type(InType),
Position(InPosition)
{
}
};
class FBoard
{
private:
// 使用 TMap 时可通过块的位置对其进行查阅
TMap<FIntPoint, FPiece> Data;
public:
bool HasPieceAtPosition(FIntPoint Position)
{
return Data.Contains(Position);
}
FPiece GetPieceAtPosition(FIntPoint Position)
{
return Data[Position];
}
void AddNewPiece(int32 PlayerId, EPieceType Type, FIntPoint Position)
{
FPiece NewPiece(PlayerId, Type, Position);
Data.Add(Position, NewPiece);
}
void MovePiece(FIntPoint OldPosition, FIntPoint NewPosition)
{
FPiece Piece = Data[OldPosition];
Piece.Position = NewPosition;
Data.Remove(OldPosition);
Data.Add(NewPosition, Piece);
}
void RemovePieceAtPosition(FIntPoint Position)
{
Data.Remove(Position);
}
void ClearBoard()
{
Data.Empty();
}
};
完整要点:TMaps
完整要点:TMap API
TSet 保存唯一值的合集,与 std::set 相似。TArray 通过 AddUnique 和 Contains 方法可用作集。然而 TSet 可更快实现这些操作,但无法像 TArray 那样将它们用作 UPROPERTY。TSet 不会像 TArray 那样将元素编入索引。
TSet<AActor*> ActorSet = GetActorSetFromSomewhere();
int32 Size = ActorSet.Num();
// 如集尚未包含元素,则将其添加到集
AActor* NewActor = GetNewActor();
ActorSet.Add(NewActor);
// 检查元素是否已包含在集中
if (ActorSet.Contains(NewActor))
{
// ...
}
// 从集移除元素
ActorSet.Remove(NewActor);
// 从集移除所有元素
ActorSet.Empty();
// 创建包含 TSet 元素的 TArray
TArray<AActor*> ActorArrayFromSet = ActorSet.Array();
完整要点:TSet API
需注意:TArray 是当前唯一能被标记为 UPROPERTY 的容器类。这意味着无法复制、保存其他容器类,或对其元素进行垃圾回收。
使用迭代器可在容器的每个元素上进行循环。以下是使用 TSet 的迭代器语法范例。
void RemoveDeadEnemies(TSet<AEnemy*>& EnemySet)
{
// 从集的开头开始迭代到集的末端
for (auto EnemyIterator = EnemySet.CreateIterator(); EnemyIterator; ++EnemyIterator)
{
// * 运算符获得当前的元素
AEnemy* Enemy = *EnemyIterator;
if (Enemy.Health == 0)
{
// RemoveCurrent 由 TSets 和 TMaps 支持
EnemyIterator.RemoveCurrent();
}
}
}
可结合迭代器使用的其他支持操作:
// 将迭代器移回一个元素
--EnemyIterator;
// 以一定偏移前移或后移迭代器,此处的偏移为一个整数
EnemyIterator += Offset;
EnemyIterator -= Offset;
// 获得当前元素的索引
int32 Index = EnemyIterator.GetIndex();
// 将迭代器重设为第一个元素
EnemyIterator.Reset();
迭代器很实用,但如果只希望在每个元素之间循环一次,则可能会有些累赘。每个容器类还支持 for each 风格的语法在元素上进行循环。TArray 和 TSet 返回每个元素,而 TMap 返回一个键值对。
// TArray
TArray<AActor*> ActorArray = GetArrayFromSomewhere();
for (AActor* OneActor :ActorArray)
{
// ...
}
// TSet - 和 TArray 相同
TSet<AActor*> ActorSet = GetSetFromSomewhere();
for (AActor* UniqueActor :ActorSet)
{
// ...
}
// TMap - 迭代器返回一个键值对
TMap<FName, AActor*> NameToActorMap = GetMapFromSomewhere();
for (auto& KVP :NameToActorMap)
{
FName Name = KVP.Key;
AActor* Actor = KVP.Value;
// ...
}
注意:auto 关键词不会自动指定指针/引用,需要自行添加。