Envision, Create, Share

Welcome to HBGames, a leading amateur game development forum and Discord server. All are welcome, and amongst our ranks you will find experts in their field from all aspects of video game design and development.

XNA Tile Collisions using "Tiled"

I don't know if any of you know XNA, but I decided to give it a whirl anyhow.

Alright, so I'm a newbie to XNA and C# in general. I got this Tile Map system from this place. It uses the .tmx (in reality just a renamed XML file) outputted by a program called Tiled, which is a 2D Map Editor.

Here's the Tile Map class:
Code:
 

using System;

using System.Collections;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Xml;

using System.Xml.Schema;

using Microsoft.Xna.Framework.Graphics;

using Microsoft.Xna.Framework.Content;

using Microsoft.Xna.Framework;

using System.IO;

using System.IO.Compression;

 

namespace RPGExperiment

{

    public class Tileset

    {

        public class TilePropertyList : Dictionary<string, string>

        {

        }

 

        public string Name;

        public int FirstTileID;

        public int TileWidth;

        public int TileHeight;

        public Dictionary<int, TilePropertyList> TileProperties = new Dictionary<int, TilePropertyList>();

        public string Image;

        protected Texture2D _Texture;

        protected int _TexWidth, _TexHeight;

 

        internal static Tileset Load(XmlReader reader)

        {

            var result = new Tileset();

 

            result.Name = reader.GetAttribute("name");

            result.FirstTileID = int.Parse(reader.GetAttribute("firstgid"));

            result.TileWidth = int.Parse(reader.GetAttribute("tilewidth"));

            result.TileHeight = int.Parse(reader.GetAttribute("tileheight"));

 

            int currentTileId = -1;

 

            while (reader.Read())

            {

                var name = reader.Name;

 

                switch (reader.NodeType)

                {

                    case XmlNodeType.Element:

                        switch (name)

                        {

                            case "image":

                                result.Image = reader.GetAttribute("source");

                                break;

                            case "tile":

                                currentTileId = int.Parse(reader.GetAttribute("id"));

                                break;

                            case "property":

                                {

                                    TilePropertyList props;

                                    if (!result.TileProperties.TryGetValue(currentTileId, out props))

                                    {

                                        props = new TilePropertyList();

                                        result.TileProperties[currentTileId] = props;

                                    }

 

                                    props[reader.GetAttribute("name")] = reader.GetAttribute("value");

                                } break;

                        }

                        

                        break;

                    case XmlNodeType.EndElement:

                        break;

                }

            }

 

            return result;

        }

 

        public TilePropertyList GetTileProperties(int index)

        {

            index -= FirstTileID;

 

            if (index < 0)

                return null;

 

            TilePropertyList result = null;

            TileProperties.TryGetValue(index, out result);

 

            return result;

        }

 

        public Texture2D Texture

        {

            get

            {

                return _Texture;

            }

            set

            {

                _Texture = value;

                _TexWidth = value.Width;

                _TexHeight = value.Height;

            }

        }

 

        internal bool MapTileToRect(int index, ref Rectangle rect)

        {

            index -= FirstTileID;

 

            if (index < 0)

                return false;

 

            int rowSize = _TexWidth / TileWidth;

            int row = index / rowSize;

            int numRows = _TexHeight / TileHeight;

            if (row >= numRows)

                return false;

 

            int col = index % rowSize;

 

            rect.X = col * TileWidth;

            rect.Y = row * TileHeight;

            rect.Width = TileWidth;

            rect.Height = TileHeight;

            return true;

        }

    }

 

    public class Layer

    {

        public SortedList<string, string> Properties = new SortedList<string, string>();

        internal struct TileInfo

        {

            public Texture2D Texture;

            public Rectangle Rectangle;

        }

 

        public string Name;

        public int Width, Height;

        public float Opacity = 1;

        public int[] Tiles;

        internal TileInfo[] _TileInfoCache = null;

 

        internal static Layer Load(XmlReader reader)

