1
\$\begingroup\$

I want to render a tile-based map and I am not sure which class structure to use to keep logic and rendering separeted.

My class structure so far on the logic side: One abstract parent class Tile with an attribute int tileType and one class extending Tile for every kind of tile. Every of the extending class defines the value, for example in the class Tree extends Tile I have tileType = 2 and in the class Wall extends Tile I have tileType = 3.

When I render a map, I go through the array of tiles and do something like this:

switch (tileType) {
...
case 2: texture = treeTexture; break;
case 3: texture = wallTexture; break;
...
}
draw(texture, x, y);

I wonder if the class structure makes sense and if this is a good solution to render tiles? Or what other solutions are there to render tiled maps while keeping logic and rendering separated?

\$\endgroup\$
2
  • \$\begingroup\$ If the tileType variable already lets you know what tile it is, what reason would there be to inherit from Tile rather than creating new tiles with a different value for tileType? Also, you can have a look at storing the tiles per type rather than all types together in one structure. When you render, you iterate over all the tiles for that type; you no longer have to determine which one it is, because you already know, for all of them. \$\endgroup\$
    – Athos vk
    Jan 22, 2017 at 21:49
  • \$\begingroup\$ The reason i let the inherit from Tile is because the tiles will differ in some ways: There will be water tiles which have a direction of water flow, door tiles which have an id for which key is needed to open it and so on. \$\endgroup\$ Jan 22, 2017 at 22:02

2 Answers 2

1
\$\begingroup\$

The way i am doing this kind of job is :

class Tile 
{
public:
    enum Type
    {
        Water,//0
        Grass,//1
        Snow,//2
        Sand//3
    };
    enum Type2// secondary type
    {
        Mountain,
        Forrest,
        None
    };
public:
    Tile(std::shared_ptr<Surface> surf, int x, int y, int width , int height,Type type);
    void Draw(int x , int y ,Graphics& gfx);
    void InitLayout2(std::shared_ptr<Surface> surf, Type2 type);
private:
    std::shared_ptr<Surface> m_terrain;
    std::shared_ptr<Surface> m_terrain2;
    Type m_type;
    Type2 m_type2;
    RectI m_rect;
    bool m_Passable;
};

then map owns all tiles std::vector m_tiles; and the way i am setting this vector is i am using map.png file , then read each of a pixel call function : Tile::Type CheckTyle(Color c); witch return me what type of tile it is then switch return value and assign tile with a pointer to surface

\$\endgroup\$
2
  • \$\begingroup\$ Isn't here the rendering coupled with the logic? As far as I understand, your Tile class seems to "know" about the graphics, but that is what I want to avoid. \$\endgroup\$ Jan 22, 2017 at 22:04
  • \$\begingroup\$ Tile tile class have pointer to a surface , and my map class who owns all tiles based on viewpoint it call tile.Draw function witch points to a surface inside Map class ,i dont see any problem here i think its a good approach to do things \$\endgroup\$
    – yahoo5000
    Jan 23, 2017 at 14:01
1
\$\begingroup\$

The best way I can think to eliminate the coupling between tiles and the rendering system is to pass whatever structure holds the map data to the rendering system.

The simple answer is, somewhere, something has to be coupled. I agree having your tiles or your map draw themselves directly is a maintainability nightmare in the long run. Even giving the rendering system an overload function, draw() which can take a world or map object and digest it to make each draw call based on type, direction, etc.

The real answer is you also need to decouple the rendering system from the map and tiles which it needs to draw. To do so is simple, but important as it pushes the non-graphic jobs out of your logical tile object and will also push the logical map parsing code out of your rendering system... You need a world object, and a matching system which is intimate with tiles and worlds. This being tiles only real dependency can split data up into blocks that other systems can parse. So, get image from tile type becomes a world job, then you pass that returned image object to the renderer.. This gives everything a nice engine layer to rest on, while keeping everything simple.

\$\endgroup\$

You must log in to answer this question.

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