► React to Editor Destroying/Deleting an Actor

Hello,

my Actor is spawning some other actors using GetWorld()->SpawnActor.
I store them inside an Array and I can already destroy all of them if needed.
The problem is that it is possible to delete the main Actor using the editor without destroying the spawned ones.

So I thought that I need to override Destroy() or K2_DestroyActor() and add a call to my custom destroy function there.
That does work ingame, but not in the editor.

Which function do I have to override to do that?

Thanks!

// Edit: Please ignore the explanation in this question. Look at the newer ones down there in the comments.

Have you tried overriding AActor::BeginDestroy?

Thank you for your answer.

I already messed around with BeginDestroy(), and it seems like it is bugged…

On top of that, I am unable to run most of my functions there (like destroying other actors). So I guess that the destruction already started.
Also, keep in mind that I am talking about deleting it in the editor, not calling destroy() ingame.

So I just debugged it a bit more, and it seems like it clears my UPROPERTY TArray before it calls BeginDestroy().
That is a bug, right?

Hey holzlag0r-

Just to make sure I understand what you’re trying to do, when you delete the main actor in the editor you want its destructor to delete the other actors it spawned? While it seems this is related to the other post you linked, would you be able to add a check to the spawned actors for the main actor(pseudo code):

if(!"main Actor")
{
dectructor();
}

This way each spawned actor would destroy itself if the main actor doesn’t exists.

Hello,
yes, that’s what I want to do.
And I guess that your solution would work. However, where would I put that code? Problem is that I can’t set any variables from the main actor, since I loose all references to the spawned ones upon destruction. Another way would be to let the spawned actors check themselves, however that would require some kind of tick function, which had to be called in editor mode (waste of resources).

You know, I don’t really even need that functionality (although it would be nice). Don’t get me wrong - I really appreciate that you want to help me doing this! However, I think that the real problem are BeginDestroy() and Destructor themselves:

  • TArray (and other stuff, see //Edit) getting emptied before BeginDestroy and Destructor ( I guess that shouldn’t really happen, since nothing should already be destroyed on BeginDestroy - as the name suggests. Or maybe some other kind of function to intercept destruction before its happening)
  • Destructors & BeginDestroy() are only called once, Undoing that does not call them again. In my case that’s not a huge problem, but I guess this could eventually lead to some things not getting cleaned up properly.

I don’t know that much about C++, so I’m not able to and understand every line of engine code, however I think that you are emptying TArrays before letting the Actor destroy itself when deleting it in the editor.

// Edit: Actually, this applies to any UPROPERTY. It seems like they get set to their default state. Look at my boolean example.

So here is the code now that I used to test this:

Output:

Cmd: DELETE

Cmd: ACTOR DELETE

LogEditorActor: Deleted Actor: BP_MyActor_C

Test:Warning: BeginDestroy @ false bTestBool

Test:Warning: BeginDestroy @ 0 comps

Test:Warning: Destructor @ false bTestBool

Test:Warning: Destructor @ 0 comps

LogEditorActor: Deleted 1 Actors (0.011 secs)

I hope that you can reproduce it like that.

Hey holzlag0r-

How do you have the Parent actor spawning the children actors in code? I’m trying to reproduce what you have and want to make sure I’m using the same process. If possible could you post the code for your main actor or walk through how it is setup.

Cheers

Hi ,

here is the code I’m using:

OAWorld.h

	/** World Component for the StarBox. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Components)
	TSubclassOf<class AOAWorldComponent> StarBox;

	/** Array that holds the currently spawned world components. */
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Components)
	TArray<AActor*> Comps;

	/** Called when the Universe should be updated. */
	UFUNCTION(BlueprintCallable, Category = Setup)
	void OnRefresh();

	/** Destroys all comps and clears array, returns number of destroyed comps. */
	UFUNCTION(BlueprintCallable, Category = Setup)
	void DestroyAllComps();

OAWorld.cpp

void AOAWorld::OnRefresh()
{
	// Delete old Components
	DestroyAllComps();

        // Spawn new Components
	if (StarBox)
		Comps.Add(GetWorld()->SpawnActor<AOAWorldComponent>(StarBox, GetActorLocation(), GetActorRotation()));
}