        {

            var result = new Layer();

 

            if (reader.GetAttribute("name") != null)

                result.Name = reader.GetAttribute("name");

            if (reader.GetAttribute("width") != null)

                result.Width = int.Parse(reader.GetAttribute("width"));

            if (reader.GetAttribute("height") != null)

                result.Height = int.Parse(reader.GetAttribute("height"));

            if (reader.GetAttribute("opacity") != null)

                result.Opacity = float.Parse(reader.GetAttribute("opacity"));

 

            result.Tiles = new int[result.Width * result.Height];

 

            while (!reader.EOF)

            {

                var name = reader.Name;

 

                switch (reader.NodeType)

                {

                    case XmlNodeType.Element:

                        switch (name)

                        {

                            case "data":

                                {

                                    if (reader.GetAttribute("encoding") != null)

                                    {

                                        var encoding = reader.GetAttribute("encoding");

                                        var compressor = reader.GetAttribute("compression");

                                        switch (encoding)

                                        {

                                            case "base64":

                                                {

                                                    int dataSize = (result.Width * result.Height * 4) + 1024;

                                                    var buffer = new byte[dataSize];

                                                    reader.ReadElementContentAsBase64(buffer, 0, dataSize);

 

                                                    Stream stream = new MemoryStream(buffer, false);

                                                    if (compressor == "gzip")

                                                        stream = new GZipStream(stream, CompressionMode.Decompress, false);

 

                                                    using (stream)

                                                    using (var br = new BinaryReader(stream))

                                                    {

                                                        for (int i = 0; i < result.Tiles.Length; i++)

                                                            result.Tiles[i] = br.ReadInt32();

                                                    }

 

                                                    continue;

                                                };

 

                                            default:

                                                throw new Exception("Unrecognized encoding.");

                                        }

                                    }

                                    else

                                    {

                                        using (var st = reader.ReadSubtree())

                                        {

                                            int i = 0;

                                            while (!st.EOF)

                                            {

                                                switch (st.NodeType)

                                                {

                                                    case XmlNodeType.Element:

                                                        if (st.Name == "tile")

                                                        {

                                                            if (i < result.Tiles.Length)

                                                            {

                                                                result.Tiles[i] = int.Parse(st.GetAttribute("gid"));

                                                                i++;

                                                            }

                                                        }

 

                                                        break;

                                                    case XmlNodeType.EndElement:

                                                        break;

                                                }

 

                                                st.Read();

                                            }

                                        }

                                    }

                                    Console.WriteLine("It made it!");

                                } break;

                            case "properties":

                                {

                                    using (var st = reader.ReadSubtree())

                                    {

                                        while (!st.EOF)

                                        {

                                            switch (st.NodeType)

                                            {

                                                case XmlNodeType.Element:

                                                    if (st.Name == "property")

                                                    {

                                                        st.Read();

                                                        if (st.GetAttribute("name") != null)

                                                        {

                                                            result.Properties.Add(st.GetAttribute("name"), st.GetAttribute("value"));

                                                        }

                                                    }

 

                                                    break;

                                                case XmlNodeType.EndElement:

                                                    break;

                                            }

 

                                            st.Read();

                                        }

                                    }

                                } break;

                        }

 

                        break;

                    case XmlNodeType.EndElement:

                        break;

                }

 

                reader.Read();

            }

 

            return result;

        }

 

        public int GetTile(int x, int y)

        {

            if ((x < 0) || (y < 0) || (x >= Width) || (y >= Height))

                throw new InvalidOperationException();

 

            int index = (y * Width) + x;

            return Tiles[index];

        }

 

        protected void BuildTileInfoCache(IList<Tileset> tilesets)

        {

            Rectangle rect = new Rectangle();

            var cache = new List<TileInfo>();

            int i = 1;

 

        next:

            for (int t = 0; t < tilesets.Count; t++)

            {

                if (tilesets[t].MapTileToRect(i, ref rect))

                {

                    cache.Add(new TileInfo

                    {

                        Texture = tilesets[t].Texture,

                        Rectangle = rect

                    });

                    i += 1;

                    goto next;

                }

            }

 

            _TileInfoCache = cache.ToArray();

        }

 

        public void Draw(SpriteBatch batch, IList<Tileset> tilesets, Rectangle rectangle, Vector2 viewportPosition, int tileWidth, int tileHeight)

