/// <summary>
        /// Handles the input and movement of the character.
        /// </summary>
        /// <param name="dt">Time since last frame in simulation seconds.</param>
        /// <param name="previousKeyboardInput">The last frame's keyboard state.</param>
        /// <param name="keyboardInput">The current frame's keyboard state.</param>
        /// <param name="previousGamePadInput">The last frame's gamepad state.</param>
        /// <param name="gamePadInput">The current frame's keyboard state.</param>
        public void Update(float dt)
        {
            if (IsActive)
            {
                //Note that the character controller's update method is not called here; this is because it is handled within its owning space.
                //This method's job is simply to tell the character to move around based on the Camera and input.

                ////Rotate the camera of the character based on the support velocity, if a support with velocity exists.
                ////This can be very disorienting in some cases; that's why it is off by default!
                //if (CharacterController.SupportFinder.HasSupport)
                //{
                //    SupportData? data;
                //    if (CharacterController.SupportFinder.HasTraction)
                //        data = CharacterController.SupportFinder.TractionData;
                //    else
                //        data = CharacterController.SupportFinder.SupportData;
                //    EntityCollidable support = data.Value.SupportObject as EntityCollidable;
                //    if (support != null && !support.Entity.IsDynamic) //Having the view turned by dynamic entities is extremely confusing for the most part.
                //    {
                //        float dot = Vector3.Dot(support.Entity.AngularVelocity, CharacterController.Body.OrientationMatrix.Up);
                //        Camera.Yaw += dot * dt;
                //    }
                //}

                if (UseCameraSmoothing)
                {
                    //First, find where the camera is expected to be based on the last position and the current velocity.
                    //Note: if the character were a free-floating 6DOF character, this would need to include an angular velocity contribution.
                    //And of course, the camera orientation would be based on the character's orientation.
                    Camera.Position = Camera.Position + CharacterController.Body.LinearVelocity * dt;
                    //Now compute where it should be according the physical body of the character.
                    Vector3 up           = CharacterController.Body.OrientationMatrix.Up;
                    Vector3 bodyPosition = CharacterController.Body.Position;
                    Vector3 goalPosition = bodyPosition + up * (CharacterController.StanceManager.CurrentStance == Stance.Standing ? StandingCameraOffset : CrouchingCameraOffset);

                    //Usually, the camera position and the goal will be very close, if not matching completely.
                    //However, if the character steps or has its position otherwise modified, then they will not match.
                    //In this case, we need to correct the camera position.

                    //To do this, first note that we can't correct infinite errors.  We need to define a bounding region that is relative to the character
                    //in which the camera can interpolate around.  The most common discontinuous motions are those of upstepping and downstepping.
                    //In downstepping, the character can teleport up to the character's MaximumStepHeight downwards.
                    //In upstepping, the character can teleport up to the character's MaximumStepHeight upwards, and the body's CollisionMargin horizontally.
                    //Picking those as bounds creates a constraining cylinder.

                    Vector3 error           = goalPosition - Camera.Position;
                    float   verticalError   = Vector3.Dot(error, up);
                    Vector3 horizontalError = error - verticalError * up;
                    //Clamp the vertical component of the camera position within the bounding cylinder.
                    if (verticalError > CharacterController.StepManager.MaximumStepHeight)
                    {
                        Camera.Position -= up * (CharacterController.StepManager.MaximumStepHeight - verticalError);
                        verticalError    = CharacterController.StepManager.MaximumStepHeight;
                    }
                    else if (verticalError < -CharacterController.StepManager.MaximumStepHeight)
                    {
                        Camera.Position -= up * (-CharacterController.StepManager.MaximumStepHeight - verticalError);
                        verticalError    = -CharacterController.StepManager.MaximumStepHeight;
                    }
                    //Clamp the horizontal distance too.
                    float horizontalErrorLength = horizontalError.LengthSquared();
                    float margin = CharacterController.Body.CollisionInformation.Shape.CollisionMargin;
                    if (horizontalErrorLength > margin * margin)
                    {
                        Vector3 previousHorizontalError = horizontalError;
                        Vector3.Multiply(ref horizontalError, margin / (float)Math.Sqrt(horizontalErrorLength), out horizontalError);
                        Camera.Position      -= horizontalError - previousHorizontalError;
                        horizontalErrorLength = margin * margin;
                    }
                    //Now that the error/camera position is known to lie within the constraining cylinder, we can perform a smooth correction.

                    //This removes a portion of the error each frame.
                    //Note that this is not framerate independent.  If fixed time step is not enabled,
                    //a different smoothing method should be applied to the final error values.
                    //float errorCorrectionFactor = .3f;

                    //This version is framerate independent, although it is more expensive.
                    float errorCorrectionFactor = (float)(1 - Math.Pow(.000000001, dt));
                    Camera.Position += up * (verticalError * errorCorrectionFactor);
                    Camera.Position += horizontalError * errorCorrectionFactor;
                }
                else
                {
                    Camera.Position = CharacterController.Body.Position + (CharacterController.StanceManager.CurrentStance == Stance.Standing ? StandingCameraOffset : CrouchingCameraOffset) * CharacterController.Body.OrientationMatrix.Up;
                }

                Vector2 totalMovement = Vector2.Zero;

#if XBOX360
                Vector3 forward = Camera.WorldMatrix.Forward;
                forward.Y = 0;
                forward.Normalize();
                Vector3 right = Camera.WorldMatrix.Right;
                totalMovement += gamePadInput.ThumbSticks.Left.Y * new Vector2(forward.X, forward.Z);
                totalMovement += gamePadInput.ThumbSticks.Left.X * new Vector2(right.X, right.Z);
                CharacterController.HorizontalMotionConstraint.MovementDirection = Vector2.Normalize(totalMovement);

                CharacterController.StanceManager.DesiredStance = gamePadInput.IsButtonDown(Buttons.RightStick) ? Stance.Crouching : Stance.Standing;

                //Jumping
                if (previousGamePadInput.IsButtonUp(Buttons.LeftStick) && gamePadInput.IsButtonDown(Buttons.LeftStick))
                {
                    CharacterController.Jump();
                }
#else
                //Collect the movement impulses.

                Vector3 movementDir;

                if (Input.ControlScheme == ControlScheme.Keyboard)
                {
                    if (Input.KeyboardState.IsKeyDown(Keys.W))
                    {
                        movementDir    = Camera.World.Forward;
                        totalMovement += Vector2.Normalize(new Vector2(movementDir.X, movementDir.Y));
                    }
                    if (Input.KeyboardState.IsKeyDown(Keys.S))
                    {
                        movementDir    = Camera.World.Forward;
                        totalMovement -= Vector2.Normalize(new Vector2(movementDir.X, movementDir.Y));
                    }
                    if (Input.KeyboardState.IsKeyDown(Keys.A))
                    {
                        movementDir    = Camera.World.Left;
                        totalMovement += Vector2.Normalize(new Vector2(movementDir.X, movementDir.Y));
                    }
                    if (Input.KeyboardState.IsKeyDown(Keys.D))
                    {
                        movementDir    = Camera.World.Right;
                        totalMovement += Vector2.Normalize(new Vector2(movementDir.X, movementDir.Y));
                    }
                }
                else if (Input.ControlScheme == ControlScheme.XboxController)
                {
                    totalMovement += Vector2.Transform(Input.CurrentPad.ThumbSticks.Left, Quaternion.CreateFromAxisAngle(Vector3.UnitZ, Camera.Yaw));
                }

                if (totalMovement == Vector2.Zero)
                {
                    CharacterController.HorizontalMotionConstraint.MovementDirection = Vector2.Zero;
                }
                else
                {
                    CharacterController.HorizontalMotionConstraint.MovementDirection = Vector2.Normalize(totalMovement);
                }

                CharacterController.StanceManager.DesiredStance = Input.KeyboardState.IsKeyDown(Keys.LeftShift) || Input.CurrentPad.IsButtonDown(Buttons.LeftTrigger) ? Stance.Crouching : Stance.Standing;

                //Jumping
                if (Input.CheckKeyboardJustPressed(Keys.Space) || Input.CurrentPad.IsButtonDown(Buttons.A))
                {
                    CharacterController.Jump();
                }

                // rotate
                //CharacterController.Body.Orientation = Quaternion.CreateFromAxisAngle(Vector3.UnitZ, Camera.Pitch);
#endif
            }
        }
