0
votes

I have a map that is 1280 by 1280 pixels. I am drawing tiles on the screen that are 32 by 32 pixels. I also give each tile a rectangle of 32 by 32 pixels. Each tile also has a bool telling me if it is blocked or not. My sprite has a rectangle of 32 by 32 pixels.

When the sprite and tile intersect the top left most pixel of the tile is the only one that makes contact with the sprites rectangle.

I'm tried inflating the rectangle with no change.

Core.playerOneSpriteRectangle = new Rectangle((int)Core.playerOneSprite.Position.X, (int)Core.playerOneSprite.Position.Y, Engine.TileWidth, Engine.TileHeight);

        #region Player One Controls
        // Player One Controls
        Tile tile;
        Rectangle destination = new Rectangle(0, 0, Engine.TileWidth, Engine.TileHeight);
        if (keyState.IsKeyDown(Keys.W) || (gamePadOneState.DPad.Up == ButtonState.Pressed))
        {
            Core.playerOneSprite.CurrentAnimation = AnimationKey.Up;
            foreach (var layer in tileMapLayers)
            {
                for (var y = 0; y < layer.Height; y++)
                {
                    destination.Y = y * Engine.TileHeight;
                    for (var x = 0; x < layer.Width; x++)
                    {
                        tile = layer.GetTile(x, y);

                        destination.X = x * Engine.TileWidth;

                        // Check Collision With Tiles
                        if (Core.playerOneSpriteRectangle.Intersects(destination) && tile.TileBlocked)
                        {
                            playerOneMotion.Y = destination.Bottom;
                            //Console.WriteLine("Intersected with" + destination + tile.TileBlocked);
                        }
                        else if (Core.playerOneSpriteRectangle.Intersects(destination) && tile.TileBlocked == false)
                        {
                            playerOneMotion.Y = -1f;
                        }  
                    }
                }
            }
        }

I'm trying to create rectangles for each tile so my sprite doesn't walk through them.

UPDATE

This is the code I currently have after modifying it with suggestions.

Tile tile;
        Point playertile = new Point((int)Core.playerOneSprite.Position.X / Engine.TileWidth, (int)Core.playerOneSprite.Position.Y / Engine.TileHeight);

        if (keyState.IsKeyDown(Keys.W) || (gamePadOneState.DPad.Up == ButtonState.Pressed))
        {
            Core.playerOneSprite.CurrentAnimation = AnimationKey.Up;
            foreach (var layer in GraelenAreaOne.graelenAreaOneSplatterTileMapLayers)
            {
                tile = layer.GetTile(playertile.X, playertile.Y - 1);

                // Check Collision With Tiles
                if (tile.TileBlocked)
                {
                    playerOneMotion.Y = 0;

                    //Console.WriteLine("Intersected with" + destination + tile.TileBlocked);
                }
                else
                {
                    playerOneMotion.Y = -1;
                }  
            }
        }

I am now able to collide with tiles however my sprites rectangle is not intersecting properly. Position 0,0 of the sprite texture is in the top left most corner and it only a 1x1 pixel. Why isn't the rectangle encompassing the entire texture as I have it set to 32x32?

1
You should do an alert or a console output on your values (Engine.TileWidth, destination.X,Y, variables within tile, etc...) to help you figure out where things are going wrong. When I look at this I don't see anything particularly wrong except that you seem to be going through every single tile when you're moving up. Why not take Math.floor(playerOneMotion.X/Engine.TileWidth) & Math.floor((playerOneMotion.Y-1)/Engine.TileHeight) and use that to get your layer tile? That would be a lot less work.Chris
@Chris I'm able to view output while debugging and all the fields are populated however the sprite is still not colliding with the tiles at their edges. Only 0, 0 of the sprite texture is being detected. Thanks for the Math tip, that works better.Devin Rawlek

1 Answers

3
votes

You should just check whether the tile you want to go to is walkable or not, without having to bother yourself with rectangles.

Some pseudo code :

// Convert pixel coordinates to tile coords
Point playerTile = new Point(player.X / tileWidth, player.Y / tileHeight);
Point targetTile = new Point(target.X / tileWidth, target.Y / tileHeight);

// Take a decision 
if(Direction == Up)
    var tile = layer.GetTile(playerTile.X, playerTile.Y - 1);
    if(tile == walkable)
        // Move to your tile
    else
        ...

Additionally, here's some code I wrote a while ago which might interest you in optimizing the drawing of your level by drawing only draws what's visible.

https://gamedev.stackexchange.com/questions/29121/organize-a-game-set/29930#29930

enter image description here

Note : https://gamedev.stackexchange.com/ is definitely a better place for these kind of questions.

EDIT

Here's a quick example that works for me :

Note that I return when player cannot move to tile.

Tiles : 0 is walkable, 1 is a wall, 2 is player

using System;
using System.Linq;
using System.Windows;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input.Touch;
using Point = Microsoft.Xna.Framework.Point;

namespace SlXnaApp1
{
    public partial class GamePage : PhoneApplicationPage
    {
        private readonly GameTimer _timer;
        private int[] _levels;
        private Point _player;
        private Texture2D _t1;
        private Texture2D _t2;
        private Texture2D _t3;
        private Texture2D[] _textures;
        private ContentManager _contentManager;
        private SpriteBatch _spriteBatch;

