When a Chaos joint constraint is first created, having linear/angular plasticity enabled will unexpectedly force its linear/angular drive position target to zero on initialization, disregarding the settings made on the Editor's Details Panel.
The specific flow starts with function Chaos::FJointConstraintPhysicsProxy::InitializeOnPhysicsThread(), which calls Chaos::FPBDRigidsEvolutionGBF::CreateJointConstraint(), which in turn calls Chaos::FPBDJointConstraintHandle::SetSettings() on the joint that has just been created. SetSettings() checks if plasticity is enabled. If it is, then it saves the values for the drive position targets before applying the new constraint settings, and restores those saved values afterwards, which means that they are reset to zero on this initialization scenario. This looks like an attempt to forbid changes to the drive position targets afer plasticity is enabled, but it also effectively prevents the initial targets from being set correctly. Note that if a constraint is created with some non-zero targets and with plasticity initially disabled, enabling plasticity later does allow the initial targets to be kept and used normally.
The backup-and-restore logic was introduced on CL 18730967 from January 2022 with commit message "Reset Joint constraint based on internal plasticity values when the settings are updated from the proxy". FJointConstraintPhysicsProxy has changed substantially since then, so it is possible that the logic worked correctly at first but not anymore.
1. Create a new level from the Basic template.
2. Place a Cone actor (will be affected by the constraint)
2.1. Location (100, 0, 200)
2.2. Enable "simulate physics"
2.3. Disable gravity
3. Place a Sphere actor (just for visual reference, not used in the constraint)
3.1. Location (200, 0, 200)
3.2. Set "Collision Preset" to "No Collision"
4. Place a PhysicsConstraintActor
4.1. Location (100, 0, 200)
4.2. On "Constraint", set "Constraint Actor 1" to the Cone.
4.3. On "Linear Limits", set all axes to "Free".
4.4. On "Angular Limits", set all motions to "Locked".
4.5. On "Linear Motor", enable all position targets, set the target to (100, 0, 0), and set the strength to 500
5. Edit the Level Blueprint
5.1. On "Begin Play", set the Cone actor location to (500, 500, 500)
6. Simulate in editor. The cone is driven to the sphere at (200,0,200), as expected.
7. Select the PhysicsConstraintActor
7.1. On "Linear Limits", enable "Linear Plasticity" and set its threshold to 1000
8. Simulate in editor. The cone is incorrectly driven to its original location at (100, 0, 200)
8.1. A breakpoint in FPBDJointConstraintHandle::SetSettings() reveals that LinearDrivePositionTarget is being unexpectedly reset to zero
9. Defer enabling linear plasticity
9.1. On the PhysicsConstraintActor, disable "Linear Plasticity"
9.2. On the Level Blueprint, at the end of Begin Play, add a node "Delay Until Next Frame", then call node SetLinearPlasticity() on the PhysicsConstraint to enable plasticity with a threshold of 1000
9.3. Simulate in editor. The cone is correctly driven to the sphere at (200,0,200).
Chaos::FPBDJointConstraintHandle::SetSettings(const Chaos::FPBDJointSettings & InSettings) Line 313
Chaos::FPBDRigidsEvolutionGBF::CreateJointConstraint(const Chaos::TVector<Chaos::TGeometryParticleHandleImp<double,3,1> *,2> & InConstrainedParticles, const Chaos::FPBDJointSettings & InJointSettings) Line 1461
Chaos::FJointConstraintPhysicsProxy::InitializeOnPhysicsThread(Chaos::FPBDRigidsSolver * InSolver, Chaos::FDirtyPropertiesManager & Manager, int DataIdx, Chaos::FDirtyChaosProperties & RemoteData) Line 130
Chaos::FPBDRigidsSolver::ProcessSinglePushedData_Internal::__l2::<lambda_3>::operator()(int) Line 1800
Chaos::FDirtySet::ForEachProxy<`Chaos::FPBDRigidsSolver::ProcessSinglePushedData_Internal'::`2'::<lambda_3>>(const Chaos::FPBDRigidsSolver::ProcessSinglePushedData_Internal::__l2::<lambda_3> & Func) Line 231
Chaos::FPBDRigidsSolver::ProcessSinglePushedData_Internal(Chaos::FPushPhysicsData & PushData) Line 1864
Chaos::FPBDRigidsSolver::ProcessPushedData_Internal(Chaos::FPushPhysicsData & PushData) Line 2091
Chaos::FPhysicsSolverProcessPushDataTask::ProcessPushData() Line 144
Chaos::FPhysicsSolverProcessPushDataTask::DoTask(ENamedThreads::Type) Line 119
TGraphTask<Chaos::FPhysicsSolverProcessPushDataTask>::ExecuteTask() Line 710
UE::Tasks::Private::FTaskBase::TryExecuteTask() Line 518
UE::Tasks::Private::FTaskBase::Init::__l2::<lambda_1>::operator()() Line 180
LowLevelTasks::FTask::Init::__l15::<lambda_1>::operator()(const bool) Line 500
Invoke(LowLevelTasks::FTask::Init::__l15::<lambda_1> &) Line 47
LowLevelTasks::TTaskDelegate<LowLevelTasks::FTask * __cdecl(bool),48>::TTaskDelegateImpl<`LowLevelTasks::FTask::Init<`UE::Tasks::Private::FTaskBase::Init'::`2'::<lambda_1>>'::`15'::<lambda_1>,0>::Call(void *) Line 162
LowLevelTasks::TTaskDelegate<LowLevelTasks::FTask * __cdecl(bool),48>::TTaskDelegateImpl<`LowLevelTasks::FTask::Init<`UE::Tasks::Private::FTaskBase::Init'::`2'::<lambda_1>>'::`15'::<lambda_1>,0>::CallAndMove(LowLevelTasks::TTaskDelegate<LowLevelTasks::FTask * __cdecl(bool),48> & Destination, void * InlineData, unsigned int DestInlineSize, bool <Params_0>) Line 171
owLevelTasks::TTaskDelegate<LowLevelTasks::FTask * __cdecl(bool),48>::CallAndMove(LowLevelTasks::TTaskDelegate<LowLevelTasks::FTask * __cdecl(bool),48> &) Line 309
owLevelTasks::FTask::ExecuteTask() Line 628
owLevelTasks::FScheduler::ExecuteTask(LowLevelTasks::FTask * InTask) Line 400
owLevelTasks::FScheduler::TryExecuteTaskFrom(LowLevelTasks::Private::FWaitEvent *) Line 701
owLevelTasks::FScheduler::WorkerLoop(LowLevelTasks::Private::FWaitEvent * WorkerEvent, LowLevelTasks::Private::TLocalQueueRegistry<1024,1024>::TLocalQueue * WorkerLocalQueue, unsigned int WaitCycles, bool bPermitBackgroundWork) Line 799
owLevelTasks::FScheduler::WorkerMain(LowLevelTasks::Private::FWaitEvent * WorkerEvent, LowLevelTasks::Private::TLocalQueueRegistry<1024,1024>::TLocalQueue * WorkerLocalQueue, unsigned int WaitCycles, bool) Line 858
owLevelTasks::FScheduler::CreateWorker::__l2::<lambda_1>::operator()() Line 220
E::Core::Private::Function::TFunctionRefBase<UE::Core::Private::Function::TFunctionStorage<1>,void __cdecl(void)>::operator()() Line 414
ThreadImpl::Run() Line 69
RunnableThreadWin::Run() Line 167
RunnableThreadWin::GuardedRun() Line 79
There's no existing public thread on this issue, so head over to Questions & Answers just mention UE-368639 in the post.
| 0 |
| Component | UE - Simulation - Core |
|---|---|
| Affects Versions | 5.7, 5.8 |
| Target Fix | 5.8 |
| Fix Commit | 51556398 |
|---|
| Created | Mar 5, 2026 |
|---|---|
| Resolved | Mar 9, 2026 |
| Updated | Mar 17, 2026 |