Here's a few options (including a couple I believe you already tried for completeness):
1) Constraints currently early-out on low impulses as an optimization. Very light objects early out in the solver faster. Disable this behavior by setting SolverSettings.DefaultMinimumImpulse = 0 or work around it by increasing object mass. (This 'feature' is going away in v2.0.0.)
2) This constraint system is pretty hard to solve. Softening the involved angular constraints should make it easier. Reduce the stiffness more than the damping to make it less prone to oscillation.
3) Instead of actually increasing the size of the chain elements, increase their inertia tensor directly along certain axes by scaling up the desired rows of the box.LocalInertiaTensor. In this case, scaling up Right and Up would help.
4) The longer a chain of objects is, the more difficult it is to solve. If you know that one end will typically be the 'root' of the chain, you can increase the mass of the boxes near the root. This is related to the 'mass ratio' problem- a very heavy object sitting on top of a light object will tend to be unstable, but a light object sitting on a heavy object is perfectly stable.
5) Increase the Space.Solver.IterationLimit.
6) Decrease the Space.TimeStepSettings.TimeStepDuration and update more frequently.
Here's an example using all of the above:
Code: Select all
float L = 0.5f;
float H = 0.1f;
float W = 0.1f;
float mass = .1f;
//Disable the solver short circuiting. It does not play well with low mass objects.
SolverSettings.DefaultMinimumImpulse = 0;
//Increase the iteration limit.
Space.Solver.IterationLimit = 20;
List<Box> boxes = new List<Box>();
var center = new Vector3(0, 5, 0);
int boxCount = 10;
for (int i = 0; i < boxCount; ++i)
{
//Note the mass; objects near the root are much heavier.
Box box = new Box(center + new Vector3(0, -H * i, 0), L, H, W, mass * (boxCount - i));
boxes.Add(box);
Space.Add(box);
//Scale up the inertia of the constrained axes.
const float inertiaMultiplier = 2f;
var localInertia = box.LocalInertiaTensor;
localInertia.Right *= inertiaMultiplier;
localInertia.Up *= inertiaMultiplier;
box.LocalInertiaTensor = localInertia;
}
var j0 = new WeldJoint(null, boxes[0], boxes[0].Position);
j0.NoRotationJoint.SpringSettings.Stiffness *= 0.01f;
j0.NoRotationJoint.SpringSettings.Damping *= 0.05f;
Space.Add(j0);
for (int i = 0; i < boxes.Count - 1; ++i)
{
var offset = new Vector3(L * ((i & 1) == 0 ? 0.5f : -0.5f), -H / 2, 0);
var joint = new RevoluteJoint(boxes[i], boxes[i + 1], boxes[i].Position + offset, Vector3.UnitZ);
//Reduce constraint stiffness more than damping to help avoid oscillation.
joint.AngularJoint.SpringSettings.Stiffness *= 0.003f;
joint.AngularJoint.SpringSettings.Damping *= 0.01f;
Space.Add(joint);
}
//Reduce the duration of the time step (and update more frequently to compensate).
Space.TimeStepSettings.TimeStepDuration = 1 / 120f;
Generally, you won't need to use all of these at once. For example, pushing the iteration limit up or the time step duration down can eliminate the need for some of the other options.
So my new question is that is there any way of introducing a group of entities such that collision only happens within this group? In other words, no collision happens between an object in the group and an object outside this group.
Yup, CollisionGroups. Check the
documentation for more information and the CollisionFilteringDemo for an example.