        {

            int i = 0;

            Vector2 destPos = new Vector2(rectangle.Left, rectangle.Top);

            Vector2 viewPos = viewportPosition;

 

            int minX = (int)Math.Floor(viewportPosition.X / tileWidth);

            int minY = (int)Math.Floor(viewportPosition.Y / tileHeight);

            int maxX = (int)Math.Ceiling((rectangle.Width + viewportPosition.X) / tileWidth);

            int maxY = (int)Math.Ceiling((rectangle.Height + viewportPosition.Y) / tileHeight);

 

            if (minX < 0)

                minX = 0;

            if (minY < 0)

                minY = 0;

            if (maxX >= Width)

                maxX = Width - 1;

            if (maxY >= Height)

                maxY = Height - 1;

 

            if (viewPos.X > 0)

            {

                viewPos.X = ((int)Math.Floor(viewPos.X)) % tileWidth;

            }

            else

            {

                viewPos.X = (float)Math.Floor(viewPos.X);

            }

 

            if (viewPos.Y > 0)

            {

                viewPos.Y = ((int)Math.Floor(viewPos.Y)) % tileHeight;

            }

            else

            {

                viewPos.Y = (float)Math.Floor(viewPos.Y);

            }

 

            TileInfo info = new TileInfo();

            if (_TileInfoCache == null)

                BuildTileInfoCache(tilesets);

 

            for (int y = minY; y <= maxY; y++)

            {

                destPos.X = rectangle.Left;

 

                for (int x = minX; x <= maxX; x++)

                {

                    i = (y * Width) + x;

                    int index = Tiles[i] - 1;

 

                    if ((index >= 0) && (index < _TileInfoCache.Length))

                    {

                        info = _TileInfoCache[index];

                        batch.Draw(info.Texture, destPos - viewPos, info.Rectangle, new Color(Color.White, this.Opacity));

                    }

 

                    destPos.X += tileWidth;

                }

 

                destPos.Y += tileHeight;

            }

        }

    }

 

    public class ObjectGroup

    {

        public SortedList<string, Object> Objects = new SortedList<string, Object>();

        public SortedList<string, string> Properties = new SortedList<string, string>();

 

        public string Name;

        public int Width, Height, X, Y;

        float Opacity = 1;

 

        internal static ObjectGroup Load(XmlReader reader)

        {

            var result = new ObjectGroup();

 

            if (reader.GetAttribute("name") != null)

                result.Name = reader.GetAttribute("name");

            if (reader.GetAttribute("width") != null)

                result.Width = int.Parse(reader.GetAttribute("width"));

            if (reader.GetAttribute("height") != null)

                result.Height = int.Parse(reader.GetAttribute("height"));

            if (reader.GetAttribute("x") != null)

                result.X = int.Parse(reader.GetAttribute("x"));

            if (reader.GetAttribute("y") != null)

                result.Y = int.Parse(reader.GetAttribute("y"));

            if (reader.GetAttribute("opacity") != null)

                result.Opacity = float.Parse(reader.GetAttribute("opacity"));

 

            while (!reader.EOF)

            {

                var name = reader.Name;

 

                switch (reader.NodeType)

                {

                    case XmlNodeType.Element:

                        switch (name)

                        {

                            case "object":

                                {

                                    using (var st = reader.ReadSubtree())

                                    {

                                        st.Read();

                                        var objects = Object.Load(st);

                                        result.Objects.Add(objects.Name, objects);

                                    }

                                } break;

                            case "properties":

                                {

                                    using (var st = reader.ReadSubtree())

                                    {

                                        while (!st.EOF)

                                        {

                                            switch (st.NodeType)

                                            {

                                                case XmlNodeType.Element:

                                                    if (st.Name == "property")

                                                    {

                                                        st.Read();

                                                        if (st.GetAttribute("name") != null)

                                                        {

                                                            result.Properties.Add(st.GetAttribute("name"), st.GetAttribute("value"));

                                                        }

                                                    }

 

                                                    break;

                                                case XmlNodeType.EndElement:

                                                    break;

                                            }

 

                                            st.Read();

                                        }

                                    }

                                } break;

                        }

 

                        break;

                    case XmlNodeType.EndElement:

                        break;

                }

 

                reader.Read();

            }

 

            return result;

        }

 

        public void Draw(Map result, SpriteBatch batch, Rectangle rectangle, Vector2 viewportPosition)

        {

            foreach (var objects in Objects.Values)

            {

                if (objects.Texture != null)

                {

                    objects.Draw(batch, rectangle, new Vector2(this.X * result.TileWidth, this.Y * result.TileHeight), viewportPosition, this.Opacity);

                }

            }

        }

    }

 

    public class Object

    {

        public SortedList<string, string> Properties = new SortedList<string, string>();

 

