Advertisement

Adding non ECS features in an ECS engine (tilemap)?

Started by May 03, 2015 06:20 AM
11 comments, last by TheChubu 9 years, 7 months ago

Hello,

I have been learning how to use entitiyx to convert my OOP game into an ECS game. I have gotten the basic components working, and created some entities and placed them in the game "world".

I am at part where I want to add my tile map in. I have three different tile maps (saved as csv txt files), created in tiled. I have map loading and rendering working in my OOP design but would like some advice on how to get this going in an ECS engine.

I have been reading a bit online and there seems to be no general consensus as to how to do this.

In my game (a 2d scrolling plane shooter) there will be no interactions with the ground at this stage. So I think I could put my tileMap in pretty much as I have it in the OOP design. It seems to me overly complicated and "expensive" to tag tiles with components.

The part I am stuck at is the organization of this code. Right now the games update() and render() functions work as follows:


void Game::update()
{
    m_systemManager.update<LevelSystem>(frameTime);
    m_systemManager.update<PlayerControlSystem>(frameTime);
    m_systemManager.update<MovementSystem>(frameTime);
}

void Game::render()
{
    graphics.beginScene();
    graphics.spriteBegin();
    m_systemManager.update<RenderSystem>(frameTime);
    m_systemManager.update<MenuSystem>(frameTime);
    graphics.spriteEnd();
    graphics.showBackbuffer();
}

The way I got tiles working in my OOP design was by adding the tiles directly to the class which inherits from Game e.g.


class TileMapExample : public Game
{
private:
    mgeTexture tileTextures;
    mgeSprite  tile;

where an mgeSprite has a draw command that goes to my graphics class. So in my old game render loop it would loop through the tiles [row] and [col] calling draw e.g. tile.draw(UINT(tileMap[row][col]));

But in my new ECS game design there is nothing to do with actual in game entities in the Game class, that stuff is all taken care off via components and my entitiyCreator. The Game calss is as follows:


class Game : public entityx::Receiver<Game>
{
private:
    Graphics graphics;
    GameManager m_gameManager;
    Input m_keyHandler;
    HWND hwnd;
    entityx::EventManager m_eventManager;
    entityx::EntityManager m_entityManager;
    entityx::SystemManager m_systemManager;
    void createSystems();
    void update();
    void render();

    // Frame rate code
    LARGE_INTEGER timeStart;        // Performance Counter start value
    LARGE_INTEGER timeEnd;          // Performance Counter end value
    LARGE_INTEGER timerFreq;        // Performance Counter frequency
    float frameTime;
    DWORD   sleepTime;
    float   fps;

So this is where I ask for some assistance. Do I just cram my tile map stuff into this game Class, and update and render it separately in Game::update() and Game::Render() so e.g. Render() would look like this


void Game::render()
{
    graphics.beginScene();
    graphics.spriteBegin();

////////added this stuff for the tile map rendering///////
    for(int row=0; row<MAP_HEIGHT; row++)
    {
        for(int col=0; col<MAP_WIDTH; col++)
        {
           etc etc loop through and draw tiles}
////////added this stuff for the tile map rendering///////

    m_systemManager.update<RenderSystem>(frameTime);
    m_systemManager.update<MenuSystem>(frameTime);
    graphics.spriteEnd();
    graphics.showBackbuffer();
}

Or is there a more elegant solution, that somehow still lets me use components and systems but not tagging the tiles as entities? It just seems the tile map render code should go into m_systemManager.update<RenderSystem>(frameTime);

which looks as:


void RenderSystem::update(entityx::EntityManager &entities, entityx::EventManager &events, double dt)
{
    Position::Handle position;
    Display::Handle display;
    for (entityx::Entity entity : entities.entities_with_components(position, display))
    {
        spriteData.setTextureRect(display->coord);
        spriteData.setPosition(position->position);
        m_graphics->drawSprite(spriteData);
    }
}

But if I had my tile drawing here and didn't have my tiles as entities..., I cant figure out how I would pass in all the tile information in, since it gets sprite info from entities which I have given position and display components.

Thanks for any input.

Why do you need to use ECS to accomplish your goal (a game)?

Why does every class member start with “m_” except the “graphics” class member? What kind of sloppiness is this?

L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

Advertisement

Why do you need to use ECS to accomplish your goal (a game)?

Why does every class member start with “m_” except the “graphics” class member? What kind of sloppiness is this?

L. Spiro

I love your posts Spiro. They are always incredibly helpful, but also strike the fear of god into me...

Why do you need to use ECS to accomplish your goal (a game)?

To be honest I am not good enough at C++ to know what I do or do not need... but when I was working with my original OOP game and wanted to start adding in enemies with different guns and shields and all sorts of fancy ideas in my head, posts that I found (not just on gamedev) kept saying ECS this ECS this ECS this so I wanted to see what the fuss is about and give it a go.

Why does every class member start with “m_” except the “graphics” class member? What kind of sloppiness is this?

Well spotted, I did not even realize that lol.. I do not really want to use the m_ prefix at all. But I found when I was initializing objects in their constructor using initialized lists, I kept getting confused about the private member I was trying to initialize, and the passed in variable or object I was initializing it with... so I say in my head (m_ goes Outside the brackets...)

thanks

Fact is that OOP does not provide inheritance alone, but also composing. While the former allows for a kind of specialization / generalization thing, the latter allows for a kind of collaboration thing. ECS was an attempt to make the OOP object level composing an architectural pattern especially with the background of game development (later on "sub-systems", cache friendliness and so on are added). It is a fuss since the game industry has determined this way of doing things, although composing as mechanism exists since the dawn of OOP at all. It should be mentioned that, following the relevant literature, composing was mentioned to be generally preferred over inheritance years before Dungeon Siege. That said, even if you do not use an ECS, the basic principle of it should still be considered. (And: ECS is not the opposite of OOP.)

On the other hand, ECS is a tool like others. Do not use a tool if it does not fit the problem. Not all stuff in a game fits in it, that's for sure. Terrain, sky, foliage, … do not fit. The conclusion is simple: Do not try to make them fit, and hence do not base the entire engine on it. Your doubts mentioned in the OP are absolutely valid.

To overcome your problem, use systems as a hierarchy of services. Each more lower layer in the hierarchy provides more basic services and uses more general structures. Even rendering is not a single layer but consists of several layers. At the bottom there is the graphics layer dealing with triangles and textures (if done this way). Above it there may be a "sprite layer". Above it there may be … At the top, not counting to rendering at all, is the scene layer which deals with terrain / ground, sky, game objects / entities. Notice please that layers in this sense are by all means possibly a couple of sub-systems. Sub-systems may collaborate with other ones on the same layer, and may use sub-systems of lower layers.

That said, during rendering, terrain / ground will be converted to some kind of graphic primitives by some rendering layer(s) along a path from the scene layer down to the graphics layer. Game objects / entities will be converted also to some kind of graphic primitives, not necessarily the same primitives, and probably not along the same path as terrain / ground. If "sprite" is your graphic primitive of choice for both ground and entities, then you will have a ground renderer and an entity renderer in the end, the first using a ground representation as input and the latter using game objects as input, and both yielding in sprites as output.

So, question is to which layer(s) do Game::render and RenderSystem::update belong to? Both possible solutions shown in the OP violate a clear abstraction. First of, usually "update(time)" is used to, well, drive the update of the world state. It has nothing to do with rendering, and hence should not call a renderer, and RenderSystem::update(time) should not exists as such. Instead, after the world has been updated properly, the renderer can use the world state to just draw it.

Coming back to the problem: Game::render seems to be the top layer of rendering, so it should work on the level of scene objects (hence iterating the ground tile by tile is a no-go there). It may iterate all drawables in the scene and invoke their render() method. That render() methods will be the second layer of rendering and perhaps already output the sprite primitives.

All the above: My 2 cents, of course ;)

Disregarding everything said in this thread:

