[UE5] PCG 101: Scatter Trees Automatically and Keep a Path Clear

Created: 2026-07-23Last updated: 2026-09-06

Start by creating candidate points on the ground with PCG and placing trees or boxes. Explains choosing between PCG and Foliage, Debug for inspecting points, excluding steep slopes, and clearing a path with a Spline and Difference, with node connections and diagrams.

After running a path through a forest, you often want to "bend it a bit more to the right". Delete trees near the path and add trees back where it opened up. Tidying placement means repeating that every time you adjust the terrain or path.

PCG is a mechanism for keeping the rules that build placement. Set up "place trees on this ground, but keep steep slopes and the area around the path clear" and you can change the path's shape and regenerate placement by that rule.

This article first gets objects lining up on the ground, then adds "do not place here" conditions. Without tree assets, you can follow the same steps with a box that ships with UE.

The finished result: trees on a hill with a tree-free band running between them

What You'll Learn

  • The relationship between candidate "points" and the meshes placed on them
  • Lining objects up on the ground with a PCG Volume and a graph
  • How to remove candidates on steep slopes
  • Clearing the area around a curve and rebuilding placement when the path changes

Sponsored

PCG is "make points, then place on them"

PCG (Procedural Content Generation) is a mechanism generating content from procedures and conditions. We use it here to place trees on the ground.

What matters is that what you create first is not trees but candidate points where trees go . Points carry not just position but rotation, scale, and other information. Build candidates, remove points where you do not want placement, and finally place trees on what remains.

Three stages: creating candidate points on the ground, removing points around the path, and placing trees on the rest

You build this procedure by connecting nodes in a PCG graph . A node is a part handling one operation such as "make points" or "remove points". Placement data flows along the wires.

Too many trees, reduce the candidates; want a wider path, widen the exclusion. Instead of fixing placement results one tree at a time, you fix the conditions that decide placement . That is where PCG earns its place.

Choosing against the Foliage tool

Foliage also places trees and grass in bulk with a brush. The difference from PCG is less about count and more about "how the placement location is decided".

Foliage painting a range with a brush versus PCG building placement from ground and path conditions
MethodThe operation that decides placementWork it suits
FoliagePaint where to place with a brushAdding roadside grass and rocks while watching the scenery
PCGGraph conditions such as ground, slope, and pathsRebuilding a forest by the same rules while changing paths and terrain

You can build the whole forest with PCG and add grass at the entrance with Foliage, for instance. Note, though, that PCG's exclusion conditions do not delete trees or grass you placed by hand separately . Adjust what each method placed individually.

First PCG: line objects up on the ground

From here we work in the editor. Success is boxes or trees lining up on a corner of the Landscape. We handle steep slopes and paths after confirming things line up.

Prepare the ground and the plugin

Open a level with a Landscape. If you have none, build ground and a small hill following the Landscape introduction. We use one corner of the ground at first.

Enable Procedural Content Generation Framework in "Edit → Plugins" and restart the editor if prompted. PCG became Production-Ready in UE5.7. Below we use that generation's basic nodes.

For what to place, you can use a tree Static Mesh you have. A Static Mesh is a 3D model asset such as a tree or rock. Without material, turn on "Show Engine Content" in the Content Browser settings so Engine/BasicShapes/Cube is selectable. Confirm the placement mechanism with boxes and swap in trees later if you like.

Prepare the volume and the graph

A PCG Volume is the Actor (an object placed in the level) enclosing the range this graph places into. Rather than floating above the ground, place it so the ground is inside the box.

  1. Search for PCG Volume in "Place Actors", put it in the level, and name it PCG_PracticeVolume .
  2. Scale it so it includes part of the flat ground and part of the hill's slope. A few dozen meters is enough at first. Overlap the ground including in height.
  3. Right-click in the Content Browser and create PCG_ForestPractice via "PCG → PCG Graph". If a template picker appears, start from an empty graph.
  4. Select the level's PCG Volume and specify this graph in "Graph" on the PCG component in the Details panel. Turn "Is Partitioned" OFF here for an unpartitioned setup.
A PCG Volume intersecting the ground and the graph assigned to its PCG component

The Volume is the range placed in the level and the graph is the placement rule used within it. They are not the same, so assign the graph to the Volume after creating it .

Connect three nodes

Open PCG_ForestPractice , right-click empty space, and add these three nodes.

Connecting Get Landscape Data's Out into Surface Sampler's Surface and its Out into Static Mesh Spawner's In
NodeIts role hereConnection
Get Landscape DataGets the Landscape's surfaceOut → Surface Sampler's Surface
Surface SamplerCreates candidate points on itOut → Static Mesh Spawner's In
Static Mesh SpawnerPlaces a mesh on each pointSet the asset to place

Select Surface Sampler and start from these values in the Details panel.

SettingStarting valueMeaning
Points Per Squared Meter0.1A guide for candidates per m². Raising it makes it denser
Point Extents50 on X/Y/ZHalf the size of the point's extent. We use the small default here
Looseness1Adds variation to point positions
UnboundedOFFRestricts to the Volume's range

