6
\$\begingroup\$

I'm currently designing a game using a game engine I created and I'm currently implementing a method that loads maps into my game. The maps are made using Tiled Map Editor, saved as an XML file and are loaded using TiledSharp.

At the moment, I'm trying to load a map, that contains 1437 'Game Objects' (These are just tiles, as far as tiled is concerned) and currently it takes around 360ms to load. I was just wondering what I can do to load my maps faster.

Here is the code that loads my maps:

public void LoadMap(string path)
{
    Console.WriteLine("Loading map...");

    var watch = new Stopwatch();
    watch.Start();

    // Import XML data using TiledSharp
    var tmxMap = new TmxMap(path);

    // Lets all the classes variables
    width = tmxMap.Width;
    height = tmxMap.Height;

    tileWidth = tmxMap.TileWidth;
    tileHeight = tmxMap.TileHeight;

    wholeWidth = width * tileWidth;
    wholeHeight = height * tileHeight;

    // Now lets load all the sprite sheets that are used in this map
    var spriteSheets = LoadSpriteSheets(tmxMap);

    // A value that indicates what layer we're drawing to
    int currentLayer = -1;

    foreach (var layer in tmxMap.Layers)
    {
        // Increment a layer foreach layer we're in
        currentLayer++;

        foreach (var tile in layer.Tiles)
        {
            // Get the tiles x, y and global id
            int x = tile.X;
            int y = tile.Y;
            int gid = tile.Gid;

            // Now lets loop through the tilesets in the map
            for (int i = 0; i < tmxMap.Tilesets.Count; i++)
            {
                var tileset = tmxMap.Tilesets[i];

                // This just figures out how many images are in the tileset

                int firstGid = tileset.FirstGid;
                int lastGid = firstGid + (int)tileset.TileCount;


                // Check if the tile we're on is in this tileset, if it isn't continue
                if (!gid.IsWithin(firstGid, lastGid)) continue;

                // Get the regular id, used to get the correct sprite from the sprite sheet
                int id = gid - firstGid;

                // Create a new object, set its position and scale
                var obj = new GameObject();
                obj.Transform.Position = new Vector2f(x * tileWidth, y * tileHeight);
                obj.Transform.Scale = new Vector2f(tileWidth, tileHeight);

                // Now lets add a sprite renderer component, and pass it the correct sprite
                var sprite = new Sprite(spriteSheets[i][id], currentLayer, 1.0f);
                var renderer = new SpriteRenderer(sprite);
                obj.AddComponent(renderer);

                // Now lets get the tiles from the tile set, and check if it contains any properties we need to know about
                // Things like if its solid, or has a different depth
                var tiles = tileset.Tiles;

                if (tiles.ContainsKey(id))
                    HandleProperties(obj, tiles[id].Properties);

                // Finally, lets add our game objec to the map
                AddGameObject(obj);
            }
        }
    }

    // Dispose of our sprite sheets
    foreach (var sheet in spriteSheets)
        sheet.Bitmap.Dispose();

    watch.Stop();
    Console.WriteLine("Load time: {0}ms", watch.ElapsedMilliseconds);

    if (GameObjects.Contains(Game1.Player)) return;

    AddGameObject(Game1.Player);
}

private void HandleProperties(GameObject obj, PropertyDict properties)
{
    var value = "";

    if (properties.ContainsKey("Solid"))
    {
        value = properties["Solid"];

        if (value == "true")
        {
            // We know we need to add physics and a collider

            var physics = new PhysicsComponent();
            physics.DetectCollisions = true;
            obj.AddComponent(physics);

            var collider = new BoxCollider();
            collider.Size = new Vector2f(tileWidth, tileHeight);
            obj.AddComponent(collider);
        }
    }

    if(properties.ContainsKey("Depth"))
    {
        // Lets set the new depth of the sprite

        value = properties["Depth"];

        int depth = int.Parse(value);

        obj.GetComponentOfType<SpriteRenderer>().Sprite.Depth = depth;
    }
}

private List<SpriteSheet> LoadSpriteSheets(TmxMap tmxMap)
{
    List<SpriteSheet> sheets = new List<SpriteSheet>();

    for (int i = 0; i < tmxMap.Tilesets.Count; i++)
        sheets.Add(new SpriteSheet(tmxMap.Tilesets[i].Image.Source, tmxMap.TileWidth, tmxMap.TileHeight, Color.White));

    return sheets;
}
\$\endgroup\$
1
  • \$\begingroup\$ I don't see any obvious optimizations in your code - it looks good. You'll have to run it with a profiler. \$\endgroup\$
    – t3chb0t
    Commented Apr 10, 2017 at 14:53

2 Answers 2

2
\$\begingroup\$

You should start with a profiler. Having said that, there is one easy optimization you can make without a lot of code changes.

Change the following line in LoadSpriteSheets from
List<SpriteSheet> sheets = new List<SpriteSheet>();
to
List<SpriteSheet> sheets = new List<SpriteSheet>(tmxMap.Tilesets.Count);

The amount of elements in the list is known in advance so you can avoid the cost of repeatedly resizing the container.

\$\endgroup\$
0
\$\begingroup\$

Your code looks well written and is easy to read and understand. As t3chb0t stated, you'll have to run a profiler and see where you might make optimizations.

One micro-optimization I found was following: the Transform properties of the GameObject can be set outside the most inner loop, since the parameters to set them don't change in the inner loop.

foreach(var layer ...)
{
    foreach(var tile ...)
    {
        int x = tile.X;
        int y = tile.Y;
        int gid = tile.Gid;

        var obj = new GameObject();
        obj.Transform.Position = new Vector2f(x * tileWidth, y * tileHeight);
        obj.Transform.Scale = new Vector2f(tileWidth, tileHeight);

        // Now lets loop through the tilesets in the map
        for (int i = 0; i < tmxMap.Tilesets.Count; i++)
        {
            //...
        }
    }
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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