How to check for number of AActors within a SpawnVolume?

Hi

I have created a Object Spawner in C++.

The problem is however it infinitely spawns objects of subclass of type AActor. The logic that I am trying to implement is that if there is less than property of maxObjects then it will spawn up until this number.

Here is a snippet of the code however it doesn’t seem to work. http://i.imgur.com/B4veHnH.png

Apologies if this is an obvious question. I have been stuck on this for several days and I cannot seem to get it working.

I have a blueprint version of the spawner which works fine but I am being marked on my C++ implementation.

Kind Regards and Many Thanks

That code seems fine to me, but perhaps GameplayStatics isn’t returning what you think it is? Have you placed a breakpoint and seen how many entries its returning?

Another solution you could do is just manage that list yourself.

TArray<TWeakObjPtr<AActor>> MySpawnedActors; // Add this in your class somewhere.

// ... in your spawning code:

// Check our spawned actors and see if any were destroyed before we attempt to allocate anymore...

// This looks nasty but its just a predicate (a locally defined function) that removes all entities that are no longer valid
MySpawnedActors.RemoveAll([ ] (TWeakObjPtr<AActor>& SpawnedActor)
{
  return !SpawnedActor.IsValid();
});

if (MySpawnedActors.Num() < MaxSpawnedActors)
{
  // Spawn a new actor.
  AActor* NewActor = World->SpawnActor(...);
  MySpawnedActors.Add(NewActor);
}

I don’t think the problem you’re having is in the code snippet you posted. I just tried this (with the obvious header and the latest build from master, but that should not matter):

AGeneralSpawnVolume::AGeneralSpawnVolume()
	: MaxSpawnedObjects{ 5 }
	, MinSpawnDelay{ 1.f }
	, MaxSpawnDelay{ 4.f }
{
}

void AGeneralSpawnVolume::BeginPlay()
{
	SpawnPickup();
}

void AGeneralSpawnVolume::SpawnPickup()
{
	TSubclassOf<AActor> ClassToFind{ AStaticMeshActor::StaticClass() };
	TArray<AActor*> FoundActors;
	UWorld* World = GetWorld();
	check(World);

	UGameplayStatics::GetAllActorsOfClass(World, ClassToFind, FoundActors);

	if (FoundActors.Num() <= MaxSpawnedObjects)
	{
		UE_LOG(LogTemp, Warning, TEXT("%d <= %d: Spawning a new actor"), FoundActors.Num(), MaxSpawnedObjects);
		AActor* SpawnedPickup{ World->SpawnActor<AActor>(AStaticMeshActor::StaticClass(), GetActorLocation(), GetActorRotation()) };
	}
	else
	{
		UE_LOG(LogTemp, Warning, TEXT("%d > %d: Not spawning a new actor"), FoundActors.Num(), MaxSpawnedObjects);
	}

	float SpawnDelay{ FMath::FRandRange(MinSpawnDelay, MaxSpawnDelay) };
	GetWorldTimerManager().SetTimer(SpawnTimer, this, &AGeneralSpawnVolume::SpawnPickup, SpawnDelay, false);
}

and it works as expected:

LogTemp:Warning: 1 <= 5: Spawning a new actor
LogTemp:Warning: 2 <= 5: Spawning a new actor
LogTemp:Warning: 3 <= 5: Spawning a new actor
LogTemp:Warning: 4 <= 5: Spawning a new actor
LogTemp:Warning: 5 <= 5: Spawning a new actor
LogTemp:Warning: 6 > 5: Not spawning a new actor
LogTemp:Warning: 6 > 5: Not spawning a new actor
LogTemp:Warning: 6 > 5: Not spawning a new actor

(You need to initialize the ClassToFind variable, otherwise you’ll spawn new actors indefinitely since GetAllActorsOfClass returns an empty array when you pass a null pointer as class to find.)

There are two places where I would start looking for the error:

  • Is it possible that you set ClassToFind to a class that has more instances than you expect? For example, if you set it to AActor::StaticClass() you get all actors in the level, which are probably many more than your value of mMaxSpawnedObjects.
  • Do you actually call the method again once have reached the mMaxSpawnedObjects for the first time? You’re only setting the timer if you spawn something, so the first time you don’t spawn anything because there are enough actors in the level your spawn volume becomes inactive until it is reactivated again.