UE4学习笔记2——宏、类型等基础总结

其实文档都有,做个整理方便查询:

类类型UObjectAActorUActorComponent 和 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。常用说明符如下:

  • Blueprintable - 此类可由蓝图延展。
  • BlueprintReadOnly - 此属性只可从蓝图读取,不可写入。
  • Category - 定义此属性出现在编辑器 Details 视图下的部分。用于组织。
  • BlueprintCallable - 可从蓝图调用此函数。
USTRUCT() - 告知虚幻引擎生成结构体的反射数据。UFUNCTION 说明符列表
GENERATED_BODY() - UE4 使用它替代为类型生成的所有必需样板文件代码。
UPROPERTY() - 使 UCLASS 或 USTRUCT 的成员变量可用作 UPROPERTY。UPROPERTY 用途广泛。它允许变量被复制、被序列化,并可从蓝图中进行访问。垃圾回收器还使用它们来追踪对 UObject 的引用数。UPROPERTY 说明符列表

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 包含。

同时可以在标记上添加额外的说明符,通过说明符可对类型拥有的特定行为进行说明。

类命名前缀

虚幻引擎为您提供在构建过程中生成代码的工具。这些工具拥有一些类命名规则。如命名与规则不符,将触发警告或错误。下方的类前缀列表说明了命名的规则。

  • 派生自 Actor 的类前缀为 A,如 AController。
  • 派生自 对象 的类前缀为 U,如 UComponent。
  • 枚举 的前缀为 E,如 EFortificationType。
  • 接口 类的前缀通常为 I,如 IAbilitySystemInterface。
  • 模板 类的前缀为 T,如 TArray。
  • 派生自 SWidget(Slate UI)的类前缀为 S,如 SButton。
  • 其余类的前缀均为 字母 F ,如 FVector。

数字类型

因为不同平台基础类型的尺寸不同,如 shortint 和 long,UE4 提供了以下类型,可用作替代品:

  • int8/uint8 :8 位带符号/不带符号 整数

  • int16/uint16 :16 位带符号/不带符号 整数

  • int32/uint32 :32 位带符号/不带符号 整数

  • int64/uint64 :64 位带符号/不带符号整数

标准 浮点 (32-bit) 和 双倍(64-bit)类型也支持浮点数。

UE4有一个模板 TNumericLimits,用于找到数值类型支持的最小和最大范围。如需了解详情,请查阅此 链接 。

字符串

UE4 提供多个不同类使用字符串,可满足多种需求。

完整要点:字符串处理

FString

FString 是一个可变字符串,类似于 std::string。FString 拥有许多方法,便于简单地使用字符串。使用 TEXT() 宏可新建一个 FString:

FString MyStr = TEXT("Hello, Unreal 4!").

完整要点:FString API

FText

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

FName 将经常反复出现的字符串保存为辨识符,以便在对比时节约内存和 CPU 时间。FName 不会在引用完整字符串的每个对象间对其进行多次保存,而是使用一个映射到给定字符串的较小存储空间 索引。这会单次保存字符串内容,在字符串用于多个对象之间时节约内存。检查 NameA.Index 是否等于 NameB.Index 可对两个字符串进行快速对比,避免对字符串中每个字符进行相等性检查。

完整要点:FName API

TCHAR

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

容器

容器也是类,它们的主要功能是存储数据集。常见的类有 TArrayTMap 和 TSet。它们的大小均为动态,因此可变为所需的任意大小。

完整要点:组件 API

TArray

在这三个容器中,虚幻引擎 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

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

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 循环

迭代器很实用,但如果只希望在每个元素之间循环一次,则可能会有些累赘。每个容器类还支持 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 关键词不会自动指定指针/引用,需要自行添加。



你可能感兴趣的:(UE4开发)