Jump to content

  • Log In with Google      Sign In   
  • Create Account

EntityID String<->Int resolution

Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
16 replies to this topic

#1 chondee   Members   -  Reputation: 135


Posted 20 April 2012 - 11:54 PM


I am working on my component based engine, and trying to implement an entityID system.
The way I am planning it is that each entity and component has a unique int ID and a unique string ID.

I thought of two ways to implement this, but not sure which would be better:

1. Each entity has their own map.
The entities are stored in a std::map<int, string> and components inside entities are also stored in a std::map<int, string> (there are multiple maps)

2. There is only 1 (or a fixed number of) global maps that resolves everything.
The entities and components only contain an int ID, on creation their string ID is going to be added in a single global std::map<int,string> that will resolve any id access by string. (or perhaps 1 map for entities, 1 for components)

For my peace of mind I would be more comfortable of knowing that all string IDs are stored in a single place, but not sure if it is a good idea, since it will take a longer time to search through a big global map to find a string.
On the other hand, I would imagine entities would be called with their int IDs, and their string IDs would be used more for debugging, so it wouldn't make a big difference (I might be completely wrong on this one.)

I would appreciate any suggestions on this.
Thanks in advance!

EDIT: Also, is it a good idea in general for each entity to contain a string id, or no, because it is inefficient?


#2 ApochPiQ   Moderators   -  Reputation: 18214


Posted 21 April 2012 - 12:21 AM

Typically you will have one ID space per type of component. If all components have the same ID space, you can't just store a map<ID, component> because now you need a base class to represent a component and the whole point of a composition-oriented architecture goes out the window.

You shouldn't - generally speaking - ever have a situation where you know a component's ID but not its type, so this is not really a disadvantage. Plus, you can get clever:

std::vector<std::shared_ptr<FooComponent>> AllFoos;
std::map<int, std::shared_ptr<FooComponent>> FoosByNumericID;
std::map<std::string, std::shared_ptr<FooComponent>> FoosByStringID;

Now you can look up a Foo by any means you like: direct pointer (via shared_ptr passed by const reference), or by integer ID, or by string ID. Maintaining these mappings should be trivial if you have centralized your component creation logic correctly.

Here's an alternative approach which I personally prefer:

std::vector<FooComponent> AllFoos;
std::map<int, size_t> FooIndexByID;

std::string FooComponent::GetStringFormOfID()
    std::ostringstream format;
    format << "FooComponent #" << MyIntegerID << " at index " << FooIndexByID(MyIntegerID);
    return format.str();

This removes a lot of indirection, and if you need to know where a Foo is, you either know its index, or its ID; you ask for its index given its ID, and then you can do a lookup straight into the vector to get the Foo itself.

Also, you get to keep your string-form IDs for debugging, but you never go from a string to an integer ID. Instead, you always get a string ID from an integer ID. This eliminates the cost of storing the strings and makes it trivial to find places where you rely on the string ID (just search for GetStringFormOfID!).

If you like, you can also maintain a second std::map<int, std::string> which maps from IDs to "comments." This is handy for situations where "FooComponent #5895 at index 384" isn't useful, but "Player's Left Hand Weapon" is useful. You can safely make the "comments" map sparse, i.e. only attach comments as you need them, so you don't have to worry about the overhead per-component. And since you don't need to look up components by their comment in well-designed architectures, you don't have to worry about the cost of searching for a commented component.

#3 chondee   Members   -  Reputation: 135


Posted 21 April 2012 - 03:08 PM

Thanks a lot, this is very helpful!

So, based on what you have suggested, if I understand it correctly, would it be a good design if:

- I have a vector<std::shared_ptr<ComponentType>> globally, containing pointers to all components of a specific type that need to be accessed together by their managers, such as GraphicsManager for GraphicComponents, InputManager for InputComponents.

- The owner entity has a vector<std::shared_ptr<BaseComponent>> containing a pointer to every component it has, without really knowing or caring about their type, since the entity will only call their general onUpdate()

My concern about the above, maybe because of the lack of knowledge on std::shared_ptr:
- Is that ok, that 2 shared_ptr point to the same entity, but by different type (one is BaseComponent, the other is GraphicsComponent)?
- Will they still know about each other, counting their references, or like this they are separated, and each thinks they are the only one owning the component they point to?

