Procedural Travel Resolution — From World Coordinates to Loaded Interiors

pavelzosim:~/atlas_●SYS.ONLINE / UTC+3

01 Overview

This article continues my Houdini-to-Unreal procedural level and environment R&D, following Procedural Address Resolution and Typed Placement Routing. The address system establishes which generated building, entrance or apartment a location represents. The next problem was connecting that identity to an interior the player can actually enter.

A canonical address is not a level asset or a spawn transform. Different addresses may use the same prepared interior, while each still needs its own destination context and a return path to the correct door. Keeping every interior loaded just to maintain those relationships would be unnecessary.

The current travel layer creates Portal and Anchor actors from generated door placements, assigns reusable interior levels to canonical addresses, and resolves each request through BP_TravelManager. The destination is loaded when needed, BP_LocationRoot receives the address context, and a matching Anchor determines the arrival position. The return route is resolved from the same relationships.

The implemented route is Street → Entrance → Apartment → Entrance → Street. This stage focuses on correct destination matching, level lifecycle and return behaviour. Visually seamless transitions and measured loading-time guarantees are outside the current scope.

FIG 01 — Procedural travel across addressed interiors. The demonstration shows navigation between generated entrances and apartments, including return routes to their corresponding doors. Watch on YouTube.

02 An address is not an arrival position

A world coordinate answers which place I mean. An arrival anchor answers where the player should stand inside the loaded level. Keeping those responsibilities separate matters as soon as two addresses share an interior asset.

Two entrances can use the same corridor layout while retaining different building and entrance segments. Their apartments still belong to different places. The level asset supplies the space; BP_LocationRoot receives the address context for this visit.

Likewise, a generated door's placement key is a binding to geometry, not its apartment number. The address layer resolves the relationship, and the travel layer uses that result. Moving an arrival point does not require inventing a new apartment identity.

ADDRESS, PLACEMENT AND ARRIVAL — THREE DIFFERENT ROLES
A stable address can use different prepared modular corridor variantsThe HDA produces prepared corridor variants A and B. The variant pool assigns one to Building 035 Entrance 001. LocationRoot receives the same logical address for either choice. A placement key binds to the selected level geometry and an Anchor supplies arrival. Choosing a variant is optional and should serve level design. MODULAR HDA → PREPARED INTERIOR VARIANTSOptional alternatives for the same travel architectureCORRIDOR ACORRIDOR BVARIANT POOL → ASSIGNED LEVELChoose a layout to suit gameplay and level designBuilding 035 / Entrance 001LocationRoot receives this visit's address contextPLACEMENT KEYDoor in the selected geometryARRIVAL ANCHORPosition and orientation
OPTIONAL VARIATIONIllustrative layouts. Different prepared corridors can represent the same address across assignments; different addresses can also reuse one corridor. Geometry binding and physical arrival remain separate from logical identity.

03 Giving generated doors travel behaviour

I already had generated HISM doors and a layer for attaching engine-side behaviour to placements. Instead of hand-placing travel actors for every apartment, MazePlacementExtras creates them through rules on those existing doors.

Each source placement supplies a stable handle and metadata through InitializeFromPlacement. A Portal is the source of a travel request. An Anchor is a candidate arrival point. Door sockets provide their placement: EntrancePortal for the Portal and ExitPortal for the Anchor.

Creation and address configuration are separate steps. Actors can register before the address layer is ready, so pending registration lets them receive their context afterwards. The rules create objects; the address and distribution layers give those objects a route.

FIG 02 — BLUEPRINT SLICE — FROM PLACEMENT TO TRAVEL
BLUEPRINT SLICE — FROM PLACEMENT TO TRAVELConsumer → MazePlacementExtrasExisting HISM door → Portal and Anchor actorsBP_LevelAddressManager + LocationDistributionCanonical address → assigned level and routeBP_TravelPortal → BP_TravelManagerSubmit request → load destination → resolve arrivalBP_LocationRoot + BP_TravelAnchorApply address context → place the player
SLICEResponsibility diagram, not a screenshot of the complete Blueprint implementation.
Unreal Engine Extras rules for Portal and Anchor actors alongside a wireframe entrance interior with travel actors at generated doors
FIG 03 — Portal and Anchor rules on a generated door. Extras configuration and the generated travel actors in the entrance interior.

04 Choosing the interior without changing the address

The next distinction is between an address and the variant assigned to it. Entrance 002 is a place in the building; Entrance_002 is a variant name. In the procedural mode, that entrance can receive either available entrance variant.

LocationDistribution uses an Entrance pool and an Apartment pool. Each entry describes a ready level, its location and arrival identifiers, and the HISM placement used as its entry door. An entrance entry also supplies the preset JSON used for apartment counts.

Selection is deterministic for the run seed, location kind and full canonical address. It is equally weighted and allows reuse. Visiting another entrance first does not change the assignment, and rearranging the pool array does not make an address select a different entry.

