user27786866
user27786866

Reputation: 1

Platformer game collision detection not working

I'm trying to implement collision detection into a platformer style game in XNA/MonoGame. I'm following two separate tutorials, one for an animation manager, and another for adding collision. I'm attempting to merge them together but they're clashing.

I think the issue is with the player's rectangle (and perhaps velocity too) not being drawn from the sprite class by the game1 file, as the sprite class uses the animation manager's Draw method. But I'm very new to coding and have no clue how I would work around that (I may be off too, it might also be an "Update" issue).

Here is the code for the main game file:

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;

namespace Test.Platformer
{
    public class Game1 : Game
    {
        private GraphicsDeviceManager _graphics;
        private SpriteBatch _spriteBatch;
        
        private Dictionary<Vector2, int> tilemap;
        private Dictionary<Vector2, int> collisions;
        private List<Rectangle> textureStore;
        private Texture2D textureMap;
        private Texture2D collisionMap;

        int tilesize = 16;

        private Sprite player;
        private List<Rectangle> intersections;

        private Texture2D rectangleTexture;

        private KeyboardState prevKBState;

        public Game1()
        {
            _graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
            IsMouseVisible = true;
            tilemap = LoadMap("../../../Data/map1_main.csv");
            collisions = LoadMap("../../../Data/map1_collisions.csv");
            intersections = new();

        }

        private Dictionary<Vector2, int> LoadMap(string filepath)
        {
            Dictionary<Vector2, int> result = new();

            StreamReader reader = new(filepath);

            int y = 0;
            string line;
            while ((line = reader.ReadLine()) != null)
            {
                string[] items = line.Split(',');

                for (int x = 0; x < items.Length; x++)
                {
                    if (int.TryParse(items[x], out int value))
                    {
                        if (value > -1)
                        {
                            result[new Vector2(x, y)] = value;
                        }
                    }

                }
                y++;
            }
            return result;
        }
        protected override void Initialize()
        {
            base.Initialize();
        }

        protected override void LoadContent()
        {
            _spriteBatch = new SpriteBatch(GraphicsDevice);

            var animations = new Dictionary<string, Animation>()
            {
                {"RunDown", new Animation(Content.Load<Texture2D>("PlayerRunDown"), 4) },
                {"RunUp", new Animation(Content.Load<Texture2D>("PlayerRunUp"), 4) },
                {"RunLeft", new Animation(Content.Load<Texture2D>("PlayerRunLeft"), 6) },
                {"RunRight", new Animation(Content.Load<Texture2D>("PlayerRunRight"), 6) },
            };
            
            textureMap = Content.Load<Texture2D>("TexturePack");
            collisionMap = Content.Load<Texture2D>("TextureCollisionPack");

            player = new Sprite(animations, new Rectangle(0,0,16,32));
        }

