1
\$\begingroup\$

The Setup

I've made a few games (more like animations) using the Object Oriented method with base classes for objects that extend them, and objects that extend those, and found I couldn't wrap my head around expanding that system to larger game ideas. So I did some research and discovered the Entity-Component system of designing games. I really like the idea, and thoroughly understood the usefulness of it after reading Byte54's perfect answer here: What is the role of "systems" in a component-based entity architecture?.

With that said, I have decided to create my current game idea using the described Entity-Component system.

Having basic knowledge of C++, and SFML, I would like to implement the backbone of this entity component system using an unordered_multimap without classes for the entities themselves.

Here's the idea:

An unordered_mulitmap stores entity IDs as the lookup term, while the value is an inherited Component object. Examlpe:

 ____________________________
|ID          |Component      |
 ----------------------------
|0           |Movable        |
|0           |Accelable      |
|0           |Renderable     |
|1           |Movable        |
|1           |Renderable     |
|2           |Renderable     |
 ----------------------------

So, according to this map of objects, the entity with ID 0 has three components: Movable, Accelable, and Renderable. These component objects store the entity specific data, such as the location, the acceleration, and render flags. The entity is simply and ID, with the components attached to that ID describing its attributes.

Problem

I want to store the component objects within the map, allowing the map have full ownership of the components. The problem I'm having, is I don't quite understand enough about pointers, shared pointers, and references in order to get that set up. How can I go about initializing these components, with their various member variables, within the unordered_multimap? Can the base component class take on the member variables of its child classes, when defining the map as unordered_multimap<int, component>?

Requirements

I need a system to be able to grab an entity, with all of its' attached components, and access members from the components in order to do the necessary calculations and reassignments for position, velocity, etc.

Need a clarification? Post a comment with your concerns and I will gladly edit or comment back!

\$\endgroup\$
  • \$\begingroup\$ frankly, for memory fragmentation reasons, I would find an implementation of open address hash map on the web (or do my own), because the STL one is close addressing. And I would use a pool of components (not a multimap, but a map of pools), which have enough space for a dozen of components by default (uninitialized), and then would chain list other pools on demand. Also you can consider using the entity ID as a direct index in an array, and use a free slots list when deletion happens. \$\endgroup\$ – v.oddou Jul 25 '14 at 2:14
  • \$\begingroup\$ @v.oddou I don't understand all of what you're saying, and to me, seems more complicated than I would like. Thanks for your information though! I have lots to learn... \$\endgroup\$ – natebot13 Jul 25 '14 at 16:09
1
\$\begingroup\$

map<Key, Value> (and it's mates) holds Value, well, by value. If you try to give it an instance of derived class to hold as value, most likely "slicing" will occur. More about slicing: https://stackoverflow.com/q/274626/1125702

std::unique_ptr<Component> seems exactly what you want. It manages lifetime and memory of owned object. By convention, only std::unique_ptr may "own" it's objects, i.e. anything other may only temporarily refer to given object (either by raw pointer or by references). It is programmer's task to ensure that std::unique_ptr will outlive any other references to "owned" object.

Usage is quite simple.

// type aliases for convenience
using ComponentPtr = std::unique_ptr<Component>;
using ComponentContainer = std::unordered_map<std::string, ComponentPtr>;

// instance of container
ComponentContainer cc;

// insert component (quick way)
cc["SomeComponent"] = std::make_unique<SomeComponent>(arg1, arg2, ...);

// 'get' returns raw pointer to owned object
SomeComponent * sc_ptr = cc["SomeComponent"].get();
sc_ptr->hello();

// reference, if '->' annoys you
SomeComponent & sc_ref = *cc["SomeComponent"].get();
sc_ref.hello();
\$\endgroup\$
  • \$\begingroup\$ Ok, that makes sense... So, I could create the multimap as std::unordered_multimap<int, std::unique_ptr<Component>>? And how would I go about adding elements into my map? I've tried mymap.emplace(id, Movable()); but the compiler doesn't like the fact that 'Movable()` isn't a Component class... \$\endgroup\$ – natebot13 Jul 25 '14 at 16:16
  • \$\begingroup\$ @natebot13 Because you are passing instance of derived class, but your multimap wants unique_ptr to Component. Try inserting std::make_unique<Movable>(). \$\endgroup\$ – Shadows In Rain Jul 27 '14 at 16:04
  • \$\begingroup\$ The standard library doesn't have make_unique. \$\endgroup\$ – natebot13 Jul 30 '14 at 6:49
  • \$\begingroup\$ @natebot13 make_unique is in C++14, but may be implemented trivially in C++11: stackoverflow.com/a/12580468/1125702 \$\endgroup\$ – Shadows In Rain Jul 30 '14 at 10:11
1
\$\begingroup\$

You might actually be better of with using std::map for this one, depending on your exact use-case. I did some testing on our little rendering framework where I thought std::unordered_map would be good for maintaining a map of graphics resources (we are using std::map there now).

I found that it's actually slower to use std::unordered_map since the hash-function (e.g. std::hash()) is slowing things down significantly.

Of course it might depend on the exact usage, but as always: profile profile profile.

And of course, like mentioned above, make sure you don't slice your class and use pointers such as std::unique_ptr.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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