How do I know when all Actors placed in a map have been actually spawned?

Goal: I want to count how many Actors of a given class have been placed, in-editor, within a certain volume.

How I’d like to do it: by having a trigger box that counts those Actors by using AActor::GetOverlappingActors.

The issue: I’m running that count in my trigger box BeginPlay(). However, what gets counted are only the Actors that have been placed in a given area *before *the trigger box had been placed there. I guess this is happening because my trigger box BeginPlay() runs before the Actors have actually spawned in game.

The question: how do I know when all placed Actors have actually been spawned, so I can run my count only then?

For reference, here’s my code:



void myTriggerBox::BeginPlay()
{
Super::BeginPlay();

// counts amount of "Poison" in the trigger
TArray<AActor*> OverlappingActors;
GetOverlappingActors(OverlappingActors, AGenericItem::StaticClass());
for (TArray<AActor*>::TIterator it = OverlappingActors.CreateIterator(); it; ++it) {
  [INDENT=2]if (Cast<AGenericItem>(*it)->Name == "Poison") [/INDENT]
  [INDENT=3]PoisonAmount++;[/INDENT]
  }
 
}


What happens is that if I place my GenericItem called “Poison” in editor before I place the trigger box, that gets counter. Otherwise, it doesn’t.

Any help would be greatly appreciated!

Thanks!

This is in the actor file


 
[QUOTE]

/**
* Actor is the base class for an Object that can be placed or spawned in a level.
* Actors may contain a collection of ActorComponents, which can be used to control how actors move, how they are rendered, etc.
* The other main function of an Actor is the replication of properties and function calls across the network during play.
*
*
* Actor initialization has multiple steps, here's the order of important virtual functions that get called:

* - UObject::PostLoad: For actors statically placed in a level, the normal UObject PostLoad gets called both in the editor and during gameplay.
* This is not called for newly spawned actors.

* - UActorComponent::OnComponentCreated: When an actor is spawned in the editor or during gameplay, this gets called for any native components.
* For blueprint-created components, this gets called during construction for that component.
* This is not called for components loaded from a level.

* - AActor::PreRegisterAllComponents: For statically placed actors and spawned actors that have native root components, this gets called now.
* For blueprint actors without a native root component, these registration functions get called later during construction.

* - UActorComponent::RegisterComponent: All components are registered in editor and at runtime, this creates their physical/visual representation.
* These calls may be distributed over multiple frames, but are always after PreRegisterAllComponents.
* This may also get called later on after an UnregisterComponent call removes it from the world.

* - AActor::PostRegisterAllComponents: Called for all actors both in the editor and in gameplay, this is the last function that is called in all cases.

* - AActor::PostActorCreated: When an actor is created in the editor or during gameplay, this gets called right before construction.
* This is not called for components loaded from a level.

* - AActor::UserConstructionScript: Called for blueprints that implement a construction script.

* - AActor::OnConstruction: Called at the end of ExecuteConstruction, which calls the blueprint construction script.
* This is called after all blueprint-created components are fully created and registered.
* This is only called during gameplay for spawned actors, and may get rerun in the editor when changing blueprints.

* - AActor::PreInitializeComponents: Called before InitializeComponent is called on the actor's components.
* This is only called during gameplay and in certain editor preview windows.

* - UActorComponent::Activate: This will be called only if the component has bAutoActivate set.
* It will also got called later on if a component is manually activated.

* - UActorComponent::InitializeComponent: This will be called only if the component has bWantsInitializeComponentSet.
* This only happens once per gameplay session.

* - AActor::PostInitializeComponents: Called after the actor's components have been initialized, only during gameplay and some editor previews.

* - AActor::BeginPlay: Called when the level starts ticking, only during actual gameplay.

* This normally happens right after PostInitializeComponents but can be delayed for networked or child actors.
*
* @see [https://docs.unrealengine.com/latest...ecture/Actors/](https://docs.unrealengine.com/latest/INT/Programming/UnrealArchitecture/Actors/)
* @see [https://docs.unrealengine.com/en-us/...ActorLifecycle](https://docs.unrealengine.com/en-us/Programming/UnrealArchitecture/Actors/ActorLifecycle)
* @see UActorComponent
*/