void AOAWorld::DestroyAllComps()
{
	for (int i = 0; i < Comps.Num(); ++i)
	{
		if (Comps[i])
			Comps[i]->Destroy();
	}
	Comps.Empty();
}

That’s basically it. AOAWorldComponent is just a class deriven from AActor, nothing else.
The spawning itself works fine and I’m able to destroy every actor by calling DestroyAllComps(). It also is (sometimes) called if the main actor is destroyed by the editor - but then the array is suddenly empty.

Hey holzlag0r-

Just so I can be sure I’m following the same setup that you’re using could you also include the code for how your OAWorldComponent is spawning other actors? Also, in the editor are you creating a blueprint based on your OAWorldComponent class or are you adding an instance of the class itself to the scene?

Hello ,
I uploaded everything related to this issue.
Also, I added another Actor (“AExample”) which should illustrate the other problem regarding non-reliable destructor and BeginDestroy() calls.

OAWorldComponent is not spawning other actors, it basically just is an actor, nothing else. I didn’t override anything so far. If you want I can upload another example, that shows that UPROPERTYs get reset upon deleting the actor.

https://github.com/holzlag0r/Shared/blob/master/Answerhub.zip?raw=true

I apologize for my confusion, however I’m not sure I’m clear on what your question is. Your original post mentioned that you have an actor (mainActor) that is spawning other actors using GetWorld()->SpawnActor. The spawned actors are then being stored in an array (a TArray varaible of mainActor I’m guessing?) You then mentioned that you were having problems when the mainActor was destroyed while the spawned actors still exist inside the array, is that correct? If I am understanding the problem wrong please elaborate on the issue. It may also help in understanding if you have a small sample project with the bug occurring that you could upload to drop box. You can send me a private message on the forums with a link to the drop box if that works for you.

Okay, maybe my explanation was not that great, especially because I was mixing the two problems I have up.


** Problem 1 - Serious bug from my POV **

What I was trying to do:

  • I have a main actor, which spawns many other actors. Pointers to these spawned ones are stored in a TArray variable in the main actor.
  • BeginDestroy() or the Destructor should loop over the loop when the main actor is destroyed/deleted and destroy every spawned actor.

What happens:

  • When deleting it in the editor (CMD DEL, or just del key), the main actor gets destroyed.
  • The Destructor or BeginDestroy() gets called, however the TArray is EMPTY by then. So lets say I spawned 5 actors, pointers of them are stored in the array. The moment I delete the main actor, this Array gets reset to 0 elements - I loose all reference, rendering me unable to destroy the spawned actors

What I think the bug is:

  • When destroying actors in the editor, variables like TArray or even a Bool get RESET. So a TArray is emptied and a Bool is false - everytime. It does not matter what you set it before. Once you reach the destructor / beginDestroy, the contents of these variables are gone, replaced by the default.

** Problem 2 - Still bad, but not as serious as the 1st one **

What I was trying to do:

  • I have an actor which has a destructor and a BeginDestroy() function.
  • Deleting the actor should call these functions once and everytime I delete an actor of that kind in the editor.

What happens:

  • Sometimes BeginDestroy and the Destructor are called MULTIPLE times in a row!
  • If you delete an actor and undo your action in the editor (Ctrl + Z), and then delete it again, the destructor / beginDestroy will NEVER be called

Sorry for the long text, but I hope that we can solve it that way. If you still don’t know what I am trying to do or why I think this is a bug, then I probably have a serious misconception about the way the engine works.

To answer your second question first, I was able to reproduce this. As discussed in the other thread you created (C++ Destructor & BeginDestroy - Run only _once_?! - Programming & Scripting - Epic Developer Community Forums) this has been bugged as UE-11680.

In testing the first problem you listed I created a class (DestroyTest) with a float and bool variable. I set each of them on the constructor and print out their value to the output log. In the destructor I set them to a different value and then print the value again. Both variables print the expected value (that set in the destructor) when I delete them. Could you post the code for how your mainActor creates the other spawned Actors as well as how it stores the references in the array? Then I can test creating/destroying the spawned actors on my end.

Cheers

Hi ,

I created an actor which works almost exactly the same way my original one does, but I removed all the the code which is not relevant for the problem.

