There's two main components: computing the desired speed, and making sure the character can actually reach that speed.
The desired speed is the frame's displacement divided by the timestep, so first we'll need a displacement. For the sake of character movement, only the horizontal motion matters- the horizontal motion constraint can't cause vertical motion anyway. So, assuming it's a single frame displacement, project RootBoneDeltaMove onto the character's horizontal movement basis:
Code: Select all
var horizontalDisplacement = new Vector2(Vector3.Dot(character.HorizontalMotionConstraint.StrafeDirection, RootBoneDeltaMove), Vector3.Dot(character.HorizontalMotionConstraint.ForwardDirection, RootBoneDeltaMove));
The time in question is just a single frame, dt, so:
Code: Select all
var targetVelocity = horizontalDisplacement / dt;
var targetSpeed = targetVelocity.Length();
In defining the target speed, you don't have to worry about things like traction or mass; that's for later. The desired direction is based on the RootBoneDeltaMove as you already noted. Since the movement direction is in terms of the horizontal motion constraint's basis still, we can then just set the movement direction directly:
Code: Select all
character.HorizontalMotionConstraint.MovementDirection = horizontalDisplacement;
To ensure that the character can actually reach the speed, it must be able to apply enough force to accelerate to that speed. This is handled primarily through the HorizontalMotionConstraint.MaximumForce property. To change velocity from x to y instantly, an impulse of mass * (y-x) is required. Since we're talking about the required
force to cause that change, we need to define how long we have to accelerate to that speed- in this case, we want to reach the speed in one timestep, or dt. If we applied an impulse of mass * (y-x) every single frame for a second, the effective
force would be mass * (y-x) / dt. So, if you wanted to compute the force required to successfully change the velocity, it would look like:
Code: Select all
character.Body.Mass * (targetSpeed - currentVelocity) / dt
where currentVelocity is the current velocity along the desired motion vector, and targetVelocity is the speed computed in the first part. To get the current velocity, you can project the current body linear velocity onto the the 3d direction of movement:
Code: Select all
var currentVelocity = Vector3.Dot(character.HorizontalMotionConstraint.MovementDirection.X * character.HorizontalMotionConstraint.StrafeDirection + character.HorizontalMotionConstraint.MovementDirection.Y * character.HorizontalMotionConstraint.ForwardDirection, character.Body.LinearVelocity);
(If the character could be standing on mobile objects, you'd have to take into account the character velocity
relative to the support velocity- conceptually, it's all the same, but it obfuscates the concepts here a bit so I left it out.)
You'll probably want to limit that force, though. If the animation does anything particularly quick, the resulting unbounded character movement force could be high enough to blast obstacles (or itself) into space. It may actually work fine without any dynamic modification to the maximum force- it'll fail to reach the goal sometimes, but that'll happen anyway when ramming into a wall.