Souls-like respawn system

“I’m trying to set up a respawn system where I get all the actors at the start of the game and then respawn them by referencing the ones I got before. But it’s acting all wacky—sometimes it kills actors, sometimes it doesn’t. Plus, when I call the get function again, it can’t kill the actors. I’m tired of this and could really use some help.”

#include "EnemySpawner.h"
#include "Kismet/GameplayStatics.h"

// Set the parent class for the spawner, which determines the type of actors to spawn.
void AEnemySpawner::SetParentClass(const TSubclassOf<AActor> ActorClass) {
	ParentClass = ActorClass; // Store the provided ActorClass as the ParentClass.
}

// Retrieve all actors of the ParentClass in the current world.
void AEnemySpawner::GetActors()
{
	TemplatedActors.Empty(); // Clear the array of previously stored actors.

	if (ParentClass) // Check if a valid ParentClass is set.
	{
		// Use GameplayStatics to get all actors of the specified ParentClass in the world.
		UGameplayStatics::GetAllActorsOfClass(GetWorld(), ParentClass, TemplatedActors);

		// Log each actor that was found.
		for (AActor* Actor : TemplatedActors)
		{
			if (Actor) // Ensure the actor pointer is valid.
			{
				UE_LOG(LogTemp, Log, TEXT("%s registered"), *Actor->GetName()); // Log the actor's name.
			}
		}
		// Log the total number of registered actors.
		UE_LOG(LogTemp, Warning, TEXT("Totally %d registered"), TemplatedActors.Num());
	}
}

// Respawn all actors that were previously stored in TemplatedActors.
void AEnemySpawner::RespawnActors()
{
	// Iterate through all saved actors to respawn them.
	for (int i = 0; i < TemplatedActors.Num(); i++)
	{
		AActor* Actor = TemplatedActors[i]; // Get the actor from the array.
		if (Actor) // Check if the actor pointer is valid.
		{
			FActorSpawnParameters SpawnParams; // Prepare spawn parameters.
			SpawnParams.Template = Actor; // Set the template to the original actor, preserving its properties.

			// Spawn a new actor of the same class as the original, using the template.
			AActor* CopiedActor = GetWorld()->SpawnActor<AActor>(Actor->GetClass(), SpawnParams);
		}
	}
}


// Destroy all actors that are currently tracked by the spawner.
void AEnemySpawner::KillAll()
{
	TArray<AActor*> ActorsToKill = TemplatedActors; // Create a copy of the current actors to kill.

	// Iterate through the copied array and destroy each actor.
	for (AActor* Actor : ActorsToKill)
	{
		if (Actor) // Check if the actor pointer is valid.
		{
			// Log the name of the actor that is about to be destroyed for debugging purposes.
			UE_LOG(LogTemp, Log, TEXT("Trying to kill %s"), *Actor->GetName());

			Actor->Destroy(); // Destroy the actor, effectively removing it from the world.
		}
	}
}

I see where your problems are.
The actors of class are getting stored in TemplatedActors via GetActors(), then every actor in TemplatedActors is either copied in RespawnActors(), or destroyed in KillAll().

TemplatedActors isn’t exactly what you think it is, and has quite a misleading name. The AActor* in TemplatedActors are not templates- when you add to TemplatedActors, you’re adding a pointer to an existing Actor. When you copy the array, you’re copying an array of pointers, not Actors.
So what’s happening is you have multiple of the same actors in the world, and then you kill every single one of them. Though if you only call GetActors once at the start before spawning anything, then you instead just kill the templates and not the spawned enemies.

I would recommend instead having an exposed UPROPERTY that has an array of some form of spawn data. Importantly this should not be the actor you want to spawn. This can be just transforms if that’s all you need, but it could be a pointer to a custom in-level actor that stores more detailed information. You can then fill out that array for each spawner in the world.

Then, whenever spawning { SpawnEnemies() }, spawn the actors based on that data, and store an array of pointers to those spawned actors.
Whenever respawning { RespawnEnemies() }, destroy all actors that were created in SpawnEnemies(), and then call SpawnEnemies().

Let me know if you have any questions or need some examples, I’m happy to help.

I get that templated actors are essentially actor references. I was experimenting with different approaches earlier and just didn’t rename the variable.

The main idea with this spawner is to avoid duplicating all parameters. I want it to just copy the actor I place in the editor so I can edit its values without worrying about it later. Thought it would be easiest approach.

My logic is to get all the actors, kill them, and then respawn them. That seemed like the best approach.

Templated actors aren’t an existing unreal thing.
You may want to watch some videos on pointers. A pointer is a tiny chunk of memory that points to another memory address. When you copy an AActor*, you’re copying that tiny chunk of memory, not what that tiny chunk of memory is pointing to.
A reference is an actual term very related to pointers, so you may want to look into that too. Though for unreal types, pointers are almost exclusively used.

Not sure what you mean by “worrying about it later,” but the latter of my suggested data types would do exactly that. You create some actor class to store data along with a physical transform, and then the spawner gets pointers to those actors. Same workflow as before, except you do need to manually select which actors you want to care about (though there are easy ways of skipping this step too).

The problem with that is that you can’t “respawn” an actor. Once you kill the actor, it’s dead. Actors don’t have a template state you can reload. You can create that kind of feature, but then you have a bunch of actors that are just hogging resources, and you have to plan around that feature in every actor you create.

If you have any hesitations, don’t hesitate to ask