3
\$\begingroup\$

I'm currently working on a simple 2D tile based game. I want to let users (=players) to create really large maps without problems with performance. That is why I'm searching the way how to optimalize the code before I actually write it. I've read some articles about streaming tiles, but I don't think I've understood them at all.

Map is stored in simple INI file like this:

[MAP]
title = "Test"
tileset="test.png"
width = 5
height = 5

tile_widht = 32
tile_height = 32

[LAYER]
type="background"
data="0,1,2,3,4,0,1,2,3,4,0,1,2,3,4,0,1,2,3,4,0,1,2,3,4"

# ... items, NPCs and quests definitions

When loading a new map, I'm reading the file line by line and parse it. After that, I create array of Tiles (std::vector tiles).

Every tile knows it's own position, value (which is the number indicating the tile from tileset), passability (number which indicates how hard is it to pass this tile), inventory (list of items lying on this tile), NPC list (list of NPCs standing on this tile) etc...

Now comes my problem. How to stream the tiles? With this file format it seems to be inpossible to load data when needed, so I have to load them at once.

Only thing which comes to my mind is, to store map data in arrays and create Tile classes only when they are really needed (if the tile position is withing the screen) and unload them after leaving the screen...

My question is, how do you suppose to solve this problem? How do you do it in your games?

Thank for your help and sorry for my English.

\$\endgroup\$
2
  • \$\begingroup\$ How big maps are you talking about? 1000x1000 or 3000x3000 or even bigger? \$\endgroup\$
    – Katu
    Jul 14, 2013 at 10:56
  • \$\begingroup\$ However big your map might be, don't optimize it before trying it without optimization. Premature optimization is bad, don't optimize unless necessary. \$\endgroup\$
    – akaltar
    Jul 14, 2013 at 11:00

2 Answers 2

4
\$\begingroup\$

Just load everything to memory. Even low-end PC nowadays, has atleast 500Mb spare memory available. You are going to have hard time while filling that memory.

You did not mention, how big your maps are going to be, but 1000x1000 has Million tiles. Doing that by hand, is lot more work, than coding your game. What if its 3000x3000, its escalating quite fast. Think realistically, will you, or someone ever create that big map to your game?

Quick test with C#:

I made map with size of 1000x1000.

Every tile had: 50 random integers ( 32bit ) and few guids strings and such. Even with 1 Million tiles, it used 350Mb of memory.

Don't optimize your code, before you know for sure, that you need to do it.

EDIT: Here, i'm assuming, that you are new to programming and to game programming. Finishing games, is far more important, than trying to create map loading mechanism that ( in the end ) might not even be needed.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ Code first, optimize later. Well said. \$\endgroup\$
    – Bloodcount
    Jul 15, 2013 at 17:07
1
\$\begingroup\$

Here is an abstract BoardStorage class that I am using on my Napoleonics engine, with maps upwards of 700 * 400 tiles. Although the code is C#, a C++ programmer should have no difficulty porting it.

  /// <summary>Abstract specification and implementation of the <c>BoardStorage</c> required by <c>HexBoard</c>.</summary>
  /// <typeparam name="THex">The type of the hex being stored.</typeparam>
  public abstract class BoardStorage<THex> : IDisposable {
    /// <summary>Initializes a new instance with the specified hex extent.</summary>
    /// <param name="sizeHexes"></param>
    protected BoardStorage(Size sizeHexes) {
      MapSizeHexes   = sizeHexes;
    }

    /// <summary>Extent in hexes of the board, as a <see cref=" System.Drawing.Size"/> struct.</summary>
    public          Size MapSizeHexes           { get; private set; }

    /// <summary>Returns the <c>THex</c> instance at the specified coordinates.</summary>
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", 
      "CA1043:UseIntegralOrStringArgumentForIndexers")]
    public abstract THex this[HexCoords coords] { get; internal set; }

    /// <summary>Returns whether the hex with <see cref="HexCoords"/> <c>coords</c> is 
    /// within the extent of the board.</summary>
    public          bool IsOnboard(HexCoords coords) { 
      return 0<=coords.User.X && coords.User.X < MapSizeHexes.Width
          && 0<=coords.User.Y && coords.User.Y < MapSizeHexes.Height;
    }

    /// <summary>Performs the specified <c>action</c> serially on all hexes.</summary>
    public abstract void ForEach(Action<THex> action);

    /// <summary>Performs the specified <c>action</c> in parallel on all hexes.</summary>
    public abstract void ParallelForEach(Action<THex> action);

    #region IDisposable implementation with Finalizeer
    bool _isDisposed = false;
    /// <inheritdoc/>
    public void Dispose() { Dispose(true); GC.SuppressFinalize(this); }
    /// <summary>Anchors the Dispose chain for sub-classes.</summary>
    protected virtual void Dispose(bool disposing) {
      if (!_isDisposed) {
        if (disposing) {
        }
        _isDisposed = true;
      }
    }
    /// <summary>Finalize this instance.</summary>
    ~BoardStorage() { Dispose(false); }
    #endregion
  }

Two complete implementations (one a Simple row-major List<List<IHex>> and the other a Blocked List<List<List<IHex>>> with internal blocks of 32 x 32 for better localization) are available here: Hexgrid Utilities for Board games.

I got about a 5-10% improvement in path-finding times using the BlockedBoardStorage on a map of 760 * 420. Bigger maps should benefit even more.

Although I agree that premature optimization is wasteful, building simple optimizable structures early is simply part of good design..

\$\endgroup\$

You must log in to answer this question.

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