So I’m writing a player class and using floats to store the positon and velocity (Vector2) as well as using it when drawing. When it comes to collision I get the player bounds using this code: public Rectangle Bounds => new Rectangle((int)_pos.X, (int)_pos.Y, 12, 23); to test it against solid blocks.
The issue comes when I apply gravity to the player and the player moves in <1 increments per frame eg. first frame 0, second frame 0.25, etc. This causes the player to jitter when above a block due to him falling down little by little until the collision code (which uses ints) pushes it back up.
One solution I came across is to use the Bounds I calculated earlier for drawing, instead of using the position directly. However there is another problem (because the problems never stop coming)… Now the jitter occurs behind the scenes causing the ground detection to not work. How can I make the float positioning and int collision system work in harmony??
The player class' file:
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Input; using Microsoft.Xna.Framework.Graphics; public class Player { public Rectangle Bounds => new Rectangle((int)_pos.X, (int)_pos.Y, 16, 16); Vector2 _prevPos; Vector2 _pos; Vector2 _vel; World _world; const float GRAVITY = 500; const float MOVE_SPEED = 100; const float JUMP_FORCE = 275; const float MAX_FALL_SPEED = 350; public Player(Vector2 position) { _pos = position; _world = Main.World; } public void Update(float dt) { // Gravity _vel.Y += GRAVITY * dt; _vel.Y = MathHelper.Clamp(_vel.Y, -MAX_FALL_SPEED, MAX_FALL_SPEED); // Horizontal movement if (Input.IsKeyHeld(Keys.A)) _vel.X = -MOVE_SPEED; else if (Input.IsKeyHeld(Keys.D)) _vel.X = MOVE_SPEED; else _vel.X = 0f; // Jumping if (Input.IsKeyDown(Keys.Space)) _vel.Y = -JUMP_FORCE; // Calculate position _prevPos = _pos; _pos += _vel * dt; HandleCollisions(); System.Console.WriteLine("Velocity: {0}", _vel); } void HandleCollisions() { Rectangle bounds = Bounds; Rectangle prevBounds = new Rectangle((int)_prevPos.X, (int)_prevPos.Y, bounds.Width, bounds.Height); // Only check blocks around player int left = bounds.Left / TileData.TILE_SIZE; int right = bounds.Right / TileData.TILE_SIZE; int top = bounds.Top / TileData.TILE_SIZE; int bottom = bounds.Bottom / TileData.TILE_SIZE; for (int x = left; x <= right; x++) { for (int y = top; y <= bottom; y++) { // Not a solid block if (_world.GetBlock(x, y) == -1) continue; Rectangle blockBounds = new Rectangle(x * TileData.TILE_SIZE, y * TileData.TILE_SIZE, TileData.TILE_SIZE, TileData.TILE_SIZE); // Resolve collision on x axis if (bounds.Intersects(blockBounds)) { // Previously to left of block if (prevBounds.Right <= blockBounds.Left) { _pos.X = blockBounds.Left - bounds.Width; _vel.X = 0f; } // Previoulsy to right of block else if (prevBounds.Left >= blockBounds.Right) { _pos.X = blockBounds.Right; _vel.X = 0f; } } // Perform further collision using new bounds bounds = Bounds; // Resolve collision on y axis if (bounds.Intersects(blockBounds)) { // Previously above block if (prevBounds.Bottom <= blockBounds.Top) { _pos.Y = blockBounds.Top - bounds.Height; _vel.Y = 0f; // _isGrounded = true; Doesn't work due to float and int mismatch } // Previoulsy below block else if (prevBounds.Top >= blockBounds.Bottom) { _pos.Y = blockBounds.Bottom; _vel.Y = 0f; } } } } } public void Draw(SpriteBatch spriteBatch) { spriteBatch.FillRectangle(Bounds, Color.White); } }