        public GamePage()
        {
            InitializeComponent();

            // Get the content manager from the application
            _contentManager = (Application.Current as App).Content;

            // Create a timer for this page
            _timer = new GameTimer();
            _timer.UpdateInterval = TimeSpan.FromTicks(333333);
            _timer.Update += OnUpdate;
            _timer.Draw += OnDraw;
        }

        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            // Set the sharing mode of the graphics device to turn on XNA rendering
            var graphicsDevice = SharedGraphicsDeviceManager.Current.GraphicsDevice;
            graphicsDevice.SetSharingMode(true);

            // Create a new SpriteBatch, which can be used to draw textures.
            _spriteBatch = new SpriteBatch(graphicsDevice);

            // TODO: use this.content to load your game content here
            _t1 = new Texture2D(graphicsDevice, 32, 32);
            _t1.SetData(Enumerable.Repeat(Color.Red, 32*32).ToArray());
            _t2 = new Texture2D(graphicsDevice, 32, 32);
            _t2.SetData(Enumerable.Repeat(Color.Green, 32*32).ToArray());
            _t3 = new Texture2D(graphicsDevice, 32, 32);
            _t3.SetData(Enumerable.Repeat(Color.Blue, 32*32).ToArray());
            _textures = new[] {_t1, _t2, _t3};
            _levels = new int[4*4]
                {
                    2, 0, 0, 0,
                    0, 0, 1, 0,
                    1, 1, 1, 0,
                    0, 0, 0, 1,
                };
            _player = new Point();

            TouchPanel.EnabledGestures = GestureType.Flick;
            // Start the timer
            _timer.Start();

            base.OnNavigatedTo(e);
        }

        protected override void OnNavigatedFrom(NavigationEventArgs e)
        {
            // Stop the timer
            _timer.Stop();

            // Set the sharing mode of the graphics device to turn off XNA rendering
            SharedGraphicsDeviceManager.Current.GraphicsDevice.SetSharingMode(false);

            base.OnNavigatedFrom(e);
        }

        /// <summary>
        ///     Allows the page to run logic such as updating the world,
        ///     checking for collisions, gathering input, and playing audio.
        /// </summary>
        private void OnUpdate(object sender, GameTimerEventArgs e)
        {
            Vector2 vector = new Vector2();
            while (TouchPanel.IsGestureAvailable)
            {
                var gestureSample = TouchPanel.ReadGesture();
                var vector2 = gestureSample.Delta;
                vector2.Normalize();
                vector = new Vector2((float) Math.Round(vector2.X), (float) Math.Round(vector2.Y));
            }

            Direction direction = new Direction();
            if (vector.X > 0) direction = Direction.Right;
            else if (vector.X < 0) direction = Direction.Left;
            else if (vector.Y < 0) direction = Direction.Up;
            else if (vector.Y > 0) direction = Direction.Down;

            Point newPos = new Point();
            switch (direction)
            {
                case Direction.None:
                    return;
                case Direction.Up:
                    if (GetTile(_player.X, _player.Y - 1) == 0)
                        newPos = new Point(_player.X, _player.Y - 1);
                    else return;
                    break;
                case Direction.Down:
                    if (GetTile(_player.X, _player.Y + 1) == 0)
                        newPos = new Point(_player.X, _player.Y + 1);
                    else return;
                    break;
                case Direction.Left:
                    if (GetTile(_player.X - 1, _player.Y) == 0)
                        newPos = new Point(_player.X - 1, _player.Y);
                    else return;
                    break;
                case Direction.Right:
                    if (GetTile(_player.X + 1, _player.Y) == 0)
                        newPos = new Point(_player.X + 1, _player.Y);
                    else return;
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }

            SetTile(_player.X, _player.Y, 0);
            SetTile(newPos.X, newPos.Y, 2);
            _player = newPos;
        }

        private int GetTile(int x, int y)
        {
            return _levels[y*4 + x];
        }

        private void SetTile(int x, int y, int value)
        {
            _levels[y*4 + x] = value;
        }

        /// <summary>
        ///     Allows the page to draw itself.
        /// </summary>
        private void OnDraw(object sender, GameTimerEventArgs e)
        {
            SharedGraphicsDeviceManager.Current.GraphicsDevice.Clear(Color.CornflowerBlue);
            _spriteBatch.Begin();
            for (int i = 0; i < _levels.Length; i++)
            {
                var tile = _levels[i];
                Point point = new Point(i%4, i/4);
                var texture2D = _textures[tile];
                _spriteBatch.Draw(texture2D, Vector2.Multiply(new Vector2(point.X, point.Y), 32), Color.White);
            }
            _spriteBatch.End();
        }

        private enum Direction
        {
            None,
            Up,
            Down,
            Left,
            Right
        }
    }
}

Also, due to the way I built my level as a 1-dimensional array, the tile moves down when for instance it is at x=3, y=0 and going to the right; this is unintended at all but is normal as I don't check bounds. You'd want to keep your level as a 2-dimensional array for simplicity.