        public string Name, Image;

        public int Width, Height, X, Y;

 

        protected Texture2D _Texture;

        protected int _TexWidth, _TexHeight;

 

        public Texture2D Texture

        {

            get

            {

                return _Texture;

            }

            set

            {

                _Texture = value;

                _TexWidth = value.Width;

                _TexHeight = value.Height;

            }

        }

 

        internal static Object Load(XmlReader reader)

        {

            var result = new Object();

 

            result.Name = reader.GetAttribute("name");

            result.X = int.Parse(reader.GetAttribute("x"));

            result.Y = int.Parse(reader.GetAttribute("y"));

            result.Width = int.Parse(reader.GetAttribute("width"));

            result.Height = int.Parse(reader.GetAttribute("height"));

 

            while (!reader.EOF)

            {

                switch (reader.NodeType)

                {

                    case XmlNodeType.Element:

                        if (reader.Name == "properties")

                        {

                            using (var st = reader.ReadSubtree())

                            {

                                while (!st.EOF)

                                {

                                    switch (st.NodeType)

                                    {

                                        case XmlNodeType.Element:

                                            if (st.Name == "property")

                                            {

                                                st.Read();

                                                if (st.GetAttribute("name") != null)

                                                {

                                                    result.Properties.Add(st.GetAttribute("name"), st.GetAttribute("value"));

                                                }

                                            }

 

                                            break;

                                        case XmlNodeType.EndElement:

                                            break;

                                    }

 

                                    st.Read();

                                }

                            }

                        }

                        if (reader.Name == "image")

                        {

                            result.Image = reader.GetAttribute("source");

                        }

 

                        break;

                    case XmlNodeType.EndElement:

                        break;

                }

 

                reader.Read();

            }

 

            return result;

        }

 

        public void Draw(SpriteBatch batch, Rectangle rectangle, Vector2 offset, Vector2 viewportPosition, float opacity)

        {

            Vector2 viewPos = viewportPosition;

 

            int minX = (int)Math.Floor(viewportPosition.X);

            int minY = (int)Math.Floor(viewportPosition.Y);

            int maxX = (int)Math.Ceiling((rectangle.Width + viewportPosition.X));

            int maxY = (int)Math.Ceiling((rectangle.Height + viewportPosition.Y));

 

            if (this.X + offset.X + this.Width > minX && this.X + offset.X < maxX)

                if (this.Y + offset.Y + this.Height > minY && this.Y + offset.Y < maxY)

                {

                    int x = (int)(this.X + offset.X - viewportPosition.X);

                    int y = (int)(this.Y + offset.Y - viewportPosition.Y);

                    batch.Draw(_Texture, new Rectangle(x, y, this.Width, this.Height), new Rectangle(0, 0, _Texture.Width, _Texture.Height), new Color(Color.White, opacity));

                }

        }

    }

 

    public class Map

    {

        public SortedList<string, Tileset> Tilesets = new SortedList<string, Tileset>();

        public SortedList<string, Layer> Layers = new SortedList<string, Layer>();

        public SortedList<string, ObjectGroup> ObjectGroups = new SortedList<string, ObjectGroup>();

        public SortedList<string, string> Properties = new SortedList<string, string>();

        public int Width, Height;

        public int TileWidth, TileHeight;

 

        public static Map Load(string filename, ContentManager content)

