Creating a new Physics constraint component in C++ causes swinging bug

Hello,

I’m working on a vehicle game in c++. So far, I got a vehicle model with only 4 wheels and root bone which is working perfectly with UE4’s WheeledVehicle system. What I’m trying to do is creating door, hood, baggage, wiper etc. animations. After I seen a lot of difficulties in only bone based solutions, I decided to use physics constraint components for connecting physical-movable parts to the car body.

http://burakertugrul.com/files/VehicleBug/Works.gif

I have properly set all necessary physics constraint components in the blueprint up. Works perfectly when I press play. I have created two modules on this. First one is disabling physics constraint components and turning collisions, physics simulation of relevant static mesh components off to be able to change rotations of these parts without physics interaction. This module works correctly.

http://burakertugrul.com/files/VehicleBug/Stopping.gif

FConstraintAndComponent* Current;
	for (int32 i = 0; i < ConstraintsAndComponents.Num(); i++)
	{
		Current = &ConstraintsAndComponents[i];
		if (Current->PhysicsConstraintComponent)
		{
			if (Current->PhysicsConstraintComponent->IsValidLowLevel())
			{
				Current->PhysicsConstraintComponent->DestroyComponent(false);
			}
		}

		if (Current->StaticMeshComponent)
		{
			if (Current->StaticMeshComponent->IsValidLowLevel())
			{
				Current->StaticMeshComponent->SetSimulatePhysics(false);
				Current->StaticMeshComponent->SetCollisionProfileName(FName("NoCollision"));
				Current->StaticMeshComponent->AttachTo(GetMesh(), NAME_None, EAttachLocation::KeepRelativeOffset, true);
				Current->StaticMeshComponent->SetRelativeTransform(FTransform(FRotator::ZeroRotator, FVector::ZeroVector, FVector(1.0f, 1.0f, 1.0f)));
			}
		}
	}

But weirdly, second module which is created to spawn a new UPhysicsConstraintComponent and to assign initial parameters of ConstraintInstance to the newly created one, works buggy. It creates the new constraint and assign limits correctly, but it never stops swinging. Doors are behaving just like a chicken that wants to fly. :slight_smile:

http://burakertugrul.com/files/VehicleBug/Buggy.gif

FConstraintAndComponent* Current;
	for (int32 i = 0; i < ConstraintsAndComponents.Num(); i++)
	{
		Current = &ConstraintsAndComponents[i];
		if (Current->PhysicsConstraintComponent)
		{
			if (Current->PhysicsConstraintComponent->IsValidLowLevel())
			{
				Current->PhysicsConstraintComponent->DestroyComponent(false);
			}
		}

		TArray<UActorComponent*> StaticMeshComponents = this->GetComponentsByClass(UStaticMeshComponent::StaticClass());
		bool FirstFound = false, SecondFound = false, SecondIsTheMesh = false;
		UPrimitiveComponent* ConstraintComponent_1 = nullptr;
		UPrimitiveComponent* ConstraintComponent_2 = nullptr;
		UStaticMeshComponent* TempStaMeshComp = nullptr;

		if (Current->Constraint_ComponentName_2 == FName("Mesh"))
		{
			SecondFound = true;
			SecondIsTheMesh = true;
			ConstraintComponent_2 = GetMesh();
		}

		for (int32 j = 0; j < StaticMeshComponents.Num(); j++)
		{
			TempStaMeshComp = (UStaticMeshComponent*)StaticMeshComponents[j];
			if (TempStaMeshComp)
			{
				if (TempStaMeshComp->ComponentTags.Num() > 0)
				{
					if (!FirstFound)
					{
						if (TempStaMeshComp->ComponentTags[0] == Current->Constraint_ComponentName_1)
						{
							FirstFound = true;
							ConstraintComponent_1 = TempStaMeshComp;
							if (SecondFound) break;
							else continue;
						}
					}
					if (!SecondFound)
					{
						if (TempStaMeshComp->ComponentTags[0] == Current->Constraint_ComponentName_2)
						{
							SecondFound = true;
							ConstraintComponent_2 = TempStaMeshComp;
							if (FirstFound) break;
							else continue;
						}
					}
				}
			}
		}

		if (FirstFound && SecondFound)
		{
			if (Current->StaticMeshComponent)
			{
				if (Current->StaticMeshComponent->IsValidLowLevel())
				{
					Current->StaticMeshComponent->SetSimulatePhysics(true);
					Current->StaticMeshComponent->SetCollisionProfileName(FName("PhysicsActor"));
				}
			}

			FConstraintInstance ConstraintInstance;
			ConstraintInstance.AngularRotationOffset = Current->AngularRotationOffset;
			ConstraintInstance.AngularSwing1Motion = Current->Swing1Motion;
			ConstraintInstance.AngularSwing2Motion = Current->Swing2Motion;
			ConstraintInstance.AngularTwistMotion = Current->TwistMotion;
			ConstraintInstance.Swing1LimitAngle = Current->Swing1Motion_Angle;
			ConstraintInstance.Swing2LimitAngle = Current->Swing2Motion_Angle;
			ConstraintInstance.TwistLimitAngle = Current->TwistMotion_Angle;
			ConstraintInstance.AngularDriveMode = EAngularDriveMode::SLERP;
			ConstraintInstance.bDisableCollision = 1;

			FString IxToAppend = FString::FromInt(GeneratedConstraintNo++);
			FString NameOfComponent = Current->ActualConstraintComponentName.ToString();

			Current->PhysicsConstraintComponent = NewObject<UPhysicsConstraintComponent>(this, FName(*NameOfComponent.Append(IxToAppend)));

			Current->PhysicsConstraintComponent->ConstraintInstance = ConstraintInstance;

			Current->PhysicsConstraintComponent->AttachTo(Current->ConstraintComponentParent, NAME_None, EAttachLocation::KeepRelativeOffset, false);
			Current->PhysicsConstraintComponent->RegisterComponent();
			Current->PhysicsConstraintComponent->SetRelativeTransform(Current->Constraint_RelativeTransform);
			Current->PhysicsConstraintComponent->ComponentTags.Add(Current->Constraint_ComponentName_1);

			Current->PhysicsConstraintComponent->ComponentName1.ComponentName = Current->Constraint_ComponentName_1;
			Current->PhysicsConstraintComponent->ComponentName2.ComponentName = Current->Constraint_ComponentName_2;
			Current->PhysicsConstraintComponent->OverrideComponent1 = ConstraintComponent_1;
			Current->PhysicsConstraintComponent->OverrideComponent2 = ConstraintComponent_2;
			Current->PhysicsConstraintComponent->ConstraintInstance.ConstraintBone1 = NAME_None;
			Current->PhysicsConstraintComponent->ConstraintInstance.ConstraintBone2 = SecondIsTheMesh == true ? FName("Root") : NAME_None;

			Current->PhysicsConstraintComponent->UpdateConstraintFrames();
			Current->PhysicsConstraintComponent->ConstraintInstance.InitConstraint(Current->PhysicsConstraintComponent, 
				ConstraintComponent_1->GetBodyInstance(NAME_None), 
				ConstraintComponent_2->GetBodyInstance(SecondIsTheMesh == true ? FName("Root") : NAME_None),
				1.0f);
		}
	}

