Physics Constraints, Create New Constraints Dynamically During Runtime
Contents
Overview
Author: ( )
Dear Community,
This is a tutorial on how to dynamically create Physics constraints during runtime!
See my videos below for examples of what you can do with dynamic physics constraints!
The focus of my videos is enabling a user of my In-game editor to make their own physics objects.... and then destroy them into dynamic physics pieces!
Video of Dynamic Physics Constraints
<youtube> https://www.youtube.com/watch?v=ZgQxWQ9oWoQ&feature=youtu.be
In this video I am showing how you can make composite physics shapes that will act as a coordinated larger physics constraint, and you can make this combinations dynamically during runtime from user input!
Video of Destructible Dynamic Physics Constraints
<youtube> https://www.youtube.com/watch?v=sOl5uKe16ZM&feature=youtu.be
In this video I demonstrate dynamic setting and detaching of physics constraints to make a composite destructible shape that can be destroyed into partial subsections that act like physics sub objects of the original whole.
C++ for Making a Dynamic Physics Constraint
void ASomeClass::CreateNewPhysicsConstraintBetween(AStaticMeshActor* RootSMA, AStaticMeshActor* TargetSMA)
{
//set up the constraint instance with all the desired values
FConstraintInstance ConstraintInstance;
//set values here, see functions I am sharing with you below
//UYourStaticLibrary::SetLinearLimits(ConstraintInstance, ...); //or make the functions below non static
//UYourStaticLibrary::SetAngularLimits(ConstraintInstance, ...);
//New Object
UPhysicsConstraintComponent* ConstraintComp = NewObject<UPhysicsConstraintComponent>(RootSMA);
if(!ConstraintComp)
{
//UE_LOG constraint UObject could not be created!
return;
}
//~~~~~~~~~~~~~~~~~~~~~~~~
//Set Constraint Instance!
ConstraintComp->ConstraintInstance = ConstraintInstance;
//~~~~~~~~~~~~~~~~~~~~~~~~
//Set World Location
ConstraintComp->SetWorldLocation(RootSMA->GetActorLocation());
//Attach to Root!
ConstraintComp->AttachTo(RootSMA->GetRootComponent(), NAME_None, EAttachLocation::KeepWorldPosition);
//~~~ Init Constraint ~~~
ConstraintComp->SetConstrainedComponents(RootSMA->StaticMeshComponent, NAME_None, TargetSMA->StaticMeshComponent,NAME_None);
}
My C++ Physics Constraint Library Functions
I've tested these and used them in the videos above!
Free,Limited,Locked
For ease of use I just pass in bytes for the free, limited or locked enums.
0 = Free 1 = Limited 2 = Locked
Example Usage
//A new constraint instance!
FConstraintInstance ConstraintInstance;
//Set Angular Limits of Constraint that was just created
UYourStaticLibrary::SetAngularLimits( //or make functions below non static, put in .h
ConstraintInstance,
1, //swing 1 limited
1, //swing 2 limited
0, //twist is free
60, //swing 1 angle limit
30 //swing 2 angle limit
10 //twist limit (not used cause its free)
);
My C++ Functions For You
static FORCEINLINE void SetLinearLimits(
FConstraintInstance& Constraint,
bool bDisableCollision,
const uint8 XLim, const uint8 YLim, const uint8 ZLim,
const float Size,
bool SoftLimit=true,
const float SoftStiffness=0,
const float SoftDampening=0
)
{
//Collision
Constraint.bDisableCollision = bDisableCollision;
switch (XLim)
{
case 0 : Constraint.LinearXMotion = ELinearConstraintMotion::LCM_Free; break;
case 1 : Constraint.LinearXMotion = ELinearConstraintMotion::LCM_Limited; break;
case 2 : Constraint.LinearXMotion = ELinearConstraintMotion::LCM_Locked; break;
}
switch (YLim)
{
case 0 : Constraint.LinearYMotion = ELinearConstraintMotion::LCM_Free; break;
case 1 : Constraint.LinearYMotion = ELinearConstraintMotion::LCM_Limited; break;
case 2 : Constraint.LinearYMotion = ELinearConstraintMotion::LCM_Locked; break;
}
switch (ZLim)
{
case 0 : Constraint.LinearZMotion = ELinearConstraintMotion::LCM_Free; break;
case 1 : Constraint.LinearZMotion = ELinearConstraintMotion::LCM_Limited; break;
case 2 : Constraint.LinearZMotion = ELinearConstraintMotion::LCM_Locked; break;
}
//~~~~~~~~~~
Constraint.LinearLimitSize = Size;
if(SoftLimit) Constraint.bLinearLimitSoft = 1;
else Constraint.bLinearLimitSoft = 0;
Constraint.LinearLimitStiffness = SoftStiffness;
Constraint.LinearLimitDamping = SoftDampening;
}
static FORCEINLINE void SetAngularLimits(
FConstraintInstance& Constraint,
const uint8 S1Lim, const uint8 S2Lim, const uint8 TLim,
const float Swing1LimitAngle,
const float Swing2LimitAngle,
const float TwistLimitAngle,
bool SoftSwingLimit=true, bool SoftTwistLimit=true,
const float SwingStiff=0, const float SwingDamp=0,
const float TwistStiff=0, const float TwistDamp=0
)
{
switch (S1Lim)
{
case 0 : Constraint.AngularSwing1Motion = EAngularConstraintMotion::ACM_Free; break;
case 1 : Constraint.AngularSwing1Motion = EAngularConstraintMotion::ACM_Limited; break;
case 2 : Constraint.AngularSwing1Motion = EAngularConstraintMotion::ACM_Locked; break;
}
switch (S2Lim)
{
case 0 : Constraint.AngularSwing2Motion = EAngularConstraintMotion::ACM_Free; break;
case 1 : Constraint.AngularSwing2Motion = EAngularConstraintMotion::ACM_Limited; break;
case 2 : Constraint.AngularSwing2Motion = EAngularConstraintMotion::ACM_Locked; break;
}
switch (TLim)
{
case 0 : Constraint.AngularTwistMotion = EAngularConstraintMotion::ACM_Free; break;
case 1 : Constraint.AngularTwistMotion = EAngularConstraintMotion::ACM_Limited; break;
case 2 : Constraint.AngularTwistMotion = EAngularConstraintMotion::ACM_Locked; break;
}
//~~~~~~~~~~
//Soft Lmit?
if(SoftSwingLimit) Constraint.bSwingLimitSoft = 1;
else Constraint.bSwingLimitSoft = 0;
if(SoftTwistLimit) Constraint.bTwistLimitSoft = 1;
else Constraint.bTwistLimitSoft = 0;
//Limit Angles
Constraint.Swing1LimitAngle = Swing1LimitAngle;
Constraint.Swing2LimitAngle = Swing2LimitAngle;
Constraint.TwistLimitAngle = TwistLimitAngle;
Constraint.SwingLimitStiffness = SwingStiff;
Constraint.SwingLimitDamping = SwingDamp;
Constraint.TwistLimitStiffness = TwistStiff;
Constraint.TwistLimitDamping = TwistDamp;
}
Conclusion
Now you have the basic code to do all sorts of fancy things with Dynamic Physics Constraints that you set up at runtime from c++ !
Play with the values of the two functions I am sharing with you to get all sorts of different effects!
Basics:
Linear Constraints limit physical motion of the target SMA through space
Angular Constraints control the rotation of the target SMA.
Enjoy!
( )
Updated C++ Functions
Author: ( )
The information above is an excellent guide ( thanks Rama! ). However, it appears as though in newer versions of Unreal Engine, the variables for these constraints were deprecated without documentation! I've done my best to provide updated code, shown below. Feel free to update this section as necessary, if additional changes are necessary or when official documentation becomes available.
//////////////////////////////////////////////////
// SetAngularLimits for Physics Constraints
//
// THE VARIABLES FOR CONSTRAINT PROPERTIES HAVE BEEN DEPRECATED WITHOUT DOCUMENTATION!
//
// Despite appearing correct, I'm not entirely certain these updated functions are being correctly used. However, they have been tested, and do appear to be working correctly.
/////////////////////////////////////////////////
static FORCEINLINE void SetAngularLimits(
FConstraintInstance& Constraint,
const uint8 Swing1Limit, const uint8 Swing2Limit, const uint8 TwistLimit,
const float Swing1LimitAngle,
const float Swing2LimitAngle,
const float TwistLimitAngle,
bool SoftSwingLimit = true, bool SoftTwistLimit = true,
const float SwingStiff = 0, const float SwingDamp = 0,
const float TwistStiff = 0, const float TwistDamp = 0
)
{
switch (Swing1Limit)
{
//case 0: Constraint.AngularSwing1Motion_DEPRECATED = EAngularConstraintMotion::ACM_Free; break;
case 0: Constraint.SetAngularSwing1Motion(EAngularConstraintMotion::ACM_Free); break;
//case 1: Constraint.AngularSwing1Motion_DEPRECATED = EAngularConstraintMotion::ACM_Limited; break;
case 1: Constraint.SetAngularSwing1Motion( EAngularConstraintMotion::ACM_Limited); break;
//case 2: Constraint.AngularSwing1Motion_DEPRECATED = EAngularConstraintMotion::ACM_Locked; break;
case 2: Constraint.SetAngularSwing1Motion(EAngularConstraintMotion::ACM_Locked); break;
}
switch (Swing2Limit)
{
//case 0: Constraint.AngularSwing2Motion_DEPRECATED = EAngularConstraintMotion::ACM_Free; break;
case 0: Constraint.SetAngularSwing2Motion(EAngularConstraintMotion::ACM_Free); break;
//case 1: Constraint.AngularSwing2Motion_DEPRECATED = EAngularConstraintMotion::ACM_Limited; break;
case 1: Constraint.SetAngularSwing2Motion(EAngularConstraintMotion::ACM_Limited); break;
//case 2: Constraint.AngularSwing2Motion_DEPRECATED = EAngularConstraintMotion::ACM_Locked; break;
case 2: Constraint.SetAngularSwing2Motion(EAngularConstraintMotion::ACM_Locked); break;
}
switch (TwistLimit)
{
//case 0: Constraint.AngularTwistMotion_DEPRECATED = EAngularConstraintMotion::ACM_Free; break;
case 0: Constraint.SetAngularTwistMotion(EAngularConstraintMotion::ACM_Free); break;
//case 1: Constraint.AngularTwistMotion_DEPRECATED = EAngularConstraintMotion::ACM_Limited; break;
case 1: Constraint.SetAngularTwistMotion(EAngularConstraintMotion::ACM_Limited); break;
//case 2: Constraint.AngularTwistMotion_DEPRECATED = EAngularConstraintMotion::ACM_Locked; break;
case 2: Constraint.SetAngularTwistMotion(EAngularConstraintMotion::ACM_Locked); break;
}
// Soft Limit?
//if (SoftSwingLimit) Constraint.bSwingLimitSoft_DEPRECATED = 1;
if (SoftSwingLimit) Constraint.ProfileInstance.LinearLimit.bSoftConstraint = 1;
//else Constraint.bSwingLimitSoft_DEPRECATED = 0;
else Constraint.ProfileInstance.LinearLimit.bSoftConstraint = 0;
//if (SoftTwistLimit) Constraint.bTwistLimitSoft_DEPRECATED = 1;
if (SoftTwistLimit) Constraint.ProfileInstance.TwistLimit.bSoftConstraint = 1;
//else Constraint.bTwistLimitSoft_DEPRECATED = 0;
else Constraint.ProfileInstance.TwistLimit.bSoftConstraint = 0;
// Limit Angles
//Constraint.Swing1LimitAngle_DEPRECATED = Swing1LimitAngle;
Constraint.SetAngularSwing1Limit(EAngularConstraintMotion::ACM_Free, Swing1LimitAngle);
//Constraint.Swing2LimitAngle_DEPRECATED = Swing2LimitAngle;
Constraint.SetAngularSwing2Limit(EAngularConstraintMotion::ACM_Free, Swing2LimitAngle);
//Constraint.TwistLimitAngle_DEPRECATED = TwistLimitAngle;
Constraint.SetAngularTwistLimit(EAngularConstraintMotion::ACM_Free, TwistLimitAngle);
//Constraint.SwingLimitStiffness_DEPRECATED = SwingStiff;
Constraint.ProfileInstance.LinearLimit.Stiffness = SwingStiff;
//Constraint.SwingLimitDamping_DEPRECATED = SwingDamp;
Constraint.ProfileInstance.LinearLimit.Damping = SwingDamp;
//Constraint.TwistLimitStiffness_DEPRECATED = TwistStiff;
Constraint.ProfileInstance.TwistLimit.Stiffness = TwistStiff;
//Constraint.TwistLimitDamping_DEPRECATED = TwistDamp;
Constraint.ProfileInstance.TwistLimit.Damping = TwistDamp;
}
//////////////////////////////////////////////////
// SetLinearLimits for Physics Constraints
//
// THE VARIABLES FOR CONSTRAINT PROPERTIES HAVE BEEN DEPRECATED WITHOUT DOCUMENTATION!
//
// Despite appearing correct, I'm not entirely certain these updated functions are being correctly used. However, they have been tested, and do appear to be working correctly.
/////////////////////////////////////////////////
static FORCEINLINE void SetLinearLimits(
FConstraintInstance& Constraint,
bool bDisableCollision,
const uint8 XLimit, const uint8 YLimit, const uint8 ZLimit,
const float Size,
bool SoftLimit = true,
const float SoftStiffness = 0,
const float SoftDampening = 0
)
{
// Collision
//Constraint.bDisableCollision_DEPRECATED = bDisableCollision;
Constraint.ProfileInstance.bDisableCollision = bDisableCollision;
switch (XLimit)
{
//case 0: Constraint.LinearXMotion_DEPRECATED = ELinearConstraintMotion::LCM_Free; break;
case 0: Constraint.SetLinearXMotion(ELinearConstraintMotion::LCM_Free); break;
//case 1: Constraint.LinearXMotion_DEPRECATED = ELinearConstraintMotion::LCM_Limited; break;
case 1: Constraint.SetLinearXMotion(ELinearConstraintMotion::LCM_Limited); break;
//case 2: Constraint.LinearXMotion_DEPRECATED = ELinearConstraintMotion::LCM_Locked; break;
case 2: Constraint.SetLinearXMotion(ELinearConstraintMotion::LCM_Locked); break;
}
switch (YLimit)
{
//case 0: Constraint.LinearYMotion_DEPRECATED = ELinearConstraintMotion::LCM_Free; break;
case 0: Constraint.SetLinearYMotion(ELinearConstraintMotion::LCM_Free); break;
//case 1: Constraint.LinearYMotion_DEPRECATED = ELinearConstraintMotion::LCM_Limited; break;
case 1: Constraint.SetLinearYMotion(ELinearConstraintMotion::LCM_Limited); break;
//case 2: Constraint.LinearYMotion_DEPRECATED = ELinearConstraintMotion::LCM_Locked; break;
case 2: Constraint.SetLinearYMotion(ELinearConstraintMotion::LCM_Locked); break;
}
switch (ZLimit)
{
//case 0: Constraint.LinearZMotion_DEPRECATED = ELinearConstraintMotion::LCM_Free; break;
case 0: Constraint.SetLinearZMotion(ELinearConstraintMotion::LCM_Free); break;
//case 1: Constraint.LinearZMotion_DEPRECATED = ELinearConstraintMotion::LCM_Limited; break;
case 1: Constraint.SetLinearZMotion(ELinearConstraintMotion::LCM_Limited); break;
//case 2: Constraint.LinearZMotion_DEPRECATED = ELinearConstraintMotion::LCM_Locked; break;
case 2: Constraint.SetLinearZMotion(ELinearConstraintMotion::LCM_Locked); break;
}
//Constraint.LinearLimitSize_DEPRECATED = Size;
Constraint.SetLinearLimitSize(Size);
// Soft Limit?
//if (SoftLimit) Constraint.bLinearLimitSoft_DEPRECATED = 1;
if (SoftLimit) Constraint.ProfileInstance.LinearLimit.bSoftConstraint = 1;
//else Constraint.bLinearLimitSoft_DEPRECATED = 0;
else Constraint.ProfileInstance.LinearLimit.bSoftConstraint = 0;
//Constraint.LinearLimitStiffness_DEPRECATED = SoftStiffness;
Constraint.ProfileInstance.LinearLimit.Stiffness = SoftStiffness;
//Constraint.LinearLimitDamping_DEPRECATED = SoftDampening;
Constraint.ProfileInstance.LinearLimit.Damping = SoftDampening;
}