1.         #region Physics Variables
  2.         private float previousBottom;
  3.         Vector2 velocity;
  4.         // Horizontal movement
  5.         private const float MoveAcceleration = 14000.0f;
  6.         private const float MaxMoveSpeed = 500.0f;
  7.         private const float GroundDragFactor = 0.58f;
  8.         private const float AirDragFactor = 0.65f;
  9.  
  10.         // Vertical movement
  11.         private const float MaxJumpTime = 0.35f;
  12.         private const float JumpLaunchVelocity = -4000.0f;
  13.         private const float GravityAcceleration = 3500.0f;
  14.         private const float MaxFallSpeed = 600.0f;
  15.         private const float JumpControlPower = 0.14f;
  16.  
  17.         bool isOnGround;
  18.  
  19.         private float movement;
  20.  
  21.         private bool isJumping;
  22.         private bool wasJumping;
  23.         private float jumpTime;
  24.  
  25.         private Rectangle localBounds;
  26.         #endregion
  27.  
  28.         #region Physics
  29.         public void ApplyPhysics(GameTime gameTime)
  30.         {
  31.             float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;
  32.  
  33.             Vector2 previousPosition = Position;
  34.  
  35.             // Base velocity is a combination of horizontal movement control and
  36.             // acceleration downward due to gravity.
  37.             velocity.X += movement * MoveAcceleration * elapsed;
  38.             velocity.Y = MathHelper.Clamp(velocity.Y + GravityAcceleration * elapsed, -MaxFallSpeed, MaxFallSpeed);
  39.  
  40.             velocity.Y = DoJump(velocity.Y, gameTime);
  41.  
  42.             // Apply pseudo-drag horizontally.
  43.             if (IsOnGround)
  44.                 velocity.X *= GroundDragFactor;
  45.             else
  46.                 velocity.X *= AirDragFactor;
  47.  
  48.             // Prevent the player from running faster than his top speed.            
  49.             velocity.X = MathHelper.Clamp(velocity.X, -MaxMoveSpeed, MaxMoveSpeed);
  50.  
  51.             // Apply velocity.
  52.             Position += velocity * elapsed;
  53.             Position = new Vector2((float)Math.Round(Position.X), (float)Math.Round(Position.Y));
  54.  
  55.             // If the player is now colliding with the level, separate them.
  56.             HandleCollisions();
  57.  
  58.             // If the collision stopped us from moving, reset the velocity to zero.
  59.             if (Position.X == previousPosition.X)
  60.                 velocity.X = 0;
  61.  
  62.             if (Position.Y == previousPosition.Y)
  63.                 velocity.Y = 0;
  64.         }
  65.  
  66.         private float DoJump(float velocityY, GameTime gameTime)
  67.         {
  68.             // If the player wants to jump
  69.             if (isJumping)
  70.             {
  71.                 // Begin or continue a jump
  72.                 if ((!wasJumping && IsOnGround) || jumpTime > 0.0f)
  73.                 {
  74.                     jumpTime += (float)gameTime.ElapsedGameTime.TotalSeconds;
  75.                 }
  76.  
  77.                 // If we are in the ascent of the jump
  78.                 if (0.0f < jumpTime && jumpTime <= MaxJumpTime)
  79.                 {
  80.                     // Fully override the vertical velocity with a power curve that gives players more control over the top of the jump
  81.                     velocityY = JumpLaunchVelocity * (1.0f - (float)Math.Pow(jumpTime / MaxJumpTime, JumpControlPower));
  82.                 }
  83.                 else
  84.                 {
  85.                     // Reached the apex of the jump
  86.                     jumpTime = 0.0f;
  87.                 }
  88.             }
  89.             else
  90.             {
  91.                 // Continues not jumping or cancels a jump in progress
  92.                 jumpTime = 0.0f;
  93.             }
  94.             wasJumping = isJumping;
  95.  
  96.             return velocityY;
  97.         }
  98.  
  99.         private void HandleCollisions()
  100.         {
  101.             // Get the player's bounding rectangle and find neighboring tiles.
  102.             Rectangle bounds = BoundingRectangle;
  103.             int leftTile = (int)Math.Floor((float)(bounds.Left) / 16);
  104.             int rightTile = (int)Math.Ceiling(((float)(bounds.Right) / 16)) - 1;
  105.             int topTile = (int)Math.Floor((float)(bounds.Top) / 16);
  106.             int bottomTile = (int)Math.Ceiling(((float)(bounds.Bottom) / 16)) - 1;
  107.  
  108.             // Reset flag to search for ground collision.
  109.             isOnGround = false;
  110.  
  111.             // For each potentially colliding tile,
  112.             for (int y = topTile; y <= bottomTile; ++y)
  113.             {
  114.                 for (int x = leftTile; x <= rightTile; ++x)
  115.                 {
  116.                     // If this tile is collidable,
  117.                     Tiles.Collision collision = Map.GetCollision(x, y);
  118.                     if (collision != Tiles.Collision.None)
  119.                     {
  120.                         //int _x = (int)((x * 16) / 16);
  121.                         //int _y = (int)((y * 16) / 16);
  122.  
  123.                         int _x = x,
  124.                             _y = y;
  125.  
  126.                         // Determine collision depth (with direction) and magnitude.
  127.                         Rectangle tileBounds = Map.GetBounds(_x, _y);
  128.                         Vector2 depth = RectangleExtensions.GetIntersectionDepth(bounds, tileBounds);
  129.                         if (depth != Vector2.Zero)
  130.                         {
  131.                             float absDepthX = Math.Abs(depth.X);
  132.                             float absDepthY = Math.Abs(depth.Y);
  133.  
  134.                             // Resolve the collision along the shallow axis.
  135.                             if (absDepthY < absDepthX || collision == Tiles.Collision.Platform)
  136.                             {
  137.                                 // If we crossed the top of a tile, we are on the ground.
  138.                                 if (previousBottom <= tileBounds.Top)
  139.                                     isOnGround = true;
  140.  
  141.                                 // Ignore platforms, unless we are on the ground.
  142.                                 if (collision == Tiles.Collision.Solid || IsOnGround)
  143.                                 {
  144.                                     // Resolve the collision along the Y axis.
  145.                                     Position = new Vector2(Position.X, Position.Y + depth.Y);
  146.  
  147.                                     // Perform further collisions with the new bounds.
  148.                                     bounds = BoundingRectangle;
  149.                                 }
  150.                             }
  151.                             else if (collision == Tiles.Collision.Solid) // Ignore platforms.
  152.                             {
  153.                                 // Resolve the collision along the X axis.
  154.                                 Position = new Vector2(Position.X + depth.X, Position.Y);
  155.  
  156.                                 // Perform further collisions with the new bounds.
  157.                                 bounds = BoundingRectangle;
  158.                             }
  159.                         }
  160.                     }
  161.                 }
  162.             }
  163.  
  164.             // Save the new bounds bottom.
  165.             previousBottom = bounds.Bottom;
  166.         }
  167.         #endregion