I have tried changing all of the parameters, disabling soft limits, raising or lowering stiffness or damping etc. Didn’t work. Thank you for your help in advance.

Hey -

I’m having a hard time following the code in the second code block. I see where Current is being defined, however I’m not sure where Constraint_ComponentName_1 or Constraint_ComponentName_2 are coming from. Also, line 21 sets SecondFound to true, so that line 43 tehn fails. Is this intended?

If you can reproduce the behavior in a new project, can you provide the steps/code used to help me reproduce this locally?

Hi ,

Thank you for your response. Of course, you can download the project.
Download Link

I’ve debugged all of those variables, nothing falls wrong in runtime. For line 21 and 43, actually there are two senarios. A car part might be attached to VehicleMesh(“Root” bone) or to another static mesh component, just like in the senario of BackWiper->Baggage. Line 21 sets SecondFound to true if constraint parent is VehicleMesh, otherwise line 43 checks for other static meshes in this actor.

Note: I’m using 4.11.2 for this project and haven’t tested it in 4.12, it might be more unstable in 4.12. You can press K and L to switch between Wake and Sleep modes after pressing play.

I tested on 4.12.2, the same issue appears.

Hey -

I have entered the bug report UE-32138 about the behavior of the physics constraints for investigation.

Cheers

,

The issues you are seeing due to a bug fix between 4.11 and 4.12. When PhysicsConstraints are set up (both PhysicsConstraintActors and PhysicsConstraintComponents), they are supposed ComponentNames, as the UI suggested. However, in 4.11 they actually used Property Names.

The simple solution in your case is just to change anywhere you reference “Mesh” for the vehicle mesh to “VehicleMesh”, both in code and in the editor. This should alleviate your issues.

Thanks,
Jon N.

Hi Widmo, thanks for your advice. I’ve tried your solution but it didn’t work. Still same result.

,

So, I think the actual issue is that you attach meshes in your SleepPhysicsOfBone that were never attached, but you never detach them in WakePhysicsOfBone.

Additionally, you actually don’t need to destroy the PhysicsConstraints. Putting the meshes to sleep is enough.

I’ve attached the SedanActor.h and SedanActor.cpp that should work with 4.11 (you’ll need to update the mesh name for 4.12+).

Thanks,
Jon N.

Hi Jon,

Have you tested the project after modifying SedanActor.cpp and .h? Now I’ve tested and this time vehicle flies away. Actually I’ve tried all possible solutions, such as disabling physics and enabling it back etc. Additionally, I’ve tried to detach in waking part of the code without removing destroy constraint part, vehicle flied away in that case too. Destroying the component and creating a new one was closest approach to objective, in other studies; vehicle flied away or broken into pieces somehow, but we still have this swinging problem in this approach. As far as I have seen from community, no one has tried using a static mesh in a constraint and disabling it’s physics during same play session. There are numbers of different kinds of bugs in vehicle development in addition to this, such as physics cannot be disabled for a part in a skeletal mesh to change it’s transform manually. This approach was the most successful one except this issue.

Thanks in advance.

Burak.

Burak,

Yes, I had tried testing with those changes and it worked with no problem. However, I had tested in 4.11 so I’ll try making changes and testing in 4.12.

Thanks,
Jon N.

Burak,

I did verify that the fixes I gave you work with 4.12. However, like I mentioned previously when going to 4.12 and beyond you must update the ComponentNames for your constraints. This includes in the source I gave you:

// We have to change == FName("Mesh") to properly reference the component name.
// if (Current->Constraint_ComponentName_2 == FName("Mesh"))
if (Current->Constraint_ComponentName_2.ToString() == GetMesh()->GetName())
{
    SecondFound = true;
    SecondIsTheMesh = true;
    ConstraintComponent_2 = GetMesh();
}

This also includes the references in the blueprint: /Game/NewVehicle_1/Sedan:

Thanks, Jon N.

Oh my god. It’s working great now. Thank you very much for you help Jon, I appreciated. By the way I realized that, using SetWorldLocation/Rotation just before enabling simulation back in WakePhysicsOfPart function makes vehicle fly away. Also this will be very helpful for community.