Also, so far the way I have my design is when a component is added to an entity, it is added to the entity's std::vector<std::shared_ptr<BaseComponent>> entityComponentVector. The component was created by std::shared_ptr<BaseComponent> (new ComponentType(...)).
Each component has a onAddedToEntity() function, through which I would like to register the component by type to the corresponding global vector of pointers by component type.
From inside the object, is there a way to pass the shared_ptr that owns it to the corresponding manager for the type so it gets registered?

To give an example of what I want, I have an Entity as the player. To this entity I give a SpriteComponent, that is a GraphicsComponent derivative, and the way I add it is by pushing its shared_ptr<BaseComponent> to the player's componentvector, so it knows what it has. After the component has been added its onAddedToEntity() function should pass either
1. its owning shared_ptr<BaseComponent> to the GraphicsManager, that would possibly cast and use it as shared_ptr<GraphicsComponent>
2. a shared_ptr<GraphicsComponent> of itself to the GraphicsManager.

Again, I am not really sure how I can do this

EDIT: Maybe I am approaching this whole thing from the wrong direction, I am not even sure if it is possible to have it such that each entity has pointers to its components as well as each manager has pointers to components it needs, all stored in different vectors of pointers.
Should there only be vectors of component types and a std::map, and managers access these vectors directly, while entities only know the indexes of the components they own, which they look up from the std::map?

(I'll also use one of the methods for string <-> intID access, for now I don't really have more concerns about that as you have provided some very clear implementations)

#4 ApochPiQ   Moderators   -  Reputation: 18214


Posted 21 April 2012 - 03:18 PM

I'm not certain of the benefit of having a BaseComponent class at all, or why you would need a vector of them to call the updates. What if your components have ordering requirements for who updates first, but you store them in a different order in the "master vector"?


- Player entity has an Input component and a Graphical component
- We call Graphical::Update()
- We call Input::Update()

Now we've lost the input for this frame because we drew the graphics too early.

In my experience it is safer to have each entity know what components it owns and how to update them in the correct order; this means you shouldn't have to use a base class for all components or worry about storing multiple references to the same component in different vectors. Part of the point of the second design I described earlier is that it avoids a lot of extra indirections due to having shared_ptrs all over the place, and makes reasoning about the system a bit easier.

#5 chondee   Members   -  Reputation: 135


Posted 21 April 2012 - 03:51 PM

I have thought of the update priority and figured that each component has an int priority, and the entity's component list is sorted by the component's priority. I thought it wouldn't be inefficient since it only needs to be sorted when a new component is added.

If I wouldn't have a BaseComponent class, then I would need to change the Entity class whenever I would come up with a new Component type, so it has a corresponding container.

Do you mean the entities should do all the processing of components without the need of Managers? That's how I tried to do this first, but when I got to collision I thought I really need an external manager that oversees every entity's every collider, and thought of using a GraphicsManager would also be useful.

Or am I only supposed to use a handful of core component types without a lot of inheritance?
For example I have:

Is that bad design, should I only have GraphicsComponent, and implement everything else from that without deriving classes?

I would just like each entity to know about all its components, and I would like globally each manager to be able to iterate through a specific type of component of every entity. I am not sure how I can achieve this. Since I have many derived classes of components, I can only store vectors of pointers to base classes (either the root ComponentBase, or by family as GraphicsComponent, InputComponent, Collider).

Sorry for all these questions, and my confusion, I am really new to component based design. :)
Your help is much appreciated.

#6 adam4813   Members   -  Reputation: 729


Posted 21 April 2012 - 05:16 PM

The design we are using for NLS engine is such that each Entity has a vector of ComponentInterface*. Each module (graphics, physics etc) can extended that base type for its own components. Then the component is also stored in a vector for that module. The module itself contains the update method. The only reason entity even known about its components is for easier debugging and entity destruction to insure all components owned by that entity go bye-bye with it.

The important point is that it's perfectly fine for both entity and module to have a reference to component. However the distinction is that entity OWNS the component, and modules UPDATE using the components.

