ue-procedural-generation について
Unreal Engine 5.8 の開発者向けプロシージャル生成スキルです。PCG、ランタイムメッシュ、インスタンス化、スプラインなどの API を、バージョンに沿って解説します。
主な機能
- 01PCG グラフとランタイム生成
- 02PCG ポイントデータの読み書き
- 03C++ による PCG ノード作成
- 04ランタイムプロシージャルメッシュ
- 05Dynamic Mesh と Geometry Script
- 06インスタンス化、スプライン、ノイズ
活用シーン
- 01PCG プラグインとモジュールの設定
- 02PCG グラフノードの作成や拡張
- 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)、Geometry Script と UDynamicMeshComponent(モジュール GeometryFramework および GeometryScriptingCore)、UInstancedStaticMeshComponent/UHierarchicalInstancedStaticMeshComponent によるインスタンス化、スプライン駆動の配置、Core による決定論的なノイズ/乱数を扱います。
コンテキスト
.agents/ue-project-context.md が存在する場合は読み込んでください(モジュール名、規約、有効なプラグイン、GAS/ネットワーキングの設定)。ファイルが見つからなくても作業を中断しないでください。
リクエストとコードベースから対象領域を特定してください。考えられる解釈が 2 つあり、それぞれで異なるコードになる場合に限り、質問してください。
| リクエストの内容 | 参照先 |
|---|---|
| PCG の有効化、モジュール/プラグインの接続 | PCG のセットアップ |
| アクターからの生成制御、ランタイム生成、パーティショニング | PCG コンポーネントとランタイム生成 |
| グラフ内のポイントの読み書き | ポイントデータ |
| C++ で新しい PCG ノードを作成する | C++ のカスタム PCG ノード |
| X を行うノード、ピンのラベル、ノード設定のフィールド | 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 | グラフを所有し、生成を制御するアクターコンポーネント |
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 を持つボリュームアクター |
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を返す場合は、値範囲を使用してください。- ノードがアクターやコンポーネントを生成する場合、または追跡されていないデータを読み取る場合、
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を継承し、BlueprintNativeEvent のExecute(const FPCGDataCollection&, FPCGDataCollection&)をオーバーライドします。シードヘルパーは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) で設定します。セクションインデックスごとにマテリアルスロットが 1 つあります。
コリジョン:
bUseComplexAsSimpleCollision(デフォルトは true)は、描画された三角形をコリジョンに使用します。精度は高いものの処理コストが大きく、シミュレーションには使用できません。メッシュを動的にする必要がある場合はfalseに設定し、AddCollisionConvexMeshにメッシュを渡してください。bUseAsyncCookingを設定すると、物理クッキングをゲームスレッド以外で実行します。コリジョンは視覚メッシュより 1 フレーム以上遅れます。遠方のストリーミングジオメトリで使用してください。
UpdateMeshSection_LinearColor は既存の頂点を移動してコリジョンを更新しますが、頂点数や三角形数は変更できません。トポロジーが変わる場合は CreateMeshSection_LinearColor を呼び出してください。配列はワーカースレッドで構築し、その後ゲームスレッドでコンポーネントを呼び出します。非同期メッシュ生成を参照してください。
ダイナミックメッシュとジオメトリスクリプト
UDynamicMeshComponent(GeometryFramework、ヘッダーは Components/DynamicMeshComponent.h)と Geometry Script ライブラリ(GeometryScriptingCore)が、現代的な方法です。ブーリアン演算、リメッシュ、法線の再計算、UStaticMesh アセットへのベイクに対応しています(エディター専用です。CopyMeshToStaticMesh は WITH_EDITOR の外では「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は複製されません。 - 順序が結果に影響する場合は、入力を消費する前にソートしてください。収集したアクターデータの反復順序は安定しているとは限りません。
オクターブ/フラクタルノイズ、ポアソンディスクサンプリング、マーチングキューブ、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を使用してスケジューラーに処理量を調整させてください。
アクターをスポーンするノードをキャッシュする: アクターやコンポーネントを作成するノードでIsCacheableをtrueのままにすると、再生成時にアーティファクトが重複したり、欠落したりします。falseを返してください。
UpdateMeshSection_LinearColorでトポロジーが変わると期待する: これは既存の頂点を書き換えるだけです。三角形を追加または削除するには、CreateMeshSection_LinearColorを使用してください。
三角形の頂点順序を時計回りにする: 表面の面は反時計回りなので、時計回りの三角形はバックフェースカリングで消えてしまいます。
ループ内でbUpdateSpline = trueを指定してAddSplinePointを呼び出す: 呼び出しごとに再パラメーター化テーブル全体が再構築されます。falseを渡し、最後に一度だけUpdateSpline()を呼び出してください。
スプラインの入力キーでインスタンス間隔を設定する: キーは弧長に比例しません。距離単位で進め、GetTransformAtDistanceAlongSplineを使用してください。
インスタンスを更新するたびにbMarkRenderStateDirty = trueを指定する: 呼び出しのたびにインスタンスバッファーが再アップロードされます。ループ内ではfalseのままにし、最後に一度だけMarkRenderStateDirty()を呼び出してください。
UProceduralMeshComponentでNaniteが使えると期待する: Naniteには対応していません。エディターでCopyMeshToStaticMesh(エディター専用)を使ってUStaticMeshにベイクするか、Nanite対応のスタティックメッシュをPCGとインスタンスコンポーネントで散布してください。
関連スキル
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— 大規模なエージェント群。群衆向けのインスタンスコンポーネントに代わる方法ue-data-assets-tables— 生成パラメーターやメッシュ/プロップテーブルを駆動するデータアセットとデータテーブルue-niagara-effects— Niagaraシステム、ユーザーパラメーター、データインターフェース、データチャネル