An open-source, generalized infinite-worldgen engine for UE 5.8 -- extracted
from the Backrooms project's WORLDGEN V2 architecture
(apps/games/Backrooms/harness/WORLDGEN_V2.md). The goal: let anyone build
their own infinite, procedurally-generated, chunk-streamed space -- a
liminal-space horror game, an open world, anything cell-based -- by authoring
DATA (a district-type table + a few small DNA classes), not by touching this
repo's engine code.
This is a first extraction pass, not a finished toolkit. See "Known gaps" below for an honest list of what is still rough.
1. MacroField District(seed, districtCoord) -> {Type, DNA, LightTint,
CorruptionBias, CeilingScale, IntensityScale}. Pure function,
order-free, infinite. Type chosen by author-defined Rarity +
a deterministic hash, corrected by canon-adjacency scoring
against neighbor districts (WORLDGEN_V2.md Layer 1).
-> Plugins/WorldgenCore/.../WgMacroField.h
2. Graph Portals/adjacency between districts. NOT ported in this pass
-- the streaming shell still guarantees full traversal (every
chunk gets its 8 neighbors built), but there is no explicit
portal-alignment layer yet. See Known gaps.
3. DNA Per-district-type layout generator: one interface,
WallAt(cell,edge) / FloorZ(cell) / CeilingZ(cell), that you
implement once per district TYPE. This is the one layer that
stays code on purpose -- geometry generation genuinely needs
to be code -- but WHICH DNA class runs for a given named type
is a data-table lookup, not an engine change.
-> Plugins/WorldgenCore/.../WgDistrictDNA.h
4. Director Anomaly/story-beat placement per resolved district. Pluggable
interface, ships with a no-op base class so a world with no
Director assigned still boots. NOT wired into the demo in
this pass -- see Known gaps.
-> Plugins/WorldgenCore/.../WgDirector.h
CHUNK (render/stream host, ported from Backrooms proven plumbing):
ask MacroField what district this chunk is in, get its DNA, sample
every cell/edge, emit ISM instances.
-> Plugins/WorldgenCore/.../WgChunkActor.h, WgWorldSubsystem.h
WorldgenKit/
WorldgenKit.uproject -- minimal UE 5.8 project, no authored map
Plugins/WorldgenCore/ -- the reusable engine (this is what you
would drop into your own project)
Source/WorldgenCore/Public/
WgTypes.h -- FWgDistrictCtx, EWgEdge, WgMath::FloorDivI
WgHash.h -- deterministic hash utilities for DNA authors
WgDistrictTypeRow.h -- the DataTable/CSV row struct you author
WgDistrictDNA.h -- the DNA contract (abstract UObject)
WgDirector.h -- the Director contract (abstract UObject)
WgMacroField.h -- UWgMacroFieldResolver (Layer 1)
WgChunkActor.h -- generic chunk actor (streaming host)
WgWorldSubsystem.h -- generic spawn-ring/despawn-budget subsystem
Source/WorldgenKit/ -- the tiny demo GAME module (not part of the
reusable engine -- this is example content)
WgDemoGameMode.h/.cpp -- boots one of the two demo DNA sets
DNA/ -- the six demo DNA classes (3 per set)
Content/WorldgenDemo/DNASets/
DT_LiminalDistricts.csv -- demo Set A
DT_OpenWorldDistricts.csv -- demo Set B
There is no authored map -- same "Generative Law, nothing handcrafted"
pattern as Backrooms: Config/DefaultEngine.ini boots the stock
/Engine/Maps/Entry with WgDemoGameMode, which spawns the world
procedurally at BeginPlay.
$UE = "E:\Programming\tools\UE_5.8\Engine"
$P = "E:\Programming\apps\games\WorldgenKit\WorldgenKit.uproject"
# Compile the editor target
& "$UE\Build\BatchFiles\Build.bat" WorldgenKitEditor Win64 Development -project="$P" -waitmutex
# Play the liminal-space demo set (default)
& "$UE\Binaries\Win64\UnrealEditor.exe" "$P" -game -windowed -resx=1280 -resy=720
# Play the open-world demo set
& "$UE\Binaries\Win64\UnrealEditor.exe" "$P" -game -windowed -resx=1280 -resy=720 -WgDNASet=OpenWorldADefaultPawn (the engine built-in free-fly pawn) is the demo controller --
WASD + mouselook, no gravity. There is no custom character/input code; the
point of this project is the worldgen, not a controller.
Both are driven by the exact same C++ (WgMacroFieldResolver,
AWgChunkActor, UWgWorldSubsystem) -- only the CSV data table and which DNA
classes it points at differ.
Set A -- liminal space (Content/WorldgenDemo/DNASets/DT_LiminalDistricts.csv):
| TypeName | Rarity | DNAClass | What it does |
|---|---|---|---|
| Hallway | 60 | WgDNA_LiminalHall | dense seeded wall grid, small rooms/corridors, flat 300u ceiling |
| StaticVoid | 15 | WgDNA_LiminalVoid | sparse walls, big empty rooms, lower 220u ceiling |
| PoolAtrium | 25 | WgDNA_LiminalPool | sparse perimeter walls, periodic basin footprint dips floor -60u |
Set B -- open world (Content/WorldgenDemo/DNASets/DT_OpenWorldDistricts.csv):
| TypeName | Rarity | DNAClass | What it does |
|---|---|---|---|
| ForestClearing | 55 | WgDNA_ForestClearing | WallAt always false, gentle +/-15u terrain undulation, no ceiling (open sky) |
| RockyOutcrop | 25 | WgDNA_RockyOutcrop | WallAt always false, sharp +/-70u per-cell terrain, no ceiling |
| RiverBank | 20 | WgDNA_RiverBank | WallAt always false, floor dips toward a meandering channel, no ceiling |
The genericness proof is literally in that WallAt column: Set A's types use
walls as their primary structuring tool (indoor maze rooms); Set B's types
never draw a single wall and instead use FloorZ height variation and the
Wg::NoCeilingZ sentinel to read as open outdoor terrain -- same three-function
interface, same chunk actor, same streaming subsystem, completely different
result, driven by which CSV the GameMode points the resolver at
(-WgDNASet=Liminal|OpenWorld, see WgDemoGameMode::InitGame).
- Write one
UWgDistrictDNAsubclass per district type you want (seeSource/WorldgenKit/DNA/*for six worked examples). ImplementWallAt,FloorZ,CeilingZas pure functions of(GlobalCell, Ctx)-- useWgHash::HS01(cellX, cellY, salt, Ctx.Seed)for any per-cell randomness so it stays deterministic. - Author a row per type, either:
- as a
UDataTableasset in-editor (Content Browser -> Miscellaneous -> Data Table -> pick row structWgDistrictTypeRow), the normal UE path, or - as a CSV file in the format the two demo sets use (see the table headers
in
DT_LiminalDistricts.csv) and load it at runtime withUWgMacroFieldResolver::LoadDistrictTypesFromCSV-- convenient for headless/CI runs and for authors who would rather hand-edit a text file.
- as a
- Point
UWgWorldSubsystem::MacroFieldat a configuredUWgMacroFieldResolverbeforeOnWorldBeginPlayruns (seeWgDemoGameMode::InitGamefor the exact hook --InitGameruns during map load, guaranteed before world BeginPlay).
That is the whole authoring loop. No engine code changes.
UWgWorldSubsystem is a generalized port of BackroomsWorldSubsystem's
"kept, proven plumbing" (WORLDGEN_V2.md): the same tuned constants
(SpawnRadius 4, DespawnRadius 5, MaxSpawnsPerTick 2, ShiftUnseenSecs 30 --
Backrooms arrived at these through real playtesting) and the same shape:
- Hard invariant: the pawn's chunk and its 8 neighbors are force-built same-tick, no budget -- makes a reachable streaming edge structurally impossible.
- Budgeted ring spawn: chunks out to SpawnRadius spawn a few per tick (nearest-first), so filling in a big ring never reads as a visible hitch.
- Budgeted despawn: chunks beyond DespawnRadius that have gone unseen for ShiftUnseenSecs despawn a couple per tick.
- Deferred-spawn chunk build:
AWgChunkActor::SpawnChunkDeferredusesSpawnActorDeferred -> Build -> FinishSpawningand Static-root/Movable- children component mobility -- both fixes for real UE engine gotchas (SetStaticMeshsilently refuses on a registered Static component after BeginPlay) that Backrooms hit and documented inBackroomsChunk.h/.cpp.
Dropped from the Backrooms original on purpose (game-specific, out of scope for a generic engine): the Organism/heat attention system, the replicated world-state ledger, multiplayer proxy rings, floor descent. The "mark observed" check here is also simplified to plain chunk-distance; Backrooms adds a forward-view-hemisphere test out to 4 chunks that this pass does not reproduce.
Being honest about what a first extraction pass does NOT yet cover:
- No Graph/portal layer. WORLDGEN_V2.md Layer 2 (guaranteed-aligned doorways between adjacent districts via a shared boundary hash) is not ported. The demo stays fully walkable because the chunk streaming guarantees every neighbor chunk exists, but nothing aligns doorways across a district boundary the way Backrooms' portal hash does.
- Director is unused in the demo.
UWgDirectorexists as an interface and compiles, butWgDemoGameModenever instantiates one or callsPlaceBeats. It is scaffolding proven to compile, not proven to run. - No floor/vertical descent. Everything is one Z band. Backrooms' pit -> floor-N+1 descent mechanic did not make this pass.
- Simplified "seen" check. Plain 2-chunk-radius distance test, not Backrooms' forward-view-hemisphere test -- fine for a free-fly demo pawn, would want revisiting for a real game.
- No collision/geometry polish. The chunk actor draws plain tinted
boxes (walls/floor/ceiling from
/Engine/BasicShapes/Cube) -- there is no door kit, no prop scatter, no material system. This proves the pipeline end to end; it is not a shippable look. - CSV parser is intentionally minimal. No quoted-comma support (the
format sidesteps this by using
;inside the AdjacencyWeights cell, never,) -- fine for this repo's two demo files, would want a real CSV library for arbitrary user data. - Not exercised in a packaged (non-editor) build. The CSV runtime-loader
path (
LoadDistrictTypesFromCSV) does not depend on the editor DataTable importer, so it should work in a packaged build, but this has only actually been run via the editor-launched-gamepath documented above.
- MacroField hashing (
WgHash::H32/HS) is a standard 32-bit integer-hash finalizer (xorshift-multiply mix) ported byte-for-byte fromapps/games/Backrooms/Source/Backrooms/MacroField.h-- this is generic utility math, not creative or copyrighted content. - The Voronoi-feature-point blob-clustering structure (nearest seeded feature
point over a 3x3 neighborhood) is the same algorithmic shape as
MacroField.h/tools/prototype_macrofield.py, generalized to read its type palette from data instead of a hardcoded Surface/Work/Home/DeepRed biome hierarchy + enum. - The chunk deferred-spawn pattern and streaming-subsystem tuning constants
are ported from
BackroomsChunk.h/.cppandBackroomsWorldSubsystem.h/.cppbecause they are documented fixes for real UE engine behavior, not Backrooms-specific content. - Zero Kane Pixels / Backrooms-canon content: no measured room dimensions, no wiki lore, no photoreal texture kit, no door/prop asset references. The demo geometry is placeholder tinted boxes only.
- Zero Tencent / HY-World dependency -- that path was evaluated and rejected before this project started; nothing here touches it.
MIT -- see LICENSE.