Different result for Play in Editor / Standalone Game / Build

Hi there!
I’m facing the problem I can’t figure out for a few days. In my game Pawn (VRPawn & AIPawn) contains UMilitaryStation component. This component is responsible for spawning an Actor AMilitaryStation. To determinate position of the stations I have my own class ASpawnPointStation.

ASpawnPointStation has just visual representation and int32 index; and bool bIsOccupied; variables.

In my map I have two Instances of ASpawnPointStation.

The goal which I want to achieve is, that each player (VRPawn, AIPawn) spawn it’s Station on different ASpawnPointStation.

UMilitaryStation component calls in BeginPlay this function:

void UMilitaryStationComponent::SetSpawnPointForStation()
{
	SpawnPointForMilitaryStation = nullptr;

	UWorld* World = GetWorld();
	if (!World)
	{
		SetSpawnPointForStation();
	}
	
	for (TActorIterator<ASpawnPointStation> It(GetWorld()); It; ++It)
	{
		ASpawnPointStation* SpawnPointStation = *It;
		if (!SpawnPointStation)
		{
			GEngine->AddOnScreenDebugMessage(1, 10.f, FColor::Green, TEXT("MilitaryStationComponent: !SpawnPointStation"));
			UE_LOG(LogTemp, Warning, TEXT("MilitaryStationComponent: !SpawnPointStation"))
		}
		AllSpawnPointsStation.Add(SpawnPointStation);
	}

	Algo::Sort(AllSpawnPointsStation, [](const ASpawnPointStation* A, const ASpawnPointStation* B) {
	return A->index < B->index;
	});
	
	if (AllSpawnPointsStation.Num() > 0)
	{
		for (ASpawnPointStation* SpawnPoint : AllSpawnPointsStation)
		{
			if (!SpawnPoint->bIsOccupied)
			{
				SpawnPointForMilitaryStation = SpawnPoint;
				SpawnPoint->bIsOccupied = true;
				SpawnMilitaryStation(OwningPawn);
				return;
			}
		}
	}
	else
	{
		GEngine->AddOnScreenDebugMessage(1, 10.f, FColor::Green, TEXT("MilitaryStationComponent: AllSpawnPointsStation.Num() < 0!!!!"));
		UE_LOG(LogTemp, Warning, TEXT("MilitaryStationComponent: AllSpawnPointsStation.Num() < 0"))
	}
}

This triggers SpawnMilitaryStation if non Occupied SpawnPoint is found.

In SpawnMilitaryStation i call:

	FVector SpawnLocation = SpawnPointForMilitaryStation->GetActorLocation();
	FRotator SpawnRotation = SpawnPointForMilitaryStation->GetActorRotation();

The problem is:
It works in Editor as I suppose!
It works well when i go thru each step in Rider.
It works well When I run game as “Standalone Game”
IT DOESN’T WORK IN BUILD!

When I build the game or run with Project Launcher, one of the player founds Spawn point correctly.
Second one always spawns Station at 0,0,0.
I’m missing something and I can’t figure it out! Any ideas.

Btw it is single player. I’ve also tried offset function with delay, call it form different actors, with different events, but the result was still same.

I’m on 5.5.1 currently.

A few things.

  1. Don’t use this. If it executes, it’ll crash your game.
    if (!World)
    {
        SetSpawnPointForStation();
    }
  1. This will always be the correct type and will never go into that if statement.
if (!SpawnPointStation)

As for your issues, you said you’re using BeginPlay(). BeginPlay is for initialization. BeginPlay is called on all actors in a random order. So if you have something that looks for other actors, not all actors may be initialized yet.

I don’t like using the level’s BeginPlay event since it doesn’t trigger if you restart the level (you must reload it). But for your case, in the level’s BeginPlay event, I’d just iterate actors and look for a MilitaryStationComponent. If so, call some method on it instead of what used to be done by BeginPlay. Or create an interface and add it to the actors you want to initialize after all the actors are initialized (have the level’s BeginPlay look for those interfaces and call it).

Since this is single player, you can also use a custom GameInstance::LoadComplete() unfortunately this doesn’t get called in the editor for the default level. For that, you need to update GameInstance::OnWorldChanged() like so… You’ll have to add the name of your level. It’s annoying but I couldn’t be bothered to find a more generic way to solve this issue.

void MyGameInstance::OnWorldChanged(UWorld* OldWorld, UWorld* NewWorld)
{
  Super::OnWorldChanged(OldWorld, NewWorld);

  const FString FirstLevelName("NewPermanent");  // Update this!!!

  if (NewWorld != nullptr && NewWorld->GetName() == FirstLevelName)
  {
#if WITH_EDITOR
    if (NewWorld->WorldType == EWorldType::PIE)
    {
      NewWorld->GetOnBeginPlayEvent().AddLambda([this,FirstLevelName](bool flag)
      {
        if (flag)
          this->LoadComplete(0.0f, FirstLevelName);
      });
    }
#endif
  }
}

Now you can use LoadComplete() to tell your actors to spawn those stations (by iterating over actors with components or iterate over interface).

You can also have a timer that periodically checks if the level is VISIBLE (loaded isn’t enough). Then iterate over actors and do one of the two solutions above.

As a sidenote, any actor that is ALREADY in the level when it is loaded will NOT call any construction scripts. Construction scripts are editor only for those actors. Not sure if this matters, but thought I’d mention it.

Oh! Thank you very much for your reply. Since your answer wasn’t the solution it contains really valuable information for me!

The case was quite silly.
In my costructor there was from some reason probably legacy from the past this:
SpawnPointForMilitaryStation = ObjectInitializer.CreateDefaultSubobject(this,TEXT(“MilitaryStationTargetPoint”));

It is quite nonsense so sorry for bothering you. I still don’t understand whz this worked for even one of the Stations but the important thing is, that problem was solved.

Thank zou one more time.