Collision problem

Mar 15, 2014 at 9:40 AM
Edited Mar 15, 2014 at 9:44 AM
Hello,

I use the default map for testing my collision code and I have two problems, my collision code works fine when I am on the ground but I have some trouble to get it works properly.

I don't know too how to calculate my player Y position when I am on slopes with differents angles.

This is a video when I show my problem.
Youtube video

This is a link to download my project.
Dropbox link

And this is my entire player.cs code.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Input;

using Library.Animation;
using Library.Managers;

using Data;

using xTile;
using xTile.Tiles;
using xTile.Dimensions;
using xTile.Layers;

namespace Library.Entities
{
    public class Player : Entity
    {
        Layer collision;
        public Tile tile;
        public Location tileLocation;

        int tx, ty;

        public Player()
        {

        }

        public override void LoadContent(ContentManager Content, Map map)
        {
            collision = map.Layers[3];

            PlayerInfo player;

            player = Content.Load<PlayerInfo>(@"Entities/Info/Player");

            moveSpeed = player.MoveSpeed;
            jumpSpeed = player.JumpSpeed;
            gravity = 14f;

            this.animation = new SimpleAnimationEntity(new SimpleAnimationDefinition()
            {
                AssetName = player.SpriteSheetAssetName,
                FrameRate = player.Framerate,
                FrameSize = player.FrameSize,
                Loop = player.Loop,
                NbFrames = player.NbFrames
            });

            animation.position = player.Position;

            this.animation.Initialize();
            this.animation.LoadContent(Content);
        }

        public override void Update(GameTime gameTime)
        {



            animation.FinishedAnimation = false;

            if (InputManager.KeyDown(Keys.D))
            {
                animation.CurrentFrame = new Point(animation.CurrentFrame.X, 2);
                velocity.X = moveSpeed * (float)gameTime.ElapsedGameTime.TotalSeconds;
            }
            else if (InputManager.KeyDown(Keys.A))
            {
                animation.CurrentFrame = new Point(animation.CurrentFrame.X, 1);
                velocity.X = -moveSpeed * (float)gameTime.ElapsedGameTime.TotalSeconds;
            }
            else
            {
                animation.CurrentFrame = new Point(0, animation.CurrentFrame.Y);
                animation.FinishedAnimation = true;
                velocity.X = 0;
            }

            if (InputManager.KeyPressed(Keys.Space, Keys.W) && onGround)
            {
                velocity.Y = -jumpSpeed * (float)gameTime.ElapsedGameTime.TotalSeconds;
                onGround = false;
            }

            if (!onGround)
                velocity.Y += gravity * (float)gameTime.ElapsedGameTime.TotalSeconds;
            else
                velocity.Y = 0;

            position += velocity;

            animation.position += velocity;
            CollisionBox = new Microsoft.Xna.Framework.Rectangle((int)position.X, (int)position.Y, 32, 32);
            Vector2 newPos = position;

            HandleCollision(animation.position);

            this.animation.Update(gameTime);
        }

