Build at scale. Keep every piece.
A modular C++ building system for Unreal Engine 5. Geometry stays lightweight as instanced meshes. Interactive pieces become Actors only when they actually need behavior.
Why it is built this way
Building systems usually hit the same wall: make every wall, floor and foundation a full Actor, and performance collapses after a few thousand pieces. Aukke Building System asks one question when a piece is placed: does it need runtime behavior, or is it just geometry?
If it is geometry only, it becomes an entry in an instanced static mesh component, tracked by a GUID. If it needs interaction or custom logic, it becomes a proper Actor. Both representations live in the same registry, so game code can query them the same way.
Everything the game needs to know about a placed piece—health, type, transform and attachment chain—lives in UBuildingRegistrySubsystem. Query the registry, not the Actor: a piece might not have one.
Getting started
Requirements
| Requirement | Value |
|---|---|
| Engine | Unreal Engine 5.8 |
| Dependency | GameplayTags, enabled automatically |
| Platform | Windows for now |
Install the plugin
- Copy the
AukkeBuildingSystemfolder into your project’sPlugins/directory. - Rebuild, or open the
.uprojectand accept the Editor rebuild prompt. - Confirm that the plugin is enabled under Edit > Plugins.
Content/Demo/Level/L_BuildingMap. It is the reference implementation for the systems described here.Wire up your own build mode
- Create a piece Blueprint based on
ABuildingPiece. Assign a mesh, aPieceTypetag and snap points. - Create a hologram Blueprint, or use the base class, and assign valid and invalid materials.
- Add
UBuildingComponentto your Character and set itsHologramClass. - Bind input for build mode, piece selection, placement, demolish mode and demolition.
| Action | Call |
|---|---|
| Toggle build mode | SetBuildingMode(Placing) / SetBuildingMode(None) |
| Cycle piece | SetCurrentPieceClass(NextClass) |
| Confirm | TryConfirmPlacement() |
| Toggle demolish mode | SetBuildingMode(Demolishing) / SetBuildingMode(None) |
| Demolish | TryDemolishPiece() |
Enter build mode, select a piece, aim and confirm. That is the complete player-facing loop.
Core concepts
Building pieces
ABuildingPiece is the abstract base class for every wall, floor, foundation, door and window.
| Property | What it controls |
|---|---|
Mesh | The visible static mesh. Offset it from PieceRoot when its source pivot is not the desired snap origin. |
PieceType | The gameplay tag that identifies the piece. |
SnapPoints | Locations where compatible pieces can attach. |
SnapSearchRadius | How far the hologram searches for a compatible snap point. |
MaxHealth / CurrentHealth | The piece health pool. Zero destroys the piece. |
AllowedFreePlacementSurfaces / bAllowPlacementOnGenericSurfaces | Surfaces accepted when no snap point is used. |
RequiredGroundClearance | Prevents a piece from floating over an unsupported gap. |
bAutoOccupancyCheckExtent / OccupancyCheckBox | The overlap volume used to prevent invalid placement. Disable automatic sizing for irregular meshes. |
IgnoredOverlapTypes | Piece types that may overlap without blocking placement. |
bAllowFlipToFaceTrace | Lets a snapped piece flip 180 degrees to face the camera. |
Editor-only helpers bAutoCenterMeshX/Y/Z can center mesh bounds on PieceRoot. For multi-leg pieces, bCheckPieceRootClearance and GroundClearanceCheckPoints define the exact support points to test.
The Actor versus instance decision
UBuildingComponent::TryConfirmPlacement checks whether the piece class implements IInteractable.
| Implements IInteractable | Does not | |
|---|---|---|
| Becomes | A real ABuildingPiece Actor | An ISM instance tracked by GUID |
| Use for | Doors, chests, levers and per-instance logic | Walls, floors, foundations, roofs and stairs |
| Cost | A full Actor and components | A small instance entry; thousands are affordable |
IInteractable “just in case”. Every class that does pays the Actor cost for every placed piece.Damage and demolition
Demolition is player-driven through TryDemolishPiece. It bypasses health and is gated by CanDemolishPiece.
Damage uses ABuildingPiece::ApplyDamage, clamps CurrentHealth, broadcasts OnHealthChanged, and destroys the piece at zero. The engine’s TakeDamage path is supported, so UGameplayStatics::ApplyDamage works for Actor-backed pieces.
When only a trace hit is available, call UBuildingCombatLibrary::ApplyDamageToHitResult. It resolves whether the hit belongs to an Actor or an instance. For a game-wide destruction event covering both representations, bind to UBuildingRegistrySubsystem::OnAnyPieceDestroyed.
Snap points, tags and placement rules
An FBuildingSnapPoint is a compatible attachment location on a piece.
| Field | Purpose |
|---|---|
RelativeTransform | Location and rotation relative to the owning piece. |
AcceptsTypes | Gameplay-tag filter for accepted piece types. |
bIsOccupied | Whether another piece is currently attached; managed automatically. |
Snap-point helpers bAutoCenterX/Y/Z keep points aligned to changing mesh bounds. Native tags ship below Building.Piece.* for foundation, floor, wall, doorframe, windowframe, roof, door, window and stair, with wood and stone examples. Extend them with native tags or through Project Settings > GameplayTags.
UBuildingPieceRules is an optional data asset for required-parent rules such as “a door requires a doorframe”. Assign it to the hologram’s PlacementRules. Omitted piece types remain unrestricted.
The runtime systems
The hologram
ABuildingHologram mirrors the selected PieceClass: mesh, offset, snap radius, occupancy and clearance settings. It traces up to TraceDistance every frame, or on the configured PlacementUpdateInterval, finds compatible snap points, checks blocking and reports through IsPlacementValid().
Assign ValidMaterial and InvalidMaterial for visual feedback. The class can be extended with particles, audio and UI. The current attachment is exposed through GetMatchedParentPiece(), GetMatchedParentPieceID(), IsMatchedParentInstance() and GetMatchedSnapPointIndex().
The Pawn component
UBuildingComponent drives placement and demolition from your own input bindings. It does not depend on Enhanced Input.
| Function | Purpose |
|---|---|
EnterBuildMode() / ExitBuildMode() | Spawn or destroy the hologram. |
SetCurrentPieceClass(Class) | Switch the selected piece. |
TryConfirmPlacement() | Place the real piece when validation succeeds. |
EnterDemolishMode() / ExitDemolishMode() | Start or stop demolition targeting. |
TryDemolishPiece() | Destroy the target when CanDemolishPiece agrees. |
SetBuildingMode(EBuildingMode) | Single switch for all modes. |
IsInBuildMode() / IsInDemolishMode() / GetBuildingMode() | Read the active mode for UI and gameplay. |
CurrentDemolishTarget and CurrentDemolishTargetID expose the current target. Feedback delegates include OnPiecePlaced, OnPieceDemolished and OnDemolishTargetChanged. Instance highlighting uses SetPieceInstanceCustomDataValue.
The registry
UBuildingRegistrySubsystem is created automatically once per world. It is the source of truth for both Actors and instances.
FBuildingPieceData Data;
if (Registry->GetPieceData(SomePieceID, Data))
{
// Data.CurrentHealth, Data.PieceType, Data.Transform...
}Use HasPiece and GetAllRegisteredPieceIDs for reads. DestroyInstancePiece is the complete removal path for an instance, including its snap data and ISM entry. Delegates OnPieceRegistered, OnPieceUnregistered and OnAnyPieceDestroyed expose world-level lifecycle events.
Chunking
The world is split into a grid of chunks, 5000 units per cell by default. Each active chunk owns one ISM component per distinct mesh used inside it. Chunks are created only when pieces are placed.
UBuildingChunkSubsystem resolves AddPieceInstance, RemovePieceInstance, UpdatePieceInstanceTransform and SetPieceInstanceCustomDataValue, including transparent movement across chunk boundaries. To drive material highlights, configure NumInstanceCustomDataFloats before placing pieces and read the values with a Per Instance Custom Data material node.
Collision channels
Building traces use Visibility by default.
| Property | Owner |
|---|---|
TraceChannel | ABuildingHologram |
DemolishTraceChannel | UBuildingComponent |
InteractionTraceChannel | UInteractionComponent |
Visibility is already used by camera collision or AI, assign these properties to a dedicated trace channel under Project Settings > Collision.Saving & loading
The plugin supplies serializable data, not a complete save-slot system. UBuildingSaveSubsystem::CaptureState fills an FBuildingSaveData; RestoreState rebuilds the world from it. Your own USaveGame decides when and where that data is written.
UPROPERTY()
FBuildingSaveData BuildingData;
// Saving
GetWorld()->GetSubsystem<UBuildingSaveSubsystem>()
->CaptureState(BuildingData);
// Loading
GetWorld()->GetSubsystem<UBuildingSaveSubsystem>()
->RestoreState(BuildingData);Restore order
Restore clears the current state, then rebuilds parents before children so attachment chains remain correct at any depth.
If a piece Blueprint was renamed or removed after a save was created, that piece is skipped and logged without aborting the rest of the restore.
Compatibility across updates
Every capture carries a version number. Older saves, including version zero, still load. A save written by a newer plugin version is rejected before any world state is modified: RestoreState returns false and logs the reason. This prevents partial or corrupt downgrades.
Extending & customizing
Three overridable functions on UBuildingComponent cover common project-specific behavior.
| Hook | Default | Override for |
|---|---|---|
GetTraceStartAndDirection | Owner camera | Top-down view, mouse-to-world or VR controller. |
CanConfirmPlacement(Hologram) | Allowed | Resource cost, cooldown and build permissions. |
CanDemolishPiece(PieceType, Piece) | Allowed | Ownership and refunds. Piece is null for instances. |
bool UMyBuildingComponent::CanConfirmPlacement_Implementation(
const ABuildingHologram* Hologram) const
{
if (!Super::CanConfirmPlacement_Implementation(Hologram))
return false;
return MyInventory->HasEnoughResources(Hologram->PieceClass);
}Blueprint subclasses can override the equivalent events without C++.
Interaction stays separate
IInteractable and UInteractionComponent are independent of the building system. Implement Interact(Instigator, HitComponent) on a door, chest, NPC or any Actor. Add UInteractionComponent to the player, configure InteractionDistance, then call TryInteract() from input. The exact response remains entirely in your Blueprint or C++ class.
Troubleshooting
Logs and debug tools
Failures are reported to LogAukkeBuildingSystem. Use log LogAukkeBuildingSystem Verbose in the console for detailed placement information.
| Toggle | Shows |
|---|---|
ABuildingPiece::bShowSnapPointDebug | Snap points in the editor viewport. |
bShowOccupancyCheckBoxDebug | The occupancy validation volume. |
bShowGroundClearanceDebug | Ground-clearance sample points. |
UBuildingChunkSubsystem::bShowChunkBoundsDebug | Chunk grid boundaries. |
All debug rendering is editor-only and removed from Shipping builds.
A piece will not place
Check that a mesh is assigned, verify whether the piece type requires a parent, inspect ground clearance, then read the log message explaining the rejected validation.
A piece is missing after loading
Its Blueprint was probably renamed or deleted after the save was written. The missing piece is logged and the rest of the save continues loading.
Two touching pieces block each other
The automatic occupancy box is slightly smaller than mesh bounds. If collision bounds still extend past the visible mesh, disable bAutoOccupancyCheckExtent and configure OccupancyCheckBox manually.
Demolish mode targets the wrong object
Move DemolishTraceChannel away from Visibility and use a dedicated project collision channel.
Current limitations
- No networking yet. Placement, demolition and saving are local and non-authoritative. Multiplayer requires an authority layer.
- Windows only for now. The restriction is currently declared in the
.uplugin; the C++ has not been validated on other platforms. - No structural integrity system yet. Removing a support does not automatically collapse descendants.
ParentPieceIDalready stores the attachment chain for a future layer. - No resource costs or permissions by default. Implement them through
CanConfirmPlacementandCanDemolishPiece.
Class index
| Class or type | Role |
|---|---|
ABuildingPiece | Base class for every piece type. |
ABuildingHologram | Placement preview and validation. |
UBuildingComponent | Build and demolition component for a Pawn. |
UBuildingRegistrySubsystem | Source of truth for placed pieces. |
UBuildingChunkSubsystem | Automatic chunked ISM management. |
UBuildingSaveSubsystem | Converts runtime state to and from save data. |
UBuildingPieceRules | Optional required-parent placement rules. |
UBuildingCombatLibrary | Applies damage from a trace hit to either representation. |
IInteractable / UInteractionComponent | Generic interaction independent of building. |
FBuildingPieceData | Per-piece registry and save data. |
FBuildingSnapPoint | One attachment point on a piece. |
FBuildingSaveData | Serializable snapshot of the complete building state. |
