Garbage collection for spawned actors

So, as I have finished this lecture I’ve noticed that none of the spawned actors were being deleted when the tile is. It poses a problem since we don’t want to have a bunch of actors needlessly sitting around in the scene, taking up memory and affecting performance.

I’ve started trying to fix that problem, thinking it would be a breeze. At first, I tried to get all children of our tile and delete them when the player collides with the “Destroy Volume”. It didn’t work. At all. After some surfing, I realized that maybe we needed to find not the children of the tile itself, but the components attached to the root, and delete them. Did just that and it worked… Only partially though. For some reason, it was deleting some objects and leaving all the other untouched, even though they were obviously attached to the root component (Shared Root).

After a bit of head scratching I was on the verge of throwing in the towel and calling in the L, but then I realized that we can do something in the C++ when the tile is being or is destroyed. Looked up some docs and overridable functions I’ve overridden BeginDestroy() function, but it gave me crashes when I tried compiling. Changed it to Destroyed(), got all the spawned objects to be stored in a private TArray, written some code to destroy all of them when the actor is destroyed, and voila, the miracle happened. Now all the actors were destroyed with their respective tiles.

Long story short, here is my solution for the problem:

In the Tile.h we override the Destroyed() method and create a private TArray of actor pointers to store the spawned actors in

in the Tile.cpp, at PlaceActors method we add actors in the array every time they spawn

and in the definition of a Destroyed() method, we go through every single spawned actor and destroy them.

Now, every time when we hit the volume that destroys the Tile object, after being destroyed it destroys all the objects that spawned under that instance of a tile. Hope this is going to help someone to solve the problem a bit faster than I’ve been torturing it. Happy codin’ folks!

3 Likes

Hi,

I had the same problem and first couldn’t solve it, tried different approaches (to destroy all GetAttachedActors in BP, but the method didn’t give them back, tried the same approach in C++ and saw that I only get one attached actor from the Tile).

Then I did the same thing you did, saved the pointers to the spawned actors in an array and the destroyed all spawned actors in the Destructor of the Tile:

ATile::~ATile()
{
	for (AActor* i : SpawnedActors)
	{
		if (IsValid(i))
		{
			i->Destroy();
		}
	}
}

This worked during play in editor and destroyed all spawned actors:
But: It crashed the editor when I ended playing, even after I used IsValid(i).
No idea why this happened, @DanM, @ben?

Your idea using Destroyed() works flawlessly, thanks!

Best
Andreas

Not sure about IsValid (haven’t used it). But what if you just checked if it’s nullptr

for (AActor* i : SpawnedActors)
{
	if (i)
	{
		i->Destroy();
	}
}

Hi Dan,

thanks for getting back to me so fast! :slight_smile:

Same problem, while it removes the spawned actors from the level during PIE it crashes the editor when you stop playing. I guess the Destructor of the Tile is called really late and the editor has already destroyed the attached actors of the tile. The pointers in the TArray are not changed and so they are not nullptr. That’s the reason why I hoped that IsValid(i) might check if the pointer i points to a valid actor.

The other problem:

Why in BP does GetAttachedActors if called for the Tile does not return an array of all attached actors (they definitely are attached)? Then I would destroy the actors in BP before I destroy the Tile.

Best
Andreas

I’ll take a look into it.

They don’t actually get attached.

Ended up being busy yesterday and today. Will do it tomorrow (unless the universe decides I have other plans).

Edit: You’re using the destructor instead of Destroyed() like OP.

Hi Dan,

thanks for looking into it. And yes, I was using the Destructor, had the problems and then with using Destroyed() it worked.

But the questions remained:

a) Why won’t it work with the destructor?
b) Why won’t GetAttachedActors (in BP or C++ both) give me back the actors I attached to the BP_Tile?

Thanks & best
Andreas

a) Wrong timing.
b) Already answered. When you “attach” them they don’t actually attach. What Sam does in the lecture doesn’t actually work and they don’t actually attach.

I was going to ask about this very thing actually - what is the point of attaching the spawned props to the tile? I assumed it was to facilitate with garbage collection but it seems from this thread that that doesn’t actually work.

It seems like a fairly common use case though, is the mechanism earlier in this thread really the best practice?

I’ve been told this works now :man_shrugging:


Doesn’t look like these are being cleaned up in my version, after I added the AttachToActor call on the spawned prop :frowning:

What version are you using? I’ll go test the repo on 4.25 later today.

Edit: Tested, seems to work. To be clear, you need to destroy them when a tile is destroyed.
Previously using GetAttachedActors didn’t return any actors that were attached at run time.

1 Like

Thanks, I’ll give that a go.

I didn’t try with Destroyed() but was previously using the BeginDestroy() override function and apparently that is too late in the lifecycle: https://answers.unrealengine.com/questions/464012/how-can-you-destroy-attached-actors-when-parent-is.html?sort=oldest

I changed this over to EndPlay() as suggested in that page and the cleanup is working for the old props with the other suggested mechanisms above.

I also noticed that the garbage collection also did not work for me with all of the actor props so I have implemented your solution. Although taking into account what @FrozenGunGames and @DanM had mentioned, I just added a line where it checks to see if the TArray is empty or not just in case…

void ATile::Destroyed()
{
    Super::Destroyed();
    if (SpawnedActors.Num() > 0)
    {
        for (AActor* Actor : SpawnedActors)
        {
            Actor->Destroy();
        }
    }
    
}

Privacy & Terms