In 4.8.3 it worked perfectly. Level streamed, actors placed, event beginplay starts. Now beginplay starts before an actors from streamed level presents at scene and Get All Actors Of Class returns an empty array.
It works fine with single level or with Always Loaded method but once same levels switched to blueprint streaming methot it stops working. Though little delay helps but thats just silly.
Steps to reproduce:
1 - create blank project with a persistent and streamed levels (blueprint method).
2 - create new actor bp with code (also tried placing it at beginplay of streamed level bp, same result) and place it at streamed level:
I have exact same issue! Worked fine in 4.8.2, but in 4.9, GetAllActorsOfClass doesn’t find anything.
It seems it is related to, that outer object is now persistent level and not streamed level. If you in C++ use GetObjectsOfClass(), you retrieve them all.
This seems to only be a problem in BeginPlay(). Maybe BeginPlay is called prior to streamed level has finished loading?
I’m having exact same issue as well and your description is spot on. It works fine in single level, but when streamed from another level it’s broken, but worked fine in 4.8. only difference for me is I’m calling GetAllActorsOfClass in C++ during BeginPlay for certain actors (instead of blueprint approach), but result is same.
Thanks for report! I was able to reproduce this in 4.9.0 as well as our internal builds, and I’ve entered a bug report for issue (UE-20736). I’ll post here when I see any update.
I spoke with developers about it, and problem is not that Event Begin Play is being fired before streaming level is loaded. Instead, problem is likely a timing issue with Get All Actors of Class. I’ll let you know if I find out more. For now, using delay is your best bet.
For me GetAllActorsOfClass is completely broken regardless of timing (For test purposes I added it to gamemode’s tick, and then entered there with a debugger long after streamed level was loaded. actor from streamed level was not there). streamed level does have actors as I get their beginplay events just fine. Also iterator seems to be broken as well - only returns actors from peristent level. I posted this here, before I found this thread:
And for completeness sake, here’s test code I added in my GameMode::Tick event :
auto loadedlevels = GetWorld()->GetLevels();
TArray<AActor*> actorstest;
for (auto level : loadedlevels)
{
for (auto actor : level->Actors)
{
actorstest.Add(actor);
}
}
TArray<AActor*> actorstest2;
for (TActorIterator ActorItr(GetWorld()); ActorItr; ++ActorItr)
{
actorstest2.Add(*ActorItr);
}
So, I’ll wait for about 10 seconds after I load level. Then I’ll go here and add a breakpoint. result - actorstest : 34 actors, actorstest2: 31 actors.
And for completeness sake, here’s test code I added in my GameMode::Tick event :
auto loadedlevels = GetWorld()->GetLevels();
TArray<AActor*> actorstest;
for (auto level : loadedlevels)
{
for (auto actor : level->Actors)
{
actorstest.Add(actor);
}
}
TArray<AActor*> actorstest2;
for (TActorIterator<AActor> ActorItr(GetWorld()); ActorItr; ++ActorItr)
{
actorstest2.Add(*ActorItr);
}
So, I’ll wait for about 10 seconds after I load level. Then I’ll go here and add a breakpoint. result - actorstest : 34 actors, actorstest2: 31 actors.
Nevermind my previous posts. That was my screwup. So yeah, it is delay issue. In my beginplay of level if I put:
TArray<AActor*> actorstest;
UGameplayStatics::GetAllActorsOfClass(GetWorld(), AMyCharacter::StaticClass(), actorstest);
It will return empty. But if I put same in gamemode’s Tick event eventually it will start returning 1 (after a few update cycles)
This bug looks like it cropped up when Actor Iterator was refactored. previous actor iterator made use of a Level Filter Class, but notice that templated Actor Iterator doesn’t specify a Level Filter. That means it was using Default Level Filter:
template<typename FILTER_CLASS,typename LEVEL_FILTER_CLASS = FDefaultLevelFilter>
class TActorIteratorBase :
public FActorIteratorBase
template <typename ActorType>
class TActorIterator :
public TActorIteratorBase< FActorFilter >
Which wasn’t culling iteration results based on whether level was visible:
However now iterator itself implements result culling and both Actor Iterator implementations perform culling:
template <typename ActorType>
class TActorIterator : public TActorIteratorBase<TActorIterator<ActorType>>
{
static bool CanIterateLevel(ULevel* Level)
{
return Level->bIsVisible;
}
This is a problem for this particular use case of iterator where streaming level calls BeginPlay() on Actors it contains prior to marking level visible.
See:
suggested fix of a Delay works around problem by using Actor Iterator outside of streaming level initialization step. An easy workaround fix in any C++ code with this problem is to implement a custom actor iterator with previous behavior.
No, and we do not expect it to be fixed for any hotfixes. As mentioned above, there has been no update on report itself, including a fix, but we will post here once there is. Thank you.
I just stumbled across this issue and solved it by setting up a timer to check whether streamed level is actually visible or not, to eventually perform whatever depends on that visibility (ie. counting actors).
In my function where I turn level visibility on (currentLevel is a pointer to loaded level whose visibility we want to turn on; FTcheckForLevelState a global timer handle):
void InitializeLevelWhenVisible(ULevelStreaming* levelToInitialize)
{
if (levelToInitialize->IsLevelVisible())
{
// Count Actors here (...and/or call other functions whose result depends on your levelToInitialize being visible)
[...]
// Reset Timer
GetWorldTimerManager().ClearTimer(FTcheckForLevelState);
}
}