        {

            var result = new Map();

            XmlReaderSettings settings = new XmlReaderSettings();

            settings.ProhibitDtd = false;

 

            using (var stream = System.IO.File.OpenText(filename))

            using (var reader = XmlReader.Create(stream, settings))

                while (reader.Read())

                {

                    var name = reader.Name;

 

                    switch (reader.NodeType)

                    {

                        case XmlNodeType.DocumentType:

                            if (name != "map")

                                throw new Exception("Invalid map format");

                            break;

                        case XmlNodeType.Element:

                            switch (name)

                            {

                                case "map":

                                    {

                                        result.Width = int.Parse(reader.GetAttribute("width"));

                                        result.Height = int.Parse(reader.GetAttribute("height"));

                                        result.TileWidth = int.Parse(reader.GetAttribute("tilewidth"));

                                        result.TileHeight = int.Parse(reader.GetAttribute("tileheight"));

                                    } break;

                                case "tileset":

                                    {

                                        using (var st = reader.ReadSubtree())

                                        {

                                            st.Read();

                                            var tileset = Tileset.Load(st);

                                            result.Tilesets.Add(tileset.Name, tileset);

                                        }

                                    } break;

                                case "layer":

                                    {

                                        using (var st = reader.ReadSubtree())

                                        {

                                            st.Read();

                                            var layer = Layer.Load(st);

                                            result.Layers.Add(layer.Name, layer);

                                        }

                                    } break;

                                case "objectgroup":

                                    {

                                        using (var st = reader.ReadSubtree())

                                        {

                                            st.Read();

                                            var objectgroup = ObjectGroup.Load(st);

                                            result.ObjectGroups.Add(objectgroup.Name, objectgroup);

                                        }

                                    } break;

                                case "properties":

                                    {

                                        using (var st = reader.ReadSubtree())

                                        {

                                            while (!st.EOF)

                                            {

                                                switch (st.NodeType)

                                                {

                                                    case XmlNodeType.Element:

                                                        if (st.Name == "property")

                                                        {

                                                            st.Read();

                                                            if (st.GetAttribute("name") != null)

                                                            {

                                                                result.Properties.Add(st.GetAttribute("name"), st.GetAttribute("value"));

                                                            }

                                                        }

 

                                                        break;

                                                    case XmlNodeType.EndElement:

                                                        break;

                                                }

 

                                                st.Read();

                                            }

                                        }

                                    } break;

                            }

                            break;

                        case XmlNodeType.EndElement:

                            break;

                        case XmlNodeType.Whitespace:

                            break;

                    }

                }

 

            foreach (var tileset in result.Tilesets.Values)

            {

                tileset.Texture = content.Load<Texture2D>(

                    Path.Combine(Path.GetDirectoryName(tileset.Image), Path.GetFileNameWithoutExtension(tileset.Image))

                );

            }

 

            foreach (var objects in result.ObjectGroups.Values)

            {

                foreach (var item in objects.Objects.Values)

                {

                    if (item.Image != null)

                    {

                        item.Texture = content.Load<Texture2D>

                        (

                            Path.Combine

                            (

                                Path.GetDirectoryName(item.Image),

                                Path.GetFileNameWithoutExtension(item.Image)

                            )

                        );

                    }

                }

            }

 

            return result;

        }

 

        public void Draw(SpriteBatch batch, Rectangle rectangle, Vector2 viewportPosition)

        {

            foreach (Layer layers in Layers.Values)

            {

                layers.Draw(batch, Tilesets.Values, rectangle, viewportPosition, TileWidth, TileHeight);

            }

 

            foreach (var objectgroups in ObjectGroups.Values)

            {

                objectgroups.Draw(this, batch, rectangle, viewportPosition);

            }

        }

    }

}

 

On to the problem, I'm trying to create a collision system by doing checks in the Input classes.
For example (pseudo-code ahoy!):
If the Player presses down:
#Check if the tile below has the property solid set to true or not.
##It doesn't? Okay, move down.

The thing is, I can't figure out how to check what tile is at a certain position, and how to retrieve the properties from it.

Any help will be appreciated.
 

OS

Sponsor

That seems like a really complicated set of classes for a beginner. I would suggest writing one from scratch that loads from text files, just to get you started.

As for your checking dilemma, all you have to do is:

tileX = player.TileX
tileY = player.TileY + 1 // Check below

It looks like the code you are using stores tile information in a singular dimension. This kind of complicates things. In fact, I would need to see that code in action and really look at it before I could say much more about it, but I am too lazy atm.

The best way to learn is to start from the ground up. I suggest you use the Tile map tutorial from the Creators Club.

EDIT: Figured while I am here I should get some practice in. I'm creating a tutorial. I'll try to have it up within the next couple days.
 
I'll agree with OS here. Although it may take more time, you will learn much more by making one yourself. If you don't, unless you are already above the level of the code you're using (and you just want to save time), you are bound to run into more problems by trying to add your code on top of an existing one. On the side note, you can always look at RM's scripts to get an idea of a map structure.
 

Thank you for viewing

HBGames is a leading amateur video game development forum and Discord server open to all ability levels. Feel free to have a nosey around!

Discord

Join our growing and active Discord server to discuss all aspects of game making in a relaxed environment. Join Us

Content

  • Our Games
  • Games in Development
  • Emoji by Twemoji.
    Top