Hey -
I’m having a hard time following your work flow. What is the end result you’re after by destroying/recreating your actor? Depending on what you’re trying to do it may be best to hide the actor rather than destroy it and then unhide it when you want to use it again.
When you call the Destroy() function for the actor it is set to be garbage collected the next time garbage collection runs. If you are creating a new actor before GC runs then this could cause issues since the first actor still technically exists.
Cheers
If you have an actor with an "ID name " , say “player”, and then you try to do the following:
…
actor = GetActorByName("player") // some function that gets by name somehow
actor->Destroy();
FActorSpawnParameters spawnParams;
spawnParams.Name = "player";
auto newactor = GetWorld()->SpawnActor(SameStaticClassAsPreviouslyDestroyedActor, &FVector::ZeroVector, &FRotator::ZeroRotator, spawnParams));
…
You will find that your newly created actor doesn’t have any components. GetComponents() results in an empty array.
I was able to workaround by doing this immediately after Destroy (both lines needed):
GetWorld()->ForceGarbageCollection(true);
GetWorld()->PerformGarbageCollectionAndCleanupActors();
So that fixed it. But I think you should be able to recreate a new actor with the same name as the one you just called Destroy on, without these workarounds, so I m reporting a bug… . I suspect this has something to do with the fact that previously destroyed actor was not garbage collected yet and maybe there’s some weird caching by name that makes it look like the components were already initialized when the new actor gets created.
Hi.
Thanks for the quick response.
Ok, so this will take some time to answer…
Basically this started with me looking for ways to persist a level. So, a level can have let’s say 10 actors that were originally placed in the editor. During the course of game 5 actors may be destroyed. Then the player might leave the level. At that time I serialize remaining 5 actors. When the actor comes back the level reloads with all 10 actors again, so I deserialize the 5 that I stored previously over the ones with the same names, and then destroy the remaining 5 that couldn’t get matched to what was in the serialized payload.
So that worked fine but I didn’t like that i have to do this name matching, so I just thought “I’ll destroy all actors when the level is re-entered right at the start and then just serialize them from the list”. So that’s when I ran into this.
Now - it could very well be that I m taking a wrong approach to serialization/deserialization of levels but the information is very scarce, so this is what I’ve come up with.
Hey -
You may want to look into saving the level and then loading that save when you return to the level. (Saving and Loading Your Game in Unreal Engine | Unreal Engine 5.1 Documentation)
This way you can set each object with a “Has been Destroyed” variable and include it as part of what is being saved. If the variable is true when the level loads then it skips creating the object.
Hi.
Yeah, I looked at SaveGame object before but that didn’t seem like it would help. I have a fairly complex hierarchy of objects and a lot of dynamic objects , pointers, etc, that I have developed a serialization system for already. (using Serialize overrides and overriden FArchive class. So the serialization/deserialization i have already works very well. It’s just the part of destroying or not creating object that is the problem.
Something like “Has been destroyed” you mentioned would be great though - I can certainly have that variable as part of my serializtion as well, but what I don’t understand is at what point would I be able to hook it up in the level loading process to tell the level “do not create this object”. That’s the part I m missing.
All my logic for deserialization is in BeginPlay of the level. Is there some earlier event prior to that where I can intercept object creation and tell the level “don’t create this object, even though you have it in your default UMAP”. That’d be EXACTLY what I need.
Yup - that’s what I think is happening too. Which is why I opened a bug - I think you should be able to create a new actor with the same name even if the old one is pending deletion right now.
The two lines I put in my first post seem to fix that :
(note that if you only try first it won’t work - you have to call this PerformGarbageCollectionAndCleanupActors() )
GetWorld()->ForceGarbageCollection(true);
GetWorld()->PerformGarbageCollectionAndCleanupActors();
I’d love to have a way of telling the level to not create actors at all though - that would be the best. Then I could have it create the actors it has the very first time it runs , and every time after that they would be loaded from my serialized file.
I have an anecdotal based recommendation.
When destroying an actor I have noticed that it is not completely removed right away. It looks like there is a garbage cycle that occurs about once a minute which cleans up everything completely. Its components may be deleted, but it is still referable for some time.
My guess would be that you destroy the actor (mark it for garbage cleanup) but then create a new one right after it, however since you are searching for the a property shared between the 2 you may be pointing to the actor you destroyed instead, which is why it shows no components.
If you wait a minute and then try to reference the actor by name and then get a list of components it may actually show the correct list of components.
If this is the case there are 2 ways around this:
- Force garbage clean up like you did in your original comment.
- Reference the object from a unique identifier. I consider this best practice, but I’m rather amateur myself.
Hey -
After speaking with one of our programmers he pointed out that your first method (serialize/deserialize what you need and destroy the rest) would be better performance than what you are currently trying to do. Basically it comes down to the first method being a “create-destroy” setup whereas your current method is more along the lines of “create-destroy-create” which would use up more resources.
Another solution would be to create a spawn actor that can be placed into the level. The spawn actor itself would then be responsible for creating the other actors based on what should/should not exist. This way you could use an array or other means to store all the objects in the level and then remove them as they are destroyed. Then when the player returns to the level, the spawn actor only spawns what’s in the array.
Cheers
Hi,
Thanks for the followup - yeah, I was using the first way until I ran into annoying issues with editor renaming my actors on its own (more on that in a second), so that’s why I attempted the second , create-destroy-create version.
Spawn actor idea is interesting - the only downside is that i wouldn’t be able to phyicaly see the actors in the editor, but maybe I could by checking if the actor is in “Editor mode” and spawning them right then, so they are visible in the editor. It would take some work to make it user friendly but I think it could be a good approach.
I think that answers this, but going back to the first method there is actually a related question… The reason why I started looking into other methods is because editor insists on renaming my actors when something changes in the base blueprint. Here’s the question:
link text
If you don’t mind can you take a quick peek at that when your schedule permits and let me know if you have any idea how to prevent this.
Thanks !