The cache stores the complete assigned variant description in SaveGame rather than an array index. Existing assignments survive pool rearrangement and edits; new addresses use the current pool. Applying changed level or entry settings to old addresses requires a new distribution. Persistence can be disabled for a session-only cache.

At present, the Entrance pool contains two levels and the Apartment pool contains one. A different seed can change entrance selection, but it cannot produce visual apartment variation while that pool contains only one level.

FIG 04 — TWO POOLS, TWO ASSIGNMENT STAGES
Entrance assignment precedes apartment assignmentA canonical Building 032 Entrance 001 address selects a corridor from the Entrance pool using run seed and location kind. Its preset JSON supplies apartment count. Counts across the building establish apartment addresses. Each full apartment address then selects from the separate Apartment pool with the run seed. Pools do not create buildings or doors; assignments cache complete definitions. The address is illustrative. BUILDING 032 / ENTRANCE 001Example canonical address · not a variant asset 1 · ENTRANCE POOLL_Entrance_001L_Entrance_002JSON count: 26JSON count: 28ASSIGN CORRIDOR TO ENTRANCE ADDRESSRun seed + Entrance kind + canonical addressAPPLY ALL ENTRANCE COUNTSResolve apartment ranges inside each Building2 · APARTMENT POOLL_Apartment_001 · one current variantPrepared level + configured arrival placementASSIGN INTERIOR TO EACH APARTMENTRun seed + Apartment kind + full apartment addressCache complete variant definition per address
ORDER MATTERSEntrance selection provides counts before apartment addresses and assignments are prepared. Each kind uses its own pool. Preparation does not load interiors. Building 032 is an illustrative parent address, not an Entrance-pool entry.

05 Preparing routes before the first visit

A destination address must be resolved before its interior is loaded. As covered in the address article, preset JSON summaries provide apartment-door counts for each assigned entrance variant. Startup applies those counts before resolving cumulative apartment ranges and assigning apartment variants. No entrance interiors need to be loaded for this preparation.

Once loaded, the corridor HISM reports its actual apartment-door count through BP_LocationRoot. That runtime count can correct the stored summary. Unknown counts remain distinct from a confirmed zero.

06 Resolving a request into a loaded destination

A configured Portal resolves its address context and submits ST_TravelRequest: the destination level, LocationID, AnchorID and optional address context. The Portal supplies the request; BP_TravelManager owns the transaction from RequestTravel to FinishTravelRequest, including its pending state.

The manager loads a streamed destination or resolves an already-loaded or persistent destination. Once the destination is shown, it finds its LocationRoot and applies the coordinate. If the request has no address context, the Root's old context is cleared: a reused interior must not retain the identity of a previous visit.

An identifier alone is not enough for that search. The manager also checks that Root and Anchor belong to the specific destination level. Different maps can share a LocationID; an older loaded Root must not satisfy a new request simply because its identifier matches. Arrival matching checks LocationID and AnchorID, then canonical address equality when the Anchor requires it.

With the Root and matching Anchor resolved, the manager emits OnDestinationReady(ActiveLocationRoot), before player arrival completes. Runtime consumers can subscribe to this readiness signal without putting delivery, run-stage or puzzle logic inside the travel manager. The payload is the LocationRoot, not subsystem-specific data.

The manager then places the player, finishes the request and releases the previous streaming interior when required. Failure and cleanup functions clear unfinished request state. Building, Entrance and Apartment remain meanings supplied by the address layer; generic travel consumes their coordinate through the same request and matching contract.

FIG 05 — TRAVEL REQUEST — LOAD, READINESS, ARRIVAL
Travel request lifecycle and destination readinessPortal → ST_TravelRequestLevel + LocationID + AnchorID + optional addressRequestTravel → destination shownLoad streamed level or resolve loaded destinationLocationRoot → ResolveDestinationAnchorSet / clear context; match within the destinationOnDestinationReady(LocationRoot)Root and Anchor resolved; player arrival not completeCompleteTravelToDestination → finishPlace player; end transaction; release old interior
LIFECYCLEDestination readiness follows successful Root and Anchor resolution. It precedes completed player arrival.

07 Returning to the door that was used

Apartment travel makes the return relationship explicit. In the corridor, a label's metadata.SourcePlacementKey binds its resolved number to the corresponding door. That door's Portal receives the full apartment address and assigned apartment level.

The paired corridor Anchor receives ApartmentReturn_NNN and the parent entrance address. Inside the apartment, the entry Portal returns to the parent's assigned entrance level and that apartment-specific return Anchor. The destination is therefore the original corridor door, rather than a generic entrance spawn point.

The current apartment entry is selected by ArrivalPlacementKey = wall:4 in the pool. This is configured data for the current level, not a universal key. Other internal doors have their routes cleared and collision disabled on their travel Portals, so crossing them does not trigger an unrelated transition.

Entrance exit follows the same principle: its route is configured from the street Anchor with the matching full entrance address. Returning to the street preserves the relationship to the entrance the player used.

