Chain Of Events When Spawning Actors
This page describes what happens when an actor is created via the Spawn() method on a server or client or replicated to a client in a network game. It lists the engine events and the UnrealScript functions subsequently called by those events and gives a short description how these events and functions could be used.
This page is based on a snipped of native UT2004 code posted on the UT2003Mods mailing list. Most parts will be similar in older engine versions, but there might be important differences in the functions called by these events.
Spawn( SpawnClass, // class of actor to spawn SpawnOwner, // what to set the new actor's Owner to SpawnTag, // what to set its Tag to SpawnLocation, // new actor's Location SpawnRotation // new actor's Rotation );
Before The Actor Is Created
Before an actual Actor object is created the native ULevel::SpawnActor() function checks that a class was passed to it and that the class is not abstract and a subclass of actor. Also this class may neither have bStatic nor bNoDelete set to True. The class also won't be spawned when its bHighDetail property is set to True and the LevelInfo's DetailMode is set to DM_Low or the LevelInfo indicates a low framerate with bDropDetail or the actor is spawned on a dedicated server.
The final check before creating the actor makes sure the actor fits in the location it should be spawned at. This check will be run for actor classes that collide with the world and for actors spawned on the server with bCollideWhenPlacing=True.
Chain Of Events
These are the events directly called from native code after the actor has been created. See the sections below for more details about functions called by these events. For actors created with the Spawn() function on this machine (i.e. not actors created because they were received on a client) all these events are executed from the Spawn() function.
- the Actor's initial properties are set:
- the actor's Tag is initialized with the actor's class name
- the actor's Region property (especially its Zone) is initialized with the level's LevelInfo PointRegion
- the actor's Level property is set to the level's LevelInfo
- the actor's XLevel property is set to the Level object itself
- if this actor was replicated to a client, the actor's Role and RemoteRole are exchanged
- the actor's Brush property is set to None if it has one, "because moving brushes are not duplicatable"
- the actor's Location and Rotation are set from the Spawn parameters
- the actor is added to the collision hash if its bCollideActors is set to True (used e.g. by the CollidingActors and VisibleCollidingActors iterator functions) If the actor is touching anything, its Touch() event is called
- the actor's PhysicsVolume is set to the DefaultPhysicsVolume
- the Actor's Owner is set from the Spawn parameters
- the Actor's Instigator is set to the spawning actor's Instigator
- Karma physics are initialized for the actor
- (in UT the actor's Spawned() event is called, in later engine versions this event is no longer available in UnrealScript)
- the Actor's PreBeginPlay() event is called
- the Actor's BeginPlay() event is called
- the Actor's actual zone and PhysicsVolume are set
- encroachment (overlapping with other actors' collision cylinders) is checked for the actor
- the Actor's PostBeginPlay() event is called
- the Actor's SetInitialState() event is called
- the actor's base is set if it's None and the actor has bCollideActors and bShouldBaseAtStartup set to True and its Physics are set to either PHYS_None or PHYS_Rotating
- the Actor's PostNetBeginPlay() event is called if this isn't a replicated actor (i.e. it was created on this machine with the Spawn() function)
- (in UT now all SpawnNotify actors receive a SpawnNotification() event if this actor's class is a subclass of the class specified in their ActorClass property, in later engine versions SpawnNotifies are no longer available)
- the Actor's actual Tag property is set
The chain of events is halted if the actor's bDeleteMe property becomes True during the initialization, i.e. when any of the events or subsequently called functions destroys the actor.
Initial Properties
The actor properties Tag, Region, Level, XLevel, Brush, Location, Rotation and PhysicsVolume are set before any UnrealScript is executed. Note that Region.Zone is set to the LevelInfo, PhysicsVolume is set to the DefaultPhysicsVolume and Tag is set to the actor's class name.
Actor Owner
Before the spawned actor can execute any UnrealScript code its Owner is set. This will call the new owner's GainedChild() event which is the first piece of UnrealScript code executed after the actor was created. At this point the actor's Owner is still None and also its GetStateName() method returns 'None'.
After GainedChild() returns the spawned actor's Instigator is set to the spawning actor's Instigator. (Not the new Owner's Instigator!)
Karma Physics Initialization (UT2003)
Does this call any UnrealScript events?
Actor.Spawned() (UT)
In UT there is the Spawned() event which is called only for actors created via the Spawn() function, not for actors placed by mappers. This is the first UnrealScript event called for the newly spawned actor and allows to execute code before the GameInfo and the mutators can modify the actor.
Actor.PreBeginPlay()
At this point the Owner is properly set, but the actor's GetStateName() method will return the actor's class name.
...in UT2003
If the actor has bGameRelevant == False and this event is not executed on a client, the game's base mutator's CheckRelevance() function is called.
CheckRelevance() first calls the base mutator's AlwaysRelevant() function which recursively calls the other mutators' AlwaysRelevant() functions. If this function returns True the actor is concidered game relevant and CheckRelevance() returns True resulting in the Actor continuing to exist.
If AlwaysRelevant() returns False, the base mutator's IsRelevant() function is called. This function calls the mutator's CheckReplacement() function and returns its result if it's False. Otherwise IsRelevant() returns the result of the next mutator's IsRelevant() function.
Finally the result of the base mutator's IsRelevant() function is returned by CheckRelevance() and again, if that result is True the actor will continue to exist, otherwise it will destroy itself.
...in UT
If the actor has bGameRelevant == False and this event is not executed on a client, the GameInfo's IsRelevant() function is called.
IsRelevant() first calls the base mutator's AlwaysRelevant() function which recursively calls the other mutators' AlwaysRelevant() functions. If this function returns True the actor is concidered game relevant and GameInfo.IsRelevant() returns True resulting in the Actor continuing to exist.
If AlwaysRelevant() returns False, the base mutator's IsRelevant() function is called. This function calls the mutator's CheckReplacement() function and returns its result if it's False. Otherwise IsRelevant() returns the result of the next mutator's IsRelevant() function.
If the base mutator's IsRelevant() function returns False the GameInfo's IsRelevant() function also returns False and the actor destroys itself. Otherwise if the bSuperRelevant property is set to 1 GameInfo.IsRelevant() returns True resulting in the Actor continuing to exist.
If the base mutator's IsRelevant() function returns True but the bSuperRelevant property is not set to 1 the GameInfo's IsRelevant() function checks whether the actor may appear in the current difficulty level, if it's a "monster" (a non-player Pawn (UT)) and if there's a random chance for the actor to not appear in the game (OddsOfAppearing). The GameInfo.IsRelevant() function will also update the number of secret goals, item goals and kill goals before returning True and allowing the actor to exist.
Actor.BeginPlay()
This event is called after an Actor is concidered "relevant" for this game, i.e. no Mutator wanted to get rid of it.
Most actors don't use this event, but if you're looking for an event that is called before the actor is initialized, but after the mutator checks are done, then this is the place for you.
Actor Zone and PhysicsVolume
At this point the Actor.Region.Zone value becomes valid. This event also causes the ZoneChange() event to be called, which sees Region.Zone still as the LevelInfo and the actual zone is passed as the NewZone parameter.
After the zone the PhysicsVolume value becomes valid. This event also causes the PhysicsVolumeChange() event to be called, which sees PhysicsVolume still as the DefaultPhysicsVolume and the actual PhysicsVolume is passed as the NewVolume parameter.
Encroachment Check
Encroachment is when two actors in the collision hash overlap. In UnrealScript there is the EncroachingOn() event which returns whether the overlapping collision hulls with another actor actually needs to be handled and the EncroachedBy() event which is called for the other actor when EncroachingOn() returned True. The newly spawned actor will be destroyed if other colliding actors detect encroachment with this actor.
Actor.PostBeginPlay()
Most actors use PostBeginPlay() to initialize their UnrealScript values. The actor's PhysicsVolume and Zone are valid but the actor is not yet in any state.
Actor.SetInitialState()
At this point the actor is concidered "initialized" by the engine (bScriptInitialized is set to True in this event) and the actor's initial state is set. If the InitialState property is set, it will be used, otherwise the actor goes to its auto state. Since SetInitialState() is a simulated function by default this happens on server and clients.
This event uses GotoState to change the state, so during its executing the initial state's BeginState() event is called. The state's state code will however not yet start executing at this point. This will happen after all the events described on this page have finished doing their work.
Actor Base
Actors with without a Base, bCollideActors = true and bShouldBaseAtStartup = true which use either PHYS_None or PHYS_Rotating try to find a base for themselves.
When a base is set the actor's BaseChange() event will be called.
Actor.PostNetBeginPlay()
PostNetBeginPlay() will only be called for actors created with the Spawn() function. For replicated actors this event will be called (if it's marked as "simulated") by the netcode when the initial variables have been replicated. That means you don't know when it'll happen... but eventually
SpawnNotify.SpawnNotification() (UT)
In UT there are special SpawnNotify actors which receive a SpawnNotification() event if the new actor is a subclass of their configured ActorClass after the new actor has been initialized. The SpawnNotify can modify the actor or even spawn a completely different actor which will be returned by the Spawn() function instead of this actor.
Actor.Tag
The actor's Tag property is set to the value passed to the Spawn() function after all initialization events have been executed. The first place where you can access its final value is the code executed after the Spawn() function that created this actor.