Jump to content
  • Advertisement
  • entries
  • comments
  • views

ECS III: Queries, and (horrible) component serialization

Sign in to follow this  


Last article:



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.
Sign in to follow this  


Recommended Comments

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.

Share this comment

Link to comment

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? ;)

Share this comment

Link to comment
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.

Share this comment

Link to comment

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 :)

Share this comment

Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Advertisement

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!