        protected override void Update(GameTime gameTime)
        {
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
                Exit();

                player.Update(Keyboard.GetState(), prevKBState, gameTime);
            player.isClinging = false;

            prevKBState = Keyboard.GetState();

            // add player's Velocity and grab the intersecting tiles
            player.rect.X += (int)player.velocity.X;
            intersections = getIntersectingTilesHorizontal(player.rect);

            foreach (var rect in intersections)
            {

                // handle collisions if the tile position exists in the tile map layer.
                if (collisions.TryGetValue(new Vector2(rect.X, rect.Y), out int _val))
                {

                    // create temp rect to handle collisions (not necessary, you can optimize!)
                    Rectangle collision = new(
                        rect.X * tilesize,
                        rect.Y * tilesize,
                        tilesize,
                        tilesize
                    );

                    if (!player.rect.Intersects(collision))
                    {
                        continue;
                    }

                    // handle collisions based on the direction the player is moving
                    if (player.velocity.X > 0.0f)
                    {
                        player.rect.X = collision.Left - player.rect.Width;
                        player.isClinging = true;
                    }
                    else if (player.velocity.X < 0.0f)
                    {
                        player.rect.X = collision.Right;
                        player.isClinging = true;
                    }

                }

            }

            // same as horizontal collisions

            player.rect.Y += (int)player.velocity.Y;
            intersections = getIntersectingTilesVertical(player.rect);
            
            foreach (var rect in intersections)
            {
                if (collisions.TryGetValue(new Vector2(rect.X, rect.Y), out int _val))
                {

                    // create temp rect to handle collisions (not necessary, you can optimize!)
                    Rectangle collision = new(
                        rect.X * tilesize,
                        rect.Y * tilesize,
                        tilesize,
                        tilesize
                    );

                    if (!player.rect.Intersects(collision))
                    {
                        continue;
                    }


                    // handle collisions based on the direction the player is moving
                    if (player.velocity.Y > 0.0f)
                    {
                        player.rect.Y = collision.Top - player.rect.Height;
                        player.velocity.Y = 2f;
                    }
                    else if (player.velocity.Y < 0.0f)
                    {
                        player.rect.Y = collision.Bottom;
                    }

                }
            }

            base.Update(gameTime);
        }
        // wabs the intersecting tiles for a Rect. This grabs all tile positions where
        // an intersection is __possible__, not if a tile actually exists there.
        public List<Rectangle> getIntersectingTilesHorizontal(Rectangle target)
        {

            List<Rectangle> intersections = new();

            int widthInTiles = (target.Width - (target.Width % tilesize)) / tilesize;
            int heightInTiles = (target.Height - (target.Height % tilesize)) / tilesize;

            for (int x = 0; x <= widthInTiles; x++)
            {
                for (int y = 0; y <= heightInTiles; y++)
                {

                    intersections.Add(new Rectangle(

                        (target.X + x * tilesize) / tilesize,
                        (target.Y + y * (tilesize - 1)) / tilesize,
                        tilesize,
                        tilesize

                    ));

                }
            }

            return intersections;
        }
        public List<Rectangle> getIntersectingTilesVertical(Rectangle target)
        {

            List<Rectangle> intersections = new();

            int widthInTiles = (target.Width - (target.Width % tilesize)) / tilesize;
            int heightInTiles = (target.Height - (target.Height % tilesize)) / tilesize;

            for (int x = 0; x <= widthInTiles; x++)
            {
                for (int y = 0; y <= heightInTiles; y++)
                {

                    intersections.Add(new Rectangle(

                        (target.X + x * (tilesize - 1)) / tilesize,
                        (target.Y + y * tilesize) / tilesize,
                        tilesize,
                        tilesize

                    ));

                }
            }

            return intersections;
        }

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.Black);

            _spriteBatch.Begin(SpriteSortMode.Deferred, null, SamplerState.PointClamp);

            int numTilesPerRow = 4;

            //collision tiles
            foreach (var item in collisions)
            {
                Rectangle dest = new(
                    (int)item.Key.X * tilesize,
                    (int)item.Key.Y * tilesize,
                    tilesize,
                    tilesize
                );
                int x = item.Value % numTilesPerRow;
                int y = item.Value / numTilesPerRow;
                Rectangle src = new(
                    x * tilesize,
                    y * tilesize,
                    tilesize,
                    tilesize
                );

                _spriteBatch.Draw(collisionMap, dest, src, Color.White);
            }
            //repeat for background tiles
            foreach (var item in tilemap)
            {
                Rectangle dest = new(
                    (int)item.Key.X * tilesize,
                    (int)item.Key.Y * tilesize,
                    tilesize,
                    tilesize
                );
                int x = item.Value % numTilesPerRow;
                int y = item.Value / numTilesPerRow;
                Rectangle src = new(
                    x * tilesize,
                    y * tilesize,
                    tilesize,
                    tilesize
                );

                _spriteBatch.Draw(textureMap, dest, src, Color.White);
            }

            player.Draw(_spriteBatch);

            _spriteBatch.End();

