Spawner
Overview
Two spawner systems manage actor lifecycle: ACActorSpawner for NPCs/objects and ACPlayerSpawner for players. Both inherit from ACAbstractSpawner (from CCOREUMODULE). The actor spawner uses a cell-based pooling pipeline that activates/deactivates actors based on proximity to a tracked pawn.
ACActorSpawner
Main actor spawner placed in the level. Reads a FCNPCMaker DataTable row, creates a UCActormaker, and drives the generation loop on tick. Requires a ANavMeshBoundsVolume and ARecastNavMesh in the level.
| Name | Description |
|---|---|
| pawnToTrack | Pawn whose location drives actor activation. If null, spawns at origin then stops |
| actorMakerRH | FDataTableRowHandle pointing to a FCNPCMaker row |
| actormaker | Transient reference to the instantiated UCActormaker |
| init | Finds NavMesh volumes, reads DataTable, instantiates the actormaker |
| Tick | Calls actormaker->buildASync with the tracked pawn position |
| clean | Destroys all managed actors |
// ACActorSpawner is placed in the level.
// Configure actorMakerRH to point to a FCNPCMaker DataTable row.
// Assign pawnToTrack to the player pawn.
// Requires ANavMeshBoundsVolume and ARecastNavMesh in the level.
// On Tick, delegates to the actormaker:
actormaker->buildASync(this,
pawnToTrack->GetActorLocation().X,
pawnToTrack->GetActorLocation().Y);UCActormaker
Core actor management object. Distributes actors across room cells, pools spawn/despawn in batches, manages NavMesh bounds, and handles actor lifecycle events.
| Name | Description |
|---|---|
| initActorMap | Iterates rooms, counts eligible actors per maker, creates UCActorInformation entries |
| buildASync | Entry point: runs drawActor async then updateActor_sync on game thread |
| wantDraw | Guards against redundant draws: checks queue and pawn cell movement |
| drawActor | Computes which actors to activate/deactivate based on pawn cell, enqueues batches |
| updateActor_sync | Dequeues one batch, calls popOn/popOff/teleportBack, updates NavMesh |
| popOn | Spawns or reactivates an actor, initializes components, waits for tile then possesses AI |
| popOff | Deactivates an actor, disables tick on all components. Destroys if killed/consumed |
| teleportBack | Teleports an actor back to its original spawn position |
| spawnNewCharacter | Spawns a new actor outside the normal DataTable flow |
| onTileReady | Possesses AI controllers when their tile becomes available |
| onTileRemoved | Deactivates actors on removed tiles |
| onActorEvent | Delegate broadcast on actor state changes (activated, deactivated, killed, consumed, destroyed) |
// Actor lifecycle:
// 1. initActorMap() distributes actors across room cells
// 2. buildASync() computes active set based on pawn proximity
// 3. popOn() spawns actor, calls UCInitializerComponent::init, waits for tile
// 4. When tile is ready (fullLOD), AI controller is possessed
// 5. popOff() deactivates: disables ticks, resets components
// 6. If killed/consumed, popOff() destroys the actor
// Dynamic spawning outside DataTable:
int advIdx = actormaker->spawnNewCharacter(
characterDetailMaker, FVector2D(x, y),
true /*preserveMaker*/, true /*immediateSpawn*/);ACPlayerSpawner
Player spawner. Creates UCPlayermaker instances and manages player switching.
| Name | Description |
|---|---|
| playerMakerRH | Pointer to the current player’s FDataTableRowHandle |
| playermakerMap | Map of player tag to UCPlayermaker instances |
| createPlayerMaker | Creates a new player from the current DataTable row |
| retrieveCurrentPlayer | Returns the UCPlayermaker for the current player tag |
| switchPlayer | Switches to a different player tag, creating if needed, with optional location and blend |
// switchPlayer changes the active player character:
playerSpawner->switchPlayer(
FName("Warrior"), // player tag
FVector2D(1000, 2000), // optional new location
false, // don't retrieve previous location
1.0f, // camera blend time
[](APawn* p) { /* ready callback */ });UCPlayerInitializer
Tickable object created by UCPlayermaker::finishSpawning() that orchestrates the “wait for world readiness then teleport and possess” workflow. Inherits UCdryxTickable and implements ICWorldBoundable, ICRoomBoundable, ICHexaBoundable to receive world/room/tile events.
The initializer binds to RoomUtility::onRoomBuilt and WorldUtility::onTileReady on start, and unbinds on stop. It follows a multi-step workflow: first it waits for rooms to be built, then searches for an eligible room by tag/probability, then waits for the target tile to reach fullLOD before teleporting and possessing the player.
| Name | Description |
|---|---|
| whereToTeleport | Optional target location. If set by caller, skips room search. Otherwise resolved via onRoomBuilt |
| uvIndexToBeTeleported | Tile index at the teleport location, used to listen for onTileReady events |
| pawn | The player pawn to teleport and possess |
| mainTag | Player’s main tag, used for room eligibility check |
| tagArray | Player’s tag array, used for room eligibility probability check |
| whatToDoWhenReady | Callback executed after teleport and possess (sets activated, resets components, sets view target, possesses) |
| initWith | Binds to onRoomBuilt and onTileReady events, then starts ticking |
| stop | Unbinds from onTileReady and onRoomBuilt, then stops ticking |
| onRoomBuilt | If whereToTeleport is already set, proceeds to teleportWorkflow. Otherwise iterates rooms via RoomUtility::iterateOnClusterRoom, finds the first room eligible for mainTag/tagArray via isRoomEligibleForTagProba, sets whereToTeleport to room center. If no eligible room found, defaults to FVector2D::Zero() |
| teleportWorkflow | Checks if the world tile at whereToTeleport is ready via WorldUtility::worldTileReadyAt. If fullLOD: teleport and possess immediately. If tile exists but not fullLOD: teleport only (possess later via onTileReady). If tile not ready: teleport only and wait for onTileReady |
| onTileReady | Called when a tile becomes available. If the tile index matches uvIndexToBeTeleported and is fullLOD: calls teleportAndPossessPlayer. If not fullLOD: calls teleport only |
| teleportAndPossessPlayer | Calls teleport(), executes whatToDoWhenReady callback (which possesses the pawn), then calls stop() to clean up |
| teleport | Computes Z from WorldUtility::zCalculate + capsule half height, then TeleportTo at the target location |
// UCPlayerInitializer workflow:
// 1. Created by UCPlayermaker::finishSpawning() via doForever<UCPlayerInitializer>
// 2. Binds to onRoomBuilt and onTileReady
// 3. onRoomBuilt fires:
// - If whereToTeleport already set: skip room search
// - Else: iterate rooms, find eligible by isRoomEligibleForTagProba(mainTag, tagArray)
// - Set whereToTeleport = room.center (or Zero if none found)
// 4. teleportWorkflow:
// - worldTileReadyAt(x, y) -> get uvIndex and fullLOD status
// - fullLOD: teleportAndPossessPlayer() immediately
// - not fullLOD or not ready: teleport() now, wait for onTileReady
// 5. onTileReady(uvIndex, fullLOD):
// - If matching uvIndex and fullLOD: teleportAndPossessPlayer()
// - If matching but not fullLOD: teleport() only
// 6. teleportAndPossessPlayer():
// - teleport() — Z = zCalculate + capsuleHalfHeight
// - whatToDoWhenReady() — setActivated, resetAll, SetViewTargetWithBlend, Possess
// - stop() — unbind events, destroy tickable
// If finishSpawning is called when rooms are already built:
// onRoomBuilt() is called immediately (not waiting for event)
if (!RoomUtility::roomExists() || RoomUtility::roomBuilt()) {
playerInitializer->onRoomBuilt();
}UCPlayermaker
Per-player instance. Creates the player pawn, manages teleportation to eligible rooms, and handles possession.
| Name | Description |
|---|---|
| playerMaker | FCPlayerMakerStruct configuration |
| playerAsCharInformation | Runtime UCActorInformation for the player |
| pawn | The spawned player pawn |
| createPlayer | Spawns the player actor and initializes components |
| finishSpawning | Starts the teleport-and-possess workflow via UCPlayerInitializer |
| clean | Unpossesses and destroys the player actor |
// Player creation flow:
// 1. createPlayer() spawns the pawn from FCCharacterDetailMaker
// 2. finishSpawning() creates a UCPlayerInitializer tickable
// 3. UCPlayerInitializer waits for room and tile readiness
// 4. When ready: teleports pawn, sets view target, possesses
playermaker->createPlayer(playermakerMap);
playermaker->finishSpawning(
FVector2D(0, 0), // optional location
[](APawn* p) {}, // ready callback
1.0f); // blend time