Hi,
I have blueprint with some destructible components attached to static mesh component, and when I’m changing a level contanins this BP to another and again to mentioned level, then close the editor the engine will crash.
Stack trace:
UE4Editor-Core-Win64-Debug.dll!StaticFailDebug(const wchar_t * Error, const char * File, int Line, const wchar_t * Description, bool bIsEnsure) Line 190 C++
UE4Editor-Core-Win64-Debug.dll!FDebug::AssertFailed(const char * Expr, const char * File, int Line, const wchar_t * Format, ...) Line 224 C++
UE4Editor-Engine-Win64-Debug.dll!FBodyInstance::InitBody(UBodySetup * Setup, const FTransform & Transform, UPrimitiveComponent * PrimComp, FPhysScene * InRBScene, physx::PxAggregate * InAggregate) Line 959 C++
> UE4Editor-Engine-Win64-Debug.dll!UPrimitiveComponent::UnWeldFromParent() Line 681 C++
UE4Editor-Engine-Win64-Debug.dll!USceneComponent::DetachFromParent(bool bMaintainWorldPosition) Line 927 C++
UE4Editor-Engine-Win64-Debug.dll!USceneComponent::OnComponentDestroyed() Line 260 C++
UE4Editor-Engine-Win64-Debug.dll!UActorComponent::BeginDestroy() Line 303 C++
UE4Editor-Engine-Win64-Debug.dll!USceneComponent::BeginDestroy() Line 1273 C++
UE4Editor-Engine-Win64-Debug.dll!UPrimitiveComponent::BeginDestroy() Line 733 C++
UE4Editor-Engine-Win64-Debug.dll!UMeshComponent::BeginDestroy() Line 20 C++
This assert was fired BodyInstance.cpp-> FBodyInstance::InitBody:
// whenever update filter, this check will trigger, it seems it's best to know when we initialize body instead.
check(BodySetup.IsValid());
The engine was trying to unweld destructible component but body instance for this component was invalid!
I started digging why engine welding destructible components.
First of all the destructible component in BP isn’t flagged with bAutoWeld, bAutoWeld is set to false.
I think this flag is ommited by engine in the USceneComponent::AttachTo, not sure if it is a bug or feature, I believe the engine should check bAutoWeld before welding:
> UE4Editor-Engine-Win64-Debug.dll!UPrimitiveComponent::WeldToImplementation(USceneComponent * InParent, FName ParentSocketName, bool bWeldSimulatedChild) Line 625 C++
UE4Editor-Engine-Win64-Debug.dll!USceneComponent::AttachTo(USceneComponent * Parent, FName InSocketName, EAttachLocation::Type AttachType, bool bWeldSimulatedBodies) Line 905 C++
UE4Editor-Engine-Win64-Debug.dll!USceneComponent::K2_AttachTo(USceneComponent * InParent, FName InSocketName, EAttachLocation::Type AttachLocationType, bool bWeldSimulatedBodies) Line 711 C++
UE4Editor-Engine-Win64-Debug.dll!USceneComponent::execK2_AttachTo(FFrame & Stack, void * const Result) Line 82 C++
UE4Editor-CoreUObject-Win64-Debug.dll!UFunction::Invoke(UObject * Obj, FFrame & Stack, void * const Result) Line 3699 C++
UE4Editor-CoreUObject-Win64-Debug.dll!UObject::CallFunction(FFrame & Stack, void * const Result, UFunction * Function) Line 395 C++
Second why destructibles are welded ???
I am looking at UPrimitiveComponent::WeldToImplementation function
BI->bWelded = true;
//There are multiple cases to handle:
//Root is kinematic, simulated
//Child is kinematic, simulated
//Child always inherits from root
//if root is kinematic simply set child to be kinematic and we're done
if (RootComponent->IsSimulatingPhysics(SocketName) == false)
{
BI->WeldParent = NULL;
SetSimulatePhysics(false);
return false; //return false because we need to continue with regular body initialization
}
//root is simulated so we actually weld the body
FTransform RelativeTM = RootComponent == AttachParent ? GetRelativeTransform() : GetComponentToWorld().GetRelativeTransform(RootComponent->GetComponentToWorld()); //if direct parent we already have relative. Otherwise compute it
RootBI->Weld(BI, GetComponentToWorld());
BI->WeldParent = RootBI;
BI->bWelded = true; is set before RootBI->Weld(BI, GetComponentToWorld()); why?
What is means ? Is it welded or not?
Besides, why BI->bWelded is set to true without checking RootBI->Weld(BI, GetComponentToWorld()); result?
I moved BI->bWelded = true; under if (RootComponent->IsSimulatingPhysics(SocketName) == false) check.
//There are multiple cases to handle:
//Root is kinematic, simulated
//Child is kinematic, simulated
//Child always inherits from root
//if root is kinematic simply set child to be kinematic and we're done
if (RootComponent->IsSimulatingPhysics(SocketName) == false)
{
BI->WeldParent = NULL;
SetSimulatePhysics(false);
return false; //return false because we need to continue with regular body initialization
}
BI->bWelded = true;
//root is simulated so we actually weld the body
FTransform RelativeTM = RootComponent == AttachParent ? GetRelativeTransform() : GetComponentToWorld().GetRelativeTransform(RootComponent->GetComponentToWorld()); //if direct parent we already have relative. Otherwise compute it
RootBI->Weld(BI, GetComponentToWorld());
BI->WeldParent = RootBI;
And I can’t reproduce this crash anymore
Conclusions:
- I think welding destructibles should be disabled by default
- Set BI->bWelded = true; when the Weld function welded something.
What do you guys think about it?
Regards
Pierdek