UE4中C++编程 III

来源:互联网 发布:多益网络 ceo 编辑:程序博客网 时间:2024/06/06 19:24

之前说过,UE4对C++进行了扩展增强,使其便于游戏开发。下面就简要介绍增加的特性。


反射系统(Unreal Reflection System)

内置的逻辑类利用了特殊的标记,所以先概览一下Unreal的原型系统。UE4自己实现了一套反射系统,动态支持诸如垃圾回收、序列化、网络复制、Blueprint/C++通讯等特性。这些特性是可选的,意味着你必须先给你的类型加上标记才能使用,不然Unreal不会给它们生成反射数据。下面列了几个基本的标记:

  • UCLASS() - 为class生成反射数据。该类必须是派生于UObject
  • USTRUCT() - 为struct生成反射数据。
  • GENERATED_BODY() - UE4会在此换成为该类型生成的模版代码。
  • UPROPERTY() - 让UCLASS/USTRUCT中的成员变量作为UPROPERTY。UPROPERTY可让变量用于复制、序列化并从Blueprint中可访问,同时受垃圾回收器追踪。
  • UFUNCTION() - 让UCLASS/USTRUCT中的成员方法作为UFUNCTION。UFUNCTION可让方法从Blueprint中调用,用作RPCs等等。

举个代码示例:

#include "MyObject.generated.h"UCLASS(Blueprintable)class UMyObject : public UObject{    GENERATED_BODY()public:    MyUObject();    UPROPERTY(BlueprintReadOnly, EditAnywhere)    float ExampleProperty;    UFUNCTION(BlueprintCallable)    void ExampleFunction();};

先注意头文件"MyObject.generated.h",Unreal生成的所有反射数据都在此文件里。需要把该文件包括在头文件列表最末。

标记里带有说明符,用于明确指定类型的行为。

  • Blueprintable - 该类暴露给 Blueprint
  • BlueprintReadOnly - 该属性在Blueprint中只读不可写
  • Category - 确定该属性在编辑器的详细面板中显示在哪个分类下,方便管理
  • BlueprintCallable - Blueprint中可调用该函数

引擎里说明符非常多,分类查阅参考:

  • UCLASS说明符
  • UPROPERTY说明符
  • UFUNCTION说明符
  • USTRUCT说明符

至于反射系统更详细的解读,比如其实现原理之类的,参阅官方博客


Object/Actor 迭代器

object迭代器用于遍历一个UObject类型的所有实例(子类)时还是很方便有用的。

// Will find ALL current UObjects instancesfor (TObjectIterator<UObject> It; It; ++It){    UObject* CurrentObject = *It;    UE_LOG(LogTemp, Log, TEXT("Found UObject named: %s"), *CurrentObject.GetName());}

给迭代器提供范围时,注意该类型需要派生自UObject,假设你有个派生于UObject的类叫UMyClass,使用迭代器,可以遍历其所有实例(与子类):