You do not need to connect the "Bounding Shape" input here. With Unbounded OFF and no other range passed in, it uses this PCG Volume's range.

Next select Static Mesh Spawner. Set "Mesh Selector Type" to PCGMeshSelectorWeighted and add one entry with the + on "Mesh Entries". Specify a tree or Cube in its "Descriptor → Static Mesh" and set "Weight" to 1 .

Save the graph, return to the level's PCG Volume, and press "Generate" on the PCG component. The specified mesh lining up on the ground inside the Volume is success. A Cube is placed with its center at the ground position, so seeing it half-buried still confirms candidates were generated. The Input and Output nodes present from the start can stay unconnected in this example.

When nothing appears, look at the "points" before the trees

When nothing appears, you do not have to hunt for the cause all at once. First confirm Surface Sampler is producing points.

Select Surface Sampler in the graph and turn Debug (displaying intermediate results) ON with the D key. Choose PCG_PracticeVolume 's component in the debug target field or "Debug Tree" and Generate. Small box markers appearing in the viewport are the candidates. Pressing D again clears the display.

Checking the ground and range when there are no candidates, and the mesh specification when there are candidates but no objects
  • No points either : check the Volume-ground overlap, the graph assignment, the Get Landscape Data to Surface connection, and the candidate count.
  • Points but no objects : check the Surface Sampler to Spawner connection and the Static Mesh specification in Mesh Entries.

Once it places, change Points Per Squared Meter from 0.1 to 0.05 and Generate. Wider spacing means you can adjust the amount placed.

To try a different arrangement, change the PCG component's Seed . A Seed is the number determining the random placement pattern. Matching terrain, settings, and Seed in the same environment makes the same placement reproducible. Note the Seed once you get an arrangement you like.

Sponsored

Remove candidates on steep slopes

Next we exclude steep slopes where we do not want trees. We use that location's normal . A normal is "the direction extending perpendicular from the ground", straight up on flat ground and angled on a slope. Comparing that direction with straight up selects flat spots.

Flat ground's normal pointing straight up and a slope's normal angled, keeping only candidates on flat spots

Add Normal To Density and Density Filter between Surface Sampler and Static Mesh Spawner.

Normal To Density writes how close the normal is to a specified direction into the point's Density . Density is a value points carry, used here to represent "how close to straight up". It differs from the earlier "how many points per m²" setting.

Passing from Surface Sampler through Normal To Density and Density Filter into Static Mesh Spawner

All connections go from the previous node's Out to the next node's In . Configure Normal To Density like this.

SettingValue
NormalX=0 , Y=0 , Z=1 (straight up)
Offset0
Strength1
Density ModeSet (replace with this value)

Set Density Filter to Lower Bound=0.85 , Upper Bound=1 , Invert Filter=OFF . On versions showing "Keep Zero Density Points" and "Normalize Output Density", turn both OFF. Only points whose value is in the specified range continue.

Generate and see meshes thin out on steep slopes while remaining on flat ground. To restrict to flatter spots, adjust by raising Lower Bound . 0.85 is a starting point; decide while looking at your actual hill.

Stand trees upright and vary their size

Points inherit the ground's orientation too. To stand trees straight up, add Transform Points between Density Filter and the Spawner. That node changes points' rotation and scale. Rewire Density Filter's Out → Transform Points' In and Transform Points' Out → the Spawner's In .

SettingValue
Absolute RotationON (replaces the rotation inherited from the ground)
Rotation Min0 on X/Y/Z
Rotation MaxX=0 , Y=0 , Z=360 (rotate only around the up axis)
Uniform ScaleON (preserves proportions)
Scale Min / Max0.8 on each axis / 1.2 on each axis

The order of checking the ground's slope before changing the trees' rotation matters. Placing Transform Points first and aligning the rotation also changes the orientation used to check the slope.

You now have trees of slightly varied size and rotation, centered on flat ground.

Clear the path area with a Spline

Let's run a tree-free band through the forest using a Spline , a curve you shape by connecting several points, serving here as the "path's center line".

Placing the line alone does not decide the path's width, though. Arranging points along the line and giving each an extent with width creates the band where trees are not placed.

A top view overlapping the square extents of points on the curve into a band that excludes the path area

What this article builds is space where trees do not grow. The ground's color and height do not change. To make it a dirt path, paint it afterwards with Landscape Paint.

Build the path's center line

  1. Create a "Blueprint Class → Actor" in the Content Browser and name it BP_PCGRoad .
  2. Open the Blueprint and add a Spline component with "Add". Turn "Closed Loop" OFF.
  3. In the Details for "Class Defaults", add one entry PCG_Road to the Actor's Tags . That is the marker for finding this Actor from PCG. Put it on the Actor, not the Spline's Component Tags.
  4. Compile, save, and place one inside the level's PCG Volume. Leave the Actor's and Spline's Scale at 1, 1, 1 .
  5. In the level, select and move the Spline's points to make a line crossing the forest. Selecting a point and moving with Alt held adds points so you can bend it.

