UE5 ECS编程与Mass插件目录总览

前言

本文将详细解释在Unreal5中MassGameplay插件是如何处理ECS与原有的Actor-Component系统结合的,以及一些典型需求的示例,由于网上资料基本没有官方文档也是潦草带过,如有疏漏或者谬误还请评论区指教。

注:目前版本5.5.3

重中之重MassAgentComponent

首先就是MassAgentComponent,打开MassAgentComponent.h,我们最先看到的是这样一个枚举

看似很多,但其实核心就是两种状态,分别是木偶(Puppet)和Agent(代理),此处木偶的含义就是该Actor是MassEntity所创建,而代理则是Actor创建MassEntity,其要解决的核心问题就是生命周期和所有权的问题,因为逻辑上Actor和Entity是存在于两个世界里,两个世界需要交流与同步,有时Actor放在关卡里并给予了这个组件,则这时候就是代理状态即运行时先生成Actor,然后Actor再去创建Mass里创建对应的实体。也有时我们通过EntityManager等创建了一批Entity,这些Entity需要再渲染世界世界中有所表现,这时候就是Entity去World里创建Actor,这时候状态就是Puppet也就是Actor是Entity的木偶。之所以上面看着这么多只仅仅是因为部分操作是延迟执行的以及解决网络复制等问题,在使用上我们只需要区分这两种就可以了。

正常状态比较简单,MassAgentComponent在注册的时候会向UMassAgentSubsystem提交创建申请,流程还是比较简单的在此不表,至于UMassAgentSubsystem是什么以及还有很多长得跟他很像的Subsystem都是干什么的下文会提。

UMassVisualizationTrait:复杂的开始

各种样例里可能会提到UMassVisualizationTrait,这也是官方提供的工程中实际使用的让Entity能够被渲染出来的方案,但是,这个玩意真的超级复杂,一点开有一大堆东西等着我,让我们慢慢道来。

首先是UMassVisualizationTrait,这个已经被弃用了,官方推荐使用它的两个子类也就是Movable和Stationary,看名字也知道一个能动一个不能动。

其次是UMassDistanceVisualizationTrait,这个也被弃用了,意思大概跟上面的那个差不多,带Distance的和不带的区别就是他们两个的LOD参数结构不同

这个是FMassDistanceLODParameters 这个是FMassDistanceLODParameters

这个是FMassVisualizationLODParameters,就是不带Distance的VisualizationTrait用的 这个是FMassVisualizationLODParameters,就是不带Distance的VisualizationTrait用的

他们两个的唯一区别就是一个只关心距离,一个还要关心平截头体,也就是相机的参数,并且DistanceLOD不关心数量上限,只要距离关系符合那就是对应的LOD

在这里我还要提一嘴在UMassLODSubsystem里有这么一个参数bUsePlayerPawnLocationInsteadOfCamera,注释是这样写的

简单解释一下就是说这个值为真的话,计算lod的时候就只会考虑玩家控制的Pawn的位置,而不是玩家相机的位置,这在俯视角游戏中与DistanceTrait的相性最好,而第三人称视角的还是用另一个吧

而具体LOD如何计算怎么控制就是另一篇文章了,还是先回到UMassVisualizationTrait上来。

UMassVisualizationTrait的配置内容 UMassVisualizationTrait的配置内容

看起来这些参数都很好理解,这里挑几个迷惑性比较强的解释一下。

首先就是表示子系统,在后面我也会多次提到UMassRepresentationSubsystem,此处的表示的意思就是控制Entity以何种方式表示的意思,表示的方式有很多种,除了Actor也可以使用InstanceStaticMesh,如果需要额外的定制也可以继承UMassRepresentationSubsystem,官方就自定义了一个人群表示的子系统。

但是他又很复杂的一点就是,他只负责干这个,连生成Actor也不是他干的,而是丢给了另一个UMassActorSpawnerSubsystem去做,包括移动,他也是不管的。

而除了UMassRepresentationSubsystem,还有一个很奇怪的东西叫表示Actor管理类,也就是UMassRepresentationActorManagement,其中比较常用的就是

Actor生成前和生成后的委托 Actor生成前和生成后的委托

这俩可以控制在当前实体的表示为Actor时,生成Actor对其进行一些修改,通过继承ActorManagement和Trait并覆写OnPostActorSpawn和BuildTemplate让Actor生成出来的时候把一些数据配进去,这样可以把需要配数据的地方集中在一起,还是蛮不错的。

为什么我的Actor不动

接下来我就要讲一些更加深层次的东西,涉及到了一些真正的Mass与Actor交互的问题并给出解决方案

首先刚才我们讲了,在Mass中,表现有两种方式分别是Actor和Instanced,然后根据设想,我们在EntityConfig中加上了UMassMovableVisualizationTrait以及Movement什么的,OK,应该可以了,然后游戏一运行,诶,为什么不动……

然后去扒官方的CitySample,也就是黑客帝国那个,发现在UMassMovableVisualizationTrait中配置的Actor里,也有MassAgentComponent,里面配了有几个叫什么Sync的东西,然后配了发现如下逻辑:

有一个叫UMassAgentSyncTrait的东西,他是负责处理Mass和World之间同步的基类,所有负责同步的都是来自他的派生比如UMassAgentFeetLocationSyncTrait,而在他们的BuildTemplate中,会有这样一句BuildContext.GetMutableObjectFragmentInitializers()xxxxxxxxx。在这里面也配置了对应的Actor生成出来的时候要对Actor做什么的函数,但是该配置只在该SyncTrait添加进MassAgentComponent中时才会生效,也就是说,如果你在EntityConfig中配了xxxxSyncTrait+UMassMovableVisualizationTrait里配Actor,那个MutableObjectFragmentInitializers就直接不运行,其原因就在刚刚的UMassAgentSubsystem中,UMassAgentSubsystem每帧都会运行一个叫HandlePendingInitialization的函数,它的作用就是处理上一帧所有的MassAgentComponent的请求,该函数分为两个部分也就是下面这样

这也对应了我最开始讲的代理和木偶两种状态,其中代理状态很正常,就是根据配置的Entity模板生成一个Entity,但木偶状态就不一样了,因为木偶状态是现有的Entity再有的Actor,本来Entity生成时会有一个模板,在生成的Actor中MassAgentComponent中还有一个模板,但两个模板只会调用MassAgentComponent的ObjectFragmentInitializers,就导致了一些问题的发生。

而除了Actor,还有一种表现方式是InstancedStaticMesh,而控制他的方法在UMassUpdateISMProcessor与UMassStationaryISMSwitcherProcessor,分别是会动的与不会动的,但是这俩只能作为样例而存在(UMassStationaryISMSwitcherProcessor甚至还有一些逻辑上的小bug),因为工程中我们极大可能会用到ISM的CustomData来手动控制一些表现,官方的CitySample样例中人物的顶点动画也用到了这个,所以官方也额外写了一个Processor并禁用了这两个简单的,所以有需要就看看这俩和官方的样例。

InstancedActor又是什么

如果经常翻代码或者看roadmap会发现官方还有一个插件叫InstancedActor,这个也跟Mass有点关系,它的核心目标是将关卡中的Actor近乎无痛的转成MassEntity,就比如场景中的树可能需要能砍,这样一个简单的StaticMesh就不能满足需求了,但是这种Actor又很多,全都在场景里也很卡,而InstancedActor就是为了解决这个而存在的。

但是目前这个功能用起来很麻烦,要配置的很多,功能也不是很完整,但是很有潜力,啥时候更新的差不多了我再在这里更


原文发表于知乎