ECS III: Queries, and (horrible) component serialization

Published December 31, 2014
Advertisement
Last article:

https://www.gamedev.net/blog/1930/entry-2260620-ecs-ii-messaging/

Queries:

Last time I mentioned that as a special case of the messaging in my ECS, I implemented queries. The queries syntax is actually quite similar to messaging:// 1. declare the query classclass CollisionQuery : public ecs::Query{public: CollisionQuery(const math::Rect& rect, const ecs::Entity& entity); const math::Rect rect; const ecs::Entity* pEntity; ecs::EntityHandle entityCollided; bool bCollision;};// 2. register a system for itvoid CollisionSystem::Init(ecs::MessageManager& messages){ // register this system to respond to the query messages.RegisterQuery(*this);}// 3. this is how you use it:CollisionQuery query(rect, *pEntity);if(m_pMessages->Query(query)){ // we got a response to the query}// 4. the system receives & handles the query:bool CollisionSystem::HandleQuery(ecs::BaseQuery& query) const{ if(auto pCollision = query.Convert()) { const math::Rect collRect = math::Rect(pCollision->rect.x, pCollision->rect.y, pCollision->rect.width -1, pCollision->rect.height-1); auto vEntity = m_pEntities->EntitiesWithComponents(); for(auto pEntity : vEntity) { if(pEntity == pCollision->pEntity) continue; auto pPosition = pEntity->GetComponent(); auto pCollisionComp = pEntity->GetComponent(); const math::Rect rect = calculateRect(pEntity, pPosition->v, pCollisionComp->vSize.x, pCollisionComp->vSize.y); if(collRect.Inside(rect)) { pCollision->entityCollided = pEntity; pCollision->bCollision = true; return true; } } return true; } return false;}
So what are the main differences in both systems?

- Messages can be received by N number of systems, while only one system can respond to a query
- Messages can alter the state of the system, while queries are passed in a "const" method
- Messages cannot be altered, but queries obviously can take output-arguments

Basically messages are for delivering information to the system, and queries are for extracting it.

Component serialization:

Now with all the I presented, the ECS is nearly functional. The only thing that was left is loading of components, and editor interaction. The design I am going to present was good enough for the beginning, but as things progressed, it really become aweful to work with

The intermediate file-format used in my engine is XML. So the first thing that came to mind when talking about loading components, was to have an interface that can be registered to a loader, and is called whenever a component is to be loaded:// 1. the interfaceclass IComponentLoader{public: virtual IComponentLoader(void) = default; virtual void Load(Entity& entity, const xml::Node& node) const = 0;}// 2. and a basic implementationvoid PositionLoader::Load(ecs::Entity& entity, const xml::Node& node) const{ const auto x = node.FirstNode(L"X")->ToFloat(); const auto y = node.FirstNode(L"Y")->ToFloat(); entity.AttachComponent(x, y);}// 3. which can be registered// this loader is executed whenever a "Position"-component-node is encounteredecs::ComponentLoader::RegisterLoader(L"Position");
The loader would then simply pick the correct interface from a map, and call Load on it. The same thing happening for saving, as you can imagine. At the beginning of the project, this was only half bad. There where only a handful of components, and writing the load/save interface did not really take that long a time. But as there began to be more and more components, this became really tedious. Not only did I have to implement out those interfaces, but a ton more (mainly for editor interaction). So adding a component could easily need 10+ files and take about half an hour. Its still not too bad in the grand scheme of things, but there certainly had to be a better solution.

And thus I built my own custom RTTI-system. Its only half-insane as it sounds... next article, things should start to get more interesting, when I tell you more about this type-system I cam up with.
2 likes 5 comments

Comments

Aardvajk
Thanks for these write ups. I just read your last three entries on this and really interesting to see some real world ECS stuff so clearly explained. I've yet to dive into this but suspect it is inevitable. The leverage of the C++11 features you are using seen almost designed with ECS in mind.
December 31, 2014 06:54 PM
Juliean

You're welcome, and thanks for your kind words! smile.png Its true, C++11 has really done much good, and personally I'd even say it makes it viable for writing high-level code, without going insane. As for ECS, I wouldn't want to do game-objects any differently anymore. Of course setting up a framework is a cost at the beginning, but it really pays off in the end, with stuff like prefabs (which I'll talk about in a later article), and being able to add a new feature to the game objects in almost no time.

EDIT: Oh, btw, I assume your downvote of the first article was by accident, or was there something particulary bad/wrong about it? ;)

January 01, 2015 11:30 AM
Aardvajk
Oh, sorry, must have been an accident. Will correct.
January 02, 2015 10:41 AM
Aardvajk
Hmm, sorry seems I can't undo. Just up voted your last comment to make up for it :) Have to be more careful doing up votes on my phone.
January 02, 2015 10:43 AM
Juliean

Not a big deal, though I appreciate your efforts to fix it ;) Just thought I'd mention it out of correctness/curiousities sake, so don't worry :)

January 02, 2015 08:07 PM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Advertisement