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!
2 Answers 2
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();
-
\$\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 triedmymap.emplace(id, Movable());
but the compiler doesn't like the fact that 'Movable()` isn't a Component class... \$\endgroup\$natebot13– natebot132014年07月25日 16:16:32 +00:00Commented Jul 25, 2014 at 16:16 -
\$\begingroup\$ @natebot13 Because you are passing instance of derived class, but your multimap wants
unique_ptr
toComponent
. Try insertingstd::make_unique<Movable>()
. \$\endgroup\$Shadows In Rain– Shadows In Rain2014年07月27日 16:04:14 +00:00Commented Jul 27, 2014 at 16:04 -
\$\begingroup\$ The standard library doesn't have
make_unique
. \$\endgroup\$natebot13– natebot132014年07月30日 06:49:10 +00:00Commented Jul 30, 2014 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– Shadows In Rain2014年07月30日 10:11:11 +00:00Commented Jul 30, 2014 at 10:11
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.
You must log in to answer this question.
Explore related questions
See similar questions with these tags.
multimap
, but amap
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\$