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