Claude
Skills
Sign in
Back

ue-procedural-generation

Included with Lifetime
$97 forever

Use this skill when working with procedural generation in Unreal Engine: PCG framework, ProceduralMesh, instanced mesh, HISM, spline, runtime mesh, noise, terrain generation, or dungeon generation. See references/pcg-node-reference.md for PCG node types and references/procedural-mesh-patterns.md for mesh generation patterns. For physics on procedural geometry, see ue-physics-collision.

Backend & APIs

What this skill does


# ue-procedural-generation

You are an expert in Unreal Engine's procedural generation systems, including the PCG framework, ProceduralMeshComponent, instanced static meshes, noise functions, and spline-based generation.

## Context Check

Before advising, read `.agents/ue-project-context.md` to determine:
- Whether the PCG plugin is enabled (plugins list)
- Target generation type: world layout, terrain, dungeon, vegetation, runtime mesh
- Performance constraints (mobile, console, Nanite enabled)
- Multiplayer requirements (server authority vs. deterministic seeding)

## Information Gathering

Ask for clarification on:
1. **Generation type**: world population (PCG), runtime mesh (ProceduralMeshComponent), instanced geometry (ISM/HISM), or spline-driven?
2. **Timing**: editor-time baked result or runtime dynamic generation?
3. **Instance count**: hundreds (ISM) or tens of thousands (HISM)?
4. **Collision**: does generated geometry need physics collision?
5. **Determinism**: same seed must produce same result across sessions or network clients?

---

## 1. PCG Framework (UE 5.2+)

Node-based rule-driven world generation. Operates on point clouds with transform, density, color, seed, and metadata attributes.

### Plugin Setup

```csharp
// Build.cs
PublicDependencyModuleNames.Add("PCG");
```
```json
// .uproject Plugins array
{ "Name": "PCG", "Enabled": true }
```

### Core Classes

| Class | Header | Purpose |
|---|---|---|
| `UPCGComponent` | `PCGComponent.h` | Actor component driving generation |
| `UPCGGraph` | `PCGGraph.h` | Asset: nodes + edges |
| `UPCGGraphInstance` | `PCGGraph.h` | Graph instance with parameter overrides |
| `UPCGPointData` | `Data/PCGPointData.h` | Point cloud between nodes |
| `UPCGSettings` | `PCGSettings.h` | Node settings base class |
| `UPCGBlueprintBaseElement` | `Elements/Blueprint/PCGBlueprintBaseElement.h` | Custom Blueprint node base |

### UPCGComponent Key API (from `PCGComponent.h`)

```cpp
// Assign graph (NetMulticast)
void SetGraph(UPCGGraphInterface* InGraph);

// Trigger generation (NetMulticast, Reliable) — use for multiplayer
void Generate(bool bForce);

// Local non-replicated generation
void GenerateLocal(bool bForce);

// Cleanup
void Cleanup(bool bRemoveComponents);
void CleanupLocal(bool bRemoveComponents);

// Notify to re-evaluate after Blueprint property change
void NotifyPropertiesChangedFromBlueprint();

// Read generated output
const FPCGDataCollection& GetGeneratedGraphOutput() const;
```

Generation triggers (`EPCGComponentGenerationTrigger`):
- `GenerateOnLoad` — one-shot on BeginPlay
- `GenerateOnDemand` — explicit `Generate()` call only
- `GenerateAtRuntime` — budget-scheduled by `UPCGSubsystem`

### UPCGGraph Node API (from `PCGGraph.h`)

```cpp
// Add node by settings class
UPCGNode* AddNodeOfType(TSubclassOf<UPCGSettings> InSettingsClass, UPCGSettings*& DefaultNodeSettings);

// Connect two nodes
UPCGNode* AddEdge(UPCGNode* From, const FName& FromPinLabel, UPCGNode* To, const FName& ToPinLabel);

// Graph parameters (typed template)
template<typename T>
TValueOrError<T, EPropertyBagResult> GetGraphParameter(const FName PropertyName) const;

template<typename T>
EPropertyBagResult SetGraphParameter(const FName PropertyName, const T& Value);
```

### Custom Blueprint PCG Node

Derive from `UPCGBlueprintBaseElement`:

```cpp
UCLASS(BlueprintType, Blueprintable)
class UMyPCGNode : public UPCGBlueprintBaseElement
{
    GENERATED_BODY()
public:
    UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "PCG|Execution")
    void Execute(const FPCGDataCollection& Input, FPCGDataCollection& Output);
};

// In Execute:
FRandomStream Stream = GetRandomStreamWithContext(GetContextHandle()); // deterministic seed

for (const FPCGTaggedData& In : Input.GetInputsByPin(PCGPinConstants::DefaultInputLabel))
{
    const UPCGPointData* InPts = Cast<UPCGPointData>(In.Data);
    if (!InPts) continue;

    UPCGPointData* OutPts = NewObject<UPCGPointData>();
    for (const FPCGPoint& Pt : InPts->GetPoints())
    {
        FPCGPoint NewPt = Pt;
        NewPt.Density = Stream.FRandRange(0.5f, 1.0f);
        OutPts->GetMutablePoints().Add(NewPt);
    }
    Output.TaggedData.Emplace_GetRef().Data = OutPts;
}
```

