UE5 PuerTS学习与实践(三)运行时编写PCGGraph法则
在学习Puerts和虚幻引擎的PCG框架过程中,发现PCG框架的大部门代码标识表记标帜为蓝图可用,而且一些函数也未做WITH_EDITOR的宏限制。既然这些蓝图可用的函数并未限制在编纂器下使用,我们便可以在Puerts中使用这些导出的函数,在运行时创建PCGGraph,而且添加相应的PCG蓝图节点,对这些节点进行连线,从而生成一系列有效的PCG法则来安插场景。有趣的是,这些操作都是在打包后的法式里面完成的。
本文简单记录在学习Puerts及PCG框架过程中,将两者融合在一起,实现动态的内容生成和场景安插。
1、涉及到的PCG框架内容简述
PCGGraph内容
在编纂器中使用PCGGraph编写PCG生成法则时,会在里面拖放PCGNode,例如GetSplineData、SplineSampler等,这些节点内部持有一个TObjectPtr<UPCGSettingsInterface> SettingsInterface,此变量决定了PCGNode节点的名称、输入/输出引脚名称以及点击节点后的细节面板信息。
在PCGGraph上任意双击一个节点跳转到C++代码中,便可看到该类型对应的是UPCGSettings的哪个子类。在后面使用Puerts脚本去生成PCGNode设置具体的UPCGSettings时,以及模拟在细节面板调整一个节点的属性值时,都需要UPCGSettings派生类中的具体信息。
这里打算在一个封锁的Spline圈出的区域内部采样一些点位,将这些点位投影到Landscape上,并做一些随机的Rotation、Scale变换,然后在每个点生成一些简单的静态网格体。
2、选择点数据输入来源
这里我们选择在一个Actor类添加一个Spline作为采点的输入源,同时里面放置一个PCGComponent组件,不外我们不必为其指定Graph的值,留着等运行时设置为临时创建的PCGGraph。
BP类设置
3、运行时编写PCGGraph法则
使用ts脚本来编写PCGGraph,同样也遵照UE编纂器下的使用模式:
[*]创建PCGGraph资产
[*]创建所需的PCGNode
[*]设置PCGNode的细节面板值
[*]连接各个PCGNode
[*]赋值到需要使用PCGGraph的处所
后面针对个点详细展开。
3.1、创建PCGGraph
使用Puerts创建一个PCGGraph斗劲简单,直接new一个相应的对象即可。
// example:运行时生成PCGGraph
let RuntimeNewPCGGraph = new UE.PCGGraph();创建一个PCGGraph后,其内部会自动创建两个节点:InputNode和OutputNode,不外这两个节点不是必需要使用的。
PCGGraph上的连线关系与蓝图类等Blueprint分歧,其内节点间维护的是数据的流动关系,数据并不必然要从InputNode开始流动,也不是必需流动到OutputNode才能发生效果。任意可以发生点数据的节点都可以作为连线的开始节点。
此次Demo便以GetSplineData节点作为数据发生的开始节点,以StaticMeshSpawner节点作为结束节点。
3.2、创建所需的PCGNode
向PCGGraph中添加一个所需的节点,只需遵循以下“公式”即可:
[*]创建一个所需类型节点的UPCGxxxSettings子类对象
[*]设置第一步创建出来的对象的一些所需的参数,该法式非必需
[*]调用PCGGraph的AddNodeInstance函数,将第一步创建出来的对象作为参数传入
颠末以上三步,便可以创建一个所需的PCGNode。
我们在代码中模拟创建以下节点:
代码需要模拟创建的PCGGraph示意图
创建一个GetSplineData节点
这个节点的创建及添加到PCGGraph斗劲简单,因为没有对PCGGetSplineSettings中的参数做改削。创建GetLandscapeData、Projection节点的方式与创建GetSplineData类似,不再赘述。
let GetSplineDataSettings = new UE.PCGGetSplineSettings();
let GetSplineDataNode = RuntimeNewPCGGraph.AddNodeInstance(GetSplineDataSettings);创建SplineSampler节点
需要对此中的个别参数进行设置。以创建SplineSampler节点为例,在代码中设置了对样条的采样方式为:对样条内的区域采样,每个采样点的空间距离=2000cm。
let SplineSamplerSettings = new UE.PCGSplineSamplerSettings();
SplineSamplerSettings.SamplerParams.Dimension = UE.EPCGSplineSamplingDimension.OnInterior;
SplineSamplerSettings.SamplerParams.InteriorSampleSpacing = 2000.0;
let SplineSamplerNode = RuntimeNewPCGGraph.AddNodeInstance(SplineSamplerSettings);此中InteriorSampleSpacing参数的感化如下图所示:
TransformPoints节点的创建方式及参数设置与之类似,我在这里对点位做了绕z轴,取值范围在(-180,180)之间的随机旋转,并对Scale做了取值范围在(0.2,1.5)之间的随机缩放。
创建StaticMeshSpawner节点
在PCGGraph中配置StaticMeshSpawner节点,如下图所示:
在PCGGraph中配置StaticMeshSpawner节点
使用代码创建一个StaticMeshSpawner节点却斗劲麻烦,因为它的参数配置并不直接显式的放在UPCGStaticMeshSpawnerSettings中。
在查看PCGStaticMeshSpawner.h文件时,发现了一个FPCGStaticMeshSpawnerEntry的布局体与上图的配置形式斗劲类似,但是它的反射标识表记标帜信息中写到DeprecationMessage=”Use MeshSelectorWeighted instead.”,说明该布局体已经弃用了,但是在头文件中并没有发现它所说的MeshSelectorWeighted的变量。
对比该节点的细节面板和头文件,发现UPCGStaticMeshSpawnerSettings中的MeshSelectorType变量是一个TSubclassOf<UPCGMeshSelectorBase>类型变量,当在细节面板上下拉选择该变量的值时,有PCGMeshSelectorWeighted字样。
同时,该头文件中还有一个TObjectPtr<UPCGMeshSelectorBase> 类型的变量MeshSelectorInstance。这不就说明一个是选择子类类型,一个是持有子类对象的指针。
因此,顺着UPCGMeshSelectorBase往下查找派生类,可以看到一个UPCGMeshSelectorWeighted类,而此中有一个TArray<FPCGMeshSelectorWeightedEntry>类型的变量MeshEntries,与细节面板上显示的对照了起来。
我们可以在代码中创建一个UPCGMeshSelectorWeighted对象,依次创建所需的对象,最后设置最内层的Static Mesh软引用即可。
设置MeshSelectorInstance
// 创建UPCGStaticMeshSpawnerSettings对象
let StaticMeshSpawnerSettings = new UE.PCGStaticMeshSpawnerSettings();
// 设置StaticMeshSpawnerSettings中的MeshSelectorInstance
let PCGMeshSelectorWeighted = new UE.PCGMeshSelectorWeighted();
...
StaticMeshSpawnerSettings.MeshSelectorInstance = PCGMeshSelectorWeighted;
// 添加一个StaticMeshSpawnerNode
let StaticMeshSpawnerNode = RuntimeNewPCGGraph.AddNodeInstance(StaticMeshSpawnerSettings);3.3、连接PCGNode的引脚
创建完所有的节点后,我们便可以使用UPCGNode类中的AddEdgeTo函数连接相应的引脚,该函数将UPCGNode输出引脚连接到一个指定的UPCGNode对象的输入引脚上,通过PinLabel来确定输入、输出引脚。
GetSplineDataNode.AddEdgeTo(”Out”,SplineSamplerNode,”Spline”);
SplineSamplerNode.AddEdgeTo(”Out”,TransformPointsNode,”In”);
TransformPointsNode.AddEdgeTo(”Out”,ProjectionNode,”In”);
ProjectionNode.AddEdgeTo(”Out”,StaticMeshSpawnerNode,”In”);
StaticMeshSpawnerNode.AddEdgeTo(”Out”,OutputNode,”Out”);
PCGLandscapeDataNode.AddEdgeTo(”Out”,ProjectionNode,”Projection Target”);这里要注意连接节点的名称,比如”Projection Target”,千万不要少打一个空格,这个名称不确定的话,到UPCGSettings相关代码中寻找PinLable的真实值。
3.4、使用PCGGraph
这里我们只需在运行时创建一个第2节设计的蓝图类的对象,获取到此中的PCGComponent,令该PCGComponent使用我们运行时生成的PCGGraph,然后调用一下PCGComponent->Generate(true)函数,即可执行生成内容。
let PCGComp = Actor_SplinePCG.GetComponentByClass(UE.PCGComponent.StaticClass()) as UE.PCGComponent;
if(PCGComp)
{
PCGComp.SetGraph(RuntimeNewPCGGraph);
PCGComp.Seed = UE.KismetMathLibrary.RandomInteger(1000);
PCGComp.Generate(true);
}代码里我做了反复活成第2节的蓝图类对象,然后给此中的PCGComponent的变量Seed一个随机值,为了使每次生成的内容都纷歧样。
4、成果
4.1、运行时创建的PCGGraph内容
我们首先借助UE编纂器的环境,查看一下我们运行时生成的PCGGraph长什么样。
运行时创建的PCGGraph内容
https://www.zhihu.com/video/1664061732761862144
可以看到,我们创建了一个名字叫做PCGGraph_61的资源,名称并不是我起的,而是UE代码中自动设置的。
点开该PCGGraph可以看到,这些PCGNode都挤在一起,这是因为我们在生成PCGNode的时候并没有指定一个它们在蓝图上的坐标,不外这里我们不需要去可视化的看它们,所以也不必设置坐标。这里我前面在连接节点的时候把OutputNdoe也连接上了,懒得断开重录视频了。。
4.2、应用PCGGraph生成内容
打包一个Shipping版本的UE法式,看一下功能。
打包后运行时创建PCGGraph
https://www.zhihu.com/video/1664065635909877760
可以在运行时创建PCGGraph来法式化生成内容,PCG框架的可玩性还是斗劲高的。在运行时写一些UI来配置这些节点的生成,连接,即可模拟一个运行时的PCGGraph蓝图系统。
感觉本文有趣的话请点个附和、存眷吧! 厉害[赞],之前还在群里看别人问,PCG能不能在运行时候编辑,大佬都做出来了 还是ue给的太慷慨! 太厉害了,之前一直没找到解决的办法,大佬真的牛👍👍👍 起来学习[抱抱]
页:
[1]