In my head it seemed like a good idea to have the player face and move forward with [W], face and move right with [D] and so on. This is the basic control scheme of most standard 2D games.
It turns out this a absolutely foolish move in 3d. It is impossibly difficult to predict how WSAD will effect the player is the perspective is 3rd person (3d). Of course I only discovered this after spending 3 hours writing some very very very nice movement code. It's a work of art! The player rotates to face the direction of movement, and can decide whether it would be faster to rotate clockwise or counter-clockwise. but its a dumb and useless control scheme
(cries) :(
code (it deserves at least this much, I'm so proud of it)
abstract class physics_player: animation_none { #region fields private Vector3 friction = new Vector3(-1f, -1f, -1f); private Vector3 acceleration = Vector3.Zero; private Vector3 accelerationRate = new Vector3(.001f); private Vector3 deccelerationRate = new Vector3(.001f); private Vector3 maximumAcceleration = new Vector3(.5f); private float maxSpeed = .1f; private float maxReverseSpeed = .1f; private float walkSpeed = 1.5f; private float newFacingDirection = 0f; private float turnRate=3f; public enum WalkState { neutral, forward, backward, left, right } public WalkState walkState = WalkState.neutral; private WalkState previousWalkState = WalkState.neutral; public enum Posture { neutral, crouch, jump, prone, } #endregion //-------------------------------------------------------------------- //applyAcceleration. //-------------------------------------------------------------------- private void applyAcceleration(float elapsedTime) { if (acceleration.X > maximumAcceleration.X) acceleration = maximumAcceleration; if (acceleration.X < (maximumAcceleration.X * -1)) acceleration = (maximumAcceleration * -1); if (velocity.X > maxSpeed) velocity.X = maxSpeed; if (velocity.X < -maxReverseSpeed) velocity.X = -maxReverseSpeed; } //------------------------------------------------------------------- //------------------------------------------------------------------- //applyGravityAndNormals private void applyGravityAndNormals(float elapsedTime) { float height; Vector3 normals; terrainManager.heightMapInfo.GetHeightAndNormal(position, out height, out normals); //handles gravity if (position.Y > height) { // acceleration.Y -= (accelerationRate.Y * elapsedTime); } else { acceleration.Y = 0; position.Y = height; } orientation = Matrix.CreateRotationY(facingDirection); //if object is near ground, orient it towards the ground if (position.Y > height - .05 && position.Y < height + 0.5) orientation.Up = normals; orientation.Right = Vector3.Cross(orientation.Forward, orientation.Up); orientation.Right = Vector3.Normalize(orientation.Right); orientation.Forward = Vector3.Cross(orientation.Up, orientation.Right); orientation.Forward = Vector3.Normalize(orientation.Forward); } //------------------------------------------------------------------- //-------------------------------------------------------------------- //applyFriction //-------------------------------------------------------------------- private void applyFriction(float elapsedTime) { velocity *= .9995f; if (velocity.X > -.01 && velocity.X < .01) velocity.X = 0; } //--------------------------------------------------------------------- //--------------------------------------------------------------------- //applyTurn //--------------------------------------------------------------------- private void applyTurn(float elapsedTime) { if (facingDirection > newFacingDirection + .05) { facingDirection -= (turnRate * elapsedTime); } else if (facingDirection < newFacingDirection - .05) { facingDirection += (turnRate * elapsedTime); } else { //facingDirection is close to newFacingDirection facingDirection = newFacingDirection; } } //-------------------------------------------------------------------- //--------------------------------------------------------------------- //applyWalkState unique player movement //--------------------------------------------------------------------- private void applyWalkState(float elapsedTime) { if (walkState == WalkState.neutral) { velocity.X = 0; velocity.Z = 0; // newFacingDirection = 0f; better if facing direction isn't reset } else if (walkState == WalkState.forward && previousWalkState!=WalkState.forward) { velocity.Z = walkSpeed; velocity.X = 0; newFacingDirection = 0f; } else if (walkState == WalkState.backward && previousWalkState != WalkState.backward) { velocity.Z = -walkSpeed; //model rotates counterclockwise velocity.X = 0; newFacingDirection = 3.14f; if (utility.near(facingDirection, -1.57f, .5f)) newFacingDirection = -3.14f; } else if (walkState == WalkState.left && previousWalkState != WalkState.left) { velocity.X = walkSpeed; velocity.Z = 0; newFacingDirection = 1.57f; } else if (walkState == WalkState.right && previousWalkState != WalkState.right) { velocity.X = -walkSpeed; velocity.Z = 0; newFacingDirection = -1.57f; if (utility.near(facingDirection, 3.14f, .5f)) newFacingDirection = 4.71f; } previousWalkState = walkState; } //--------------------------------------------------------------------- public void updatePhysics(float elapsedTime) { applyWalkState(elapsedTime); applyTurn(elapsedTime); //applyAcceleration(elapsedTime);//gets the new acceleration and applys it to the velocity applyGravityAndNormals(elapsedTime); velocity += acceleration; applyFriction(elapsedTime);//applys friction position += (velocity * elapsedTime); } }
edit: heh my "//applys friction" comment at the end there is embarrassing. I'm not sure why I felt a method named "applyFriction" needed clarification. I must have been drunk
edit 2: and no, I don't think that friction is one dimensional, I just haven't gotten around to applying it to Z (Y will probably never get it, as I'm not simulating the friction of air).