ZipFile

The zip contains two files.
Add them to your project, make the required changes (LogCategory, GAME.h) and then compile. Then create a Blueprint. Place it into the world, and assign “Actor to Spawn” to an actor of your choice. This should spawn the desired actor. Then delete the main actor.

I get the following Output:

:Warning: Comps.Num() = 1
Cmd: DELETE
Cmd: ACTOR DELETE
LogEditorActor: Deleted Actor: BP_TEST_C
:Warning: Comps.Num() = 0
LogEditorActor: Deleted 1 Actors (0.019 secs)

Hey holzlag0r-

Using this call UE_LOG(LogTemp, Warning, TEXT("Mem Address = %i"), this);
I checked the memory address of the main actor during OnConstruction() and when it is destroyed, which produced two separate results. This means that the mainActor that is creating the other actors during OnConstruction() and the mainActor that gets deleted are technically two different actors (which could cause other issues with hanging references). What exactly are you attempting to do as there may be safer ways to accomplish the same thing.

I’m trying to create a procedurally generated level, and the actor in question is responsible for previewing it Pre-PIE.

So I just added a bool uproperty now that allows me to destroy all spawned actors (checking it in OnConstruction) and I use that whenever I want to delete the main actor.

I don’t think you have to track the behaviour described above as a bug - it’s rather a little limitation. And I guess I’m also the only one who tries to do something like this - so rewriting that part of the editor is not necessary.

And by the way: Thank you for your patience, .
It seems like Epic really wants to support the community - and I really appreciate that! I guess that’s it for now.
Thank you and have a great day!

I can confirm this, but it get’s even more interesting. I know where the rogue actor(s) are coming from. It seems that whenever you drag an object over the
editor pane, a new object is created. Then, when you release the object, an additional one is created. This additional one is what we see in the editor. In fact, if you pull in and out of the editor pane, you’ll create multiple “phantoms.” Things get weirder when you try to delete the object we can see in the editor. Apparently, the code will call BeginDestroy() on all the phantoms, but NOT the one in the editor that you actually deleted. Which is probably why we are seeing our arrays as empty when it is called. Weirder, the “real” actor ends up getting removed in the end, despite it never getting a BeginDestroy().

Here’s and excerpt from the log I ran on this:

// Dragging "Gameboard" class back and forth onto the pane.  I finally dropped it on "Gameboard_3"
LogEditor: Attempting to add actor of class 'Gameboard' 
LogTemp:Warning: Constructor: Gameboard_0
LogEditor: Attempting to add actor of class 'Gameboard' 
LogTemp:Warning: Constructor: Gameboard_1
LogEditor: Attempting to add actor of class 'Gameboard' 
LogTemp:Warning: Constructor: Gameboard_2
LogEditor: Attempting to add actor of class 'Gameboard' 
LogTemp:Warning: Constructor: Gameboard_3
// Gameboard_3 is now visible
    
// Deleting the Actor visible in the editor, yet it calls BeginDestroy on the phantoms
LogEditorActor: Deleted Actor: Gameboard
LogTemp:Warning: BeginDestroy: Gameboard_0
LogTemp:Warning: BeginDestroy: Gameboard_1
LogTemp:Warning: BeginDestroy: Gameboard_2
LogEditorActor: Deleted 1 Actors (0.016 secs) // Gameboard_3 officially deleted here?
// No BeginDestory called for Gameboard_3

I can provide code on request.

I’m trying to do the same thing and have been running into the same issues you have.

In the meantime I’m placing all of the spawned actors to their own folder path. That way, when I want to delete the object, I just delete the main one, and then the subfolder.

AGameBoard_Column* added = World->SpawnActor<AGameBoard_Column>(AGameBoard_Column::StaticClass());

added->SetFolderPath("GameBoardColumns");
Columns.Add(added);

SetFolderPath will automatically create the folder, so all you have to do is delete the folder every time you reset.

Not a permanent solution, but a good workaround.

Hey -

Thank you for the extra information. I have included this with the original bug report which is still being investigated.

Cheers

I’m also running into this as I also have a single Actor generate child actors prior to PIE. I have a Region actor that spawns Room actors and the Region actor needs to be able to remove the spawned children if they still exist.