FIG 06 — ROUND TRIP — PRESERVING THE PARENT AND DOOR
Entering and returning through the same addressed doorThree separate spaces show street, parent entrance corridor and apartment. Solid teal arrows travel downwards; dashed arrows return upwards to the same door and street Anchor. Text occupies a separate column, away from routes. STREETBuilding 035 · Entrance 001ENTRANCE 001Assigned corridorApartmentReturn_015at the source doorAPARTMENT 015Exit → parent variant+ source-door Anchor 014016APT 015Entry / exitEnter ↓ · ↑ ReturnEnter ↓ · ↑ ReturnSOLID: ENTER · DASHED: RETURNCIRCLES: MATCHING RETURN ANCHORS
ROUND TRIPExample: Building 035 / Entrance 001 / Apartment 015. Illustrative layout, not physical adjacency between streamed levels. Apartment exit resolves the assigned parent entrance and the source door's ApartmentReturn_NNN; entrance exit resolves its matching street Anchor.

08 The route worked; the door still did not

Resolving the destination was only part of making travel work. The remaining failures were in configuration scope, source-geometry collision and player arrival.

A separate failure came from configuration scope. Configuring street Portals could clear a route belonging to a loaded interior. Street entrance configuration now handles only Portals in the street manager's level. Interior travel configuration explicitly sets the entrance exit route and enables its Trigger.

Even with a valid route, the player could not reach that Trigger: the entrance door's convex collision covered the open doorway. Switching that mesh to Use Complex Collision As Simple kept collision on the visible frame and opened the passage for the capsule. The Portal Box uses overlap and does not block the Pawn. This changed collision configuration without changing HDA geometry.

Another issue appeared when loading an apartment level containing 33 internal doors. Those doors are not apartments in the parent entrance. The manager now checks for an Apartment address segment before accepting a local apartment count, preventing the apartment's own geometry from overwriting the entrance count.

Finally, a correct arrival position could still produce a poor return if the capsule sat in the floor or the camera faced the door. Placement now finds the floor below the Anchor, excluding other Pawns as support. For a source-bound pair, yaw points from Portal to Anchor so the player arrives facing away from the door. Character and PlayerController receive that yaw together; pitch, roll and velocity are reset. A manually placed Anchor without a pair uses its own yaw.

09 What has been checked

The 5 October verification record reports a successful Development Editor build and passing PIE scenarios on the real street, entrance and apartment levels. They cover a seeded first visit to Entrance 002, apartment travel and return to the same door, preservation of the parent count, and starting a new run on the street.

The exit checks also move the capsule through the real doorway to the overlap Trigger, verify street return and interior unloading, and check player height and Character/PlayerController orientation. Distribution checks cover visit-order independence, pool rearrangement, SaveGame continuation and reset, and changed JSON counts.

Verification boundary: these checks ran in Editor PIE with null RHI. They confirm travel logic, transforms and scalar numbers. This stage did not include a visual camera/material review in the open editor or a separate packaged build. The media slots in this draft remain capture requests.

10 Result and remaining work

The current system can resolve travel between a street, a generated entrance and a specific apartment, then return to the corresponding source doors. Canonical address identity, the selected interior variant and physical arrival are handled as separate responsibilities. Reusing a level therefore does not require reusing its address or its return destination.

Possible applications

One possible extension is access control. The HDA already accounts for door and key relationships, which could be connected to the travel layer. A generated apartment could have a locked door: the player would need the corresponding key before its Portal submits a travel request. Unlocking it would change the access state without changing the canonical address, assigned interior or return route.

The same boundary could support scripted restrictions or progression rules. A separate gameplay layer would decide whether travel is allowed; the Travel Manager would continue to handle destination loading and arrival. Key checks before travel requests are not integrated or tested as part of the current travel implementation.

FIG 07 — POSSIBLE EXTENSION — ACCESS BEFORE TRAVEL
A proposed access check before entering an addressed apartmentA player in a corridor approaches Apartment 015. Without permission, the locked door denies travel. With the corresponding key, a gameplay access check permits the Portal to submit TravelRequest and the Travel Manager loads the addressed interior. Unlocking changes access state, not address identity. This integration is proposed, not implemented. CORRIDORAPARTMENT 015Building 035 / Entrance 001Same address · same return route Player → doorKeyPortal → TravelRequest LOCKEDUNLOCKEDDeny travel · remain outsideSubmit request → load interiorGAMEPLAY DECIDES ACCESS · TRAVEL RESOLVES THE DESTINATION
POSSIBLE EXTENSIONThe HDA supplies door/key relationships. Connecting an access check before TravelRequest remains proposed; unlocking would change access state while preserving address identity and return routing.

Remaining work

Variant assignment can be refreshed for a new run after the active interior has been unloaded and no travel request is in progress. The travel layer also exposes OnDestinationReady(LocationRoot) so other runtime systems can work with the resolved destination without becoming part of BP_TravelManager. The remaining work includes smoother transitions, broader validation of prepared interior variants and testing outside Editor PIE.

// END OF ARTICLE // EOF