Svelto.ECS 2.0 is production ready

This an introductory post to Svelto.ECS 2.0. Other two posts will follow, one explaining the examples line by line and another explaining all the Svelto.ECS concepts in a simpler fashion than before. Therefore this article is written for who already knows Svelto.ECS.

Svelto.ECS 2.0 is (at the time of writing this) available in alpha stage (pick the alpha branch to have a look now) and, compared to Svelto.ECS, introduce new features, new optimizations and unluckily some breaking changes (main reason why I decided to bump the major version).

I have also updated the Svelto.ECS.Examples code (available on the alpha branch at the time of writing) and added a new “vanilla” example which I can consider the minimum amount of code to write to show almost all the framework features available without using Unity.  The new example is, in fact, targeting Net.Core and Net.Standard.

I will quickly go through the list of new features:

  • New terminologies have been introduced. If you read the previous articles or used Svelto.ECS 1.0, you must know that the term “Node” has been abolished as it was meaningless in the framework context and has been renamed to “EntityView“. I will explain why it makes more sense with the next articles.
  • The GenericEntityDescriptor has been extensively used in all the examples to show how to reduce the boilerplate code with minimum effort.
  • EntityDescriptors are now pure static classes and must not be instantiated. The EntityDescriptor must conceptually identify your entity. The future articles will explain why it’s a big design mistake to identify Entities as EntityViews instead. A Entity is now built with the new signature entityFactory.BuildEntity<SimpleEntityDescriptor>(entityID, implementors). In any case you should rarely need to inherit from EntityDescriptor as most of the time you must inherit from GenericEntityDescriptor and MixedEntityDescriptor.
  • EntityDescriptors can now refers, using the same signature, either to class based EntityViews and struct based Entities, so that entityFactory.BuildEntity<SimpleStructEntityDescriptor>(entityID); will actually build struct based entities. Previously the struct based entities were build in a more awkward fashion. While I deprecated the previous method to build struct entities, changes may be introduced later to support thread safe code as currently Svelto.ECS data structures are not thread safe (custom structures or strategies must be adopted to support thread safe engines). With the next articles I will explain when and how to use EntityViews and Entities as struct. EntityViews should be used for flexibility and Entities as structs for speed. An EntityDescriptor must inherit from the new MixedEntityDescriptor generic class to build entities made out of Entity structs and/or EntityViews.
  • Another very important feature is the possibility to create entities inside buckets (groups). In this way is possible to query a set of entities from a specific group. This is a feature that was absolutely needed in our products (and very likely in yours too). For example we can now easily query all the wings for each machine in Robocraft. This means that we could potentially create a Svelto.Tasks taskroutine for each machine inside the WingsPhysicEngine and stop a specific task if all the wings of a specific machine are shot off. Previously there wasn’t a way to be able to split wings per machine unless custom datastructures were used inside the engines. The new function has the signature: entityFactory.BuildEntityInGroup<SimpleStructEntityDescriptor>(entityID, groupID) and can be used both for struct Entities and class EntityViews.
  • Entities can also be moved between groups. This was a last minute idea and I need to experiment more with it.
  • EnginesRoots must never be hard referenced. Previously I made two mistakes: first hard referencing it inside the SubmissionNodeScheduler (now called SubmisisonEntityViewScheduler) and second letting it be passed around through the IEntityFactory interface. Now an IEntityFactory wrapper must be generated from the EngineRoot.
  • A IRemoveComponent cannot be implemented with a custom implementor any more. Just use it and don’t worry to pass a RemoveImplementor among the other implementors (it will be ignored as it is created by the framework now).
  • The framework is now more rigid and output more warnings in case of misuse.
  • All the previous framework engines, except for the SingleEntityViewEngine and MultiEntityViewsEngine, have been deprecated. An engine can inherit either from one or the another and/or implement IQueryingEntityViewEngine (which is an IEngine too now)
  • I am adding comments all over the code, but I will add more while I write the new articles to come.

then optimizations:

  • Building an entity can be up to 3.3 times faster than in svelto.ECS. However remember that entities should be sporadically or never built during the execution of the gameplay. Entities should always be prebuilt and enabled/disabled when needed. This is even more effective than pooling them, so design them properly. Below a table to show how long takes to build 256k entities as class with Unity 2017.3 (building entities as struct is an order of magnitude faster).
    Version Platform Time(MS)
    1.0 .net 2.0 903
    2.0 .net 2.0 344
    2.0 .net 4.6 260(!)
    2.0 .net Core UWP 203
    2.0 .net Core IL2CPP 288

    if you wonder while IL2CPP is a bit slower, is because I cannot dynamically create code that allows avoid Reflection functions, however the c++ implementation of the reflection is much faster so no much trouble there.

  • Thanks to the new feature to move entities between groups, you can create a group to store the disabled entities, so that you can retrieve a disabled entity yourself to be reused later.
  • Several optimizations here and there, especially to reduce allocations.