Esempio n. 2
0
        /// <summary>
        /// Handles the input and movement of the character.
        /// </summary>
        /// <param name="dt">Time since last frame in simulation seconds.</param>
        /// <param name="previousKeyboardInput">The last frame's keyboard state.</param>
        /// <param name="keyboardInput">The current frame's keyboard state.</param>
        /// <param name="previousGamePadInput">The last frame's gamepad state.</param>
        /// <param name="gamePadInput">The current frame's keyboard state.</param>
        public void Update(float dt)
        {
            if (IsActive)
            {
                //Note that the character controller's update method is not called here; this is because it is handled within its owning space.
                //This method's job is simply to tell the character to move around.

                CameraControlScheme.Update(dt);

                Vector2 totalMovement = Vector2.Zero;


                if (Input.ControlScheme == ControlScheme.XboxController)
                {
                    totalMovement += new Vector2(Input.CurrentPad.ThumbSticks.Left.X, Input.CurrentPad.ThumbSticks.Left.Y);

                    CharacterController.HorizontalMotionConstraint.SpeedScale        = Math.Min(totalMovement.Length(), 1); //Don't trust the game pad to output perfectly normalized values.
                    CharacterController.HorizontalMotionConstraint.MovementDirection = totalMovement;

                    CharacterController.StanceManager.DesiredStance = Input.CurrentPad.IsButtonDown(Buttons.B) ? Stance.Crouching : Stance.Standing;

                    //Jumping
                    if (Input.CurrentPadLastFrame.IsButtonUp(Buttons.A) && Input.CurrentPad.IsButtonDown(Buttons.A))
                    {
                        CharacterController.Jump();
                    }
                }
                else if (Input.ControlScheme == ControlScheme.Keyboard)
                {
                    //Collect the movement impulses.

                    if (Input.KeyboardState.IsKeyDown(Keys.W))
                    {
                        totalMovement += new Vector2(0, 1);
                    }
                    if (Input.KeyboardState.IsKeyDown(Keys.S))
                    {
                        totalMovement += new Vector2(0, -1);
                    }
                    if (Input.KeyboardState.IsKeyDown(Keys.A))
                    {
                        totalMovement += new Vector2(-1, 0);
                    }
                    if (Input.KeyboardState.IsKeyDown(Keys.D))
                    {
                        totalMovement += new Vector2(1, 0);
                    }
                    if (totalMovement == Vector2.Zero)
                    {
                        CharacterController.HorizontalMotionConstraint.MovementDirection = Vector2.Zero;
                    }
                    else
                    {
                        CharacterController.HorizontalMotionConstraint.MovementDirection = Vector2.Normalize(totalMovement);
                    }


                    CharacterController.StanceManager.DesiredStance = Input.KeyboardState.IsKeyDown(Keys.LeftShift) ? Stance.Crouching : Stance.Standing;

                    //Jumping
                    if (Input.KeyboardLastFrame.IsKeyUp(Keys.Space) && Input.KeyboardState.IsKeyDown(Keys.Space))
                    {
                        CharacterController.Jump();
                    }
                }
                CharacterController.ViewDirection = Camera.WorldMatrix.Forward;

                #region Grabber Input

                //Update grabber

                if (((Input.ControlScheme == ControlScheme.XboxController && Input.CheckXboxJustPressed(Buttons.X)) ||
                     (Input.ControlScheme == ControlScheme.Keyboard && Input.CheckKeyboardPress(Keys.E))) && !grabber.IsUpdating)
                {
                    //Find the earliest ray hit
                    RayCastResult raycastResult;
                    if (Space.RayCast(new Ray(Renderer.Camera.Position, Renderer.Camera.WorldMatrix.Forward), 6, RayCastFilter, out raycastResult))
                    {
                        var entityCollision = raycastResult.HitObject as EntityCollidable;
                        //If there's a valid ray hit, then grab the connected object!
                        if (entityCollision != null)
                        {
                            var tag = entityCollision.Entity.Tag as GameModel;
                            if (tag != null)
                            {
                                tag.Texture.GameProperties.RayCastHit(tag.Texture.GameProperties);
                                if (!tag.Texture.Wireframe && tag.Texture.GameProperties.Grabbable)
                                {
                                    grabber.Setup(entityCollision.Entity, raycastResult.HitData.Location);
                                    try
                                    {
                                        CollisionRules.AddRule(entityCollision.Entity, CharacterController.Body, CollisionRule.NoBroadPhase);
                                    }
                                    catch { }
                                    grabDistance = raycastResult.HitData.T;
                                }
                                else
                                {
                                    Program.Game.Loader.InvalidApplicationRemovalGrab.Play();
                                }
                            }
                            else
                            {
                                Program.Game.Loader.InvalidApplicationRemovalGrab.Play();
                            }
                        }
                    }
                    else
                    {
                        Program.Game.Loader.InvalidApplicationRemovalGrab.Play();
                    }
                }
                else if (grabber.IsUpdating)
                {
                    if ((Input.ControlScheme == ControlScheme.XboxController && Input.CheckXboxJustPressed(Buttons.X)) ||
                        (Input.ControlScheme == ControlScheme.Keyboard && Input.CheckKeyboardPress(Keys.E)))
                    {
                        try
                        {
                            CollisionRules.RemoveRule(grabber.Entity, CharacterController.Body);
                        }
                        catch { }
                        grabber.Release();
                    }
                    grabber.GoalPosition = Renderer.Camera.Position + Renderer.Camera.WorldMatrix.Forward * grabDistance;
                }
                #endregion
            }
        }