找回密码
 立即注册
查看: 326|回复: 0

Unreal一些工具、模板类使用汇总(1)

[复制链接]
发表于 2023-3-27 09:41 | 显示全部楼层 |阅读模式
读源码时总会遇到一些Unreal特有的工具或模板类,但是很多时候不太确定它的正确使用场景或者方式。这里我会汇总一些我读Lyra工程中官方用到非常多的类的使用以及它在Lyra中的使用场景,部分例子我会借助GPT举一些使用场景。(大坑。不是
模板类
TFunctionRef
一般用来做函数指针的参数传递
#include "CoreMinimal.h"

// 定义一个接受TFunctionRef参数的函数
// <返回值>(参数1,参数2)
void ExecuteFunction(const FString& Name, TFunctionRef<void()> FuncToExecute)
{
    UE_LOG(LogTemp, Log, TEXT("%s is being executed"), *Name);
    FuncToExecute(); // 调用传递的函数
}

// 示例函数
void ExampleFunction()
{
    UE_LOG(LogTemp, Log, TEXT("ExampleFunction is called"));
}

void YourClass::YourFunction()
{
    // 使用Lambda表达式调用ExecuteFunction
    ExecuteFunction(TEXT("Lambda"), []() { UE_LOG(LogTemp, Log, TEXT("Lambda is called")); });

    // 使用已定义函数调用ExecuteFunction
    ExecuteFunction(TEXT("ExampleFunction"), ExampleFunction);
}

在上述示例中,我们定义了一个名为ExecuteFunction的函数,它接受一个FString参数Name和一个类型为TFunctionRef<void()>的参数FuncToExecute。我们可以看到ExecuteFunction函数接收一个可调用对象(如Lambda表达式或已定义的函数)作为参数,并在函数内部执行它。这是一个简单的TFunctionRef使用示例,你可以根据需要在你的项目中使用它。
TSharedRef
在Unreal Engine中,TSharedRef是一个模板类,用于表示对对象的强引用。它是一种智能指针,用于确保引用的对象始终保持有效。当使用TSharedRef时,对象的引用计数会增加,当引用计数为0时,对象将被自动删除。这有助于避免内存泄漏和悬空指针问题。以下是一些关于如何正确使用TSharedRef的建议:

  • 使用TSharedRef时,请确保引用的对象始终有效。当你创建一个TSharedRef时,它必须引用一个已经存在的对象。你不能创建一个空的TSharedRef。
  • 使用MakeShareable函数创建TSharedRef。这是一个辅助函数,用于从原始指针创建TSharedRef。例如:
TSharedRef<MyObjectType> MySharedRef = MakeShareable(new MyObjectType());
当需要传递TSharedRef给一个函数或存储在一个容器中时,使用const TSharedRef<>&类型的引用。这样可以避免不必要的引用计数增加。例如:
void MyFunction(const TSharedRef<MyObjectType>& InSharedRef)
{
    // 使用InSharedRef
}
使用TSharedPtr与TSharedRef配合。TSharedPtr是一种可以为空的弱引用。当你需要有一个可以为空的引用时,使用TSharedPtr。在需要确保对象始终有效时,使用TSharedRef。例如:
TSharedPtr<MyObjectType> MySharedPtr = MySharedRef; // 将TSharedRef转换为TSharedPtr
不要将TSharedRef用于循环引用。当两个对象相互引用时,这可能导致内存泄漏。在这种情况下,使用TWeakPtr来表示弱引用,以避免循环引用问题。
TScriptInterface
TScriptInterface是Unreal Engine中的一个模板类,用于在C++代码中表示脚本接口。脚本接口允许你定义一组函数签名,这些函数签名可以在不同的类中实现。这种方法提供了一种松散的耦合,使你能够在不修改源代码的情况下轻松地替换实现。接口在Unreal Engine中非常常用,特别是在使用蓝图编程时。
在C++中使用TScriptInterface的基本步骤如下:
定义接口:首先,你需要创建一个接口类。接口类应该继承自UInterface,并使用UINTERFACE()宏进行标记。接口中的函数应该在纯虚拟函数的形式定义在接口类中。例如:
#include "CoreMinimal.h"
#include "MyInterface.generated.h"

UINTERFACE(BlueprintType)
class UMyInterface : public UInterface
{
    GENERATED_BODY()
};

class IMyInterface
{
    GENERATED_BODY()

public:
    UFUNCTION(BlueprintCallable, Category = "MyInterface")
    virtual void MyFunction() = 0;
};

实现接口:要在类中实现接口,需要将接口类添加到类的继承列表中,并实现接口中的所有函数。例如:
#include "CoreMinimal.h"
#include "MyInterface.h"
#include "MyActor.generated.h"

UCLASS(BlueprintType)
class AMyActor : public AActor, public IMyInterface
{
    GENERATED_BODY()

public:
    virtual void MyFunction() override;
};

使用TScriptInterface:要在另一个类中引用实现了接口的对象,可以使用TScriptInterface。例如:
UCLASS()
class AMyOtherActor : public AActor
{
    GENERATED_BODY()

public:
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MyInterface")
    TScriptInterface<IMyInterface> MyInterfaceObject;
};

调用接口方法:要调用TScriptInterface中的接口方法,需要先检查引用是否有效,然后调用相应的方法。例如:
if (MyInterfaceObject.IsValid())
{
    MyInterfaceObject->MyFunction();
}

这些步骤概述了如何在Unreal Engine C++代码中使用TScriptInterface。使用接口可以提高代码的可扩展性和可维护性,特别是在需要支持不同实现的场景中。
该类在Lyra中使用频繁,其中一个典型场景就是,函数接受参数的时候,如下是一段包裹相关的代码,当AddPickup时,使用TScriptInterface可以更轻易的将所有类型的类包含在内而不需要进行复杂的类型审查。
static ULyraInventoryItemInstance* AddPickupInventory(ULyraInventoryManagerComponent* InventoryComponent, TScriptInterface<IPickupable> Pickupable);
FThreadHeartBeat
FThreadHeartBeat是Unreal Engine中的一个类,用于监控线程的运行状态。它会定期检查线程是否仍在正常运行,以便及时检测到潜在的线程阻塞或死锁问题。当引擎检测到一个线程停止响应时,FThreadHeartBeat可以触发一个错误或警告,以帮助开发者诊断问题。
在Unreal Engine中,FThreadHeartBeat通常用于监控主线程(Game Thread)和渲染线程(Rendering Thread)。引擎会自动创建一个FThreadHeartBeat实例并对这些关键线程进行监控。通常情况下,你不需要手动创建或使用FThreadHeartBeat实例。
然而,如果你确实需要在自定义线程上使用FThreadHeartBeat,可以参考以下步骤:
创建一个FThreadHeartBeat实例。例如:
FThreadHeartBeat& ThreadHeartbeat = FThreadHeartBeat::Get();
在线程的主循环中,定期调用HeartBeat()方法以报告线程仍在正常运行。例如:
void MyThreadFunction()
{
    FThreadHeartBeat& ThreadHeartbeat = FThreadHeartBeat::Get();

    while (!bStopThread)
    {
        // 执行线程任务

        ThreadHeartbeat.HeartBeat(); // 报告线程心跳
    }
}

当线程即将结束时,调用KillHeartBeat()方法以停止心跳监控。例如:
void StopMyThread()
{
    bStopThread = true;
    FThreadHeartBeat::Get().KillHeartBeat();
}

这些步骤描述了如何在自定义线程上使用FThreadHeartBeat。但请注意,在大多数情况下,你不需要手动创建或管理FThreadHeartBeat实例,因为Unreal Engine已经内置了对主线程和渲染线程的监控功能。
在Lyra中,LoadingScreen的Update使用了FThreadHeartBeat的MonitorCheckpointStart()、~End()来监控跟踪线程的进展。该函数接受一个Name和一个HangDuration,当引擎在监控该进程时如果发现了线程挂起超过HangDuration时则判定其为死锁或卡死。之后我也会写一篇文章详细分析下,Lyra的LoadingScreen加载机制。
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-11-16 22:01 , Processed in 0.100017 second(s), 27 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表