and breaking changes (at least the ones I took note of):

  • The BuildEntity signature is totally different and will need to be explained in an another article.
  • IRemoveComponent was breaking a not enforced (yet) rule of Svelto.ECS which is that a component cannot hold references to other classes (except Unity ones at the moment). This is because a component must be seen a data container and cannot be used to call external functions. Thus, the method to remove an entity has changed to _entityFunctions.RemoveEntity<SimpleEntityDescriptor>(entityView.ID);
  • Mass Renames (hope I didn’t get any wrong, I will fix later in case):
    • rename IQueryableNodeEngine into IQueryingEntityViewEngine
    • rename IEngineNodeDB into IEngineEntityViewDB
    •  rename NodeWithID into EntityView
    •  .QueryNode< to .QueryEntityView<
    • .QueryNodes< to .QueryEntityViews<
    • .TryQueryNode( to .TryQueryEntityView(
    • all the nodesDB must be renamed to entityViewsDB
    •  MultiNodesEngine to MultiEntityViewsEngine
    • SingleNodeEngine to SingleEntityViewEngine
    • UnitySumbmissionNodeScheduler is UnitySumbmissionEntityViewScheduler
    • INodeBuilder to IEntityViewBuilder (you should use the GenericEntityDescriptor/MixedEntityDescriptor though)
    • NodeBuilder to EntityViewBuilder  (you should use the GenericEntityDescriptor/MixedEntityDescriptor though)
  •  ICallBackOnAddEngine doesn’t exist anymore and it has been merged into the IQueryingEntityViewEngine interface. This means that the Ready function must be always implemented in these cases.

N.B.: As long as Svelto.ECS 2.0 stays in to alpha state, do not use in a production environment. It still needs to be heavily tested and I will need to write some unit tests too. However you are invited to start experimenting with it and leave some feedback.

8 thoughts on “Svelto.ECS 2.0 is production ready

  1. Would you refactor example to have Input not embedded in the systems? I want to see how would input would be done if it enter the system as entities.

    How would you go about implementing FSM as a base engine of the game and all other engines to respect it? Can you turn on/off engines somehow? Maybe create engines per state and enable disable them with state changes.

    1. How to manage the input is a common question. I don’t want to make the example code more complicated than it is to not confuse new people and also because input management is a problem regardless the paradigm you adopt. How would have you solved it in Unity if you don’t want to use the Input static class?
      A common approach is to create an InputEngine, maybe specialized for each Entity, that takes as input an EntityInputView which has components that are read by other views as gameplay states. For example, the Input engine read the action linked to the jump, and it will set the node component isjumping boolean to true.
      There isn’t a suggested way though, it’s up to you.
      Similar reasoning for state machines. We don’t use state machines that much, but it’s up to you to find a way to make them fit with the ECS paradigm. As long as you follow the ECS rules of the Svelto framework, you shouldn’t be wrong.
      BTW, I really want to thank you for the interest you are showing, please keep it coming.

      1. What I want to see from any type of highly decoupled opinionated framework is ways to do the most common things in decoupled ways. Why use this highly decoupled ECS if we are going to have input embedded inside Engines and thus not unit-testable? I know there are ways to do it but I disagree that “make the example code more complicated”, it is already complicated enough. Showing how input should be handled will not make it more complicated in my opinion.

        1. Yes that’s obvious, but the example shows just one way to use the ECS framework. Under my point of view I see an ECS framework more like an extension of the language than a framework (and that’s why I often speak of paradigm), as it forces to think different ways to approach classic problems. So even if I am trying to make it as rigid as possible, it doesn’t mean that the user cannot fall in to the classic coding errors. Obviously you say that I shouldn’t promote bad coding…that’s a good point.

          Well you made me think, because so far I have always seen the implementors as bridges between the ECS framework and the hosting platform, but in the case of static classes is a bit awkward as they could be used everywhere. In the case of the Input class is even worse, as it needs to be ticked.

          So far I would have thought to use an engine to wrap the functionality, but it’s something that I will think better maybe with a dedicated example.

        2. heads up about the changes almost finished. You can already give a look at the solutions you were seeking.

  2. Both Svelto.ECS and Svelto.Tasks has Utilities. Including both projects as submodules leads to Utilities duplication. Maybe they should be a submodule also?

      1. if Svelto.ECS and Svelto.Tasks have the same submodule, you will need to decide which one to initialize anyway. I wonder if there is a better solution.

Leave a Reply