Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I define a tile with a rectangle. This is displayed below.

public class Tile : Sprite
{
    Rectangle rectangle;

    public Rectangle GetRectangle
    {
        get
        {
            return rectangle;
        }
    }

    public Tile(Texture2D texture, Vector2 position, int tileWidth, int tileHeight) :
        base(texture, position)
    {
        rectangle = new Rectangle((int)position.X, (int)position.Y, tileWidth, tileHeight);
    }
}

These tiles are created by my level editor and placed on a map.

I have a player class here but I can't get it to behave with my tiles. Either I can get collisions working on the y, but then the character can't move on the x. Or I can get it moving on the x but if the player walks into tiles from the side the character won't respond.

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using GameStateManagement;

namespace NecromancerGame
{
    public class Player : Sprite
    {

        private float speed;
        private float jumpHeight;
        Rectangle rectangle;
        //private Vector2 velocity;
        private float timer;
        private float timeToChangeFrame;
        private int frameNumber;
        private int fps;
        private int frameWidth;
        private int frameHeight;
        private bool facingRight;
        private float gravity;
        SpriteEffects effect;

        public Vector2 GetPosition
        {
            get
            {
                return position;
            }
        }

        public Player(Texture2D texture, Vector2 pos, int fps, float moveSpeed):
            base(texture, pos)
        {
            int timer = 0;
            int frameNumber = 0;
            timeToChangeFrame = 0.20f;
            this.fps = fps;
            frameWidth = texture.Width / (fps + 1);
            frameHeight = texture.Height;
            facingRight = true;
            speed = moveSpeed;
            rectangle = new Rectangle((int)position.X, (int)position.Y, frameWidth, frameHeight);
            gravity = Stats.gravityStrength;
            origin = new Vector2(frameWidth / 2, frameHeight / 2);
        }

        public void StartJump()
        {
            velocity.Y = -Stats.jumpHeight;
        }

        public void ChangeDirection()
        {
            facingRight = !facingRight;
        }

        public void Update(GameTime gameTime, List<Tile> tileList)
        {
            Vector2 oldPosition = position;
            bool colliding = false;
            timer += (float)gameTime.ElapsedGameTime.TotalSeconds;
            if (timer > timeToChangeFrame)
            {
                frameNumber++;
                if (frameNumber >= fps)
                {
                    frameNumber = 0;
                }
                timer = 0;
            }

            if (facingRight)
            {
                velocity.X = speed;
                effect = SpriteEffects.None;
            }
            else
            {
                velocity.X = -speed;
                effect = SpriteEffects.FlipHorizontally;
            }

            velocity.Y += gravity;

            Vector2 potentialPosition = oldPosition + velocity;
            Rectangle potentialNewPosition = new Rectangle(rectangle.Center.X + (int)velocity.X, rectangle.Center.X + (int)velocity.Y, frameWidth, frameHeight);

            for (int i = 0; i < tileList.Count; i++)
            {
                if (potentialNewPosition.Intersects(tileList[i].GetRectangle))
                {
                    //Point point = potentialNewPosition.Center - tileList[i].GetRectangle.Center;
                    position = oldPosition;
                    //position = oldPosition;
                    //velocity.X = 0;
                    //position.X = oldPosition.X;
                    colliding = true;

                }
            }

            position += velocity;

            rectangle.X = (int)position.X;
            rectangle.Y = (int)position.Y;

            base.Update(gameTime);
        }

        public override void Draw(SpriteBatch spriteBatch)
        {
            base.Draw(spriteBatch, frameNumber, frameWidth, frameHeight, effect);
        }

    }
}
share|improve this question
    
Voting to close as 'too localised', since this appears to be a "debug my code for me" question. –  Trevor Powell Jan 27 '13 at 7:16
add comment

closed as too localized by Byte56, Trevor Powell, Laurent Couvidou, Sean Middleditch, Jimmy Feb 4 '13 at 18:21

This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.If this question can be reworded to fit the rules in the help center, please edit the question.

2 Answers

Not saying this fixes the problem but this line seems suspect:

Rectangle potentialNewPosition = new Rectangle(rectangle.Center.X + (int)velocity.X, rectangle.Center.X + (int)velocity.Y, frameWidth, frameHeight);

Should be:

Rectangle potentialNewPosition = new Rectangle(rectangle.X + (int)velocity.X, rectangle.Y + (int)velocity.Y, frameWidth, frameHeight);

There were 2 problems:

  1. You were using rectangle.Center.X for both x, y
  2. You were using the center of the player rectangle as your top, left when creating potentialNewPosition rectangle.
share|improve this answer
add comment

Or I can get it moving on the x but if the player walks into tiles from the side the character won't respond.

Well, I've noticed one fallacy in your logic that is most probably what's leading to this:

for (int i = 0; i < tileList.Count; i++)
{
    if (potentialNewPosition.Intersects(tileList[i].GetRectangle))
    {
        position = oldPosition;
        colliding = true;
    }
}

If a collision is found, the player's movement is simply 'cancelled'. Here, if he collides with a block, he simply is sent back to where he came from. What we should do when dealing with tile collision is not cancel a movement, sending the object back to its starting position, but rather correct it.

What your algorithm should do is place your player's hitbox outside of the colliding tile, but no further from it. For example, considering that your player is basically a rectangle, if it was found to be inside a tile, you could make it so that Player.Right = Tile.X (if he was moving rightwards), or Player.Bottom = Tile.Y (for downward movement), or Player.X = Tile.Right (leftward), or Player.Y = Tile.Bottom (upward). This places him right alongside the tile, outside.

share|improve this answer
    
But how do I tell what the movement is supposed to be? For example, there's gravity always creating a collision every frame. Now if I move my character right and is walking on top of a block but he's also being pushed down by the gravity, the x velocity will just be removed surely? –  Lucas Jan 26 '13 at 17:13
    
@Lucas Yes, this is why I'll recommend you to move on one axis at a time. First you move the character on the x, where you can determine the direction of movement by your facingRight bool. Then, you move it on the y, where you'd determine the direction by checking wether the speed is negative (upwards) or positive (downwards). –  Mutoh Jan 26 '13 at 17:25
add comment

Not the answer you're looking for? Browse other questions tagged or ask your own question.