How to properly restart a sub level or restart an existing one?

I’m really struggling to understand how to load and unload levels. I have a persistent level that handles the main screen and some other stuff. It’s a tower defense game where I want to load each level in a sublevel. The first time, it works great.

But if I want to restart or load a new level, it doesn’t work. The first problem I had is that the game freezes if the game is paused. So I unpaused it, but that means that things are still in motion and it wreaks havoc on the new level being loaded in.

For example if there’s 100 points available for enemies to escape and you’re down to zero, it will reset to 100 when loading a level and enemies that were escaping in the previous attempt continue to escape and the points go down to 0 causing the level to reload again and the game freezes or crashes.

So why do the enemy actors not go away? I used a sublevel actor as the owner for all the enemies. They don’t reset. Also, when I unpause the game, my internal game state is empty, so the active enemies will try to update their state, find they don’t actually exist, null pointers galore and the game crashes.

  1. Why can’t I pause the game when unloading the sublevel and loading it again?
  2. Why don’t all enemy actors disappear? Do I have to remove them myself?
  3. When I reload a level, BeginPlay isn’t called. Do I have to store level stats somewhere else?

For #3, I would just store some stats (starting points and certain gameplay restrictions) in the level and assign it to the main game logic when the sublevel’s BeginPlay is called. Maybe storing level stats with the level is too crazy??? In any case, the sublevel’s BeginPlay doesn’t get called when reloading. Is this by design?

Any help would be appreciated. All of this is driving me nuts.

Also, in C++, I can’t just load a level. It will crash. I have to do this in 3 parts:

Part 1:

    FLatentActionInfo info;
    info.CallbackTarget = this;
    info.ExecutionFunction = FName("PostDestroyLevel");
    info.Linkage = 0;
    
    UGameplayStatics::UnloadStreamLevel(World, FName(LevelName), info, false);

Then this:

void AGameActor::PostDestroyLevel()
{
  FLatentActionInfo info;
  info.CallbackTarget = this;
  info.ExecutionFunction = FName("PostLoadLevel");
  info.Linkage = 0;
  UGameplayStatics::LoadStreamLevelBySoftObjectPtr(this->GetWorld(), this->ActiveSoftLevel, true, false, info);
}

Finally, I can start the level.

void AGameActor::PostLoadLevel()
{
  this->StartGame();
}

But like I said, this doesn’t remove any actors spawned into and owned by an actor in the sublevel. Yet, it kinda does. Because the enemies are all gone by the time the new level is loaded, but there seems to be an overlap where they’re still there affecting the new level being loaded for a little while at the very start. Enough to mess up the game state.

Oh, actually, maybe when I’m unpausing, it’s still updating and it tries to reload multiple times since every enemy that exits will trigger a new end game request. So my question is there a way to pause everything and still be able to load a new level? Looks like my best bet is to manually delete all the spawned actors and ensure no game state gets updated. That would be very messy.

edit: I already have code that destroys all towers and all enemies when you lose (or win). I’m even more confused than before.

I’m refactoring a lot of stuff so that when the level ends, all enemies stop and no longer update any stats. Hopefully, that will fix a lot of my issues. I saw that my towers had a timer for when to shoot. I’ve disabled these as well when the level ends.

That just leaves 2 questions.

  1. Is it possible to load a new sublevel (or restart a sublevel) while paused?
  2. Is it by design that BeginPlay doesn’t get called when reloading a sublevel?

Will BeginPlay be called if I load a new sublevel?

I figured out what I was doing wrong. I thought a level was a different world because the soft pointers are UWorld. But in game, you only have one world. Also, when you list all StreamingLevels, it will list them all regardless if they are loaded or not. You can use Level->IsLevelLoaded() to find out if it’s loaded. So I was unloading the wrong level.

Everything is working fine now.

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.