What is the correct way to destroy an actor?
We added some functionality to allow a certain type of actor to destroy itself by calling AActor::Destroy() with the default parameters.
However, we’re seeing that when we do this, its Tick() method is still getting called, and it’s affecting the game world from beyond the grave when it should be dead.
We’re not holding onto any smart pointers to the object or anything that could be keeping it alive (we use TWeakObjectPtr at most).
What could be causing this?
The typical approach to deleting actors is simply to call Destroy() and forget about it. It should stop ticking and GC should do the rest. If you have other routes to call into your actor and want to check if it has been destroyed, you can use AActor::IsPendingKill() to test if Destroy() has been called.
We’re not aware of any issues with regard to this code, so I’m interested to hear more about the actor you’re trying to delete. Is it doing any interesting or atypical lifetime management?
Also, I wondered if you could clarify what you mean buy “actually physically delete” and the Delete() method, which I’m unaware of.
I think I have some insights into this. I was having a hard time figuring out quite how to make actors tickable, as I hadn’t found this thread
and the documentation is not at all clear as to how to make actors tickable in C++. So I should have been setting PrimaryActorTick.bCanEverTick = true on them, but I was instead deriving from FTickableObjectBase.
And this had the desired result of making them tickable, but it ended up somehow preventing the objects from getting deleted properly (presumably because it meant there was now a smart pointer somewhere keeping them alive?).
I’ve now changed it to just set PrimaryActorTick.bCanEverTick and no longer inherit from FTickableObjectBase, and objects are getting deleted properly now – though I’m still forcibly calling garbage collection immediately afterward a la Rama’s function below.
Also, by “actually physically deleting,” I meant using the delete keyword, i.e.
(… which of course is NOT something I would want to do if I can avoid it.)
I’d love to get rid of my special destruction function now, but is Destroy() guaranteed to immediately destroy the object by the end of the frame? If any of my actors sticks around even 1 frame longer than necessary, it will cause problems for us.
No, the objects are not guaranteed to be completely gone by the end of the frame. They do stop doing Actor-y things though, so they should be effectively “gone” for most purposes.
Without knowing the particulars of your use case, I’ll say that in my experience, I’ve found IsPendingKill() and the Destroyed() event to be useful and sufficient to handle any complications caused by deferred destruction.
Also, one point about using “delete pSomeActor” – this is not a valid thing to do, so please do avoid that.
Yes, I figured “delete pSomeActor” was a bad idea
I can highly recommend
/** Destroy the actor */
UFUNCTION(BlueprintCallable, Category="Destroy", meta=(FriendlyName = "DestroyActor"))
virtual void K2_DestroyActor();
that’s what I use
I have an in-game editor so reliably destroying stuff during frequent edits or during level changes is really important
It seems to work perfectly!
K2 stands for Kismet 2
PS: it took me a reaaallly long time to find this function so I can sympathize with needing this post
That doesn’t seem to work, either – I get the same behavior.
I call full purge of garbage collection after deleting stuff
and it works great for me
forgot 2 mention that the first time
#MY Entire Delete Function For You
I just did rigorous testing using UE_LOG and tick function for my 3d HUD Static Mesh Actors
I create and destroy them very frequently, several times per minute
I ran many tests
the function I use is working perfectly, instantly removing prior actors after I call my function on them
#VDestroy(UObject * ToDestroy)
void AVictoryGamePlayerController::VDestroy(UObject * ToDestroy)
if (!ToDestroy) return;
if (!ToDestroy->IsValidLowLevel()) return;
AActor * AnActor = Cast(ToDestroy);
ToDestroy = NULL;
//Object - BeginDestroy
ToDestroy = NULL;
Good news and bad news.
The good news is – this works. Thank you, Rama!
The bad news is – that we apparently have to use it, and every other approach I’ve tried either causes crashes or keeps the object alive and ticking longer than it should.
Epic, could you please take a look at this? Sorry to sound a bit cynical, but this just doesn’t seem right at all. Deleting objects is incredibly important, and this seems like far more steps than should be required to do this.
There really should be some way to reliably, safely, and cleanly delete an object in one line of code by calling some well-named engine function. At the very least, calling an actor’s “Destroy()” function should do what it says on the tin. And if I really have to go through all this just to delete an actor properly, that feels to me very much like a …
When you are frequently deleting objects, to avoid crashes, you have to always use
if (!UObject::IsValidLowLevel()) return;
so the way I check validity of frequently-deleted-pointer references is like this:
At least, this is what has worked for me in my editor mode where I delete stuff all the time.
So until Epic answers you with some additional info
just add the IsValidLowLevel() checks
and you should stop the crashing
I delete sometimes 30 material instances within seconds and entire levels full of actors in a single tick, and my method has worked so far
but I agree
this is all not exactly intuitive or simple to approach
Which is why I think regular delete functions dont instantly do anything drastic (like a full purge of GC), to avoid the crashing
Thanks, yes – already did all that.
Yes … think so … just waiting to hear from Epic on this.
It looks like the correct fix is to actually physically delete the object after calling its Delete(). This is the only thing that generates correct results.
Epic: can you verify this?
see my updated answer above
I’ve now done rigorous testing and provided you my entire code
I´m new with Unreal, but I´m not new to C++.
As of version 4.25.1, calling K2_destroy is the same of calling Actor::Destroy with no params (in fact it is what the c++ code does).
This calls World->destroyactor, and so on.
That function sets a very interesting state : IsPendingKillPending()
This is my tick function, which verifies of the actor is currently begin destroyed:
void ::Tick(float DeltaTime)
… normal stuff here