3
\$\begingroup\$

In the game I am working on, many of my tiles must have special attributes (such as triggers when the player steps on them). I was wondering if it would be bad practise to create an object for every tile, so that I can do map.getTile(5, 3).setWalkable(true); (psuedocode).

The only alternative I can think of to this would be to create one instance of every tile, then just look at the properties of that type of tile, which might be okay.

Is it okay to have an object per tile?


I am using C++ with SFML, but I doubt that matters. I know there is a similar existing question, but it doesn't answer how to manage the tiles if they do unique things.

\$\endgroup\$
3
  • \$\begingroup\$ Why not have a main tile class and use inheritance to create different types of tiles? \$\endgroup\$ Commented Oct 11, 2014 at 2:50
  • 1
    \$\begingroup\$ btw, avoid inheritance and prefer composition when possible. You don't need or want a class to maintain for every tile type. It is fine to have a generic tile type and configure it with the needed attributes. If the tile differs from most other tiles then consider using inheritance for it. Remember that inheritance is oft overused. Only use it if it makes life easier for you and adds needed flexibility to your design. This is not part of the answer cause that was not the question in the first place. \$\endgroup\$
    – AturSams
    Commented Oct 11, 2014 at 3:38
  • \$\begingroup\$ Why do you think it would be bad? \$\endgroup\$
    – Anko
    Commented Oct 11, 2014 at 16:31

5 Answers 5

4
\$\begingroup\$

It is fine to have lots of instances. An instance of a class without virtual methods is just like a POD C struct in terms of memory consumption which is similar to primitive data types. It is no problem.

Your concern when instantiating many instances of a class are resource related I would think.

  1. CPU - should not be affected because you will be instantiating whilst the level loads (so it will not affect gameplay).
  2. Memory footprint - If you don't use virtual methods, it's the same as the sum of the data types plus a little bit of padding perhaps.

The only thing I would change, is perhaps to not use any tile for an empty tile and use a 'sparse array'. Including only the outer walls to conserve some memory and improve performance.

Another consideration you may wish to take for optimization is to use a 1-d array and getting the tile by doing something like x + y * stageWidth because I find that 2-d array could be a slight hindrance to performance. This is a very minor issue unless you are doing getTile(x, y); very often.

\$\endgroup\$
1
  • \$\begingroup\$ I added some details on how you could use a hash aka a sparse array and avoid storing data for empty tiles. This is relevant if your game levels are huge and mostly empty. \$\endgroup\$
    – AturSams
    Commented Oct 11, 2014 at 3:32
3
\$\begingroup\$

You need to remember that C++ is a multi-paradigmatic (OOP, functional, procedural, ..) language and you should use the programming paradigm that best solves your current issue.

OOP doesn't lend itself well to this problem. In OOP you think about single objects in isolation (concept of "a tile"). But most of your algorithms will operate on a whole collection of tiles, not a single tile.

  • Find a walkable path from A to B
  • Render the whole map
  • Load a whole map (not load one tile N times)
  • Save the whole map (not save one tile N times)
  • Turn all water tiles in a radius of R into ice tiles (some magic spell for example)
  • Move all actors along the map while resolving collisions

Just turning a single water tile into ice for example is a special case then. The other way around, using a function that sets a single water tile to an ice tile does not scale well at all however.

We can assume for example that most tiles on the map will not have some special script to execute (e.g. trap tiles). If you think about a single tile in isolation, then create a 2D array of tiles, every tile will have this functionality.

If you think about a whole collection of tiles instead, you can have some separate container that has the scripts to execute only for tiles that actually do require this functionality (which will probably be few in comparison).

This saves memory and performance (glampert mentioned better use of the CPU cache). It also makes your algorithms simpler and gives other advantages such as much easier and more efficient multi-threading.

OOP has its place, but in this case even people who use OOP as a silver bullet will tell you to scale it down (advice such as composition over inheritance, have a Map class but no Tile class etc).

\$\endgroup\$
1
  • \$\begingroup\$ You are right. Remember that this is not the case. The game might need to check what happens if the player's character stepped on a lava tile. Even your ice example will operate on the single tiles when it changes the way they affect the game characters and the way they are rendered. \$\endgroup\$
    – AturSams
    Commented Mar 17, 2016 at 11:05
1
\$\begingroup\$

(I cannot comment so I'm answering)

If you build an array with reference for the types of the files (I don't know much of C++, so I'll pseudocode)

 tiles[0,0] = ref_to_fire_tile
 tiles[0,1] = ref_to_fire_tile
 tiles[0,2] = ref_to_grass_tile
 ...

Where ref_to_*_tile holds a reference for a specific type of tile which inherits from a generic one, this way you combine arrays, with inhiritance and use fewer objects (in my exemple for 3 tiles the user perceive, I used only 2 tile instances, so you can spare some memory).

EDIT

This will allow me slightly more freedom, because it will mean that I can make something apply to a tile that's just at a location

Given that you want to use location, I'd sugest to change you code for use a Tile class (which holds the location and a reference for a tile type) and a TileType class (which is a "root" class to be specialized; it performs actions on who steps on it).

Or more easily just use the array to be your location (as if the array index itself was an instance of the Tile class I mentioned before)

\$\endgroup\$
1
\$\begingroup\$

I would advise against using inheritance to manage different tiles. Imagine how annoying it would be having to define a new class every time you add seemingly different types of tiles. That would result in a lot of implementations for simple things like a grass or a dirt tile. It is much simpler and more maintainable to make tiles configurable. Define a Tile type and derive its behavior from its data/parameters. This is a simple form of component-based design.

Having a single type/class of tile also unlocks an optimization: You no longer need to store pointers to polymorphic objects, but the objects themselves. This will result in much better data locality when processing the tiles. E.g.:

With an abstract Tile and N implementations, you would have to store pointers:

Tile * tiles[TILEMAP_SIZE];

Iterating the tiles array will result in poor CPU cache usage, since the data is scattered over the memory.

With a concrete configurable Tile, you can store by value and have very good data locality:

Tile tiles[TILEMAP_SIZE];
\$\endgroup\$
0
\$\begingroup\$

To give an example to my comment

    public class Tile
    {
        //some variable stuff;
        public Tile()
        {
            //some constructor stuff;
        }
    }

    public class FireTile
    {
        //some variable stuff;
        public FireTile : Tile()
        {
            //sets player on fire
            //some constructor stuff
        }
    }

And then

 if(player steps on fireTile)
      set player on fire;
\$\endgroup\$
3
  • \$\begingroup\$ I was planning on doing this of course, yes. I am not gonna process textrues in every tile class lol. I was asking if it was fine to have lots of instances of these classes (5 filetils, 70 stone tiles, 10 pipe tiles, etc.) \$\endgroup\$
    – 5Mixer
    Commented Oct 11, 2014 at 3:13
  • \$\begingroup\$ Oh I misunderstood what you asked lol. Instances would be your way to go rather than creating a new object of the class for every tile. \$\endgroup\$ Commented Oct 11, 2014 at 3:24
  • \$\begingroup\$ Hmm, alright. I think I might just make a new instance of a type of tile for every tile in my world, considering the answer below. This will allow me slightly more freedom, because it will mean that I can make somthing apply to a tile that's just at a location, without affecting all of that type of tile. Thanks for your help :) (I wish I could upvote :/) \$\endgroup\$
    – 5Mixer
    Commented Oct 11, 2014 at 3:31

You must log in to answer this question.

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