            Console.WriteLine(player.wallJump);

            base.Draw(gameTime);
        }


    }
}

Here is the sprite class:

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection.Emit;
using Test.Platformer;

namespace Test.Platformer;

public class Sprite
{

    public Texture2D texture;
    public Rectangle rect;
    public Vector2 velocity;
    public float Speed = 3f;


    Vector2 position = new Vector2(40, 350);


    private Animation idleAnimation;
    private Animation runAnimation;
    private Animation jumpAnimation;
    private Animation wallAnimation;

    AnimationManager animationManager;

    protected Dictionary<string, Animation> animations;

    public Vector2 Position
    {
        get { return position; }
        set
        {
            position = value;

            if (animationManager != null)
                animationManager.Position = position;
        }
    }    

    public Sprite(Dictionary<string, Animation> animations, Rectangle rect)
    {
        this.animations = animations;
        animationManager = new AnimationManager(animations.First().Value);
        this.rect = rect;
    }

    public Sprite(Texture2D texture)
    {
        this.texture = texture;
    }


    public void Update(KeyboardState keystate, KeyboardState prevKBState, GameTime gameTime)
    {
        Move();

        SetAnimations();


        animationManager.Update(gameTime);

        Position += velocity;
        velocity = Vector2.Zero;

    }

    private void SetAnimations()
    {
        if (velocity.X > 0)
            animationManager.Play(animations["RunRight"]);
        else if (velocity.X < 0)
            animationManager.Play(animations["RunLeft"]);
        else if (velocity.Y < 0)
            animationManager.Play(animations["RunUp"]);
        else if (velocity.Y > 0)
            animationManager.Play(animations["RunDown"]);
        else animationManager.Stop();
    }

    private void Move()
    {
        if (Keyboard.GetState().IsKeyDown(Keys.Up))
            velocity.Y = -Speed;
        else if (Keyboard.GetState().IsKeyDown(Keys.Down))
            velocity.Y = Speed;
        else if (Keyboard.GetState().IsKeyDown(Keys.Left))
            velocity.X = -Speed;
        else if (Keyboard.GetState().IsKeyDown(Keys.Right))
            velocity.X = Speed;
    }

    public virtual void Draw(SpriteBatch spriteBatch)
    {
        if (texture != null)
            spriteBatch.Draw(texture, Position, Color.White);
        else if (animationManager != null)
            animationManager.Draw(spriteBatch);
        else throw new Exception("This ain't right..!");
    }

}

And here is the animation manager:

using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Test.Platformer
{
    public class AnimationManager
    {
        private static Animation _animation;

        private float _timer;

        public Vector2 Position { get; set; }

        public AnimationManager(Animation animation)
        {
            _animation = animation;

        }

        public void Draw(SpriteBatch spriteBatch)
        {
            spriteBatch.Draw(_animation.Texture,
                             Position,
                             new Rectangle(_animation.CurrentFrame * _animation.FrameWidth,
                                           0,
                                           _animation.FrameWidth,
                                           _animation.FrameHeight),
                             Color.White);

            
        }

        public void Play(Animation animation)
        {
            if (_animation == animation)
                return;

            _animation = animation;

            _animation.CurrentFrame = 0;

            _timer = 0;
        }

        public void Stop()
        {
            _timer = 0f;

            _animation.CurrentFrame = 0;
        }

        public void Update(GameTime gameTime)
        {
            _timer += (float)gameTime.ElapsedGameTime.TotalSeconds;

            if (_timer > _animation.FrameSpeed)
            {
                _timer = 0f;

                _animation.CurrentFrame++;

                if (_animation.CurrentFrame >= _animation.FrameCount)
                    _animation.CurrentFrame = 0;
            }
        }
    }
}

The animation class gets FrameWidth and FrameHeight from the size of the texture.

Full project and assests here

Any help is greatly appreciated

Upvotes: 0

Views: 32

Answers (0)

Related Questions