[/QUOTE]


This is in the world.h file. Not sure if it will do what you want until you try it out.


// Group actors currently "active"
UPROPERTY(transient)
TArray<AActor*> ActiveGroupActors;

TL’DR Problem NOT solved!

Thank you for this, the initialization sequence of an Actor was indeed the first thing I checked, but unless I’m missing something (and I’d be glad if you could point that out!) it’s not really useful – it refers to the lifecycle of a single actor, saying nothing about its relationships to the lifecycle of other actors.

Maybe there’s an event that gets triggered somewhere when* all* actors placed in a map have actually spawned, so that I can call a function that counts them on that event’s call?

try the bottom code from world.h in above post see if that will work.

Thank you again! Unfortunately that did go nowhere. (1) I do not understand how to follow up on your suggestion. I have limitation in understanding what the array you’re pointing to actually means, and how you suggest I use it to my end. (2) However I followed up the hint of checking out UWorld. The function [FONT=Courier New]UWorld::AreActorsInitialized()](UWorld::AreActorsInitialized | Unreal Engine Documentation)sounded promising, but it actually doesn’t do the job. It returns “true” even when the actors placed in the level *after *I’ve placed the trigger do not actually get counted.

TL’DR issue not solved yet :frowning:

Its a hierarchy problem then

And so, any clue on how to solve it? :slight_smile:

For the moment, I’m using this workaround: I put my counting function on a timer, and kick it off [3] seconds after BeginPlay() has been called on my trigger.

It does the job, but it’s extremely inelegant.

There has to be a way of knowing when all Actors placed in a level/world are actually there!

1 Like

I had to do the same with last project for loading a menu. To get the menu to show up. I had to delay it on a timer for 0.7 seconds. Other wise BeginPlay() would not finish. Once that function finished then i could pop up my menu. Sounds like the same thing. the only fix i found was delaying the call until BeginPlay(); finished its run thru. Then it would work. Sounds just like that.

What i am working on will need same kind of code in next few days.
If you figure it out, post here.
If i figure it out, i will post back here.

We need a PostBeginPlay(); that gets called after BeginPlay(); Then we should be able to use that?
Thanks

BeginPlay is initially triggered by the GameMode when the “match” starts. You can quite easily inject game-specific code after that call. This is all part of the Gameplay Framework.

EDIT: AGameModeBase begins play straight away. AGameMode begins play when the match starts.

@ can you elaborate on your statement? Consider what I’m referring to it’s a very simple single-player situation – no concept of “match”. But it seems you’re implying there’s someplace, within GameMode, where you can call a function being sure all actors placed in a map have been spawn and gone through their respective BeginPlay(). Ís that the case? If that’s true, how do you actually do that? Can you post an example, or just be specific about where to call that function?

@gamepainters ofc let’s post here the “right” solution if we’ll ever find it. Although by now the calling the function-by-delaying-it-on-a-counter has become my way to go. Even if I hate its inelegance, brutality, and overall flimsiness.

Where is The call at, So i can add in PostBeginPlay.

The Gameplay Framework is still running underneath your game even if you aren’t using most of it - look at AGameModeBase::StartPlay(), this is the function that ultimately results in BeginPlay() being called on all actors (through a small chain that goes via GameState and WorldSettings), so you can add your code there.

Initialization order when it comes to BeginPlay can be fickle, especially when you do enter the realm of multiplayer - so it’s best not to be ***too ***reliant on it. Ideally you want actors to be able to initialize themselves as much as possible. Adding a PostBeginPlay is all well and good, until you need a Post-PostBeginPlay() etc. You’d be better off making bespoke event-driven initialization in the long run if you have some actors dependant on others.

Note that you can also use FCoreUObjectDelegates::PostLoadMapWithWorld(), which is an event broadcast after BeginPlay() is despatched once the world has loaded.

3 Likes

Sweet Thanks for the info

This works beautifully, thanks a TON for the info!