We draw the path from above here and keep the Spline's points at the same Z height. Keep each point's Scale at 1, 1, 1 too. Putting the whole curve near the hill's base is enough. We build a tall exclusion volume next, so there is no need to trace the terrain's undulations closely.

Adding a Spline to BP_PCGRoad and finding it from the graph via the Actor Tag PCG_Road

Give the line width and height

Back in the PCG graph, build Get Spline Data → Spline Sampler → Bounds Modifier on a separate row from the tree flow. Each connection is OutIn .

Set Get Spline Data's "Actor Filter" to All World Actors , "Actor Selection" to By Tag , and "Actor Selection Tag" to PCG_Road . With one path here, leave "Select Multiple" OFF and "Must Overlap Self" OFF where present.

Set Spline Sampler's "Dimension" to On Spline , "Mode" to Distance , and "Distance Increment" to 100 . UE distances are normally cm, so that creates a point every meter along the line .

Bounds Modifier changes the extent points carry. Set "Mode" to Set and specify these values.

SettingValue
Bounds MinX=-300 , Y=-300 , Z=-3000
Bounds MaxX=300 , Y=300 , Z=3000
Affect SteepnessON
Steepness1

That is a box extending about 3 m to each side and 30 m up and down around each point. Steepness is how the value falls off near the boundary; we use 1 to separate inside from outside clearly. The one-meter-spaced boxes overlap into an exclusion range following the curve.

Connecting Get Spline Data into Spline Sampler and Bounds Modifier to build the path's exclusion range

Because this method overlaps boxes, corners will not be a strictly constant-width band. Use it as a way to clear a roughly 6 m wide walkway . With large tree canopies, widen the X/Y extents for margin.

Pass candidates and the exclusion range into Difference

Finally add Difference , the node that "subtracts a specified range from the original candidates". Connect the two inputs distinctly so it is clear which is subtracted.

Passing Density Filter's candidates into Source and Bounds Modifier's exclusion range into Differences, with the remainder going to Transform Points
Connected fromDifference's input
Density Filter's Out in the tree flowSource (the originals)
Bounds Modifier's Out in the path flowDifferences (what to subtract)

Set Difference's "Density Function" to Binary , "Mode" to Discrete , and "Keep Zero Density Points" OFF. Those settings remove candidates falling in the path's range and pass the rest along.

Disconnect the earlier Density Filter → Transform Points wire and rewire Difference's Out → Transform Points' In . Transform Points → Static Mesh Spawner stays as is.

The flow is now "make candidates from the ground → remove steep slopes → remove the path area → change rotation and scale → place".

Sponsored

Bend the path and rebuild placement

Save the graph and Generate on the PCG Volume. A gap following the Spline among the trees is success. Look from above first and confirm the curve and the cleared area match.

Comparing before and after bending the path's center line and regenerating, with the tree-free band bending too

Next move the Spline's points sideways a little and Generate again. The gap changing with the path's shape means you can adjust the path without fixing trees one by one. Some settings auto-update, but confirm with Generate when you see no change.

Finally look from the player's height. Even without trunks, branches may overhang the path. In that case widen the exclusion range or reduce the tree size. A forest that looks tidy from above shows the margin it actually needs once you walk it.

ProblemWhere to check
Trees on steep slopes tooNormal To Density's settings and Density Filter's range. Transform Points, which aligns rotation, goes after them
The path area does not clearWhether the Actor Tag is PCG_Road and Get Spline Data looks for that tag. Whether Spline Sampler's Debug shows points
It clears on flat ground but trees remain on tall hillsWhether the path boxes reach the ground. Adjust the Spline's height or Bounds Min/Max Z
Grass or trees remain on the cleared pathWhether they are hand-placed Actors or Foliage. Difference targets this graph's placement candidates
So much placement that editing is sluggishShrink the Volume or lower Surface Sampler's Points Per Squared Meter

Bonus: good to know up front

Generate rebuilds the placement. Easy condition changes are PCG's advantage, but wide ranges and many candidates take time to process. Settling the rules on a small range before expanding keeps tuning manageable. We generate in the editor here and do not cover dynamic generation at runtime.

More trees means more rendering cost. Static Mesh Spawner batches the same mesh as instances, but overlapping foliage and shadows are not free. If it gets heavy after expanding, check with stat unit and also tune LOD and culling.

Confirm placement and collision separately. Even with trees visible, whether you pass through depends on the mesh's collision shape and the Spawner's settings. Decide whether they are background forest or trees that block the player, then walk it in game and confirm.

Building this flow once lets you swap trees for rocks or widen the roadside clearance, trying different scenery on the same terrain. Start by moving one path and finding how much margin makes a forest comfortable to walk.

Summary

  • PCG follows "emit points → narrow them → place meshes"
  • Remove steep slopes and the path at the point stage
  • Change the underlying terrain or curve and placement can be rebuilt

The question to ask before using it is "is this important enough to place by hand?" Important things go by hand; things carried by sheer quantity go to PCG.

The cost of drawing many identical objects is in Instancing, and the terrain itself in the Landscape introduction.

Further Reading

Unreal Engine Notes in this section98