void IBeforeSolverUpdateable.Update(float dt) { CorrectContacts(); bool hadTraction = SupportFinder.HasTraction; var supportData = CollectSupportData(); //Compute the initial velocities relative to the support. Vector3 relativeVelocity; ComputeRelativeVelocity(ref supportData, out relativeVelocity); float verticalVelocity = Vector3.Dot(supportData.Normal, relativeVelocity); Vector3 horizontalVelocity = relativeVelocity - supportData.Normal * verticalVelocity; //Don't attempt to use an object as support if we are flying away from it (and we were never standing on it to begin with). if (SupportFinder.HasTraction && !hadTraction && verticalVelocity < 0) { SupportFinder.ClearSupportData(); HorizontalMotionConstraint.SupportData = new SupportData(); } //If we can compute that we're separating faster than we can handle, take off. if (SupportFinder.HasTraction && verticalVelocity < -VerticalMotionConstraint.MaximumGlueForce * dt / VerticalMotionConstraint.EffectiveMass) { SupportFinder.ClearSupportData(); supportData = new SupportData(); } //Attempt to jump. if (tryToJump && StanceManager.CurrentStance != Stance.Crouching) //Jumping while crouching would be a bit silly. { //In the following, note that the jumping velocity changes are computed such that the separating velocity is specifically achieved, //rather than just adding some speed along an arbitrary direction. This avoids some cases where the character could otherwise increase //the jump speed, which may not be desired. if (SupportFinder.HasTraction) { //The character has traction, so jump straight up. float currentUpVelocity = Vector3.Dot(Body.OrientationMatrix.Up, relativeVelocity); //Target velocity is JumpSpeed. float velocityChange = Math.Max(jumpSpeed - currentUpVelocity, 0); ApplyJumpVelocity(ref supportData, Body.OrientationMatrix.Up * velocityChange, ref relativeVelocity); //Prevent any old contacts from hanging around and coming back with a negative depth. foreach (var pair in Body.CollisionInformation.Pairs) { pair.ClearContacts(); } SupportFinder.ClearSupportData(); supportData = new SupportData(); } else if (SupportFinder.HasSupport) { //The character does not have traction, so jump along the surface normal instead. float currentNormalVelocity = Vector3.Dot(supportData.Normal, relativeVelocity); //Target velocity is JumpSpeed. float velocityChange = Math.Max(slidingJumpSpeed - currentNormalVelocity, 0); ApplyJumpVelocity(ref supportData, supportData.Normal * -velocityChange, ref relativeVelocity); //Prevent any old contacts from hanging around and coming back with a negative depth. foreach (var pair in Body.CollisionInformation.Pairs) { pair.ClearContacts(); } SupportFinder.ClearSupportData(); supportData = new SupportData(); } } tryToJump = false; //Try to step! Vector3 newPosition; if (StepManager.TryToStepDown(out newPosition) || StepManager.TryToStepUp(out newPosition)) { TeleportToPosition(newPosition, dt); } if (StanceManager.UpdateStance(out newPosition)) { TeleportToPosition(newPosition, dt); } //if (SupportFinder.HasTraction && SupportFinder.Supports.Count == 0) //{ //There's another way to step down that is a lot cheaper, but less robust. //This modifies the velocity of the character to make it fall faster. //Impacts with the ground will be harder, so it will apply superfluous force to supports. //Additionally, it will not be consistent with instant up-stepping. //However, because it does not do any expensive queries, it is very fast! ////We are being supported by a ray cast, but we're floating. ////Let's try to get to the ground faster. ////How fast? Try picking an arbitrary velocity and setting our relative vertical velocity to that value. ////Don't go farther than the maximum distance, though. //float maxVelocity = (SupportFinder.SupportRayData.Value.HitData.T - SupportFinder.RayLengthToBottom); //if (maxVelocity > 0) //{ // maxVelocity = (maxVelocity + .01f) / dt; // float targetVerticalVelocity = -3; // verticalVelocity = Vector3.Dot(Body.OrientationMatrix.Up, relativeVelocity); // float change = MathHelper.Clamp(targetVerticalVelocity - verticalVelocity, -maxVelocity, 0); // ChangeVelocityUnilaterally(Body.OrientationMatrix.Up * change, ref relativeVelocity); //} //} //Warning: //Changing a constraint's support data is not thread safe; it modifies simulation islands! //If your game can guarantee that character controllers will be the only ones performing such shared modifications //while this updateable stage runs, then addressing this is fairly simple. Wrap this section (minimally, the SupportData property sets) //in a critical section. A single contended resource isn't great, but then again, the lock will be fairly brief compared to the stepping //and support queries performed above. Implementing such parallelization is probably only it worth when the number of characters gets fairly high. //These characters seem to cost about 50-400 microseconds a piece on a single core of a [email protected], with 400 microseconds being a temporary worst case. HorizontalMotionConstraint.SupportData = supportData; //Vertical support data is different because it has the capacity to stop the character from moving unless //contacts are pruned appropriately. SupportData verticalSupportData; Vector3 movement3d = new Vector3(HorizontalMotionConstraint.MovementDirection.X, 0, HorizontalMotionConstraint.MovementDirection.Y); SupportFinder.GetTractionInDirection(ref movement3d, out verticalSupportData); VerticalMotionConstraint.SupportData = verticalSupportData; }