-1
\$\begingroup\$

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);
        }

    }
}
\$\endgroup\$
1
  • \$\begingroup\$ Voting to close as 'too localised', since this appears to be a "debug my code for me" question. \$\endgroup\$ Commented Jan 27, 2013 at 7:16

2 Answers 2

1
\$\begingroup\$

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.
\$\endgroup\$
1
\$\begingroup\$

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.

\$\endgroup\$
2
  • \$\begingroup\$ 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? \$\endgroup\$
    – Lucas
    Commented Jan 26, 2013 at 17:13
  • \$\begingroup\$ @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). \$\endgroup\$
    – Mutoh
    Commented Jan 26, 2013 at 17:25

Not the answer you're looking for? Browse other questions tagged .