for (TObjectIterator<UMyClass> It; It; ++It){    // ...}

actor迭代器用法和object迭代器一样,但只能用于派生自AActor的对象。返回的只有当前游戏实例中使用的对象。所以,使用actor迭代器时,需要提供一个UWorld实例。大部分UObject类比如APlayerController都提供GetWorld方法来方便获取。如果不确定,还可以先用ImplementsGetWorld方法检查一下该UObject对象是否实现了GetWorld方法。

APlayerController* MyPC = GetMyPlayerControllerFromSomewhere();UWorld* World = MyPC->GetWorld();// Like object iterators, you can provide a specific class to get only objects that are// or derive from that classfor (TActorIterator<AEnemy> It(World); It; ++It){    // ...}

内存管理与垃圾回收

介绍点UE4中基本的内存管理机制和垃圾回收系统。

UObjects与垃圾回收

UE4使用反射系统实现垃圾回收系统。受助于垃圾回收,你不再手动管理删除各类UObject,只需维持他们的合法引用即可。只有派生自UObject的类才能利用垃圾回收系统。

UCLASS()class MyGCType : public UObject{    GENERATED_BODY()};

在垃圾回收器中,有个叫根集合的概念。根集合中包含一系列回收器永远不会回收的对象。只要某个子对象能通过引用路径上溯到根集合中的对象,则该子对象也不会被回收。反之,该子对象处于不可及的状态,将会在下一个垃圾回收周期中被回收(删除)。垃圾回收器会在固定间隔内运行一次。

何为“引用”计数?就是任何UObject指针必须存储在UPROPERTY中。举个简单的例子。

void CreateDoomedObject(){    MyGCType* DoomedObject = NewObject<MyGCType>();}

当调用上述函数时,新建了一个UObject,但我们并没有将其指针存储在某个UPROPERTY中,意味着该对象不属于根集合。最终,垃圾回收器将检测到该对象不可及并销毁它。


Actors与垃圾回收

actor不完全受垃圾回收控制。播种(spawn)出来的actor必须手动调用其Destroy()函数。调用之后也不会立即删除,而是在下一个垃圾回收阶段进行清理。

下面列一个常见的情况,actor拥有部分UObject属性。

UCLASS()class AMyActor : public AActor{    GENERATED_BODY()public:    UPROPERTY()    MyGCType* SafeObject;    MyGCType* DoomedObject;    AMyActor(const FObjectInitializer& ObjectInitializer)        : Super(ObjectInitializer)    {        SafeObject = NewObject<MyGCType>();        DoomedObject = NewObject<MyGCType>();    }};void SpawnMyActor(UWorld* World, FVector Location, FRotator Rotation){    World->SpawnActor<AMyActor>(Location, Rotation);}

调用上述函数,将在游戏世界诞生一个actor。该actor构造函数中会创建两个对象。一个分配了UPROPERTY,另一个没有。由于actor会自动加入根集合,SafeObject因为可以关联到根集合,将不被当作垃圾而回收;而DoomedObject就不行。我们没有用UPROPERTY标记它(DoomedObject),垃圾回收器不知道它被引用,于是销毁该对象。

当某个UObject回收后,所有引用该对象的UPROPERTY会置空(nullptr)。这样可以安全检测某对象是否已被回收。

if (MyActor->SafeObject != nullptr){    // Use SafeObject}

特别注意前面一段提醒:actor调用Destroy()函数后不会立即清除,而是在下一次垃圾回收器工作时才清理。可以使用IsPendingKill()函数来检测某对象是否处于等待清理的状态。函数返回true的话,就不要使用它了。


UStructs

UStruct可以看成轻量级UObject,但不受垃圾回收管理。如果要使用动态UStruct实例,最好利用智能指针。


非UObject引用

一般来说,非UObject对象可以添加一个引用来阻止被垃圾回收掉。前提是,你的类要继承自FGCObject并重载AddReferencedObjects

class FMyNormalClass : public FGCObject{public:    UObject* SafeObject;    FMyNormalClass(UObject* Object)        : SafeObject(Object)    {    }    void AddReferencedObjects(FReferenceCollector& Collector) override    {        Collector.AddReferencedObject(SafeObject);    }};

使用FReferenceCollector手动添加一条对UObject的引用以拒绝垃圾回收。当该对象被删除,调用析构函数时,对象会自动清理身上所有的引用。


总结

  • UE4的反射系统实现原理暂且不表,使用方式还是简洁明了的。简单加上宏标记即可。
  • 通过反射系统,C++、编辑器、Blueprint之间协同工作。
  • 内置object迭代器(TObjectIterator)和actor迭代器(TActorIterator),用来遍历对象很方便。
  • 派生自UObject的类自动支持垃圾回收;非UObject类可通过其它方式避免被回收;UStruct不支持垃圾回收,其他与UObject无二。
  • 垃圾回收机制是简单的标记-清除模式,以根集合为起点,遍历所有对象,不能达到(没标记)的就回收掉。(和Lua的垃圾回收机制很像。)
原创粉丝点击