/// <summary> /// Constructs a new character controller with the default configuration. /// </summary> public CharacterController() { Body = new Cylinder(Vector3.Zero, 1.7f, .6f, 10); Body.IgnoreShapeChanges = true; //Wouldn't want inertia tensor recomputations to occur when crouching and such. Body.CollisionInformation.Shape.CollisionMargin = .1f; //Making the character a continuous object prevents it from flying through walls which would be pretty jarring from a player's perspective. Body.PositionUpdateMode = PositionUpdateMode.Continuous; Body.LocalInertiaTensorInverse = new Matrix3X3(); //TODO: In v0.16.2, compound bodies would override the material properties that get set in the CreatingPair event handler. //In a future version where this is changed, change this to conceptually minimally required CreatingPair. Body.CollisionInformation.Events.DetectingInitialCollision += RemoveFriction; Body.LinearDamping = 0; SupportFinder = new SupportFinder(this); HorizontalMotionConstraint = new HorizontalMotionConstraint(this); VerticalMotionConstraint = new VerticalMotionConstraint(this); StepManager = new StepManager(this); StanceManager = new StanceManager(this); QueryManager = new QueryManager(this); //Enable multithreading for the sphere characters. //See the bottom of the Update method for more information about using multithreading with this character. IsUpdatedSequentially = false; }
void IBeforeSolverUpdateable.Update(float dt) { CorrectContacts(); bool hadTraction = SupportFinder.HasTraction; 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(); 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); //} //} //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); //Warning: //Changing a constraint's support data is not thread safe; it modifies simulation islands! //If something other than a CharacterController can modify simulation islands is running //simultaneously (in the IBeforeSolverUpdateable.Update stage), it will need to be synchronized. ConstraintAccessLocker.Enter(); HorizontalMotionConstraint.SupportData = supportData; VerticalMotionConstraint.SupportData = verticalSupportData; ConstraintAccessLocker.Exit(); }