Key `UPCGBlueprintBaseElement` properties:
- `bIsCacheable = false` — when node spawns actors or components
- `bRequiresGameThread = true` — for actor spawn, component add
- `CustomInputPins` / `CustomOutputPins` — extra typed pins

### PCG Determinism

PCG graphs are deterministic by default — the same seed produces identical output. Each node receives a seeded random stream via `GetRandomStreamWithContext()`. To vary output across instances, set the PCG component's `Seed` property. For multiplayer, ensure all clients use the same seed (replicate via GameState or pass as spawn parameter).

```cpp
// Set PCG seed at runtime for deterministic variation
UPCGComponent* PCG = FindComponentByClass<UPCGComponent>();
PCG->Seed = MyDeterministicSeedValue;
PCG->Generate(); // Regenerate with new seed
```

### PCG Data Types

| Type | Contains | Use for |
|---|---|---|
| `FPCGPoint` / Point Data | Position, rotation, scale, density, color | Scatter placement, foliage, instance positioning |
| `UPCGSplineData` | Spline points + tangents | Roads, rivers, paths, boundary definitions |
| `UPCGLandscapeData` | Height + layer weight sampling | Terrain-aware placement, biome queries |
| `UPCGVolumeData` | 3D bounds | Volume-based filtering and generation |

Point data is the most common — most PCG nodes consume and produce point collections. Also available: `UPCGTextureData`, `UPCGPrimitiveData`, `UPCGDynamicMeshData`.

See `references/pcg-node-reference.md` for all node types, settings fields, and pin labels.

---

## 2. ProceduralMeshComponent

```csharp
// Build.cs
PublicDependencyModuleNames.Add("ProceduralMeshComponent");
```

### Core API

```cpp
// Create section: vertices, triangles (CCW = front), normals, UVs, colors, tangents
void CreateMeshSection(int32 SectionIndex,
    const TArray<FVector>& Vertices, const TArray<int32>& Triangles,
    const TArray<FVector>& Normals,  const TArray<FVector2D>& UV0,
    const TArray<FColor>& VertexColors, const TArray<FProcMeshTangent>& Tangents,
    bool bCreateCollision);

// Updates vertex positions (incl. collision if enabled). Cannot change topology.
void UpdateMeshSection(int32 SectionIndex,
    const TArray<FVector>& Vertices, const TArray<FVector>& Normals,
    const TArray<FVector2D>& UV0,    const TArray<FColor>& VertexColors,
    const TArray<FProcMeshTangent>& Tangents);

void ClearMeshSection(int32 SectionIndex);
void ClearAllMeshSections();
void SetMeshSectionVisible(int32 SectionIndex, bool bNewVisibility);
void SetMaterial(int32 ElementIndex, UMaterialInterface* Material);
```

### Terrain Grid Example

```cpp
void ATerrainActor::Build(int32 Grid, float Cell)
{
    TArray<FVector> Verts; TArray<int32> Tris; TArray<FVector> Norms;
    TArray<FVector2D> UVs; TArray<FColor> Colors; TArray<FProcMeshTangent> Tangs;

    for (int32 Y = 0; Y <= Grid; Y++)
    for (int32 X = 0; X <= Grid; X++)
    {
        float Z = SampleOctaveNoise(X * Cell, Y * Cell, 4, 0.5f, 2.f, 80.f);
        Verts.Add(FVector(X * Cell, Y * Cell, Z));
        Norms.Add(FVector::UpVector);
        UVs.Add(FVector2D((float)X / Grid, (float)Y / Grid));
    }
    for (int32 Y = 0; Y < Grid; Y++)
    for (int32 X = 0; X < Grid; X++)
    {
        int32 BL = Y*(Grid+1)+X, BR=BL+1, TL=BL+(Grid+1), TR=TL+1;
        Tris.Add(BL); Tris.Add(TL); Tris.Add(TR);
        Tris.Add(BL); Tris.Add(TR); Tris.Add(BR);
    }
    ProceduralMesh->CreateMeshSection(0, Verts, Tris, Norms,
                                       UVs, Colors, Tangs, /*bCreateCollision=*/true);
}
```

### Performance Notes

- One draw call per `CreateMeshSection`. Keep vertex count < 65K per section.
- `UpdateMeshSection` updates vertex positions and collision (if enabled) but cannot change topology — call `CreateMeshSection` for new

Related in Backend & APIs