简体   繁体   English

瓷砖矩形碰撞

[英]Tile Rectangle Collisions

I have a map that is 1280 by 1280 pixels. 我有一张1280 x 1280像素的地图。 I am drawing tiles on the screen that are 32 by 32 pixels. 我正在屏幕上绘制32 x 32像素的图块。 I also give each tile a rectangle of 32 by 32 pixels. 我还给每个图块一个32 x 32像素的矩形。 Each tile also has a bool telling me if it is blocked or not. 每个磁贴都有一个布尔值,告诉我是否被阻塞。 My sprite has a rectangle of 32 by 32 pixels. 我的精灵有一个32 x 32像素的矩形。

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. Sprite纹理的0,0位置在最左上角,并且只有1x1像素。 Why isn't the rectangle encompassing the entire texture as I have it set to 32x32? 设置为32x32时,为什么矩形不包含整个纹理?

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 https://gamedev.stackexchange.com/questions/29121/organize-a-game-set/29930#29930

在此处输入图片说明

Note : https://gamedev.stackexchange.com/ is definitely a better place for these kind of questions. 注意: https : //gamedev.stackexchange.com/绝对是解决此类问题的好地方。

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 瓷砖:0是可步行的,1是墙壁,2是玩家

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; 同样,由于我将级别构建为一维数组的方式,因此,例如在x = 3,y = 0时,图块会向下移动并向右移动; 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. 为了简单起见,您希望将级别保持为二维数组。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM