Jump to content

  • Log In with Google      Sign In   
  • Create Account


Component entity system - create from template


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
15 replies to this topic

#1 skwee   Members   -  Reputation: 341

Like
0Likes
Like

Posted 01 November 2013 - 07:56 AM

Hi there!

 

I'm trying to implement Entity component system and I'm stuck with defining templates for entities.

 

My components are simply PODs (C++ structs/classes with public parameters).

Entity is simply uint-s.

 

Now I could do something like (pseudo-code):

Entity e = world.createEntity();
e.attach(new Component1(..));
e.attach(new Component2(..));
..etc 

But instead I want to define entities outside of the game code, probably inside JSON file, something like:

dog.json 
{
  "components":
  { 
    "sprite": .., 
    "anitmation": .., 
    ..etc 
  } 
} 

And then in the game (or scripting engine in JS) do something like

world.createEntity("dog", position); 

To do this, I need to have some sort of "Entity template". A list of all possible entities with all their components and their components initial data.

So at my games loading phase, I load all the needed entities and create templates from them, and then I can clone each template into its own instance of the entity.

 

One solution I could do, is to store the JSON in memory, and then on world.createEntity("name"); re-parse the json and create new entity with components.

But this would be inefficient (I guess) if I'm going to create many entities. I want to avoid parsing the JSON every time I create an entity.

 

I'm looking for some design pattern in C++ that would allow me to parse the JSON once, and store the parsed data somehow so that any next call to createEntity("name") would simply clone the already existing entity with as lower overhead as possible.

 

Unfortunately there is no information about entity templates/definitions in the web so I'm asking your help!

 

Thanks!


I would love to change the world, but they won’t give me the source code.


Sponsor:

#2 haegarr   Crossbones+   -  Reputation: 4173

Like
2Likes
Like

Posted 01 November 2013 - 09:06 AM


I'm looking for some design pattern in C++ that would allow me to parse the JSON once, and store the parsed data somehow so that any next call to createEntity("name") would simply clone the already existing entity with as lower overhead as possible.

If you're after OOP patterns, then the Factory and the Prototype are possibilities. The entity services are asked for a new entity of type "dog". The services looks up "dog" in its entity prototype cache. If it is found, then the entity is cloned and returned. Otherwise the JSON reader is invoked, or perhaps the reader was invoked once at initialization time to fill up the services' cache a-priorily. However, the reader gets a map with all known components. The keys to the map are obviously the components' names. The values are factories for the belonging components, or else itself prototypes to the components. The reader builds a new entity accordingly to the JSON prescription and returns it to the entity services which itself place the result as entity prototype in its cache.



#3 jms bc   Members   -  Reputation: 421

Like
0Likes
Like

Posted 01 November 2013 - 09:34 AM

Artemis uses entity templates, I think. At least the c# port example (Star Warrior) does. I haven't looked too closely at the implementation so I can't say much about it, but that might be a decent start if you want to see what other people are doing.


The Four Horsemen of Happiness have left.


#4 simber   Members   -  Reputation: 425

Like
3Likes
Like

Posted 01 November 2013 - 10:15 AM

I would do it this way:

std::vector< std::unique_ptr<Component> > prototype;
// ... parse JSON and create/add components to prototype
// store prototype somewhere

// create an entity from prototype
Entity e = createEntity();
for( auto &comp : prototype )
{
   e.attach( comp->clone() );
}

For this approach your components need a virtual clone function. With the use of CRTP u can automatically create it:


class Component
{
public:
   virtual ~Component() { }
   virtual Component* clone() = 0;
};

template <class Derived>
class TComponent : public Component
{
public:
   virtual Component* clone()
   {
      return new Derived(static_cast<Derived const &>(*this));
   }
};

class Position : public TComponent<Position>
{
    ...
};

 



#5 skwee   Members   -  Reputation: 341

Like
0Likes
Like

Posted 01 November 2013 - 10:35 AM

Thanks for the replies!

 

haegarr

Thanks for mentioning the Prototype pattern! I learned something new today!

 

jms bc

Thanks, I heard about Artemis, and as far as I remember it is written in Java. Ill take a look into it.

 

simber

Thanks a lot! I've already though about clone method and CRTP.

 

However I'm still missing a link. Assuming my code is similar to what simber wrote (with the exception that I do not have Entity per se, its just a typedef to uint). 

In that case, assuming the code looks like this

Entity e = entitySpawner().spawn();
for(auto &comp: prototype) {
    entitySpawner.attach(e, comp->clone());
}

And assuming EntitySpawner knows about all the Component Systems, how do I register a component within a specific system?

In component based entity system, there are systems (or processes) that updates the components, I'm still not sure how I can introduce the needed components to the correct system in an elegant way.

 

Any solutions?


Edited by skwee, 01 November 2013 - 01:03 PM.

I would love to change the world, but they won’t give me the source code.


#6 Pieisgood   Members   -  Reputation: 202

Like
0Likes
Like

Posted 01 November 2013 - 02:55 PM

The way I understand this type of actor component system is to have a map structure that accepts the string name of the component type and then accesses a factory function to return a new instance of that component (with specified parameters). 

 

So you would have a base type of "Component" and all your components would derive from this single type. Now you don't need to work with templates. Then your map would look like

map< String, Component>

This way you could just access them in your factory with some sort of function that only accepted a string.

Component* createComponent(string jsonString){
Component *comp = new map[jsonString.name];

//... extra stuff goes here for each different component

return comp; // return a pointer the a component and not the exact type of component
}


#7 haegarr   Crossbones+   -  Reputation: 4173

Like
0Likes
Like

Posted 02 November 2013 - 04:28 AM

EDIT: Err, where is the main part of my answer gone? Dammit! Half an hour gone down the drain...

 


In that case, assuming the code looks like this
Entity e = entitySpawner().spawn();
for(auto &comp: prototype) {
    entitySpawner.attach(e, comp->clone());
}

Edited by haegarr, 02 November 2013 - 06:18 AM.


#8 simber   Members   -  Reputation: 425

Like
0Likes
Like

Posted 02 November 2013 - 11:05 AM


And assuming EntitySpawner knows about all the Component Systems, how do I register a component within a specific system?

In component based entity system, there are systems (or processes) that updates the components, I'm still not sure how I can introduce the needed components to the correct system in an elegant way.

 

You can explicitly add entities to systems like this (assuming Entity is just an identifier):

class System
{
public:
   void add(Entity e);

protected:
   std::vector<Entity> m_entities;
};

// update example
void MoveSystem::update(EntitySpawner& spawner)
{
   for( auto &e : m_entities )
   {
      Position& position = spawner.get<Position>(e);
      Velocity& velocity = spawner.get<Velocity>(e);
      position += velocity;
   }
}

Or you let the system query combinations of Components, no adding needed:

void MoveSystem::update(EntitySpawner& spawner)
{
   std::vector<Entity> entities = spawner.getEntitiesWithComponents<Position, Velocity>();
   for( auto &e : entities )
   {
      Position& position = spawner.get<Position>(e);
      Velocity& velocity = spawner.get<Velocity>(e);
      position += velocity;
   }
   
}

I used EntitySpawner here like you did , which may not be the best name for what it does.

 

These are only two incomplete examples, there are dozens of design possibilities for components and systems. For the second approach I can suggest to have a look at entityx .



#9 skwee   Members   -  Reputation: 341

Like
0Likes
Like

Posted 03 November 2013 - 01:35 AM

Thanks for the examples and reference to entityx (which I already checked), simber. However, it still doesn't fix my issue: How components are stored inside the spawner (or whatever we call it)?

 

Going back to your previous example

Entity e = spawner.spawn();
for( auto &comp : prototype )
{
   spawner.attach(e, comp->clone());
}

Lets continue with spawner, even though the name is incorrect and Ill probably add another class that will be responsible for storing the components.

 

First of all, how would attach() be implemented?

Second of all, how would getEntitiesWithComponents<X, Y ..>() be implemented?

 

I'm pretty weak with templates this why I ask.

 

[Edit]

I looked a bit into entityx. I see how they implement the component storage. They provide a family for each component. This is an elegant solution, you then can store all components in one list, instead of separating each component into its own list. What do you think about this approach?

 

Thanks a lot!


Edited by skwee, 03 November 2013 - 04:03 AM.

I would love to change the world, but they won’t give me the source code.


#10 haegarr   Crossbones+   -  Reputation: 4173

Like
0Likes
Like

Posted 03 November 2013 - 04:20 AM

For sure there are many ways of implementing a CES...

 

... but IMHO there is a design flaw in the above concept. A (so far) unknown object asks the spawner to spawn a new entity which seems to be just a UID. The spawner is hence a generator for UIDs. The said unknown object then iterates the component prototypes which are obviously already restricted to those belonging to the requested kind of entity. This iteration triggers cloning of the prototypes, and invokes the UID spawner to attach them to the entity. The "UID generator" is then responsible to drive the component collection?!

 

Another thing is this: Before going any deeper in code, you have to tell / decide how the entire system should work. Do you have an explicit collection of components that make up an entity? OP's statements "Entity is simply uint-s" and "Entity e = world.createEntity(); e.attach(new Component1(..));" contradict themselves. You said also that components are just data containers, and sub-systems are responsible for updating them. That allows the component instances to be stored with the belonging sub-systems instead of in entity oriented collections. Your question "how do I register a component within a specific system?" points in this direction. However the code snippets with "MoveSystem::update(EntitySpawner& spawner)" points to something else.

 

Obviously, how components are updated depends heavily on whether the one or other (or third) concept is chosen.



#11 skwee   Members   -  Reputation: 341

Like
0Likes
Like

Posted 03 November 2013 - 04:45 AM

haegarr

I agree with you.

 

Most of the code is pseudo-code and is in process of evolution. As I noted, spawner, or as you call it "UID generator", obviously should not care about storing and updating components, it only should be able to construct an entity. This was the initial concept of the EntitySpawner. It was then expanded by simber who suggest to use another class that will be responsible for storing the components and third class for each system, to update the needed components.

 

This question is also a theoretical discussion of different approaches to achieve the same. Whether there should be one system per component, or one "mega" system to store all components? What are the pitfalls of each approach?

 

I'm not so experienced in ECS and I need help and suggestions of those who know better than me, those we can see the pros and cons of each method.


I would love to change the world, but they won’t give me the source code.


#12 haegarr   Crossbones+   -  Reputation: 4173

Like
0Likes
Like

Posted 03 November 2013 - 07:19 AM

My favorite concept (because I'm about to implement it myself ;)) is the following:

 

An entity in the sense of a CES exists during runtime of gameplay only, i.e. it is part of the world. As such it is a UID and a couple of entries distributed over some sub-systems (named "services" in my engine). The entity instantiation process works on a resource kind of the Model class. A Model instance by itself is a composite of concrete Component instances. For each possible concrete Component sub-class there is a corresponding concrete ComponentType singleton-like instance. Such a ComponentType has a name (i.e. the one you use in JSON), is a factory for the belonging concrete Component, and can be understood as an outpost of one of the sub-systems.

 

Loading means to make Model instances, populate it with Component instances, and store them in the resource library. The ComponentType instances, addressed by their name, work as factories and de-serializer, so to say. The names of all used ComponentType instances are stored (indirectly) within the Model, too. There is another mechanism that may put objects into a Model instance. Components need not be specified in their entirety. Instead it is possible to express that some properties need to be defined at entity instantiation time (example: auto-generated individual name for each new instance of an orc-ish soldier).

 

Entity instantiation then means that the EntityServices sub-system is invoked to create a new entity by name. A parameter map is needed to be given at this moment if the Model of the requested entity has variables. The EntityServices creates a new entity UID, looks up the Model belonging to the given name in the resource library for Model instances, iterates the ComponentType instances for which a concrete Component instance is attached to the found Model instance, and invokes them to do their portion of entity creation. The entity UID, Model instance, and current Component instance is overhanded during this invocation. The ComponentType then investigates the Model instance for completeness, if necessary, does all allocation and initialization for the new entity inside the belonging sub-system, including eventually the filling of variables from the provided parameter map.

 

A ComponentType may return either with a Success, Failure, or Rework state. The former 2 are probably clear. However, it may happen that a ComponentType requires the set-up of another ComponentType (i.e. of another sub-system) to be done already, although those ComponentType wasn't iterated yet. So a ComponentType may tell the EntityServices that it want to be re-invoked once after the list of ComponentType is iterated first. What exactly happens within the sub-system during entity instantiation depends on the sub-system itself. It may be a cloning of Component as a prototype and linking the clone, or reserving and filling-in a place in a pre-allocated table, or whatever is suitable for the sub-system.

 

Notice please that the described mechanism doesn't require a 1:1 correspondence between sub-system and ComponentType; instead a 1:n correspondence is possible. Notice further that Component is just a data carrier, although it may define a behavior, but it does not perform any behavior.



#13 skwee   Members   -  Reputation: 341

Like
0Likes
Like

Posted 04 November 2013 - 08:04 AM

Looks similar to what I want to implement, but with different names for the classes. Thanks!


I would love to change the world, but they won’t give me the source code.


#14 phil_t   Crossbones+   -  Reputation: 3259

Like
0Likes
Like

Posted 04 November 2013 - 12:48 PM


A ComponentType may return either with a Success, Failure, or Rework state. The former 2 are probably clear. However, it may happen that a ComponentType requires the set-up of another ComponentType (i.e. of another sub-system) to be done already, although those ComponentType wasn't iterated yet. So a ComponentType may tell the EntityServices that it want to be re-invoked once after the list of ComponentType is iterated first. What exactly happens within the sub-system during entity instantiation depends on the sub-system itself. It may be a cloning of Component as a prototype and linking the clone, or reserving and filling-in a place in a pre-allocated table, or whatever is suitable for the sub-system.

 

Can you elaborate on this ("Rework")? Maybe some examples of what kinds of problems this is intended to solve? I don't have anything like this in my implementation, so I'm wondering if I'm missing something (or if I solved it another way).


Edited by phil_t, 04 November 2013 - 12:48 PM.


#15 haegarr   Crossbones+   -  Reputation: 4173

Like
0Likes
Like

Posted 04 November 2013 - 01:08 PM


Can you elaborate on this ("Rework")? ...

If the components are iterated in order C1, C2, C3, and the sub-system handling component C2 requires access to whatever C3 will cause later, then it will fail because C3 isn't handled yet. It then returns the Rework state to tell the entity services that it wants to be invoked again. The services memorizes all sub-systems that return Rework. After the initial run through all component the services iterates all memorized sub-systems and invoked them again. For the example, the total sequence then would be C1, C2, C3, C2. This is repeated (with resetting the memorized list each turn, of course) until at least one sub-system returned Failure, or none returned Rework, or the amount of Reworks was not lesser than as in the previous run (this last condition avoids endless looping).

 

The above is a mechanism to solve dependencies without a-priori bringing the sub-system invocations into a special order. It is perhaps not required for your implementation.


Edited by haegarr, 04 November 2013 - 01:10 PM.


#16 avision   Members   -  Reputation: 155

Like
2Likes
Like

Posted 03 December 2013 - 01:32 PM

I would probably arrange things differently.. I will try to describe it with examples.

I would use Entities, Components, Nodes and Systems. Ok what are they..

 

Entity: Could be just a UID

Components: Things like a 3D-mesh component, visibility-component, location in 3d-space component, AI-component ..

Nodes: below..

Systems: Render System, AI-System, Pathfinding-System

 

When game data is loaded there are basically a set of components describing an entity. Like a mesh-component with the filename and the like and probably an ai-component. The component is only a set of values with some sort of identifier.

 

Then this set of components is examined. Means all systems check if they want an entity having this _combination_ of components. If the render-system sees a entity having a mesh-component and a visibility component it creats a render-node for this entity and stores this in a list, dictionary, vector or whatever..

 

On each frame each systems iterates over the nodes it has and does it`s work. If an entity looses a component the systems are notified about that and check if they have to destory/remove the respetive node (Or some other piece of code does this and only notifies the systems loosing a node/entity). For example if a entity looses its visibility-component the render-system may want to remove the render-node but the resources-system may want to keep its resource-node.

In pratice most system will need to share data. So the goal should be to try to group data as much as you can if the data is not shared by multiple systems, if data is shared by multiple systems, spread it in different components. The way systems will store the entites/component-sets will be different from system to system, most systems will probably use a simple list, others will use a different way cause they want to process the nodes with multithreading or have a search tree or whatever.

To identify if an entity has a set of components a component could have an integer-id and the check if a entity has a set of components could be done with bit-masking. (The check which is done when an entity is created and the systems want to know if they "want" this entity).

 

just my ..

avision






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.



PARTNERS