  • TileMap is a 2D array of ints, it shouldn't be a component.
  • TileMapRenderer Contains a TileMap and uses it plus below component to render the map. Should be a component.
  • TileMapSettings links ints to sprites and is read by the TileMapRenderer (Via GetComponent or whatever you want). Should be a component.

The tile map renderer could also manage splitting into multiple meshes to optimise very large tile maps! Pretty robust I think.

Engineering Manager at Deloitte Australia

To be honest I am not good enough at C++ to know what I do or do not need... but when I was working with my original OOP game and wanted to start adding in enemies with different guns and shields and all sorts of fancy ideas in my head, posts that I found (not just on gamedev) kept saying ECS this ECS this ECS this so I wanted to see what the fuss is about and give it a go.

This could have been a chance for you to explore data-driven systems, but apparently your natural path of exploration was sabotaged by the non-contextual advice of those online who I assume were not addressing your specific need.
Thanks to Unity 3D “ECS” has become a buzz-word, and fewer and fewer people are considering whether or not it suits their specific needs and instead just going with it because it’s the cool new trend. By your own admission you don’t even know why it is “suitable” for you. I place most blame of those who simply promote it as the end-all-be-all solution to every problem (which is far from the truth), but you are certainly not blameless for going down a path without even knowing why you are going down that path.

One might gather from my past posts that I am anti-ECS, but it’s really just that I am anti-…“Use the wrong tool for the job”.
ECS has a place and it solves some problems very well. They are usually very specialized problems. For example I’d call it the perfect solution to Unity 3D’s ability to work for a large user base.
If you are not writing an engine whose selling point is the ability to write your own extensions without access to the source code, ECS should be lower on your list of design considerations.

ECS has a certain role to fulfill, but since it dances dangerously close to bad practices such as de-modularization it’s best to leave it for the time when you really know how to wield it later in your career. You didn’t say you are working on an RPG game but you mentioned monster stats etc., which are extremely prevalent in RPG games. tri-Ace uses absolutely no ECS what-so-ever and at Square Enix I’ve only heard rumors that we use a bit of it. I haven’t even verified that we use it at all.

With more details on what you are making, you could get better advice, but as it stands I see no reason for your move towards ECS.

L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

Advertisement

Don't be too hard on him, it sounded like he just wanted to give it a try as an experiment, doing something just to learn about it is a totally worthwhile endeavor. (As a hobby, I wouldn't do that for a paying job if given a task...)

Entity component systems can naturally be evolved to a more data-oriented approach. If you're making a tile based game then it makes a lot of sense to group the tile data and store it separately instead of treating each tile as a generic entity. The only difference compared to what you were doing in your object oriented approach would be that the tiles themselves are just plain old data that don't know how to update and render themselves.

Thanks to Unity 3D “ECS” has become a buzz-word, and fewer and fewer people are considering whether or not it suits their specific needs and instead just going with it because it’s the cool new trend.

I meant to +1 this, but I missed. It seems like trying to fit an entity component structure would be trying to shoehorn in something that isn't needed for the scope of the project. Stick with learning how to build robust OOP solutions that are data driven instead.


I meant to +1 this, but I missed.

Up-voted to cancel it out.

Hello to all my stalkers.

This topic is closed to new replies.

Advertisement