FAQ |
Here is a collection of frequently asked questions, common problems and solutions.
This topic contains the following sections:
If, for some reason, you want to force the simulation to use a variable time step, then you have to update FixedTimeStep before each SimulationUpdate call:
mySimulation.Settings.Timing.FixedTimeStep = variableTime; mySimulation.Update(variableTime);
The RigidBody class has flags which let you disable rotations about certain axes (e.g, LockRotationX). And you can use constraints like a PointOnPlaneConstraint to keep all movement in the 2D plane. But: A 3D physics engine is more complex than a 2D engine and it has to do additional work to keep objects in a 2D plane. A dedicated 2D engine would be more efficient for pure 2D games.
If you use a moving kinematic object, do not change the Pose directly in each frame. Instead, set the velocity (LinearVelocity and AngularVelocity) in each frame and let the simulation compute the new pose.
If you change the pose of a rigid body directly, the simulation "teleports" the body to the new position and does not treat it as a moving body with a non-zero velocity. Collision response between a kinematic and a dynamic body will only be smooth if you use LinearVelocity and AngularVelocity, and let the simulation move the body.
You may have to disable "sleeping" for the body:
myRigidBody.CanSleep = false;
"Sleeping" means: The simulation is allowed to deactivate slow moving bodies to improve performance and stability. If you apply a velocity which is less than the sleeping threshold, the body does not wake up. See SleepingSettings.
The MassFrame has a limit (MassFrameMassLimit, which, is around 1e10 per default). If an object has a mass beyond this limit, it will be treated like a kinematic body that is not affected by forces or collisions. MassFrameMassLimit is static property that can be changed.
To find out if a body is done moving, you could check the RigidBodyIsSleeping flag. The simulation sets this flag if a body has stopped moving. See Sleeping. Please note that it is possible that "sleeping" is disabled for a rigid body or that the sleeping settings of the simulation are not suitable for your purpose (e.g. the sleeping time threshold is too large).
You could also manually check if a body is not moving. There are several options, for example:
Usually, option 2 is used. Please note, that small simulation errors can cause bodies to jitter a bit, therefore these test need to use tolerance values. To get more robust results you could add additional conditions, e.g. "the linear velocity must not be rising".
There are several options:
The solution depends on your requirements and your game physics experience. Constraints provide the most sophisticated solution, but you need more experience to get them right.
First, if not already done, set a broad-phase collision filter that disables collision computation for static vs. static. See the code example in Best Practices and Recommended Literature. Per default, the physics engine computes collisions between two static bodies. The collision info is computed only once - but if you have many static bodies, many contact sets an be generated which needlessly clog the system.
Tip 1: Use a CompositeShape
The broad-phase of the collision detection already uses spatial partitioning to manage all rigid bodies. But each rigid body has an overhead: It can be enabled/disabled, could have individual collision filtering, individual material, etc.. So it is better to reduce the number of rigid bodies and combine them if possible.
Try to create a single rigid body with a CompositeShape that contains all static parts. And set an AabbTreeT as the spatial partition for this composite shape, for example:
compositeShape.Partition = new AabbTree<int>();
This should give you a significant speedup.
The AabbTreeT has a property BottomUpBuildThreshold which you can set to a lower value if the building of the tree takes too long. (In our test-suite, we have a test-level with a composite shape with 80 000 boxes (on PC). The level building takes quite some time, but after creation it runs in real-time.)
Tip 2: Use several CompositeShapes
One extreme is: A single rigid bodies with a composite shape of N parts. The other extreme is: N individual rigid bodies. The optimal case could lie between those extremes, for example: 10 rigid bodies with 100 parts each. That depends on your level geometry and should be determined by experimentation.
Tip 3: TriangleMeshShape
Using a TriangleMeshShape could be even faster because you only need a vertex and index buffer to define the mesh containing all the parts. The disadvantage of a triangle mesh is that it is sometimes less stable than composite shapes of convex bodies: The triangles are "thin" and that can lead to tunneling and other simulation problems.
Perhaps you call RigidBodyAddForce only once. In this case the force is applied only for a single time step. To apply a permanent force, the method needs to be called every frame.
Damping is really simple. Damping can be done using a force effect which directly reduces the velocity (without applying any forces). Here is the gist of a damping class:
public class Damping : ForceField { public float LinearDamping { get; set; } public float AngularDamping { get; set; } public override void Apply(RigidBody body) { float fixedTimeStep = Simulation.Settings.Timing.FixedTimeStep; body.LinearVelocity = (1 - LinearDamping * fixedTimeStep) * body.LinearVelocity; body.AngularVelocity = (1 - AngularDamping * fixedTimeStep) * body.AngularVelocity; } }
Following article describes the used damping formula: Damping in Computer Games. You don't even need to use the "exact" formulas or a force effect. Try to simply reduce the angular velocity manually between SimulationUpdate calls (or at SimulationSubTimeStepFinished events if your simulation is sub-stepping):
var localVelocity = body.Pose.ToLocalDirection(body.AngularVelocity); localVelocity.X *= 0.9f; // Damp on a single axis. body.AngularVelocity = body.Pose.ToWorldDirection(localVelocity);
This could be caused by the rigid body sleeping. Please disable sleeping and check if the problem disappears. (To disable sleeping you just have to set SleepingSettingsTimeThreshold to a large value.)
If sleeping is the culprit, you can create your own Buoyancy effect which wakes up all rigid bodies in the water. Something like:
class MyBuoyancy : Buoyancy { public override void Apply(RigidBody body) { body.WakeUp(); base.Apply(body); } }
This link should cover what your looking for: Trajectory of a projectile. Look at the vacuum based formulas. These formulas are also explained in detail in the book "Game Programming Gems 2", chapter "Inverse Trajectory Determination".
When using this formulas in the physics simulation, you will probably not hit the target exactly for two main reasons:
A quick note on impulses and velocities: If you want to shoot an object, you can apply an impulse. Or you simply set the start velocity of the projectile body directly - without worrying about impulse formulas.
The predicate GlobalAreaOfEffectExclude is a simple callback function. That means you can implement any kind of logic. For example:
// Create a global list of excluded bodies in your game. public static List<RigidBody> ExcludedRigidBodies = new List<RigidBody>(); ... // The Exclude-predicate checks whether a rigid body is in the list. globalAreaOfEffect.Exclude = rigidBody => ExcludedRigidBodies.Contains(rigidBody);
For example:
public class CustomGlobalAreaOfEffect : IAreaOfEffect { public List<RigidBody> ExcludedRigidBodies { get; private set; } public CustomGlobalAreaOfEffect() { ExcludedRigidBodies = new List<RigidBody>(); } public void Apply(ForceField forceField) { var simulation = forceField.Simulation; if (simulation != null) { foreach (var body in simulation.RigidBodies) if (body.MotionType == MotionType.Dynamic && !ExcludedRigidBodies.Contains(body)) forceField.Apply(body); } } }
Notes: If the number of excluded rigid bodies is large, List<T>.Contains() might be too expensive. You can for example store a flag in RigidBodyUserData and check the flag in the Exclude-predicate. Or you can check RigidBody.CollisionObject.CollisionGroup.
If the game objects are large, you might have the feeling that the objects fall a bit slower than you would expect in reality - especially if the scene is abstract with no reference objects. This is caused by the scale of the objects - in the real world we observe small falling objects more frequently than huge falling object. For example: Bodies in the samples are usually around 1 m in size, like a big rock or crate in a game. If the bodies would be 1 cm, the falling velocity of the bodies relative to their size would be much higher and feel different.
So the simulation speed is correct - as long as the simulation can keep up with the target frame rate. If the simulation is too slow for the target frame rate, it will drop some simulation time to avoid freezing the game. That means, if the frame rate drops significantly below the target frame rate, the bodies will appear to move in slow-motion. (See also Timing)
var velocity = (myCharacterController.Position - oldPosition) / deltaTime;
A common approach is to have the character controller capsule that handles the movement and a ragdoll for detailed collision detection with the limbs. You need to disable collisions between the character controller body and the ragdoll bodies (e.g. using CollisionGroup).
If you want to have a very detailed character, you can combine the character controller with a ragdoll and configure collision filters to do this:
Velocity motors simply set the linear and angular velocities of the bodies in each frame to move the bodies to the target positions. If such motors are used, the ragdoll follows the target animation very closely, and the ragdoll is kept upright. Forces and impacts acting on the bodies have minimal impact because the velocity motors override the bodies' velocities in each frame.
Constraint motors use physics constraints (like QuaternionMotor) to control the relative orientation of the bodies. This is used in the ActiveRagdollSample of the Samples. Since only the relative orientation is controlled, the ragdoll falls to the floor but keeps playing the animation. Bodies controlled by constraints react to impacts.
Given:
Wanted:
Solution:
Explanation:
Code example:
myBody.Pose = poseModel * new Pose(Matrix33F.CreateRotationY(MathHelper.ToRadians(90))) * poseModel.Inverse * myBody.Pose;
Option A:
While the character is standing, the animation is played. The bodies are dynamic and in each frame the position of the bodies is updated from the animation using RagdollUpdateBodiesFromSkeleton (as in the CollisionDetectionOnlyRagdollSample). After the hit, the bodies update the skeleton using RagdollUpdateSkeletonFromBodies. Since the bodies are dynamic all the time, they will react to the impact. The disadvantage is that while the animation is active the dynamic bodies do not properly push other rigid bodies. - Often this is okay because a character controller is used and takes care of the pushing while the ragdoll is used only for collision detection.
In other words: Start with a Ragdoll with disabled joints, limits and motors.
_ragdoll.Pose = _pose; _ragdoll.UpdateBodiesFromSkeleton(_skeletonPose); _ragdoll.DisableJoints(); _ragdoll.DisableLimits(); _ragdoll.DisableMotors();
and in each frame call
_ragdoll.UpdateBodiesFromSkeleton(_skeletonPose);
to move the bodies to upright or animated skeleton position.
Then once you have detected a hit (similar to the CollisionDetectionOnlyRagdollSample), switch to the passive ragdoll:
_ragdoll.EnableJoints(); _ragdoll.EnableLimits(); foreach (RagdollMotor motor in _ragdoll.Motors) { if (motor != null) { motor.Mode = RagdollMotorMode.Constraint; motor.ConstraintDamping = 5; motor.ConstraintSpring = 0; } } _ragdoll.EnableMotors();
and in each frame call
_ragdoll.UpdateSkeletonFromBodies(_skeletonPose);
That means: First the skeleton controls the bodies. After the hit the bodies control the skeleton.
Option B:
You use kinematic bodies and motors as in the KinematicRagdollSample. When the character is hit you make all bodies dynamic and use RagdollUpdateSkeletonFromBodies. Such a character properly pushes other objects while the animation is active. But it will not react to the impact. You can then manually call RigidBodyApplyImpulse to apply an impact impulse (after the bodies were set to dynamic).
Option A) Simply clamp LinearVelocity of the chassis body manually after SimulationUpdate. This can look OK with one minor problem: If there is a large instant force (like an explosion), the body can have a larger velocity for one frame.
Option B) Create a constraint that limits the velocity. This is better because it applies the velocity limit inside the simulation update. Here is an example constraint:
class LinearSpeedLimit : Constraint { public float MaxSpeed { get; set; } protected override void OnSetup() { } protected override bool OnApplyImpulse() { var speed = BodyA.LinearVelocity.Length; if (speed <= MaxSpeed + Numeric.EpsilonF) return false; BodyA.LinearVelocity = BodyA.LinearVelocity * (MaxSpeed / speed); return true; } public override Vector3F LinearConstraintImpulse { get { throw new System.NotImplementedException(); } } public override Vector3F AngularConstraintImpulse { get { throw new System.NotImplementedException(); } } }
And use it like this:
Simulation.Constraints.Add(new LinearSpeedLimit { BodyA = chassis, BodyB = chassis, MaxSpeed = 50 });