public void HandleMovement(IPhysicsProxy physics, Vector3 movement) { var xyMovement = Vector3.Normalize(new Vector3(movement.X, movement.Y, 0)); var xyMagSquared = xyMovement.LengthSquared(); if (float.IsNaN(xyMagSquared) || xyMagSquared <= 0) { return; } if (this.state == ControllerState.Walking) { var velocityChange = GetVelocityChange(physics, xyMovement, desiredGroundSpeed, deltaGroundSpeed); if (velocityChange.LengthSquared() > 0) { physics.AddVelocity(velocityChange); } } else if (this.state == ControllerState.Falling) { /* * goal: allow, at maximum, desiredAirSpeed velocity while falling * edge case: when jumping from movement, or sliding off edge, don't limit speed down to desiredAirSpeed */ Vector3 currentVelocity = physics.GetVelocity(); var likeness = Vector3.Dot(xyMovement, Vector3.Normalize(currentVelocity)); if (float.IsNaN(likeness)) { likeness = 0f; } var currentVelocityInDesiredDirection = currentVelocity * likeness; var deltaMagnitude = desiredAirSpeed - currentVelocityInDesiredDirection.Length(); if (deltaMagnitude != 0) { var change = xyMovement * MathExt.Clamp(deltaMagnitude, 0, deltaAirSpeed); if (change.LengthSquared() > 0) { physics.AddVelocity(change); } } } else { // TODO: handle sprinting and sliding movement // TODO: desired feel during sliding is 0 friction in the movement direction return; } }
// BUG: can get 'stuck' at the top edge of a ramp private Vector3 GetVelocityChange(IPhysicsProxy physics, Vector3 movement, float desiredMagnitude, float deltaMagnitude) { Vector3 currentVelocity = physics.GetVelocity(); movement *= desiredMagnitude; // TODO: figure out a better limiting method? velocity going up slopes is limited Vector3 velocityChange = (movement - currentVelocity); velocityChange.X = MathExt.Clamp(velocityChange.X, -deltaMagnitude, deltaMagnitude); velocityChange.Y = MathExt.Clamp(velocityChange.Y, -deltaMagnitude, deltaMagnitude); velocityChange.Z = 0; return(velocityChange); }