Significance Manager

使用Significance Manager以为项目量身打造的方式调整性能。

Choose your operating system:

Windows

macOS

Linux

要满足即将发布的游戏的性能目标,通常需要通过降低场景复杂度的方式达到目标分辨率或帧率。大家经常使用的方法包括适用于几何体、动画甚至音频的细节层级系统,但是在某些情况下,这些基于距离的、按Actor的方法无法满足需求。在多人游戏中,具有大量可能会在单个区域中聚集的玩家或AI控制的角色,此问题就更加突出。

Significance Manager 提供了一个支持编写特定于项目的灵活代码的能力的集中框架,这些代码可用于对对象求值并确定它们相对于彼此的优先顺序。通过使用该评估方法,对象可通过关闭粒子发射器等组件或以较低的频率运行复杂AI代码的方式修改其行为。

实际上,Significance Manager自身并不会提升性能,但是大家可以覆盖和自定义它提供的系统以满足项目的特定需求。

设置

由于Significance Manager存在于插件中,需要在 编辑(Edit) > 插件(Plugins) 菜单中启用它,而且必须将它的模块添加到项目的"Build.cs"文件。

SignificancePlugin.png

Significance Manager位于"插件(Plugins)"菜单的"编程(Programming)"部分中。

启用Significance Manager插件之后,可能需要重启引擎。

启用插件之后,需要在项目的"Build.cs"文件中将"SignficanceManager"添加到 PublicDependencyModuleNames 中。以下示例代码行来自"基础C++(Basic C++)"项目模板,我们对它进行了修改,以使用Significance Manager:

PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "SignificanceManager" });

Significance Manager的基本功能

Significance Manager插件包含一个单独的类 USignificanceManager ,它可作为对管理对象的"重要性"进行求值的可扩展框架。然后,这些对象可基于它们的重要性数值以自定义的方式调整它们的行为,以减小它们对性能的影响。可通过游戏代码中的对象自定义这些可带来性能提升的特定行为。例如,播放不明显的音频提示或粒子效果的Actor可选择在其具有低重要性数值时不播放音频提示或粒子效果。在更加高级的用例中,它可能会将相似的Actor聚集到一起然后按Actor类型制定预算。其中的一个用法可能是:为确保玩家控制的Pawn在距离摄像机较近时始终以高细节级别运行,将通过相应地限制以高细节级别运行的AI控制的Pawn的数量,来减小多个玩家聚集在摄像机周围带来的性能影响。

RegisterObject / UnregisterObject

对象可以被注册给Significance Manager,并基于用户指定的名称与其他已注册的对象分组到一起。在注册过程中,用户可以指定用于对对象的重要性进行求值的函数,还可以指定将在求值后运行的可选函数。注册过程中,将使用最近一次调用Significance Manager的Update函数时使用的Transform计算对象的初始重要性(如果可能)。这也使执行更高级别的处理成为可能,例如,基于已知的已注册对象列表(不同的类型可能具有不同的列表)构建内部数据结构,在游戏为不同类型的对象执行基于类别的预算时,这非常有用。

在4.20之前的引擎版本中,通过原始指针来存储对象。因此,必须手动调用 UnregisterObject ,否则,Significance Manager将尝试基于无效内存运行。

GetSignificance / QuerySignificance

这些函数报告对象的缓存重要性数值。如果未向Significance Manager注册对象,该值将为零。 QuerySignificance 函数与 GetSignificance 不同,它也会通过返回 false 来表明对象未注册。

Update

该函数需要用到Transform数组,并使用与对象关联的重要性函数基于每个Transform求得每个管理对象的重要性值。最终结果将为返回的最高值(如果 bSortSignificanceAscending 设置为 true ,则为最低值)。可以覆盖该函数来满足游戏的需求,例如,对系统实现处理前或处理后步骤。在求得对象的重要性之后,将会调用其"重要性求值后函数(Post Significance Function)"(如已指定)。如果对象的"重要性求值后类型(Post Significance Type)"是"并发(Concurrent)",将立即调用该函数。如果其类型为"串行(Sequential)",将会按重要性从高到低的顺序调用它,而所有其他管理对象则使用"串行更新后处理(Sequential post-update)"。如果未提供Transform,重要性数值将为零。

重要性求值和重要性求值后函数并行运行,这就要求这些函数是线程安全的函数。重要性求值后函数可通过顺序运行避开该要求(请参阅下面的 FPostSignificanceFunction 部分获取细节)。

Update 函数不自动运行。在大部分情况下,开发者每帧都需要调用它,而且每帧仅调用一次。调用它的合理位置可能在 UGameViewportClient 的被覆盖版本中,如以下代码所示:

