Ue Specifiers
fjz13/UnrealSpecifiers
A skill your agent uses when writing, editing, reviewing, validating, or explaining Unreal Engine C++ reflected declarations: UCLASS, USTRUCT, UENUM, UINTERFACE, UPROPERTY, UFUNCTION, UPARAM, UMETA…
A skill your agent uses when generating world content or geometry procedurally in Unreal Engine: PCG graphs, custom PCG nodes in C++, runtime mesh building, instancing and seeded noise.
$ npx skills add quodsoler/unreal-engine-skills --skill ue-procedural-generation -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install quodsoler/unreal-engine-skills ue-procedural-generation --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/quodsoler/unreal-engine-skills.git skills-src && mkdir -p .claude/skills && cp -r skills-src/skills/ue-procedural-generation .claude/skills/ue-procedural-generation && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "ue-procedural-generation" agent skill from https://github.com/quodsoler/unreal-engine-skills/tree/main/skills/ue-procedural-generation into .claude/skills/ue-procedural-generation/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "ue-procedural-generation", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/quodsoler/unreal-engine-skills/tree/main/skills/ue-procedural-generationType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add quodsoler/unreal-engine-skills --skill ue-procedural-generation -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install quodsoler/unreal-engine-skills ue-procedural-generation --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/quodsoler/unreal-engine-skills.git skills-src && mkdir -p .agents/skills && cp -r skills-src/skills/ue-procedural-generation .agents/skills/ue-procedural-generation && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "ue-procedural-generation" agent skill from https://github.com/quodsoler/unreal-engine-skills/tree/main/skills/ue-procedural-generation into .agents/skills/ue-procedural-generation/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "ue-procedural-generation", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add quodsoler/unreal-engine-skills --skill ue-procedural-generation -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install quodsoler/unreal-engine-skills ue-procedural-generation --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/quodsoler/unreal-engine-skills.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/skills/ue-procedural-generation .cursor/skills/ue-procedural-generation && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "ue-procedural-generation" agent skill from https://github.com/quodsoler/unreal-engine-skills/tree/main/skills/ue-procedural-generation into .cursor/skills/ue-procedural-generation/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "ue-procedural-generation", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/quodsoler/unreal-engine-skills.git --path skills/ue-procedural-generation--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add quodsoler/unreal-engine-skills --skill ue-procedural-generation -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install quodsoler/unreal-engine-skills ue-procedural-generation --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/quodsoler/unreal-engine-skills.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/skills/ue-procedural-generation .gemini/skills/ue-procedural-generation && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "ue-procedural-generation" agent skill from https://github.com/quodsoler/unreal-engine-skills/tree/main/skills/ue-procedural-generation into .gemini/skills/ue-procedural-generation/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "ue-procedural-generation", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install quodsoler/unreal-engine-skills ue-procedural-generationInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add quodsoler/unreal-engine-skills --skill ue-procedural-generation -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/quodsoler/unreal-engine-skills.git skills-src && mkdir -p .github/skills && cp -r skills-src/skills/ue-procedural-generation .github/skills/ue-procedural-generation && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "ue-procedural-generation" agent skill from https://github.com/quodsoler/unreal-engine-skills/tree/main/skills/ue-procedural-generation into .github/skills/ue-procedural-generation/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "ue-procedural-generation", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add quodsoler/unreal-engine-skills --skill ue-procedural-generation -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install quodsoler/unreal-engine-skills ue-procedural-generation --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/quodsoler/unreal-engine-skills.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/skills/ue-procedural-generation .opencode/skills/ue-procedural-generation && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "ue-procedural-generation" agent skill from https://github.com/quodsoler/unreal-engine-skills/tree/main/skills/ue-procedural-generation into .opencode/skills/ue-procedural-generation/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "ue-procedural-generation", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
ue-procedural-generationA skill your agent uses when generating world content or geometry procedurally in Unreal Engine: PCG graphs, custom PCG nodes in C++, runtime mesh building, instancing and seeded noise.
Ue Procedural Generation is an agent skill from quodsoler/unreal-engine-skills. Use when generating world content or geometry procedurally in Unreal Engine: PCG graphs, custom PCG nodes in C++, runtime mesh building, instancing and seeded noise. Also use when the user mentions 'PCG', 'UPCGComponent', 'UPCGSettings', 'IPCGElement', 'UPCGBasePointData', 'surface sampler', 'static mesh spawner', 'runtime generation', 'CreateMeshSection', 'UProceduralMeshComponent', 'UDynamicMeshComponent', 'Geometry Script', 'AddInstance', 'HISM', 'spline mesh', 'PerlinNoise', 'FRandomStream', 'scatter foliage'…
Its SKILL.md is about 7.6k tokens, which your agent loads only when the skill is triggered. The skill folder holds 3 other files, including reference files (for example `references/pcg-node-reference.md` and `references/procedural-mesh-patterns.md`).
It sits in Game Development. It works with C++ and Unreal Engine. The repository describes itself as: Unreal Engine C++ skills for AI coding agents. 27 skills covering gameplay, rendering, networking, animation, and more. Works with Claude Code, Cursor, Windsurf, and any agent… The licence is MIT.
Read from SKILL.md and the folder at commit f3742d7. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
No scripts in the folder and no shell commands in SKILL.md (its code samples are cpp, csharp and json).
From the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md.
From URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Ue Procedural Generation loads about 7.6k tokens when it runs, and up to ~24k if it reads all its reference files. Until then it costs about 181 tokens; SKILL.md has 1,662 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from quodsoler/unreal-engine-skills at commit f3742d7, republished under its MIT licence (© quodsoler). 1,662 words, ~7,598 tokens.
.claude/skills/ue-procedural-generation/SKILL.md (or your agent's skills folder). This skill also uses 2 other files; get the full folder from GitHub.Target engine: UE 5.8. APIs below are verified against the 5.8 headers; older forms are listed under "Deprecated — do not use".
Covers the PCG framework (plugin PCG at Engine/Plugins/PCG, enabled by default, Build.cs module PCG), runtime mesh building with UProceduralMeshComponent (plugin ProceduralMeshComponent, module ProceduralMeshComponent) and UDynamicMeshComponent + Geometry Script (modules GeometryFramework and GeometryScriptingCore), instancing with UInstancedStaticMeshComponent/UHierarchicalInstancedStaticMeshComponent, spline-driven placement, and deterministic noise/random from Core.
Read .agents/ue-project-context.md if it exists (module names, conventions, enabled plugins, GAS/networking setup). Do not stop if it is missing.
Identify the area from the request and the codebase. Ask only when two plausible readings would produce different code.
| Request is about… | Go to |
|---|---|
| Enabling PCG, module/plugin wiring | PCG setup |
| Driving generation from an actor, runtime generation, partitioning | PCG component and runtime generation |
| Reading/writing points inside a graph | Point data |
| Writing a new PCG node in C++ | Custom PCG node in C++ |
| Which node does X, pin labels, node settings fields | PCG node reference |
| Building triangles at runtime | ProceduralMeshComponent |
| Boolean ops, primitives, baking to a static mesh | Dynamic Mesh and Geometry Script |
| Thousands of repeated meshes | Instanced static meshes |
| Roads, rivers, fences, cables | Splines |
| Heightfields, scatter, reproducible results | Noise and deterministic random |
| Marching cubes, BSP dungeons, WFC, Poisson disc | Procedural mesh patterns |
// MyGame.Build.cs
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "PCG" });{ "Name": "PCG", "Enabled": true }| Class | Header | Role |
|---|---|---|
UPCGComponent | PCGComponent.h | Actor component that owns a graph and drives generation |
UPCGGraph | PCGGraph.h | Graph asset: nodes, edges, UserParameters |
UPCGGraphInstance | PCGGraph.h | Graph instance with per-instance parameter overrides |
UPCGGraphInterface | PCGGraph.h | Common base of graph and graph instance |
UPCGSettings | PCGSettings.h | Node settings base class |
IPCGElement | PCGElement.h | The executable half of a node |
FPCGContext | PCGContext.h | Per-execution state: InputData, OutputData, Node, ExecutionSource |
UPCGBasePointData | Data/PCGBasePointData.h | Abstract point collection |
UPCGPointArrayData | Data/PCGPointArrayData.h | Structure-of-arrays point data |
UPCGSubsystem | Subsystems/PCGSubsystem.h | Scheduling, partitioning, runtime generation |
APCGVolume | PCGVolume.h | Volume actor carrying a UPCGComponent |
#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
}| Call | Replication | Use for |
|---|---|---|
Generate(bool bForce) / Cleanup(bool bRemoveComponents) | NetMulticast, Reliable | Server-driven generation that must appear on clients |
GenerateLocal(bool bForce) / CleanupLocal(bool bRemoveComponents) | none | Client-side or single-player generation |
NotifyPropertiesChangedFromBlueprint() | none | Mark dirty and conditionally regenerate after editing exposed properties |
CancelGeneration() | none | Abort an in-flight generation |
GetGeneratedGraphOutput() | none | Read back the FPCGDataCollection the graph produced |
EPCGComponentGenerationTrigger (PCGComponent.h:77): GenerateOnLoad, GenerateOnDemand, GenerateAtRuntime.
Partitioning and runtime generation:
SetIsPartitioned(bool) / IsPartitioned() back the bIsComponentPartitioned property; partitioned components dispatch work to local components on a grid.GenerateAtRuntime hands the component to the runtime-gen scheduler. Tune it with SchedulingPolicyClass / SchedulingPolicy (UPCGSchedulingPolicyBase) and bOverrideGenerationRadii + GenerationRadii (FPCGRuntimeGenerationRadii).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() returns the subsystem; RefreshAllComponentsFiltered(Filter, ChangeType) forces a refresh of a subset (WITH_EDITOR only, Subsystems/PCGSubsystem.h:227-230).Hierarchical generation lives on UPCGGraph: bUseHierarchicalGeneration, HiGenGridSize (EPCGHiGenGrid::Grid4 … Grid2048, plus Unbounded), HiGenGridSizeMultiplier, bUse2DGrid.
Graph parameters are an FInstancedPropertyBag UserParameters on UPCGGraph, read and written through 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);
}Point collections are UPCGBasePointData. UPCGPointArrayData is the structure-of-arrays implementation; UPCGPointData is the array-of-FPCGPoint implementation kept for compatibility. Allocate through the context so the project-configured class is used:
UPCGBasePointData* Points = FPCGContext::NewPointData_AnyThread(Context);Never iterate GetPoints()/GetMutablePoints() in new code — that only exists on UPCGPointData and forces a conversion. Read and write through value ranges instead:
#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];Per-point native properties (EPCGPointNativeProperties in PCGPointPropertiesTraits.h): Transform, Density, BoundsMin, BoundsMax, Color, Steepness, Seed, MetadataEntry, plus All and AllProperties. Sizing and allocation:
Output->SetNumPoints(Input->GetNumPoints(), /*bInitializeValues=*/false);
Output->AllocateProperties(Input->GetAllocatedProperties() | EPCGPointNativeProperties::Density);
Output->CopyUnallocatedPropertiesFrom(Input);Other data types: UPCGSpatialData (base), UPCGSplineData, UPCGLandscapeData, UPCGVolumeData, UPCGTextureData, UPCGPrimitiveData, UPCGDynamicMeshData, and UPCGParamData for attribute sets. UPCGSpatialData::ToBasePointData(FPCGContext*, const FBox&) discretizes any spatial data into points.
A node is a UPCGSettings subclass plus an IPCGElement. Settings hold data; the element is const and stateless and reads everything from 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;
}Rules that fall out of the headers:
SupportsBasePointDataInputs returning false (the default) makes PCG convert every input to UPCGPointData before your element runs. Return true and use value ranges.IsCacheable must return false if the node spawns actors or components, or reads untracked data.CanExecuteOnlyOnMainThread returning true serializes the node onto the game thread; keep it false unless you touch UWorld or components.FPCGAsync::AsyncProcessingRangeEx(&Context->AsyncState, NumIterations, Initialize, ProcessRange, MoveDataRange, Finished, bEnableTimeSlicing) (Helpers/PCGAsync.h), which time-slices and multithreads.PCGPinConstants::DefaultInputLabel ("In"), DefaultOutputLabel ("Out"), DefaultParamsLabel ("Overrides"), DefaultExecutionDependencyLabel.UPCGBlueprintBaseElement and override the Execute(const FPCGDataCollection&, FPCGDataCollection&) BlueprintNativeEvent; seed helpers are GetSeedWithContext(GetContextHandle()) and GetRandomStreamWithContext(GetContextHandle()).See PCG node reference for node settings classes, pin behaviour, metadata attributes and GPU nodes.
Triangle-level control at runtime. No Nanite support, no automatic LODs.
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();Materials come from UMeshComponent::SetMaterial(int32 ElementIndex, UMaterialInterface* Material) — one material slot per section index.
Collision:
bUseComplexAsSimpleCollision (default true) uses the rendered triangles for collision. Accurate, expensive, and cannot be simulated — set it to false and feed AddCollisionConvexMesh when the mesh must be dynamic.bUseAsyncCooking moves physics cooking off the game thread. Collision lags a frame or more behind the visual mesh; use it for far-away streamed geometry.UpdateMeshSection_LinearColor moves existing vertices and refreshes collision, but cannot change vertex or triangle count — call CreateMeshSection_LinearColor when topology changes. Build the arrays on a worker thread, then call the component on the game thread; see async mesh generation.
UDynamicMeshComponent (GeometryFramework, header Components/DynamicMeshComponent.h) plus the Geometry Script libraries (GeometryScriptingCore) are the modern path: boolean operations, remeshing, normals recomputation, and baking to a UStaticMesh asset (editor only — CopyMeshToStaticMesh errors "Not currently supported at Runtime" outside WITH_EDITOR, MeshAssetFunctions.cpp:471).
PublicDependencyModuleNames.AddRange(new string[] { "GeometryFramework", "GeometryScriptingCore" });| Library | Representative functions |
|---|---|
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 |
A worked example is in dynamic mesh with Geometry Script.
Collision on UDynamicMeshComponent: EnableComplexAsSimpleCollision(), SetComplexAsSimpleCollisionEnabled(bool bEnabled, bool bImmediateUpdate), SetSimpleCollisionShapes(const FKAggregateGeom&, bool bUpdateCollision), bDeferCollisionUpdates + UpdateCollision(bool bOnlyIfPending). ADynamicMeshActor ships a component at the root via GetDynamicMeshComponent().
UInstancedStaticMeshComponent | UHierarchicalInstancedStaticMeshComponent | |
|---|---|---|
| Header | Components/InstancedStaticMeshComponent.h | Components/HierarchicalInstancedStaticMeshComponent.h |
| Best for | Small, frequently mutated sets | Large, mostly static sets |
| Culling | Start/end cull distance | Hierarchical tree plus cull distance |
| Removal | Cheap | Triggers a tree rebuild (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;Per-instance floats set with SetNumCustomDataFloats / SetCustomDataValue are read in materials through the PerInstanceCustomData node. Cull properties: InstanceStartCullDistance, InstanceEndCullDistance, InstanceLODDistanceScale, bUseGpuLodSelection.
Batch large populations: PreAllocateInstancesMemory first, build the whole TArray<FTransform>, then one AddInstances call. A full seeded scatter that traces onto terrain and fills per-instance custom data is in vegetation scatter.
Foliage (module Foliage): painted foliage lives on AInstancedFoliageActor backed by UFoliageInstancedStaticMeshComponent; simulation-driven placement uses UProceduralFoliageComponent with a UProceduralFoliageSpawner. PCG's Static Mesh Spawner node is usually the better fit for graph-driven scatter.
// 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.
Pass bUpdateSpline = false while batching edits and call UpdateSpline() once — each update rebuilds the reparameterization table. Distance along the spline is arc length; the input key is not, so always space instances by distance.
// 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));
}Determinism rules:
FRandomStream::Initialize(int32) resets a stream; GetCurrentSeed() / GetInitialSeed() let you checkpoint one.ReadRanges.SeedRange[Index], or take the node seed from FPCGContext::GetSeed(). Do not call FMath::Rand.Generate(bForce); GenerateLocal never replicates.Octave/fractal noise, Poisson disc sampling, marching cubes, BSP dungeons and wave function collapse are implemented in procedural mesh patterns.
| Do not emit | Use in 5.8 | Source |
|---|---|---|
FSimplePCGElement | IPCGElement | UE_DEPRECATED(5.4) in PCGElement.h:271 |
IPCGElement::Initialize(const FPCGDataCollection&, TWeakObjectPtr<UPCGComponent>, const UPCGNode*) | Initialize(const FPCGInitializeElementParams&) | UE_DEPRECATED(5.6) in PCGElement.h:147 |
FPCGContext::SourceComponent | FPCGContext::ExecutionSource | UE_DEPRECATED(5.6) in PCGContext.h:122 |
FPCGContext::GetComponentName() | GetExecutionSourceName() | UE_DEPRECATED(5.6) in PCGContext.h:207 |
FPCGContext::Stack | GetStack() | UE_DEPRECATED(5.6) in PCGContext.h:147 |
UPCGSpatialData::IntersectWith/ProjectOn/UnionWith/Subtract/CopyInternal without a context | overloads taking FPCGContext* | UE_DEPRECATED(5.5) in Data/PCGSpatialData.h:208-280 |
ToPointData() (no context) | ToBasePointData(FPCGContext*) / ToBasePointDataWithContext | DeprecatedFunction in Data/PCGSpatialData.h:159 |
UPCGPointData::GetOctree() / IsOctreeDirty() | GetPointOctree() / IsPointOctreeDirty() | UE_DEPRECATED(5.6) in Data/PCGPointData.h:145,147 |
UPCGComponent::CleanupLocal(bRemoveComponents, bSave) | CleanupLocal(bRemoveComponents) | UE_DEPRECATED(5.6) in PCGComponent.h:236 |
UPCGComponent::GenerateLocal(..., EPCGHiGenGrid Grid, ...) | overload taking a uint32 grid size | UE_DEPRECATED(5.8) in PCGComponent.h:872 |
UPCGGraph::HiGenExponential / GetGridExponential() | HiGenGridSizeMultiplier / GetGridSizeMultiplier() | UE_DEPRECATED(5.8) in PCGGraph.h:866,872 |
UPCGGraph::bIsEditorOnly | ShouldCook (FPerPlatformBool) | UE_DEPRECATED(5.8) in PCGGraph.h:868 |
UPCGSettings::BP_GetTypeUnionOfIncidentEdges | GetTypeUnionIDOfIncidentEdges | UE_DEPRECATED(5.7) in PCGSettings.h:520 |
UInstancedStaticMeshComponent::InstanceBodies | GetInstanceBodies() | UE_DEPRECATED(5.8) in Components/InstancedStaticMeshComponent.h:524 |
UInstancedStaticMeshComponent::InitInstanceBody | InstancePhysicsBodies | UE_DEPRECATED(5.8) in Components/InstancedStaticMeshComponent.h:771 |
ApplyPerlinNoiseToMesh | ApplyPerlinNoiseToMesh2 | UE_DEPRECATED(5.7) in GeometryScript/MeshDeformFunctions.h:401 |
CopyMeshToStaticMesh without bUseSectionMaterials | overload taking bUseSectionMaterials | UE_DEPRECATED(5.5) in GeometryScript/MeshAssetFunctions.h:311 |
Iterating FPCGPoint arrays in a custom element: GetPoints() only exists on UPCGPointData, so PCG silently converts every input and you pay a full copy. Override SupportsBasePointDataInputs to return true and read FConstPCGPointValueRanges.
Forgetting AllocateProperties before writing: FPCGPointValueRanges built with bAllocate = false leaves unallocated ranges empty, so indexing them fails the checkf range check (Utils/PCGValueRange.h:139). Call SetNumPoints then AllocateProperties for every property you intend to write.
GenerateLocal in multiplayer: it is not a network function, so clients never generate. Use Generate(bool bForce) (NetMulticast, Reliable) and replicate the seed.
Generating from Tick: PCG generation schedules graph tasks. Use GenerateOnDemand and call Generate only when inputs change, or GenerateAtRuntime and let the scheduler budget it.
Caching a node that spawns actors: leaving IsCacheable at true for a node that creates actors or components produces duplicated or missing artifacts on regeneration. Return false.
Expecting UpdateMeshSection_LinearColor to change topology: it only rewrites existing vertices. Adding or removing triangles requires CreateMeshSection_LinearColor.
Clockwise triangle winding: front faces are counter-clockwise, so clockwise triangles vanish under back-face culling.
Calling AddSplinePoint with bUpdateSpline = true in a loop: each call rebuilds the whole reparameterization table. Pass false and call UpdateSpline() once.
Spacing instances by spline input key: the key is not proportional to arc length. Step by distance and use GetTransformAtDistanceAlongSpline.
bMarkRenderStateDirty = true on every instance update: each call re-uploads the instance buffer. Leave it false in the loop and call MarkRenderStateDirty() once.
Expecting Nanite from UProceduralMeshComponent: it has no Nanite path. Bake to a UStaticMesh in the editor with CopyMeshToStaticMesh (editor-only) or scatter Nanite static meshes with PCG and instanced components.
ue-actor-component-architecture — component construction, registration, attachment and lifecycle for the components created hereue-physics-collision — collision profiles, body setup, complex vs simple collision, traces used to project scatter onto terrainue-materials-rendering — material instances, PerInstanceCustomData, Nanite and virtual texturing for generated geometryue-world-level-streaming — World Partition, data layers and HLODs that PCG partitioning and runtime generation build onue-async-threading — ParallelFor, Async, task graph and thread-safety rules for background mesh and point computationue-mass-entity — large agent populations, an alternative to instanced components for crowdsue-data-assets-tables — data assets and data tables that drive generation parameters and mesh/prop tablesue-niagara-effects — Niagara systems, user parameters, data interfaces and data channels© quodsoler, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
SKILL.md and 2 other files (references) in skills/ue-procedural-generation of quodsoler/unreal-engine-skills.
Open the folder on GitHubat commit f3742d7
Ue Procedural Generation next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Ue Procedural Generation this skillquodsoler/unreal-engine-skills | 362 | — | ~7.6k | Automated safety check: Pass | MIT | |
| Ue Specifiersfjz13/UnrealSpecifiers | 1.1k | — | ~1.4k | Automated safety check: Pass | Custom licence | |
| Unreal Engine MCPEpicGames/unreal-engine-skills-for-claude-code-plugin | 338 | — | ~1.8k | Automated safety check: Pass | MIT | |
| Unreal Live CodingItalink/UnrealClientProtocol | 127 | — | ~1k | Automated safety check: Pass | MIT | |
| Unreal Cpp Gameplayukanwat/overtime | 387 | 1 repos | ~1.5k | Automated safety check: Pass | Apache-2.0 | |
| Unreal Enhanced Inputukanwat/overtime | 387 | 1 repos | ~1.5k | Automated safety check: Pass | Apache-2.0 |
fjz13/UnrealSpecifiers
A skill your agent uses when writing, editing, reviewing, validating, or explaining Unreal Engine C++ reflected declarations: UCLASS, USTRUCT, UENUM, UINTERFACE, UPROPERTY, UFUNCTION, UPARAM, UMETA…
EpicGames/unreal-engine-skills-for-claude-code-plugin
Drives a live Unreal Editor through the unreal-mcp server: spawn actors, edit Blueprints, materials and sequences, change properties and trigger Live Coding.
Italink/UnrealClientProtocol
Trigger Live Coding compilation via UCP. An agent skill from Italink/UnrealClientProtocol.
ukanwat/overtime
Write Unreal Engine 5 C++ gameplay code: the UCLASS/UPROPERTY/UFUNCTION reflection macros, the Gameplay Framework (GameMode, Pawn, Character, PlayerController, Actor components), and the module…
ukanwat/overtime
Set up player input in Unreal Engine 5 with Enhanced Input: Input Actions, Input Mapping Contexts, modifiers and triggers, adding the mapping context, and binding actions by ETriggerEvent.
ukanwat/overtime
Create and control VFX in Unreal Engine 5 with Niagara: systems and emitters, modules and the spawn/update stages, exposed User parameters, and spawning or driving effects from Blueprints or C++.
quodsoler/unreal-engine-skills
Unreal Engine C++ guidance for AActor and UActorComponent: lifecycle hooks, spawning, component creation and attachment, ticking, interfaces and composition.
quodsoler/unreal-engine-skills
Guides Unreal Engine C++ AI work across AI controllers, behavior trees, blackboards, perception, navmesh pathfinding, EQS and Smart Objects.
quodsoler/unreal-engine-skills
Reference for writing and debugging Unreal Engine 5.8 animation C++: AnimInstance and proxy, montages, notifies, curves, blend spaces, state machines, linked layers and root motion.
quodsoler/unreal-engine-skills
Picks and applies the right Unreal Engine C++ concurrency API, from UE::Tasks and FPipe to ParallelFor, FRunnable, locks and timers, with game-thread safety rules.
quodsoler/unreal-engine-skills
A skill your agent uses when playing, mixing or debugging sound in Unreal Engine — one-shot SFX, looping ambience, music, dialogue or procedural audio.
quodsoler/unreal-engine-skills
Reference for exposing Unreal Engine C++ to Blueprint and calling Blueprint from C++, covering UFUNCTION and UPROPERTY specifiers, latent and async nodes, and interfaces.
Works with
Categories
A skill your agent uses when generating world content or geometry procedurally in Unreal Engine: PCG graphs, custom PCG nodes in C++, runtime mesh building, instancing and seeded noise. Ue Procedural Generation is an agent skill from quodsoler/unreal-engine-skills. Use when generating world content or geometry procedurally in Unreal Engine: PCG graphs, custom PCG nodes in C++, runtime mesh building, instancing and seeded noise.
Ue Procedural Generation fits situations like: generating world content; geometry procedurally in Unreal Engine: PCG graphs; custom PCG nodes in C++; runtime mesh building.
Run `npx skills add quodsoler/unreal-engine-skills --skill ue-procedural-generation -a claude-code`. Or copy the skill folder (skills/ue-procedural-generation in quodsoler/unreal-engine-skills) into .claude/skills/ue-procedural-generation in your project. Claude Code loads it when a task matches its description.
Run `npx skills add quodsoler/unreal-engine-skills --skill ue-procedural-generation -a codex`. Or copy the skill folder (skills/ue-procedural-generation in quodsoler/unreal-engine-skills) into .agents/skills/ue-procedural-generation in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add quodsoler/unreal-engine-skills --skill ue-procedural-generation -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/ue-procedural-generation, .gemini/skills/ue-procedural-generation, .github/skills/ue-procedural-generation and .opencode/skills/ue-procedural-generation in your project.
SKILL.md names no scripts, command-line tools or credentials: Ue Procedural Generation is instructions for the agent only.
SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Ue Procedural Generation is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 7.6k tokens (SKILL.md is roughly 30k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full. Its references folder adds about 17k tokens, read only when the agent opens those files.
Skills that share tags, products or a category with Ue Procedural Generation: Ue Specifiers (fjz13/UnrealSpecifiers, 1.1k stars), Unreal Engine MCP (EpicGames/unreal-engine-skills-for-claude-code-plugin, 338 stars), Unreal Live Coding (Italink/UnrealClientProtocol, 127 stars) and Unreal Cpp Gameplay (ukanwat/overtime, 387 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
quodsoler (a GitHub user) maintains it in quodsoler/unreal-engine-skills, which has 362 GitHub stars. The repository holds 31 skills in this directory. The repository was last updated on September 28, 2026.
Source: quodsoler/unreal-engine-skills on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.