关于 ue-procedural-generation
面向 Unreal Engine 5.8 开发者的程序化生成技能,汇集 PCG、运行时网格、实例化和样条线等 API 用法,并强调当前引擎版本的接口。
主要功能
- 01PCG 图与运行时生成
- 02PCG 点数据读写
- 03自定义 C++ PCG 节点
- 04运行时程序化网格
- 05Dynamic Mesh 与 Geometry Script
- 06实例化、样条线与随机噪声
适用场景
- 01配置 PCG 插件和模块
- 02创建或扩展 PCG 图节点
- 03运行时构建或更新网格
- 04用样条线放置对象或生成地形
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ quodsoler/ unreal-engine-skills --skill ue-procedural-generation安装后在 Claude Code 中输入/ue-procedural-generation确认已加载。
附带文件references/pcg-node-reference.mdreferences/procedural-mesh-patterns.md
详细介绍
UE 程序化生成
目标引擎:UE 5.8。以下 API 已根据 5.8 头文件验证;旧版形式列于“已弃用 — 请勿使用”部分。
涵盖 PCG 框架(插件 PCG,位于 Engine/Plugins/PCG,默认启用,Build.cs 模块为 PCG)、使用 UProceduralMeshComponent 进行运行时网格构建(插件 ProceduralMeshComponent,模块 ProceduralMeshComponent),以及使用 UDynamicMeshComponent + Geometry Script 进行运行时网格构建(模块 GeometryFramework 和 GeometryScriptingCore)、使用 UInstancedStaticMeshComponent/UHierarchicalInstancedStaticMeshComponent 进行实例化、由样条线驱动的放置,以及来自 Core 的确定性噪声/随机数。
上下文
如果 .agents/ue-project-context.md 存在,请阅读它(模块名称、约定、已启用的插件、GAS/网络设置)。如果该文件不存在,也请继续。
根据请求和代码库确定所属领域。只有在两种可能的理解会导致不同代码时才提问。
| 请求涉及…… | 转到 |
|---|---|
| 启用 PCG、模块/插件接线 | PCG 设置 |
| 从 Actor 驱动生成、运行时生成、分区 | PCG 组件和运行时生成 |
| 在图中读取/写入点 | 点数据 |
| 用 C++ 编写新的 PCG 节点 | 用 C++ 编写自定义 PCG 节点 |
| 哪个节点能执行某操作、引脚标签、节点设置字段 | PCG 节点参考 |
| 在运行时构建三角形 | ProceduralMeshComponent |
| 布尔运算、基本体、烘焙为静态网格体 | 动态网格和 Geometry Script |
| 数千个重复网格体 | 实例化静态网格体 |
| 道路、河流、围栏、电缆 | 样条线 |
| 高度场、散布、可复现结果 | 噪声和确定性随机数 |
| 行进立方体、BSP 地下城、WFC、泊松圆盘 | 程序化网格模式 |
PCG 设置
// MyGame.Build.cs
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "PCG" });
{ "Name": "PCG", "Enabled": true }
| 类 | 头文件 | 作用 |
|---|---|---|
UPCGComponent | PCGComponent.h | 持有图并驱动生成的 Actor 组件 |
UPCGGraph | PCGGraph.h | 图资产:节点、边、UserParameters |
UPCGGraphInstance | PCGGraph.h | 带有每个实例参数覆盖项的图实例 |
UPCGGraphInterface | PCGGraph.h | 图和图实例的公共基类 |
UPCGSettings | PCGSettings.h | 节点设置基类 |
IPCGElement | PCGElement.h | 节点中负责执行的部分 |
FPCGContext | PCGContext.h | 每次执行的状态:InputData、OutputData、Node、ExecutionSource |
UPCGBasePointData | Data/PCGBasePointData.h | 抽象点集合 |
UPCGPointArrayData | Data/PCGPointArrayData.h | 结构数组式点数据 |
UPCGSubsystem | Subsystems/PCGSubsystem.h | 调度、分区、运行时生成 |
APCGVolume | PCGVolume.h | 携带 UPCGComponent 的体积 Actor |
PCG 组件和运行时生成
#include "PCGComponent.h"
#include "PCGGraph.h"
void AMyGenerator::StartGeneration(UPCGGraphInterface* Graph, int32 InSeed)
{
UPCGComponent* PCG = FindComponentByClass<UPCGComponent>();
if (!PCG)
{
return;
}
PCG->Seed = InSeed;
PCG->bActivated = true;
PCG->SetGraph(Graph); // NetMulticast, Reliable
PCG->Generate(/*bForce=*/true); // NetMulticast, Reliable
}
| 调用 | 复制 | 用途 |
|---|---|---|
Generate(bool bForce) / Cleanup(bool bRemoveComponents) | NetMulticast, Reliable | 由服务器驱动的生成,必须在客户端上显示 |
GenerateLocal(bool bForce) / CleanupLocal(bool bRemoveComponents) | 无 | 客户端或单人游戏生成 |
NotifyPropertiesChangedFromBlueprint() | 无 | 标记为脏,并在编辑公开属性后有条件地重新生成 |
CancelGeneration() | 无 | 中止正在进行的生成 |
GetGeneratedGraphOutput() | 无 | 读取图表生成的 FPCGDataCollection |
EPCGComponentGenerationTrigger(PCGComponent.h:77):GenerateOnLoad、GenerateOnDemand、GenerateAtRuntime。
分区和运行时生成:
SetIsPartitioned(bool)/IsPartitioned()对应bIsComponentPartitioned属性;分区组件会将工作分派给网格上的本地组件。GenerateAtRuntime会将组件交给运行时生成调度器。可通过SchedulingPolicyClass/SchedulingPolicy(UPCGSchedulingPolicyBase)以及bOverrideGenerationRadii+GenerationRadii(FPCGRuntimeGenerationRadii)进行调整。- 调度器 CVar:
pcg.RuntimeGeneration.Enable、pcg.RuntimeGeneration.NumGeneratingComponents、pcg.RuntimeGeneration.GlobalRadiusMultiplier、pcg.RuntimeGeneration.EnablePooling、pcg.RuntimeGeneration.BasePoolSize、pcg.RuntimeGeneration.FramesBeforeFirstGenerate、pcg.RuntimeGeneration.EnableChangeDetection、pcg.RuntimeGeneration.EnableDebugging。 UPCGSubsystem::GetSubsystemForCurrentWorld()返回子系统;RefreshAllComponentsFiltered(Filter, ChangeType)强制刷新子集(仅限WITH_EDITOR,Subsystems/PCGSubsystem.h:227-230)。
层级生成位于 UPCGGraph 上:bUseHierarchicalGeneration、HiGenGridSize(EPCGHiGenGrid::Grid4 … Grid2048,以及 Unbounded)、HiGenGridSizeMultiplier、bUse2DGrid。
图表参数是 UPCGGraph 上的 FInstancedPropertyBag UserParameters,通过 UPCGGraphInterface 读取和写入:
TValueOrError<double, EPropertyBagResult> Result = Graph->GetGraphParameter<double>(TEXT("SpawnRadius"));
if (Result.HasValue())
{
const double Radius = Result.GetValue();
Graph->SetGraphParameter<double>(TEXT("SpawnRadius"), Radius * 2.0);
}
点数据
点集合属于 UPCGBasePointData。UPCGPointArrayData 是结构数组实现;UPCGPointData 是为兼容性保留的 FPCGPoint 数组实现。通过上下文分配,以使用项目配置的类:
UPCGBasePointData* Points = FPCGContext::NewPointData_AnyThread(Context);
新代码中不要遍历 GetPoints() / GetMutablePoints()——这些方法仅存在于 UPCGPointData 上,且会强制进行转换。请改用值范围进行读写:
#include "Data/PCGBasePointData.h"
const FConstPCGPointValueRanges ReadRanges(InputPoints);
FPCGPointValueRanges WriteRanges(OutputPoints, /*bAllocate=*/false);
WriteRanges.TransformRange[Index] = ReadRanges.TransformRange[Index];
WriteRanges.DensityRange[Index] = ReadRanges.DensityRange[Index];
每点原生属性(PCGPointPropertiesTraits.h 中的 EPCGPointNativeProperties):Transform、Density、BoundsMin、BoundsMax、Color、Steepness、Seed、MetadataEntry,以及 All 和 AllProperties。大小和分配:
Output->SetNumPoints(Input->GetNumPoints(), /*bInitializeValues=*/false);
Output->AllocateProperties(Input->GetAllocatedProperties() | EPCGPointNativeProperties::Density);
Output->CopyUnallocatedPropertiesFrom(Input);
其他数据类型:UPCGSpatialData(基类)、UPCGSplineData、UPCGLandscapeData、UPCGVolumeData、UPCGTextureData、UPCGPrimitiveData、UPCGDynamicMeshData,以及用于属性集的 UPCGParamData。UPCGSpatialData::ToBasePointData(FPCGContext*, const FBox&) 会将任意空间数据离散化为点。
C++ 中的自定义 PCG 节点
节点由 UPCGSettings 子类和一个 IPCGElement 组成。设置类保存数据;元素是 const 且无状态的,并从 FPCGContext 读取所有内容。
// MyPCGJitter.h
#pragma once
#include "PCGElement.h"
#include "PCGSettings.h"
#include "MyPCGJitter.generated.h"
UCLASS(BlueprintType, ClassGroup = (Procedural))
class MYGAME_API UMyPCGJitterSettings : public UPCGSettings
{
GENERATED_BODY()
public:
#if WITH_EDITOR
virtual FName GetDefaultNodeName() const override { return FName(TEXT("MyJitter")); }
virtual FText GetDefaultNodeTitle() const override { return NSLOCTEXT("MyPCGJitter", "NodeTitle", "My Jitter"); }
virtual EPCGSettingsType GetType() const override { return EPCGSettingsType::PointOps; }
#endif
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Settings, meta = (PCG_Overridable))
double JitterRadius = 100.0;
protected:
virtual TArray<FPCGPinProperties> InputPinProperties() const override { return Super::DefaultPointInputPinProperties(); }
virtual TArray<FPCGPinProperties> OutputPinProperties() const override { return Super::DefaultPointOutputPinProperties(); }
virtual FPCGElementPtr CreateElement() const override;
};
class FMyPCGJitterElement : public IPCGElement
{
protected:
virtual bool ExecuteInternal(FPCGContext* Context) const override;
virtual bool IsCacheable(const UPCGSettings* InSettings) const override { return true; }
virtual bool CanExecuteOnlyOnMainThread(FPCGContext* Context) const override { return false; }
virtual bool SupportsBasePointDataInputs(FPCGContext* InContext) const override { return true; }
virtual EPCGElementExecutionLoopMode ExecutionLoopMode(const UPCGSettings* Settings) const override { return EPCGElementExecutionLoopMode::SinglePrimaryPin; }
};
// MyPCGJitter.cpp
#include "MyPCGJitter.h"
#include "PCGContext.h"
#include "Data/PCGBasePointData.h"
#include "Data/PCGSpatialData.h"
#include "Math/RandomStream.h"
FPCGElementPtr UMyPCGJitterSettings::CreateElement() const
{
return MakeShared<FMyPCGJitterElement>();
}
bool FMyPCGJitterElement::ExecuteInternal(FPCGContext* Context) const
{
const UMyPCGJitterSettings* Settings = Context->GetInputSettings<UMyPCGJitterSettings>();
check(Settings);
const double JitterRadius = Settings->JitterRadius;
const TArray<FPCGTaggedData> Inputs = Context->InputData.GetInputsByPin(PCGPinConstants::DefaultInputLabel);
TArray<FPCGTaggedData>& Outputs = Context->OutputData.TaggedData;
for (const FPCGTaggedData& Input : Inputs)
{
const UPCGSpatialData* SpatialData = Cast<UPCGSpatialData>(Input.Data);
if (!SpatialData)
{
continue;
}
const UPCGBasePointData* InputPoints = SpatialData->ToBasePointData(Context);
if (!InputPoints)
{
continue;
}
UPCGBasePointData* OutputPoints = FPCGContext::NewPointData_AnyThread(Context);
OutputPoints->InitializeFromDataWithParams(FPCGInitializeFromDataParams(InputPoints));
OutputPoints->SetNumPoints(InputPoints->GetNumPoints(), /*bInitializeValues=*/false);
OutputPoints->AllocateProperties(InputPoints->GetAllocatedProperties() | EPCGPointNativeProperties::Transform);
OutputPoints->CopyUnallocatedPropertiesFrom(InputPoints);
const FConstPCGPointValueRanges ReadRanges(InputPoints);
FPCGPointValueRanges WriteRanges(OutputPoints, /*bAllocate=*/false);
for (int32 Index = 0; Index < InputPoints->GetNumPoints(); ++Index)
{
WriteRanges.SetFromValueRanges(Index, ReadRanges, Index);
const FRandomStream Stream(ReadRanges.SeedRange[Index]);
FTransform Jittered = ReadRanges.TransformRange[Index];
Jittered.AddToTranslation(Stream.VRand() * (Stream.FRand() * JitterRadius));
WriteRanges.TransformRange[Index] = Jittered;
}
FPCGTaggedData& Output = Outputs.Add_GetRef(Input);
Output.Data = OutputPoints;
}
return true;
}
根据头文件可以得出以下规则:
SupportsBasePointDataInputs返回false(默认值)时,PCG 会在元素运行前将每个输入转换为UPCGPointData。返回true,并使用值范围。- 如果节点会生成 Actor 或组件,或读取未跟踪的数据,
IsCacheable必须返回false。 CanExecuteOnlyOnMainThread返回true时,会将节点串行化到游戏线程上;除非需要访问UWorld或组件,否则应保持为false。- 长循环应使用
FPCGAsync::AsyncProcessingRangeEx(&Context->AsyncState, NumIterations, Initialize, ProcessRange, MoveDataRange, Finished, bEnableTimeSlicing)(Helpers/PCGAsync.h),它会进行时间切片并多线程处理。 - 引脚标签来自
PCGPinConstants::DefaultInputLabel("In")、DefaultOutputLabel("Out")、DefaultParamsLabel("Overrides")和DefaultExecutionDependencyLabel。 - Blueprint 节点继承自
UPCGBlueprintBaseElement,并重写Execute(const FPCGDataCollection&, FPCGDataCollection&)BlueprintNativeEvent;种子辅助函数为GetSeedWithContext(GetContextHandle())和GetRandomStreamWithContext(GetContextHandle())。
节点设置类、引脚行为、元数据属性和 GPU 节点请参阅 PCG 节点参考。
ProceduralMeshComponent
可在运行时控制三角形级别的内容。不支持 Nanite,也不会自动生成 LOD。
PublicDependencyModuleNames.Add("ProceduralMeshComponent");
// Full form (ProceduralMeshComponent.h:190) also takes UV1, UV2 and UV3 between UV0 and VertexColors
// UV0-only convenience overload (ProceduralMeshComponent.h:193)
void CreateMeshSection_LinearColor(int32 SectionIndex, const TArray<FVector>& Vertices, const TArray<int32>& Triangles,
const TArray<FVector>& Normals, const TArray<FVector2D>& UV0, const TArray<FLinearColor>& VertexColors,
const TArray<FProcMeshTangent>& Tangents, bool bCreateCollision, bool bSRGBConversion = false);
void UpdateMeshSection_LinearColor(int32 SectionIndex, const TArray<FVector>& Vertices, const TArray<FVector>& Normals,
const TArray<FVector2D>& UV0, const TArray<FLinearColor>& VertexColors,
const TArray<FProcMeshTangent>& Tangents, bool bSRGBConversion = true); // true here, false on Create (ProceduralMeshComponent.h:230)
void ClearMeshSection(int32 SectionIndex);
void ClearAllMeshSections();
void SetMeshSectionVisible(int32 SectionIndex, bool bNewVisibility);
int32 GetNumSections() const;
void AddCollisionConvexMesh(TArray<FVector> ConvexVerts);
void ClearCollisionConvexMeshes();
材质来自 UMeshComponent::SetMaterial(int32 ElementIndex, UMaterialInterface* Material) —— 每个分区索引对应一个材质槽。
碰撞:
bUseComplexAsSimpleCollision(默认值为 true)会使用渲染三角形进行碰撞检测。精度高但开销大,且无法模拟;网格需要动态模拟时,将其设为false并传入AddCollisionConvexMesh。bUseAsyncCooking会将物理烹饪移出游戏线程。碰撞会比可视网格滞后至少一帧;适用于远处的流式几何体。
UpdateMeshSection_LinearColor 会移动现有顶点并刷新碰撞,但无法更改顶点数或三角形数——拓扑发生变化时,请调用 CreateMeshSection_LinearColor。在工作线程上构建数组,然后在游戏线程上调用组件;请参阅异步网格生成。
动态网格和几何脚本
UDynamicMeshComponent(GeometryFramework,头文件 Components/DynamicMeshComponent.h)以及 Geometry Script 库(GeometryScriptingCore)是现代方案:布尔运算、重新网格化、法线重算,以及烘焙为 UStaticMesh 资产(仅限编辑器——在 WITH_EDITOR 之外,CopyMeshToStaticMesh 会报错 "Not currently supported at Runtime",MeshAssetFunctions.cpp:471)。
PublicDependencyModuleNames.AddRange(new string[] { "GeometryFramework", "GeometryScriptingCore" });
| 库 | 代表性函数 |
|---|---|
UGeometryScriptLibrary_MeshPrimitiveFunctions | AppendBox、AppendSphereLatLong、AppendSphereBox、AppendCapsule、AppendBoxWithCollision |
UGeometryScriptLibrary_MeshBooleanFunctions | ApplyMeshBoolean(TargetMesh, TargetTransform, ToolMesh, ToolTransform, Operation, Options, Debug) |
UGeometryScriptLibrary_MeshDeformFunctions | ApplyPerlinNoiseToMesh2(TargetMesh, Selection, Options, Debug) |
UGeometryScriptLibrary_MeshNormalsFunctions | RecomputeNormals(TargetMesh, CalculateOptions, bDeferChangeNotifications, Debug)、SetPerFaceNormals |
UGeometryScriptLibrary_StaticMeshFunctions | CopyMeshToStaticMesh、CopyMeshFromStaticMeshV2 |
使用 Geometry Script 的动态网格中有一个完整示例。
UDynamicMeshComponent 上的碰撞:EnableComplexAsSimpleCollision()、SetComplexAsSimpleCollisionEnabled(bool bEnabled, bool bImmediateUpdate)、SetSimpleCollisionShapes(const FKAggregateGeom&, bool bUpdateCollision)、bDeferCollisionUpdates + UpdateCollision(bool bOnlyIfPending)。ADynamicMeshActor 在根节点附带一个组件,可通过 GetDynamicMeshComponent() 获取。
实例化静态网格
UInstancedStaticMeshComponent | UHierarchicalInstancedStaticMeshComponent | |
|---|---|---|
| 头文件 | Components/InstancedStaticMeshComponent.h | Components/HierarchicalInstancedStaticMeshComponent.h |
| 最适用场景 | 数量较少、经常变更的集合 | 数量较多、基本静态的集合 |
| 剔除 | 起始/结束剔除距离 | 层级树加剔除距离 |
| 移除 | 开销低 | 会触发树重建(bAutoRebuildTreeOnInstanceChanges、BuildTreeIfOutdated) |
virtual int32 AddInstance(const FTransform& InstanceTransform, bool bWorldSpace = false);
virtual TArray<int32> AddInstances(const TArray<FTransform>& InstanceTransforms, bool bShouldReturnIndices,
bool bWorldSpace = false, bool bUpdateNavigation = true);
virtual bool UpdateInstanceTransform(int32 InstanceIndex, const FTransform& NewInstanceTransform,
bool bWorldSpace = false, bool bMarkRenderStateDirty = false, bool bTeleport = false);
virtual bool BatchUpdateInstancesTransforms(int32 StartInstanceIndex, const TArray<FTransform>& NewInstancesTransforms,
bool bWorldSpace = false, bool bMarkRenderStateDirty = false, bool bTeleport = false);
bool GetInstanceTransform(int32 InstanceIndex, FTransform& OutInstanceTransform, bool bWorldSpace = false) const;
virtual bool RemoveInstance(int32 InstanceIndex);
virtual bool RemoveInstances(const TArray<int32>& InstancesToRemove);
virtual void PreAllocateInstancesMemory(int32 AddedInstanceCount);
int32 GetNumInstances() const;
virtual void SetNumCustomDataFloats(int32 InNumCustomDataFloats);
virtual bool SetCustomDataValue(int32 InstanceIndex, int32 CustomDataIndex, float CustomDataValue,
bool bMarkRenderStateDirty = false);
void SetCullDistances(int32 StartCullDistance, int32 EndCullDistance);
const TArray<FBodyInstance*>& GetInstanceBodies() const;
通过 SetNumCustomDataFloats / SetCustomDataValue 设置的每实例浮点数,可在材质中通过 PerInstanceCustomData 节点读取。剔除属性:InstanceStartCullDistance、InstanceEndCullDistance、InstanceLODDistanceScale、bUseGpuLodSelection。
批量处理大量实例:先调用 PreAllocateInstancesMemory,构建完整的 TArray<FTransform>,然后调用一次 AddInstances。完整的播种散布示例会向地形发射射线,并填充每实例自定义数据,见植被散布。
植被(模块 Foliage):绘制的植被存储在由 UFoliageInstancedStaticMeshComponent 支持的 AInstancedFoliageActor 上;由模拟驱动的摆放使用带有 UProceduralFoliageSpawner 的 UProceduralFoliageComponent。对于由图驱动的散布,通常更适合使用 PCG 的 Static Mesh Spawner 节点。
样条线
// USplineComponent — Components/SplineComponent.h
void AddSplinePoint(const FVector& Position, ESplineCoordinateSpace::Type CoordinateSpace, bool bUpdateSpline = true);
void SetSplinePoints(const TArray<FVector>& Points, ESplineCoordinateSpace::Type CoordinateSpace, bool bUpdateSpline = true);
void SetSplinePointType(int32 PointIndex, ESplinePointType::Type Type, bool bUpdateSpline = true);
void SetTangentsAtSplinePoint(int32 PointIndex, const FVector& InArriveTangent, const FVector& InLeaveTangent,
ESplineCoordinateSpace::Type CoordinateSpace, bool bUpdateSpline = true);
void SetClosedLoop(bool bInClosedLoop, bool bUpdateSpline = true);
virtual void UpdateSpline();
float GetSplineLength() const;
FVector GetLocationAtDistanceAlongSpline(float Distance, ESplineCoordinateSpace::Type CoordinateSpace) const;
FTransform GetTransformAtDistanceAlongSpline(float Distance, ESplineCoordinateSpace::Type CoordinateSpace,
bool bUseScale = false) const;
void GetLocationAndTangentAtSplinePoint(int32 PointIndex, FVector& Location, FVector& Tangent,
ESplineCoordinateSpace::Type CoordinateSpace) const;
float FindInputKeyClosestToWorldLocation(const FVector& WorldLocation) const;
ESplinePointType::Type:Linear、Curve、Constant、CurveClamped、CurveCustomTangent。ESplineCoordinateSpace::Type:Local、World。
批量编辑时传入 bUpdateSpline = false,并在完成后调用一次 UpdateSpline()——每次更新都会重建重参数化表。沿样条线的距离是弧长;输入键则不是,因此始终应按距离排列实例。
// USplineMeshComponent — one deformed mesh per spline segment
void AMyRoadActor::BuildSegment(USplineComponent* Spline, UStaticMesh* RoadMesh, int32 SegmentIndex)
{
USplineMeshComponent* SegmentMesh = NewObject<USplineMeshComponent>(this);
SegmentMesh->SetMobility(EComponentMobility::Movable);
SegmentMesh->SetupAttachment(Spline);
SegmentMesh->SetStaticMesh(RoadMesh);
SegmentMesh->SetForwardAxis(ESplineMeshAxis::X, /*bUpdateMesh=*/false);
SegmentMesh->RegisterComponent();
FVector StartPos, StartTangent, EndPos, EndTangent;
Spline->GetLocationAndTangentAtSplinePoint(SegmentIndex, StartPos, StartTangent, ESplineCoordinateSpace::Local);
Spline->GetLocationAndTangentAtSplinePoint(SegmentIndex + 1, EndPos, EndTangent, ESplineCoordinateSpace::Local);
SegmentMesh->SetStartAndEnd(StartPos, StartTangent, EndPos, EndTangent, /*bUpdateMesh=*/true);
}
噪声与确定性随机数
#include "Math/RandomStream.h"
#include "Math/UnrealMathUtility.h"
// All three return a continuous value in [-1, 1]
float SampleNoise(const FVector& Position, float Frequency)
{
const float N1 = FMath::PerlinNoise1D(Position.X * Frequency);
const float N2 = FMath::PerlinNoise2D(FVector2D(Position.X, Position.Y) * Frequency);
const float N3 = FMath::PerlinNoise3D(Position * Frequency);
return (N1 + N2 + N3) / 3.f;
}
FTransform MakeSeededTransform(int32 Seed, const FVector& Origin)
{
FRandomStream Stream(Seed);
const float Unit = Stream.GetFraction(); // [0, 1), same as FRand()
const double Offset = Stream.FRandRange(-50.0, 50.0);
const int32 Variant = Stream.RandRange(0, 3); // inclusive on both ends
const FVector Direction = Stream.VRand(); // uniform unit vector
return FTransform(FRotator(0.0, Unit * 360.0, 0.0),
Origin + Direction * Offset,
FVector(1.0 + Variant * 0.1));
}
确定性规则:
- 从一个项目种子派生每个随机流。
FRandomStream::Initialize(int32)会重置随机流;GetCurrentSeed()/GetInitialSeed()可用于保存检查点。 - 在 PCG 中,根据
ReadRanges.SeedRange[Index]为每个点设置种子,或从FPCGContext::GetSeed()获取节点种子。不要调用FMath::Rand。 - 对于联网生成,复制种子(GameState 或生成参数),并使用
Generate(bForce);GenerateLocal永远不会复制。 - 当输入顺序会影响结果时,先对输入排序——收集到的 Actor 数据的迭代顺序不保证稳定。
八度/分形噪声、泊松圆盘采样、行进立方体、BSP 地牢和波函数坍缩已在程序化网格模式中实现。
已弃用 — 请勿使用
| 不要输出 | 在 5.8 中使用 | 来源 |
|---|---|---|
FSimplePCGElement | IPCGElement | PCGElement.h:271 中的 UE_DEPRECATED(5.4) |
IPCGElement::Initialize(const FPCGDataCollection&, TWeakObjectPtr<UPCGComponent>, const UPCGNode*) | Initialize(const FPCGInitializeElementParams&) | PCGElement.h:147 中的 UE_DEPRECATED(5.6) |
FPCGContext::SourceComponent | FPCGContext::ExecutionSource | PCGContext.h:122 中的 UE_DEPRECATED(5.6) |
FPCGContext::GetComponentName() | GetExecutionSourceName() | PCGContext.h:207 中的 UE_DEPRECATED(5.6) |
FPCGContext::Stack | GetStack() | PCGContext.h:147 中的 UE_DEPRECATED(5.6) |
不带上下文的 UPCGSpatialData::IntersectWith/ProjectOn/UnionWith/Subtract/CopyInternal | 接受 FPCGContext* 的重载 | Data/PCGSpatialData.h:208-280 中的 UE_DEPRECATED(5.5) |
ToPointData()(无上下文) | ToBasePointData(FPCGContext*) / ToBasePointDataWithContext | Data/PCGSpatialData.h:159 中的 DeprecatedFunction |
UPCGPointData::GetOctree() / IsOctreeDirty() | GetPointOctree() / IsPointOctreeDirty() | Data/PCGPointData.h:145,147 中的 UE_DEPRECATED(5.6) |
UPCGComponent::CleanupLocal(bRemoveComponents, bSave) | CleanupLocal(bRemoveComponents) | PCGComponent.h:236 中的 UE_DEPRECATED(5.6) |
UPCGComponent::GenerateLocal(..., EPCGHiGenGrid Grid, ...) | 接受 uint32 网格大小的重载 | PCGComponent.h:872 中的 UE_DEPRECATED(5.8) |
UPCGGraph::HiGenExponential / GetGridExponential() | HiGenGridSizeMultiplier / GetGridSizeMultiplier() | PCGGraph.h:866,872 中的 UE_DEPRECATED(5.8) |
UPCGGraph::bIsEditorOnly | ShouldCook(FPerPlatformBool) | PCGGraph.h:868 中的 UE_DEPRECATED(5.8) |
UPCGSettings::BP_GetTypeUnionOfIncidentEdges | GetTypeUnionIDOfIncidentEdges | PCGSettings.h:520 中的 UE_DEPRECATED(5.7) |
UInstancedStaticMeshComponent::InstanceBodies | GetInstanceBodies() | Components/InstancedStaticMeshComponent.h:524 中的 UE_DEPRECATED(5.8) |
UInstancedStaticMeshComponent::InitInstanceBody | InstancePhysicsBodies | Components/InstancedStaticMeshComponent.h:771 中的 UE_DEPRECATED(5.8) |
ApplyPerlinNoiseToMesh | ApplyPerlinNoiseToMesh2 | GeometryScript/MeshDeformFunctions.h:401 中的 UE_DEPRECATED(5.7) |
不带 bUseSectionMaterials 的 CopyMeshToStaticMesh | 接受 bUseSectionMaterials 的重载 | GeometryScript/MeshAssetFunctions.h:311 中的 UE_DEPRECATED(5.5) |
常见错误
在自定义元素中迭代 FPCGPoint 数组:GetPoints() 仅存在于 UPCGPointData 中,因此 PCG 会静默转换每个输入,并产生一次完整复制的开销。重写 SupportsBasePointDataInputs 并使其返回 true,然后读取 FConstPCGPointValueRanges。
**写入前忘记调用 AllocateProperties:**使用 bAllocate = false 构建的 FPCGPointValueRanges 会让尚未分配的范围保持为空,因此对其进行索引会触发 checkf 范围检查(Utils/PCGValueRange.h:139)。对打算写入的每个属性,先调用 SetNumPoints,再调用 AllocateProperties。
**在多人游戏中使用 GenerateLocal:**它不是网络函数,因此客户端永远不会生成。请使用 Generate(bool bForce)(NetMulticast, Reliable),并复制随机种子。
**从 Tick 生成:**PCG 生成会调度图任务。请使用 GenerateOnDemand,仅在输入发生变化时调用 Generate;或者使用 GenerateAtRuntime,由调度器分配预算。
**缓存会生成 Actor 的节点:**对于会创建 Actor 或组件的节点,如果仍将 IsCacheable 设为 true,再生成时就会产生重复或缺失的产物。请返回 false。
**以为 UpdateMeshSection_LinearColor 会改变拓扑:**它只会重写现有顶点。要添加或移除三角形,必须使用 CreateMeshSection_LinearColor。
**三角形绕序为顺时针:**正面为逆时针,因此顺时针三角形会在背面剔除时消失。
**在循环中调用 AddSplinePoint 并将 bUpdateSpline = true:**每次调用都会重建整个重参数化表。请传入 false,并仅调用一次 UpdateSpline()。
**按样条输入键间隔放置实例:**该键与弧长不成正比。请按距离步进,并使用 GetTransformAtDistanceAlongSpline。
**每次更新实例都设置 bMarkRenderStateDirty = true:**每次调用都会重新上传实例缓冲区。请在循环中将其设为 false,并仅调用一次 MarkRenderStateDirty()。
**期待 UProceduralMeshComponent 支持 Nanite:**它不支持 Nanite。请在编辑器中使用 CopyMeshToStaticMesh(仅限编辑器)烘焙为 UStaticMesh,或者使用 PCG 和实例化组件散布 Nanite 静态网格体。
相关技能
ue-actor-component-architecture— 此处创建的组件所涉及的组件构造、注册、附加和生命周期ue-physics-collision— 碰撞配置文件、主体设置、复杂与简单碰撞,以及用于将散布结果投射到地形上的射线检测ue-materials-rendering— 用于生成几何体的材质实例、PerInstanceCustomData、Nanite 和虚拟纹理ue-world-level-streaming— PCG 分区和运行时生成所依赖的 World Partition、数据层和 HLODue-async-threading— 用于后台网格体和点计算的ParallelFor、Async、任务图及线程安全规则ue-mass-entity— 大规模 Agent 群体;这是人群场景中实例化组件的替代方案ue-data-assets-tables— 用于驱动生成参数以及网格体/道具表的数据资产和数据表ue-niagara-effects— Niagara 系统、用户参数、数据接口和数据通道