UActorComponent::Rename() on a SkeletalMesh leads to a engine crash on unregistering the components!

Hello guys,

I stumbled across this when using the GameplayAbilityTargetActor_ActorPlacement with an actor that contains a SkeletalMeshComponent. I need a way to be able to use SkeletalMeshComponents on these actors!

What happens is the following:

  1. The TargetActor gets the class of the actor to place and hands it to the GameplayAbilityWorldReticle_ActorVisualization (very nice name).
  2. The Reticle spawns an instance of the actor to place. Here, for the skeletal mesh component, a FAnimUpdateRateParameters struct is saved in the FAnimUpdateRateManager in a map during OnRegister(), using the owner actor as a key.. Then, detaches all mesh components from it and attaches it to itself.
  3. It calls UActorComponent::Rename() on all those mesh components and makes itself the outer of them.
  4. The actor to place, now without the mesh components, gets destroyed.
  5. Targeting can happen.
  6. When Targeting is finished, the Reticle gets destroyed, and with it all the mesh components. This is where the crash happens: During OnUnregister(), the FAnimUpdateRateParameters should get deleted, but the owner that is used as a key to find them has changed! This results in a failed assert!

The UActorComponent::Rename() method is called in AGameplayAbilityWorldReticle_ActorVisualization::InitializeReticleVisualizationInformation(), if that helps someone.


Steps to Reproduce the error like I got it:

  1. Create a project with the Gameplay Abilities stuff enabled.
  2. Use The GameplayAbilityTargetActor_ActorPlacement class, e.g. with an AbilityTask_WaitTargetData, to place any actor that contains a SkeletalMeshComponent.

Steps to Reproduce the error more easily:

  1. UActorComponent::Rename() a skeletal mesh component to have another actor as outer and the other actor.

It would be very very nice if someone could help me in finding a workaround for this problem! Is this a bug and the programmers have not thought about it or am I doing something wrong?

Thanks in advance!


Relevant part of the crash report:

Assertion failed: Pair != nullptr [File:D:\Build\++UE4\Sync\Engine\Source\Runtime\Core\Public\Containers/Map.h] [Line: 476]

UE4Editor_Core!FDebug::AssertFailed() [d:\build\++ue4\sync\engine\source\runtime\core\private\misc\assertionmacros.cpp:417]
UE4Editor_Engine!FAnimUpdateRateManager::CleanupUpdateRateParametersRef() [d:\build\++ue4\sync\engine\source\runtime\engine\private\components\skinnedmeshcomponent.cpp:159]
UE4Editor_Engine!USkeletalMeshComponent::OnUnregister() [d:\build\++ue4\sync\engine\source\runtime\engine\private\components\skeletalmeshcomponent.cpp:617]
UE4Editor_Engine!UActorComponent::ExecuteUnregisterEvents() [d:\build\++ue4\sync\engine\source\runtime\engine\private\components\actorcomponent.cpp:1280]
UE4Editor_Engine!UActorComponent::UnregisterComponent() [d:\build\++ue4\sync\engine\source\runtime\engine\private\components\actorcomponent.cpp:1066]
UE4Editor_Engine!AActor::UnregisterAllComponents() [d:\build\++ue4\sync\engine\source\runtime\engine\private\actor.cpp:4118]
UE4Editor_Engine!UWorld::DestroyActor() [d:\build\++ue4\sync\engine\source\runtime\engine\private\levelactor.cpp:694]
UE4Editor_Engine!AActor::Destroy() [d:\build\++ue4\sync\engine\source\runtime\engine\private\actor.cpp:3698]
UE4Editor_GameplayAbilities!AGameplayAbilityTargetActor_ActorPlacement::EndPlay() [d:\build\++ue4\sync\engine\plugins\runtime\gameplayabilities\source\gameplayabilities\private\abilities\gameplayabilitytargetactor_actorplacement.cpp:25]
UE4Editor_Engine!AActor::Destroyed() [d:\build\++ue4\sync\engine\source\runtime\engine\private\actor.cpp:2086]
UE4Editor_Engine!UWorld::DestroyActor() [d:\build\++ue4\sync\engine\source\runtime\engine\private\levelactor.cpp:592]
UE4Editor_Engine!AActor::Destroy() [d:\build\++ue4\sync\engine\source\runtime\engine\private\actor.cpp:3698]
UE4Editor_GameplayAbilities!UAbilityTask_WaitTargetData::OnDestroy() [d:\build\++ue4\sync\engine\plugins\runtime\gameplayabilities\source\gameplayabilities\private\abilities\tasks\abilitytask_waittargetdata.cpp:350]
UE4Editor_GameplayTasks!UGameplayTask::EndTask() [d:\build\++ue4\sync\engine\source\runtime\gameplaytasks\private\gameplaytask.cpp:173]
UE4Editor_GameplayAbilities!UAbilityTask_WaitTargetData::OnTargetDataReadyCallback() [d:\build\++ue4\sync\engine\plugins\runtime\gameplayabilities\source\gameplayabilities\private\abilities\tasks\abilitytask_waittargetdata.cpp:291]
UE4Editor_GameplayAbilities!TBaseUObjectMethodDelegateInstance<0,UAbilityTask_WaitTargetData const ,void __cdecl(FGameplayAbilityTargetDataHandle const & __ptr64)>::ExecuteIfSafe() [d:\build\++ue4\sync\engine\source\runtime\core\public\delegates\delegateinstancesimpl.h:679]
UE4Editor_GameplayAbilities!TBaseMulticastDelegate<void,FGameplayAbilityTargetDataHandle const & __ptr64>::Broadcast() [d:\build\++ue4\sync\engine\source\runtime\core\public\delegates\delegatesignatureimpl.inl:974]
UE4Editor_GameplayAbilities!AGameplayAbilityTargetActor_Trace::ConfirmTargetingAndContinue() [d:\build\++ue4\sync\engine\plugins\runtime\gameplayabilities\source\gameplayabilities\private\abilities\gameplayabilitytargetactor_trace.cpp:203]
UE4Editor_GameplayAbilities!AGameplayAbilityTargetActor::ConfirmTargeting() [d:\build\++ue4\sync\engine\plugins\runtime\gameplayabilities\source\gameplayabilities\private\abilities\gameplayabilitytargetactor.cpp:94]

Okay, since I don’t know any better solution yet, I ended up copying two classes. My fix for this problem is to call UnregisterComponent() before Rename() and RegisterComponent() after it again, to have the right owner as key in the map in FAnimUpdateRateManager. To do this, I had to change and therefore copy the GameplayAbilityWorldReticle_ActorVisualization class, and to use my own class, the GameplayAbilityTargetActor_ActorPlacement class, too.

This is not too bad since these classes are really just examples on how to use the system, but well, copying code is never good. Also, there might be quite a bit of performance impact due to the reregistering of the component.

I am definitely open to better solutions! That is why I don’t mark this question as answered. I hope that that is okay.