#include "MyGameViewportClient.h"
#include "SignificanceManager.h"
#include "Kismet/GameplayStatics.h"
void UMyGameViewportClient::Tick(float DeltaTime)
{
    // 调用超类的Tick函数。
    Super::Tick(DeltaTime);
    // 确保具有有效的世界场景和Significance Manager实例。
    if (UWorld* World = GetWorld())
    {
        if (USignificanceManager* SignificanceManager = FSignificanceManagerModule::Get(World))
        {
            // 仅使用玩家0的全局变换,每帧更新一次。
            if (APawn *PlayerPawn = UGameplayStatics::GetPlayerPawn(World, 0))
            {
                // Significance Manager使用ArrayView。构造单元素数组来容纳Transform。
                TArray<FTransform> TransformArray;
                TransformArray.Add(PlayerPawn->GetTransform());
                // 使用通过ArrayView传入的单元素数组来更新Significance Manager。
                SignificanceManager->Update(TArrayView<FTransform>(TransformArray));
            }
        }
    }
}

项目侧功能

Significance Manager仅提供用于确定对象的重要性的框架,具体的计算需要由开发者在项目中定义。向Significance Manager注册对象时,也将注册与以下类型匹配的函数:

  • FSignificanceFunction

  • FPostSignificanceFunction

在Significance Manager进行更新的过程中,将针对对象调用这些函数。

FSignificanceFunction

这是使用Significance Manager时必须要编写的主要求值函数。它需要使用对象参数和单个Transform来计算对象的重要性,返回的重要性数值为 float 类型。在Significance Manager的更新过程中,将针对每个传入的Transform调用一次该函数。最终结果将由Significance Manager的Update函数确定,默认情况下,它将为最高值。注册对象时,需要将每个对象与类型为 FSignificanceFunction 的函数相关联。

FPostSignificanceFunction

该类型的函数需要用到对象本身、其原重要性数值、其新重要性数值(除非对象已注销,如果已注销,该值为一)以及指示对象当前是否已被注销的 bool 。与重要性求值函数不同,该函数没有返回值。它为游戏提供了一种方法来处理对对象的重要性或在管理对象的总体顺序中的位置的更改。Significance Manager将基于对象的注册方式来调用该函数:

重要性求值后类型

行为

无(None)

函数将为null。无重要性求值后回调。

并发(Concurrent)

函数将不为null,将在进行对象重要性求值时立即调用。用这种方式调用的函数必须是线程安全的函数,因为它们将并行运行。

串行(Sequential)

函数将不为null,将会在对所有对象进行重要性求值之后按它们相对于其他顺序对象的排序调用。

在这种情况下,不要求代码是线程安全的代码。

粒子系统中的重要性

粒子系统组件和粒子发射器 支持重要性概念,可作为良好的实现示例。每个发射器都具有其自己的"重要性级别(Signficance Level )"(使用列举的类型 EParticleSignificanceLevel ),范围为"低(Low)"到"关键(Critical)",如下所示。

ParticleEmitterSignificance.png
具有不同重要性级别的粒子发射器。

粒子系统组件具有"必需的重要性级别(Required Significance Level)"(通过调用 SetRequiredSignificance 设置),它指示其每个发射器要处于活动状态就必须具有的重要性级别。例如,"重要性级别(Significance Level)"为"中等(Medium)"的粒子发射器处于活动状态的条件是拥有它的粒子系统组件的"必需的重要性级别(Required Significance Level)"为"中等(Medium)"或"低(Low)",如果其"必需的重要性级别(Required Significance Level)"达到"高(High)"或"关键(Critical)",将停止生成粒子。如果粒子系统组件检测到其所有的发射器都未达到"必需的重要性级别(Required Significance Level)",它甚至会停用其自己的"tick"函数,并在发射器达到必需的重要性级别时重新激活"tick"函数。这种设计使得游戏系统(在本示例中,为粒子系统组件和粒子发射器)能够控制实现,仅让Significance Manager负责确定"重要性级别(Significance Level)"和在必要时进行函数调用以通知粒子系统组件的任务。

如果决定不让粒子发射器达到必需的重要性级别,它们将以与粒子系统组件所采用的方式相同的方式暂时停用其自己的"tick"函数(仅在 bDisableWhenInsignficant 设置为 true 时)。否则,它们仅停止生成新粒子,然是仍将更新。

欢迎帮助改进虚幻引擎文档!请告诉我们该如何更好地为您服务。
填写问卷调查
取消