        public override void HandleCollision(Vector2 newPos)
        {
            //bottom left
            tileLocation = new Location(((int)newPos.X) / collision.TileWidth, 
                ((int)newPos.Y + collisionBox.Height) / collision.TileHeight);
            tile = collision.Tiles[tileLocation];
            if (tile != null && tile.TileIndex == 1 || 
                tile != null && tile.TileIndex == 5 || 
                tile != null && tile.TileIndex == 15 || 
                tile != null && tile.TileIndex == 4 || 
                tile != null && tile.TileIndex == 13 || 
                tile != null && tile.TileIndex == 108)
            {
                onGround = true;
            }
            else
            {
                onGround = false;
            }

            //bottom right
            tileLocation = new Location(((int)newPos.X + 32 / 2) / collision.TileWidth, 
                ((int)newPos.Y + collisionBox.Height) / collision.TileHeight);
            tile = collision.Tiles[tileLocation];
            if (tile != null && tile.TileIndex == 1 || 
                tile != null && tile.TileIndex == 5 || 
                tile != null && tile.TileIndex == 15 || 
                tile != null && tile.TileIndex == 4 || 
                tile != null && tile.TileIndex == 13 || 
                tile != null && tile.TileIndex == 108)
            {
                onGround = true;
            }
            else
            {
                onGround = false;
            }

            // top left
            tileLocation = new Location(((int)newPos.X) / collision.TileWidth, 
                ((int)newPos.Y - collisionBox.Height) / collision.TileHeight);
            if (tileLocation != new Location(0, -1))
            {
                tile = collision.Tiles[tileLocation];
                if (tile != null && tile.TileIndex == 1 || 
                    tile != null && tile.TileIndex == 5 || 
                    tile != null && tile.TileIndex == 4 || 
                    tile != null && tile.TileIndex == 13 || 
                    tile != null && tile.TileIndex == 15 || 
                    tile != null && tile.TileIndex == 107 || 
                    tile != null && tile.TileIndex == 108 || 
                    tile != null && tile.TileIndex == 106)
                {
                    ActivateGravity = true;
                }
                else
                {
                    ActivateGravity = true;
                }
            }
            
            // top right
            tileLocation = new Location(((int)newPos.X + collisionBox.Width / 2) / collision.TileWidth, 
                ((int)newPos.Y - collisionBox.Height) / collision.TileHeight);
            if (tileLocation != new Location(0, -1))
            {
                tile = collision.Tiles[tileLocation];
                if (tile != null && tile.TileIndex == 1 || 
                    tile != null && tile.TileIndex == 5 || 
                    tile != null && tile.TileIndex == 4 || 
                    tile != null && tile.TileIndex == 13 || 
                    tile != null && tile.TileIndex == 15 || 
                    tile != null && tile.TileIndex == 107 || 
                    tile != null && tile.TileIndex == 108 || 
                    tile != null && tile.TileIndex == 106)
                {
                    ActivateGravity = true;
                }
                else
                {
                    ActivateGravity = true;
                }
            }

            // Slope
            tileLocation = new Location((int)newPos.X / collision.TileWidth, 
                ((int)newPos.Y + 32) / collision.TileHeight);
            tile = collision.Tiles[tileLocation];

            tx = ((int)newPos.X - 32) / 2 % collision.TileWidth;
            ty = ((int)newPos.Y - 32) % collision.TileHeight;

            if (tile != null && tile.TileIndex == 22 || 
                tile != null && tile.TileIndex == 20 || 
                tile != null && tile.TileIndex == 21 || 
                tile != null && tile.TileIndex == 17 || 
                tile != null && tile.TileIndex == 16 || 
                tile != null && tile.TileIndex == 22 || 
                tile != null && tile.TileIndex == 21 || 
                tile != null && tile.TileIndex == 19 || 
                tile != null && tile.TileIndex == 18 || 
                tile != null && tile.TileIndex == 24 || 
                tile != null && tile.TileIndex == 25 || 
                tile != null && tile.TileIndex == 23 || 
                tile != null && tile.TileIndex == 18)
            {

                if (ty > collision.TileHeight - tx)
                {
                    onGround = true;
                }
                if (ty > collision.TileHeight - tx / 2)
                { 
                    onGround = true;
                }
                if (ty > collision.TileHeight / 2 - tx / 2)
                {
                    onGround = true;
                }
            }


            

        }

        public override void Draw(SpriteBatch spriteBatch)
        {
            this.animation.Draw(spriteBatch, false);
        }
    }
}
And considering I will use the map viewport to follow the player, there is something I have to do for collisions ?
Coordinator
Mar 17, 2014 at 9:15 AM
Regarding of the positioning of your sprites on the map, you need to take into account the viewport by subtracting it's top-left coordinates from your sprite's absolute map positions when rendering them on the screen. If you fail to do this, the moment you move your viewport, say to the right, your sprites will not correspondingly pan out of the viewport to the left.

As for collision detection, I find the following link to be a very comprehensive explanation of how to implement various collision features in a 2D platformer, including one-way platforms, walls, inclined ramps and so on:
The guide to implementing 2D platformers

I hope this helps. If I find some time, I will try to implement something. May favourite technique is to insert a secondary *Functional) layer with functional tiles, for example: a wall tile, one-way platform tiles, ramp tiles (and ramp-fixers to prevent fall-throughs.. more on that some other time). This layer is marked invisible so it isn't rendered (Layer.Visible property). I then apply logic similar to the link above on the tiles of the Functional layer. It is possible to use this layer for other things, such as marking the spawn points of enemies, exit points etc.