For reference you can catch NLS engine at https://bitbucket.org/adam4813/nls-engine . We are currently reworking for our v1. Within the next few days the biggest chunk of that rework will be live and you can see the interaction between components, modules, and entities.

#7 chondee   Members   -  Reputation: 135


Posted 21 April 2012 - 08:21 PM

Thanks for your reply adam4813,

I have a similar idea on the design of my system, although with mine Entities do actually get to call each component's Update() function, but that only does the general logic.
Graphical components further need to be processed by the GraphicsManager, that has access to more functions than Update() (Render for example).
Colliders position and transformations get updated by the Entity, but it will be the CollisionManager that will check for actual collision and send out events.

Most of this is still only on paper, so it might turn out to be better to separate all functionality from entities, I'll see what happens.

Another Question:
When I am trying to handle events, such as an KeyboardInputComponent maps a key to an event.
For example: (I'm doing this in SFML2)
std::map<sf::Keyboard::Key, String> inputTable
if any of the keys on the map are pressed, send an eventMessage to the evenChannel, such as if (sf::Keyboard::IsPressed(sf::Keyboard::Space)) sendEvent("Fire");

The eventHandler will decide what to do with "Fire".

What I would like though is that I can work with all events being human readable strings, but I don't want the program to actually be comparing strings because it is probably inefficient. Also, I want this in a way that the event words wouldn't need to be hardcoded into the engine, it could be parsed from an xml or txt file, and a new event could be generated even during runtime.

How can I make this work, so it would work like enums (without always having to extend the values inside the enum if I want to extend it), so I work with readable words, but the program deals with more computer-friendly data.

Would I also use 2 maps, one as String->Int and another for Int->String?
When I add the event by string it would only put the corresponding int ID on the event channel, and when the eventHandler checks for events by string it would get the corresponding int ID?

#8 adam4813   Members   -  Reputation: 729


Posted 21 April 2012 - 08:59 PM

We trying having a general multi-purpose messaging system, but it wasn't really worth it. We have fully encapsulated each module and component so that there is no events or messages between them. The glue however is our scripting system. Scripts can register to events and act accordingly.

You might want to check out boost::bimap is it is similar to what you are doing with your 2 maps, but in one nice container.

#9 ApochPiQ   Moderators   -  Reputation: 18214


Posted 22 April 2012 - 12:24 PM

My personal view (and this is not to say that other opinions are not valid) is that the purpose of a component system is to avoid inheritance as much as possible. Inheritance-heavy designs tend to become either very brittle or very convoluted as they get more complex. There are typically three outcomes:

- Huge amounts of multiple inheritance
- "God" base classes and lots of thin derived classes
- Lots of code duplication

The whole idea of composition-based design is to avoid this by dividing your code into totally distinct "components" and linking them via composition in "entity" and "system" classes.

Ideally, a component should not need a base interface, because every component should do unique things. An entity might have a simple abstract interface base, but even that isn't really necessary. Systems control interactions between entities and do not need base classes because they talk directly to the components involved.

It may seem like it is a disadvantage that you have to explicitly write code every time an entity gets a new type of component, but the reality is that this is a huge advantage. It makes the relationships between things very clear in your code, and has practical benefits beyond that. For instance, you remove a lot of pointer indirection and virtual function dispatch, as I outlined above. It also guarantees that you only build game data that is "sensible" - you will never have a Tree entity holding a Weapon component because that just doesn't make sense, and the code just plain won't let you.

Now, does that mean you need a Tree entity and a Bush entity and a Rock entity? Absolutely not! The goal is to find common elements between Tree and Bush and Rock, and notice that they all make sense together as a Scenery entity, or whatever you want to call it. Then you combine your component model with data-driven techniques so that a Scenery entity has a Graphical component which points to the image for a Tree, or a Bush, or a Rock. Now your code enforces that the game world always makes sense but doesn't require a huge nest of inheritance hierarchies to accomplish that.

If you want to know if a Scenery is a Rock, just ask it; no need for RTTI, dynamic_cast, or any other potentially messy reflection-style systems. Each entity should hold a tag that tells you what variant of that entity it represents, and you just retrieve that tag. So your Collision system can query Scenery to find out if it should be a blocking obstacle (Trees and Rocks) or if you can walk through it (Bushes). And the Scenery needs to know nothing about the collision system at all!

This is the power of good component-based design. My general rule of thumb is: if you find yourself using inheritance anywhere, think extremely hard about what you're doing. It's probably not the best alternative.

#10 Kyan   Members   -  Reputation: 395


Posted 22 April 2012 - 03:32 PM

The whole idea of composition-based design is to avoid this by dividing your code into totally distinct "components" and linking them via composition in "entity" and "system" classes

I agree.

However, I think I've gotten confused with the terminology, since different "component systems" use the words "component", "entity", and "system" in various (and sometimes conflicting) contexts. In something like Artemis, which uses "systems" to process functionality while "components" are just data that are associated with an entity (but the entity is merely a container itself), there is a base Component interface but it's just an empty marker to use as a reflection platform. The basic idea, as far as I can tell, is similar but there doesn't seem to be a reason to have explicit entity classes.

If you didn't mind, could you elaborate - or provide a simple example - on why/how you'd create two different entities in the system you proposed?

#11 phantom   Members   -  Reputation: 9276


Posted 22 April 2012 - 04:04 PM

The system we work with has two parts to it's component system;

- modules
- components

Entities in the world grab references to components from modules (more or less) to access them BUT that is ALL they do.

Modules handle 'update' calls and, if required, can forward that call down to their components (not all components in the world require a directly ticked update).

Module update order is configured in an XML file.

(Due to engine design 'update' is also 'render' as the renderer side modules sit on a different thread to game side logic).

#12 adam4813   Members   -  Reputation: 729


Posted 22 April 2012 - 05:19 PM

After discussing with my team member, we have finalized the design for v1 of NLS Engine, and in fact it is very similar to what you list phantom. We only have 1 base interface for our components as a convenience for including all entity related references. In fact entity has no practical need to know about its components in any way as our entity is just a position, rotation, scale structure with a reference to its parent for relative positioning, and nothing more.

#13 ApochPiQ   Moderators   -  Reputation: 18214


Posted 22 April 2012 - 07:46 PM

The whole idea of composition-based design is to avoid this by dividing your code into totally distinct "components" and linking them via composition in "entity" and "system" classes

I agree.

However, I think I've gotten confused with the terminology, since different "component systems" use the words "component", "entity", and "system" in various (and sometimes conflicting) contexts. In something like Artemis, which uses "systems" to process functionality while "components" are just data that are associated with an entity (but the entity is merely a container itself), there is a base Component interface but it's just an empty marker to use as a reflection platform. The basic idea, as far as I can tell, is similar but there doesn't seem to be a reason to have explicit entity classes.

If you didn't mind, could you elaborate - or provide a simple example - on why/how you'd create two different entities in the system you proposed?

I suppose I shouldn't call them "entity classes" because you don't really have one entity object per conceptual game entity, as you might in a standard inheritance-based architecture.

I tend to think of components as blobs of data, entities as the logic that contains the blobs and ties together their interactions, and systems as the logic that governs how entities interact with each other.

So for example:

class WeaponComponent { /* blah */ };
class PositionComponent { /* blah */ };
class HealthComponent { /* blah */ };

// Note that ONE instance of this class handles ALL character entities
class CharacterEntities
    std::vector<WeaponComponent> Weapons;
    std::vector<PositionComponent> Positions;
    std::vector<HealthComponent> HealthLevels;

    int GetWeaponStrength(size_t character)
        return Weapons[character].Strength;

    void Damage(size_t character, int amount)
        HealthLevels[character] += amount;

class CombatSystem
    void Fight(CharacterEntities& characters, size_t attacker, size_t defender)
            characters.Damage(defender, characters.GetWeaponStrength(attacker));

#14 chondee   Members   -  Reputation: 135


Posted 22 April 2012 - 11:03 PM

Hmm, the above example of a component based system really makes me confused as it completely goes against what I have believed component based design stand for.

I thought that ideally there should be only 1 entity type, that doesn't differentiate between a weapon, a rock or a spaceship.
I have also thought that the components should be mostly uniform, or at least to the entity (as to the entity the only interface open is Update() towards component).

For example in your sample, what if that character system works for almost every character, but one guy is on fire and needs a particle emmitter component?
Do you rewrite the whole CharacterEntities class and add a vector for particle emmitters, that won't ever be used on 99% of the characters, or do you accept the limitations of the CharacterEntities class and represent the flaming guy in his own class, as an exception? How about the player character? Should all characters have some function, or component vector for components that handle input, or playerCharacter needs a separate class? Or if you want to include a special weapon that works differently for every other one, should the whole Weapon class get rewritten because of it?

I though things like these were the reason why game development started to shift towards component based design from object oriented.

Please correct me if I am seeing this from the wrong perspective, or if my picture on component based design is wrong.
This is what convinced me of redesigning my engine to component based: GDC_Powerpoint_OOvsComponentOriented

#15 ApochPiQ   Moderators   -  Reputation: 18214


Posted 22 April 2012 - 11:49 PM

You would probably set up a GraphicsComponent which could hold ParticleEmitter objects; then all renderable characters have a GraphicsComponent. Simple as that. Now anything that is graphical can also emit particles if the necessary particle emitter subcomponent is attached. The trick is to have the complexity of relationships modeled as ownership hierarchies instead of inheritance hierarchies.

Anything that can move has a MovementController attached. Players have a special MovementController which receives user input; everyone else has an AI-driven MovementController. This is one case where interfaces come in handy, but note the distinction between true interfaces and simply inheriting from a base class.

As for specialized weapons... generally these kinds of things just fit into the data-driven side of the architecture. It is possible to come up with ideas that don't fit neatly, but you can always find one-off workarounds as necessary. Not all designs are going to be perfectly theoretically beautiful :-) However, that said, it's generally (in my experience) trivial to find good balances between component types and data-driven attributes.

I should re-emphasize that this is just my own view. There are as many opinions on how to do component architectures as there are programmers. I've worked with things that people called component architectures that just devolved into a giant mass of random classes derived from huge hierarchies of EntityThis and EntityThat and BlahComponent and it was a total nightmare. I don't really see the need for inheritance or virtual entity interfaces if you know what kind of game you are making. There might be an argument for doing highly generic stuff if you're trying to write a very reusable engine, but even then, I suspect most of that stuff can be crammed into special cases and the vast bulk of general functionality left in the kind of approach I describe.

It does take some getting used to, but I think the adjustment is worth it.

#16 adam4813   Members   -  Reputation: 729


Posted 23 April 2012 - 03:32 PM

Your system is a tad more specialized than ours. Our system is more or less as follows:

class Entity {
	vector3 position;
	vector3 rotation;
	float scale;
	Entity* parent;
	vector3 GetPosition(); // Position relative to parent
	vector3 GetRotation(); // Rotation relative to parent
	float GetScale(); // Scale relative to parent

	void SetPosition(vector3); // Position relative to parent
	void SetRotation(vector3); // Rotation relative to parent
	void SetScale(float); // Scale relative to parent

	Entity* GetParent();
	void SetParent(Entity*);

class Component {
	Entity* onwer;
	Entity* GetOwner();
	void SetOwner();

	void Update(Module*, float delta);

class Module {
	vector<Component*> components;
	void Update(float delta) {
		foreach(comp in components) {
			comp->Update(this, delta);

Then all that happens in the main loop is the delta change in time is calculated between this update call and the last. Components use update for various things such as rendering themselves. However a physics module may do the update itself and never call the update of its components. However components can modify their owning entity's position, rotation, and scale.

#17 Inferiarum   Members   -  Reputation: 735


Posted 24 April 2012 - 03:23 AM

OK, so I am curious what you guys say about my idea for a entity system.
Basically i have a class that stores the Game state in form of a container of entities.
class GameState {
Dictionary<type,Dictionary<id,Entity>> entitiesByType;
and provides various functionality to enumerate through certain maps specified by a list of entity types etc.

The entities are a container of variables
class Entity {
Dictionary<string,object> properties;

Now i have different systems which operate on these containers and perform the state transition from one step to the next (if you think of the whole game as a finite state machine). Each of the systems knows the types of entities it operates on and gets a suitable enumerator from the game state for each update.

if an entity is created, it registers with all systems it uses. Then the systems can add the necessary properties if not already there. In my opinion this is a pretty flexible and easily extendable system

Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.