LotManager placed in your level with a valid grid. See the Getting Started guide if you haven't done that yet.
BurbArchitect uses a tile-based grid where every surface — floors, terrain, rooms — is defined on a uniform grid of square tiles. Each tile is further subdivided into four triangles (Top, Right, Bottom, Left) which allows the system to handle diagonal walls, partial floors, and per-triangle pattern assignment.
The key components you'll interact with:
| Component | Role |
|---|---|
UGridComponent | Renders the visual build grid. One draw call per level. |
UFloorComponent | Manages all floor geometry as merged procedural meshes. |
UTerrainComponent | Manages terrain surface geometry and height maps. |
ABuildFloorTool | Player-facing tool for placing and painting floors. |
ABuildBasementTool | Specialized room tool for building below ground. |
ABrushRaiseTool / ABrushLowerTool / etc. | Terrain sculpting brush tools. |
All of these live on or are spawned by the LotManager actor (ALotManager).
The grid is defined by three properties on your LotManager:
| Property | Type | Description |
|---|---|---|
GridSizeX | int32 | Number of tiles along the X axis. |
GridSizeY | int32 | Number of tiles along the Y axis. |
GridTileSize | float | World-unit size of each square tile (default: 100 cm). |
The UGridComponent renders this grid as a merged procedural mesh — one mesh section per level — driven by a material that draws grid lines procedurally. This keeps draw calls minimal even on large lots.
Each floor level has its own grid mesh section. You can show/hide individual levels:
// Show only the current level's grid
GridComponent->SetLevelVisibility(2, true); // Show level 2
GridComponent->SetLevelVisibility(1, false); // Hide level 1
Or toggle the entire grid:
GridComponent->SetGridVisibility(false); // Hide grid (keep boundary lines)
GridComponent->SetGridVisibility(false, true); // Hide grid AND boundary lines (live mode)
Every tile is addressed by (Row, Column, Level). Conversion between world positions and tile coordinates goes through the LotManager:
int32 Row, Column;
bool bFound = LotManager->LocationToTile(WorldPosition, Row, Column);
FVector TileCenter;
LotManager->TileToGridLocation(Level, Row, Column, /*bCenter=*/true, TileCenter);
Each tile is divided into four triangles by its diagonals. This is fundamental to how BurbArchitect handles diagonal walls — a single tile can be split between two rooms, with each triangle independently assigned to a room, floor pattern, or state.
Triangle types are defined in ETriangleType: Top, Right, Bottom, Left.
From the user's perspective, this subdivision is mostly invisible — floors look like solid tiles. It becomes relevant when diagonal walls split a tile or when you use the Single Triangle placement mode.
BurbArchitect uses an integer-based level system where the ground floor is at index equal to the number of basements.
Level 0 → Bottom-most basement (if Basements ≥ 1)
Level 1 → Second basement (if Basements ≥ 2)
...
Level Basements → Ground floor
Level Basements+1 → Second storey
Level Basements+2 → Third storey
...up to Basements + Floors
Example: If Basements = 1 and Floors = 2:
Level switching is controlled through LotManager->SetCurrentLevel(NewLevel) and is typically driven by your game's UI. The CurrentLevel property on LotManager tracks the active level.
When a player switches levels:
UFloorComponent::SetMeshSectionVisible.When viewing basements, the UBurbBasementViewSubsystem automatically hides non-lot actors (trees, props, landscape) while preserving their shadow casting. This gives players a clean underground view while maintaining correct lighting.
UBurbBasementViewSubsystem* BasementView = GetWorld()->GetSubsystem<UBurbBasementViewSubsystem>();
BasementView->SetBasementViewEnabled(true); // Enter basement view
BasementView->SetBasementViewEnabled(false); // Exit basement view
Keep actors visible in basement view by adding the gameplay tag BasementView.AlwaysVisible to any actor. Light actors and anything owned by the LotManager are automatically kept visible.
ABuildFloorTool is the primary tool for placing, replacing, and deleting floor tiles.
The tool creates preview meshes during the drag operation that show exactly what will be placed. Previews are rendered with a slight Z-offset (5 cm) to prevent Z-fighting with existing terrain or floors.
The tool supports two modes, toggled via the PlacementMode property:
| Mode | Enum Value | Behavior |
|---|---|---|
| Full Tile | EFloorPlacementMode::FullTile | Places all 4 triangles per tile (default). |
| Single Triangle | EFloorPlacementMode::SingleTriangle | Places only the triangle under the cursor. |
Toggle from Blueprint (for example, bind to Ctrl+F):
FloorTool->PlacementMode = (FloorTool->PlacementMode == EFloorPlacementMode::FullTile)
? EFloorPlacementMode::SingleTriangle
: EFloorPlacementMode::FullTile;
In Single Triangle mode, the tool tracks CurrentHoveredTriangle — which of the four triangles the cursor is over — and only places that triangle.
Hold Shift while hovering over a room to see a preview of the entire room filled with the current floor pattern. Shift+Click commits the fill.
The auto-fill is room-aware: it respects diagonal wall boundaries and only fills triangles that are geometrically inside the room polygon (using centroid-based polygon testing for each triangle).
Set bDeletionMode = true to switch the tool to deletion. In this mode, clicking/dragging removes floor tiles instead of placing them. The tool queries which triangles exist at each location and deletes all of them.
When dragging within a room, floor placement is constrained to that room. The DragStartRoomID is captured on click, and only tiles belonging to the same room are included during the drag.
Dragging over tiles that already have floors will update their pattern rather than creating duplicate geometry. The system distinguishes between "create" and "update" operations internally and routes them through appropriate commands.
All floor operations are replicated:
Server_BuildFloors / Multicast_BuildFloors — for creation and updateServer_DeleteFloors / Multicast_DeleteFloors — for deletionThe local client creates preview meshes, then on release sends authoritative RPCs through the server to all clients.
UFloorPattern is a data asset (inheriting from UArchitectureItem → UCatalogItem) that defines the visual appearance of a floor tile. Each pattern contains:
| Property | Type | Description |
|---|---|---|
BaseTexture | UTexture* | The diffuse/albedo texture. |
NormalMap | UTexture* | Normal map for surface detail. |
bUseDetailNormal | bool | Enable a secondary detail normal layer. |
DetailNormal | UTexture* | Detail normal map texture. |
DetailNormalIntensity | float | Strength of detail normals (0–32). |
RoughnessMap | UTexture* | Roughness/smoothness map. |
BaseMaterial | UMaterialInstance* | Optional per-pattern base material override. |
bUseColourSwatches | bool | Enable color swatch recoloring. |
bUseColourMask | bool | Use RMA alpha channel as color mask. |
ColourSwatches | TArray<FLinearColor> | Array of available recolor tints. |
FloorPattern in the Content Browser.BaseTexture, NormalMap, RoughnessMap).DefaultFloorPattern or add it to your catalog UI.When a floor is placed, the tool creates a UMaterialInstanceDynamic from the base material and sets these parameters:
| Material Parameter | Type | Source |
|---|---|---|
FloorMaterial | Texture | Pattern->BaseTexture |
FloorNormal | Texture | Pattern->NormalMap |
FloorRoughness | Texture | Pattern->RoughnessMap |
bUseColourSwatches | Scalar (0/1) | Whether swatch tinting is active |
bUseColourMask | Scalar (0/1) | Whether color mask channel is used |
FloorColour | Vector (Linear Color) | The selected swatch color |
ShowGrid | Scalar (0/1) | Grid line overlay visibility |
Your base material must expose these parameter names for patterns to work correctly.
The swatch system allows players to recolor a pattern:
ColourSwatches[Index - 1].Set SelectedSwatchIndex on the floor tool to control which swatch is applied:
FloorTool->SelectedSwatchIndex = 3; // Use the third color swatch
When bUseColourMask is enabled, only the regions defined by the RMA texture's alpha channel are tinted — useful for patterns like hardwood where you want to recolor the wood but not the grout.
The floor tool includes paint-bucket-style pattern detection:
GetDominantPatternAtTile() — Returns the most common pattern at a tile.FloodFillPatternRegion() — Finds all contiguous tiles with the same pattern (tile-level).FloodFillPatternRegionTriangles() — Triangle-level flood fill that correctly handles diagonal walls.These are used internally for intelligent fill operations and are available for Blueprint use.
BurbArchitect includes five terrain sculpting tools. All terrain tools operate only on the ground floor level and use the UTerrainComponent's height map system.
Terrain uses a corner-based height map (FTerrainHeightMap) where heights are stored at tile intersections (corners), not tile centers. This means:
The terrain mesh uses exactly two mesh sections:
| Property | Default | Description |
|---|---|---|
TerrainThickness | 600 | Downward extrusion depth (cm). |
MaxTerrainHeight | 900 | Maximum height above lot base (cm). |
MinTerrainHeight | -(Thickness - 10) | Maximum depth below lot base. |
ABrushRaiseTool)Raises terrain within a circular brush. Click-and-hold to continuously raise.
| Property | Default | Description |
|---|---|---|
Radius | 500 | Brush radius in world units. |
TimerDelay | 0.1s | Interval between terrain updates while held. |
The raise amount per tick uses a span-based falloff calculated from the 9 surrounding tile centers, creating a smooth, natural-looking raise profile rather than hard edges.
ABrushLowerTool)Identical to the Raise Tool but pushes terrain downward. Same properties and brush behavior.
| Property | Default | Description |
|---|---|---|
Radius | 500 | Brush radius in world units. |
TimerDelay | 0.1s | Interval between terrain updates while held. |
ABrushSmoothTool)Uses Laplacian smoothing to blend terrain heights toward the average of their neighbors. Great for softening harsh terrain edits.
| Property | Default | Description |
|---|---|---|
Radius | 500 | Brush radius in world units. |
SmoothingStrength | 0.5 | Blend factor: 0.0 = no change, 1.0 = full average. |
TimerDelay | 0.1s | Interval between smoothing passes while held. |
The smoothing operates on the corner grid, not the tile grid, giving sub-tile precision.
ABrushFlattenTool)Flattens terrain within a brush radius to a sampled target height. On click, the tool samples the terrain height at the cursor position, then all corners within the radius are set to that height as you hold/drag.
| Property | Default | Description |
|---|---|---|
Radius | 200 | Brush radius in world units. |
TimerDelay | 0.1s | Interval between flatten updates while held. |
This is useful for creating flat building pads — click on the height you want, then paint the surrounding area flat.
ABrushFlattenLotTool)A one-click tool that flattens the entire lot to a specified height. Inherits from ABrushFlattenTool but operates on every tile simultaneously.
| Property | Default | Description |
|---|---|---|
DefaultHeight | 0.0 | Target height relative to lot BaseZ. |
bHideDecal | true | Hides the brush circle (not needed for whole-lot ops). |
Use this as a "reset terrain" button or to establish a baseline height.
All brush tools use a UDecalComponent projected downward to show the brush radius. The decal follows the cursor in real-time. Brush radius and visual size are linked — changing Radius automatically scales the decal.
The system automatically flattens terrain under placed floors and along wall segments. This prevents Z-fighting between terrain and floors and ensures clean building foundations:
// These are called automatically by the build pipeline:
TerrainComponent->FlattenTerrainUnderFloor(Level, Row, Column, TileSectionState, TargetHeight);
TerrainComponent->FlattenTerrainAlongWall(Level, StartRow, StartCol, EndRow, EndCol, TargetHeight);
When sculpting terrain, the system uses batch operations to avoid per-tile mesh rebuilds:
TerrainComponent->BeginBatchOperation();
// ... make many terrain changes ...
TerrainComponent->EndBatchOperation(); // Single rebuild for all changes
All built-in brush tools handle this automatically. If you write custom terrain manipulation, wrap your changes in batch operations for performance.
ABuildBasementTool extends ABuildRoomTool (the standard room drawing tool) to support building underground levels.
The basement tool behaves contextually based on which level you're viewing:
| Viewing Level | Build Target | Behavior |
|---|---|---|
Ground floor (Basements) | One level below (top basement) | Draws basement walls while viewing the ground floor above. |
| Any basement level | Current level | Behaves as a normal room tool. |
| Upper floors | Current level | Behaves as a normal room tool. |
This means you can draw a basement layout while looking at the ground floor, which is more intuitive than switching to an empty underground view first.
The tool snaps to the basement-level grid (offset by the level height) but renders wall previews that are visible from the ground floor perspective.
The GetTargetLevel() helper determines where to build:
Ground floor level = LotManager->Basements (e.g., 1 if one basement configured)
If CurrentLevel == GroundFloorLevel → target = GroundFloorLevel - 1
Else → target = CurrentLevel
When switching to view a basement level, enable the UBurbBasementViewSubsystem to get a clean underground view:
auto* Sub = GetWorld()->GetSubsystem<UBurbBasementViewSubsystem>();
Sub->SetBasementViewEnabled(true);
Actor filtering rules (what stays visible in basement view):
BasementView.AlwaysVisible → always visible// Place a floor triangle
UFUNCTION(BlueprintCallable)
void AddFloorTile(const FFloorTileData& TileData, UMaterialInstance* Material);
// Remove a floor triangle
void RemoveFloorTile(int32 Level, int32 Row, int32 Column, ETriangleType Triangle);
// Check if any floor exists at a tile
bool HasAnyFloorTile(int32 Level, int32 Row, int32 Column) const;
// Get which triangles exist at a tile
FTileSectionState GetExistingTriangles(int32 Level, int32 Row, int32 Column) const;
// Find a specific floor triangle (returns nullptr if not found)
FFloorTileData* FindFloorTile(int32 Level, int32 Row, int32 Column, ETriangleType Triangle) const;
// Get all triangles at a tile (up to 4)
TArray<FFloorTileData*> GetAllTrianglesAtTile(int32 Level, int32 Row, int32 Column);
// Batch operations (wrap bulk edits for performance)
UFUNCTION(BlueprintCallable)
void BeginBatchOperation();
UFUNCTION(BlueprintCallable)
void EndBatchOperation();
// Change placement mode
UPROPERTY(BlueprintReadWrite, EditAnywhere)
EFloorPlacementMode PlacementMode;
// Set the active pattern
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly)
UFloorPattern* DefaultFloorPattern;
// Set the active swatch (0 = default, 1+ = swatch index)
UPROPERTY(BlueprintReadWrite)
int32 SelectedSwatchIndex;
// Toggle deletion mode
UPROPERTY(BlueprintReadWrite, EditAnywhere)
bool bDeletionMode;
// Query tiles in a room
UFUNCTION(BlueprintCallable)
TArray<FTileData> GetTilesInRoom(int32 RoomID, int32 Level);
// Get dominant pattern at a tile
UFUNCTION(BlueprintCallable)
UFloorPattern* GetDominantPatternAtTile(int32 Level, int32 Row, int32 Column);
// Flood-fill to find all tiles with matching pattern
UFUNCTION(BlueprintCallable)
TArray<FIntVector> FloodFillPatternRegion(int32 Level, int32 StartRow, int32 StartColumn,
UFloorPattern* TargetPattern, int32 RoomID);
// Create a patterned dynamic material
UFUNCTION(BlueprintCallable)
UMaterialInstanceDynamic* CreatePatternedMaterial(UMaterialInstance* BaseMaterial, UFloorPattern* Pattern);
// Sample terrain height at a tile position
UFUNCTION(BlueprintCallable)
float SampleTerrainElevation(int32 Level, int32 Row, int32 Column);
// Flatten a rectangular region
UFUNCTION(BlueprintCallable)
void FlattenRegion(int32 FromRow, int32 FromCol, int32 ToRow, int32 ToCol,
float TargetHeight, int32 Level);
// Smooth a circular region (Laplacian)
UFUNCTION(BlueprintCallable)
void SmoothCornerRegion(int32 Level, int32 CenterRow, int32 CenterCol,
float Radius, float Strength);
// Flatten terrain under a floor tile
UFUNCTION(BlueprintCallable)
void FlattenTerrainUnderFloor(int32 Level, int32 Row, int32 Column,
const FTileSectionState& TileSectionState,
float TargetHeight, bool bBypassLock = false);
// Flatten terrain along a wall
UFUNCTION(BlueprintCallable)
void FlattenTerrainAlongWall(int32 Level, int32 StartRow, int32 StartColumn,
int32 EndRow, int32 EndColumn,
float TargetHeight, bool bBypassLock = false);
// Remove terrain tiles (for stairs, openings)
UFUNCTION(BlueprintCallable)
void RemoveTerrainTile(int32 Level, int32 Row, int32 Column);
UFUNCTION(BlueprintCallable)
void RemoveTerrainRegion(int32 Level, int32 FromRow, int32 FromCol, int32 ToRow, int32 ToCol);
// Reset entire lot terrain
UFUNCTION(BlueprintCallable)
void DestroyAllTerrain();
// Apply a material to all terrain
UFUNCTION(BlueprintCallable)
void ApplySharedTerrainMaterial(UMaterialInstance* Material);
// Batch operations
UFUNCTION(BlueprintCallable)
void BeginBatchOperation();
UFUNCTION(BlueprintCallable)
void EndBatchOperation();
// Get/create height map for a level
FTerrainHeightMap* GetOrCreateHeightMap(int32 Level);
// Update a single corner height
void UpdateCornerHeight(int32 Level, int32 CornerRow, int32 CornerColumn,
float NewHeight, bool bBypassLock = false);
The FTerrainHeightMap struct provides direct corner manipulation:
FTerrainHeightMap* HeightMap = TerrainComponent->GetOrCreateHeightMap(Level);
float Height = HeightMap->GetCornerHeight(CornerRow, CornerColumn);
HeightMap->SetCornerHeight(CornerRow, CornerColumn, NewHeight);
HeightMap->AdjustCornerHeight(CornerRow, CornerColumn, DeltaHeight);
float CenterHeight = HeightMap->GetTileCenterHeight(TileRow, TileColumn);
// Generate the full grid mesh
UFUNCTION(BlueprintCallable)
void GenerateGridMesh(const TArray<FTileData>& GridData, int32 GridSizeX, int32 GridSizeY,
float GridTileSize, UMaterialInterface* GridMaterial, int32 ExcludeLevel = -1);
// Rebuild a single level's grid
UFUNCTION(BlueprintCallable)
void RebuildGridLevel(int32 Level, const TArray<FTileData>& GridData,
UMaterialInterface* GridMaterial, int32 ExcludeLevel = -1);
// Show/hide the grid
UFUNCTION(BlueprintCallable)
void SetGridVisibility(bool bShowGrid, bool bHideBoundaryLines = false);
// Show/hide a specific level's grid
UFUNCTION(BlueprintCallable)
void SetLevelVisibility(int32 Level, bool bShow);
// Debug: color tiles by room ID
UFUNCTION(BlueprintCallable)
void UpdateAllTileColorsFromRoomIDs(const TArray<FTileData>& GridData);
If you see flickering between floors and terrain, verify that:
FloorTopOffset on UFloorComponent is set to a small positive value (default: 0.1 cm).LotManager->bRemoveTerrainUnderFloors should be true).PreviewZOffset (5 cm) — don't set this to zero.When writing custom terrain code, always wrap changes in batch operations:
TerrainComponent->BeginBatchOperation();
for (/* each tile */)
{
TerrainComponent->UpdateCornerHeight(Level, Row, Col, NewHeight);
}
TerrainComponent->EndBatchOperation(); // Rebuilds mesh once
Without batch operations, each corner change triggers a full mesh rebuild.
All brush tools enforce CurrentLevel == Basements before operating. This is by design — terrain exists only at the ground floor. If you need to modify terrain from a different level context, call TerrainComponent methods directly with the correct level parameter (usually LotManager->Basements).
The FFloorTileData struct stores data per-triangle, not per-tile. When iterating floor data, remember that one visual "tile" can have up to 4 entries in the spatial map. Use GetAllTrianglesAtTile() to retrieve all triangles at a grid position.
If your custom base material doesn't respond to pattern changes, ensure it exposes these exact parameter names:
FloorMaterial (Texture2D)FloorNormal (Texture2D)FloorRoughness (Texture2D)FloorColour (Vector/LinearColor)bUseColourSwatches (Scalar)bUseColourMask (Scalar)ShowGrid (Scalar)Floor and terrain components use packed integer keys for O(1) spatial lookups:
// Floor: 8 bits level | 10 bits row | 10 bits column | 2 bits triangle
int32 Key = UFloorComponent::MakeGridKey(Level, Row, Column, Triangle);
// Terrain: 8 bits level | 12 bits row | 12 bits column
int32 Key = UTerrainComponent::MakeGridKey(Level, Row, Column);
This means floor grids support up to 1024×1024 tiles per level, and terrain supports up to 4096×4096. These limits are more than sufficient for typical lot sizes.
All floor and terrain operations go through the Command system (UFloorCommand, UTerrainCommand) via UBuildServer. Operations are undoable by default when committed through the standard tool pipeline. If you build floors or modify terrain through BuildServer, undo/redo is handled automatically.
Floor tool operations are replicated via Server → Multicast RPCs. The pattern goes:
Server_BuildFloors or Server_DeleteFloors.BuildServer executes the actual commands.Terrain brush tools currently execute locally. If you need replicated terrain, route through your own server RPCs calling the same BuildServer methods.