Search Unity

[Open Source] Svelto.ECS - Lightweight Entity Component System for C# and Unity

Discussion in 'Works In Progress - Archive' started by sebas77, Oct 28, 2017.

  1. sebas77

    sebas77

    Joined:
    Nov 4, 2011
    Posts:
    1,642
    remembers ECS implementations vary a lot. The basic theory is clear, but many practices are not standard, especially the communication between systems has been historically a clear pain as it doesn't go along very well with the theory itself that systems must be encapsulated.

    Unity ECS has been designed around thread safety and performance and to work together with the Unity Jobs System. You can achieve the same with Svelto.ECS using EntityStructs and Svelto.Tasks, but the Survival examples only uses EntityViews.

    working with structs and thread safety in mind is very limiting and people must really fundamentally change their way to code even more than what Svelto makes you do with the simpler EntityViews. Also let's be clear, what Unity wants to achieve ideally, which is 100% multi-threaded code, without even the concept of main-thread, is what we all should end up to, but it must be achieved gradually.

    Because of the use of the Implementors the entity views are not implicity thread-safe, but the point is that once you get at this advanced level of understanding, you will see clearly how and when using EntityStructs and how and when using EntityViews. I will probably write some advanced articles in future regarding all this.

    However the truth is that all this stuff is still super advanced for the majority of the unity users, therefore Svelto is designed to introduce these users to the concepts without being forced to face the more advanced ones. It's also designed to use the advanced features if you know what you are doing.

    Now forget what I just wrote, I wrote it just to let you understand that what Unity wants to achieve is different to what Svelto wants to achieve. I want to use ECS to improve the quality of the code, Unity wants to use ECS to make your code faster.

    Let's go back to the first question:ECS is diametrically opposed to Object Oriented Design. You don't need to use inheritance at all with it. In Svelto.ECS you achieve it designing your behaviors in levels of abstractions. In the survival example, the HealthEngine is abstracted, because it can work either for Enemies and Players. You reuse the same code in the sense that you can use the same engine for both entity types. HealthEngine MUST NOT know if the entity is a player or an enemy, it will know only that the entity has health.

    I hope this makes sense to you!
     
    Last edited: Mar 15, 2018
    andreiagmu and Kuptsevych-Yuriy like this.
  2. MadBanny

    MadBanny

    Joined:
    Jul 23, 2014
    Posts:
    3
    Hello Sebastiano! First of all thank you for all work you did.
    For now i'm trying recreate Unity Tanks! example for Svelto.ECS, but I can't understand some things for example:
    • How I can change game state (Menu state, Play state, Game over state)
    • How I can implement more then 6 EntityViews in EntityDescriptor
    Can you give me a hint? Thanks.
    P.S.
    Sorry for my english because i'm from Russia :)
     
  3. sebas77

    sebas77

    Joined:
    Nov 4, 2011
    Posts:
    1,642
    Hello MadBanny!!!!

    thanks for the question. The first question is a bit too generic and hard to answer in a short way, however always keep in mind that states must always be entity components value, now the problem is then to identify the entities and their components. Remember an entity must always be identified from a game design domain element, never create meaningless abstract entities just for the purpose to store your data!

    second question is simpler, when the provided EntityDescriptors are not sufficient anymore you can just create your own:

    Code (CSharp):
    1. namespace Simulation.BattleRoyale.Deployment
    2. {
    3.     public class LanderEntityDescriptor : IEntityDescriptor
    4.     {
    5.         static LanderEntityDescriptor()
    6.         {
    7.             entityViewBuilders = new IEntityViewBuilder[]
    8.             {
    9.                 new EntityViewBuilder<LanderEntityView>(),
    10.                 new EntityViewBuilder<LanderVfxEntityView>(),
    11.                 new EntityViewBuilder<LanderGuiEntityView>(),
    12.                 new EntityViewBuilder<TopSpeedNode>(),
    13.                 new EntityViewBuilder<MovementStatsNode>(),
    14.                 new EntityViewBuilder<CameraRelativeTurnDampingNode>()
    15.             };
    16.         }
    17.  
    18.         public IEntityViewBuilder[] entityViewsToBuild
    19.         {
    20.             get { return entityViewBuilders; }
    21.         }
    22.  
    23.         public static readonly IEntityViewBuilder[] entityViewBuilders;
    24.     }
    25. }
    26.  
     
    andreiagmu and MadBanny like this.
  4. MadBanny

    MadBanny

    Joined:
    Jul 23, 2014
    Posts:
    3
    Thanks for your reply. But, in fact, I have many questions for you. I think that Svelto.ECS is more than a framework, and I really want to understand it correctly. Can you provide more use cases of Svelto for Unity in your blog or github when you have time for it? I also end the translation of github wiki into Russian and will soon post it. upload_2018-3-31_1-17-15.png
     
  5. sebas77

    sebas77

    Joined:
    Nov 4, 2011
    Posts:
    1,642
    I am working on some refactoring and new features now, at the same time I am improving the Survival example even more after I received some feedback.

    Next there will be more articles on cache friendly code using structs and multithread. These will take more time.

    I didn't watch the Unity videos regarding ECS, but I am sure most of the theory they explain applies to Svelto as well. Then choosing on or the other is just a matter of preferences!
     
  6. MadBanny

    MadBanny

    Joined:
    Jul 23, 2014
    Posts:
    3
    Hello Sebastiano again!
    Can you show me a simple pool of entities? Or how I can create it?
     
  7. sebas77

    sebas77

    Joined:
    Nov 4, 2011
    Posts:
    1,642
    The simplest thing to do is to use groups, for example you can have a "disabled" group and an "enabled" group. Swap entities between groups, your engines will iterate only over the enabled group.

    Note: I will write an article about the build entity performance and the new features I am introducing to make it even faster
     
    Last edited: Apr 15, 2018
    MadBanny likes this.
  8. zhuchun

    zhuchun

    Joined:
    Aug 11, 2012
    Posts:
    433
    Greetings! We're looking for a framework to make a client-server game. But you know, the server-side code has nothing to do with the Unity component system, which means we probably need to write an authority game system for server-side and then re-write it again in Unity style, that's not ideal.

    If I understand it right, we can write ECS codes in Unity and copy them to the server-side w/o rendering systems. However, I'm not sure about if this idea works and which ECS frameworks could run on both sides. Would you please make a brief introduction to us?
     
  9. sebas77

    sebas77

    Joined:
    Nov 4, 2011
    Posts:
    1,642
    Hello there, an ECS approach would help you to write uncoupled code much more easily for sure. Svelto.ECS has been designed to be platform agnostic and the Survival Example is written now with testable engines, which theoretically could be just used as they are on other platforms.

    By design, the implementors are the bridge between the platform and the code so, in this sense, Svelto.ECS can help you.

    I guess your code is not physic based, as if it would have been, you would need to use unity component on the server too.

    In conclusion ECS won't enable you to do anything that you can't do already, but it would make it simpler and more intuitive. Regarding if suggest you to use Svelto.ECS or not, well that depends by you. Svelto.ECS has different design goals than Unity ECS, but even so, you won't loose the "performance by default" option as Svelto.ECS can use cache friendly entity as structs too.

    P.S.: Svelto.ECS is in continuous development and between major releases there could be breaking changes.
     
    zhuchun likes this.
  10. sebas77

    sebas77

    Joined:
    Nov 4, 2011
    Posts:
    1,642
  11. LazeRs

    LazeRs

    Joined:
    Feb 21, 2015
    Posts:
    13
    Hi, sebas!
    Why, after .BuildEntity , i can't find it in IEntityViewsDB (at same func )?

    I have entity's - rooms. Room's come from server - with N count. Each room have enum building type. My solution:

    In my room creation engine :
    Code (CSharp):
    1.  for (int i = 0; i < 100; i++)
    2.             {
    3.                 var go = factory.Build(prefab);
    4.                 var implementors = new List<IImplementor>();
    5.                 int type = UnityEngine.Random.Range(0, 100);
    6.                 implementors.Add(new RoomImplementor(type));
    7.                 _entityFactory.BuildEntity<RoomEntityDescriptor>(go.GetInstanceID(), implementors.ToArray());
    8.             }
    And in my building creation engine:
    Code (CSharp):
    1. using System.Collections.Generic;
    2. using Svelto.Context;
    3. using Svelto.ECS;
    4. using Svelto.ECS.Example.Survive;
    5. using UnityEngine;
    6.  
    7. namespace Assets
    8. {
    9.     public class BuilingEngine : SingleEntityEngine<RoomEntityView>, IQueryingEntityViewEngine
    10.     {
    11.         public IEntityViewsDB entityViewsDB { get; set; }
    12.         private readonly Dictionary<int, int> _dTypeToViewId;
    13.         private readonly IEntityFactory _entityFactory;
    14.         private readonly GameObjectFactory _factory;
    15.  
    16.  
    17.         public BuilingEngine(IEntityFactory entityFactory, GameObjectFactory factory)
    18.         {
    19.             _dTypeToViewId = new Dictionary<int, int>();
    20.             _entityFactory = entityFactory;
    21.             _factory = factory;
    22.         }
    23.  
    24.         public void Ready() { }
    25.  
    26.         void _createBuilding(RoomEntityView entityView)
    27.         {
    28.             int type = entityView.RoomTypeComponent.Type;
    29.             bool buildingExists = false;
    30.  
    31.  
    32.  
    33.             //Can't use entityViewsDB - always don't found any BuildingEntityView
    34.  
    35.             //int count = 0;
    36.             //var allBuildingEntityViews = entityViewsDB.QueryEntities<BuildingEntityView>(out count);
    37.             //Debug.Log(allBuildingEntityViews.Length);
    38.             //foreach (var building in allBuildingEntityViews)
    39.             //{
    40.             //    if (building.RoomTypeComponent.Type == type)
    41.             //    {
    42.             //       //We find BuildingEntityView with req type
    43.             //        buildingExists = true;
    44.             //        break;
    45.             //    }
    46.             //}
    47.  
    48.  
    49.             //another way:
    50.  
    51.             buildingExists = _dTypeToViewId.ContainsKey(entityView.ID.entityID);
    52.             Debug.Log(_dTypeToViewId.Keys.Count);
    53.  
    54.             if (!buildingExists)
    55.             {
    56.                 GameObject prefab = new GameObject(""); // will be some prefab
    57.  
    58.                 var go = _factory.Build(prefab);
    59.                 var implementors = new List<IImplementor>();
    60.                 implementors.Add(new BuildingImplementor(type, entityView.ID.entityID));
    61.                 _entityFactory.BuildEntity<BuildingEntityDescriptor>(go.GetInstanceID(), implementors.ToArray());
    62.                 _dTypeToViewId.Add(entityView.ID.entityID, go.GetInstanceID());
    63.             }
    64.             else
    65.             {
    66.                 //We find BuildingEntityView ID with req type
    67.             }
    68.  
    69.  
    70.  
    71.  
    72.  
    73.         }
    74.  
    75.         protected override void Add(ref RoomEntityView entityView)
    76.         {
    77.             _createBuilding(entityView);
    78.         }
    79.  
    80.         protected override void Remove(ref RoomEntityView entityView)
    81.         {
    82.  
    83.         }
    84.     }
    85. }
    86.  

    but i think it's wrong way, what is right way to do this?
     
  12. sebas77

    sebas77

    Joined:
    Nov 4, 2011
    Posts:
    1,642
    Hello,

    Before to start answering your question, have you got the time to have a look at the latest version of Svelto.ECS? The example has been updated as well. The new Svelto.ECS promote the use of structs instead of classes and the option to not use implementors but just pure EntityStructs in case implementors are not necessary. I explained my thoughts in my last article: http://www.sebaslab.com/svelto-ecs-2-5-and-allocation-0-code/.

    let's dig in your issue:

    you are right to feel uncomfortable, unless strictly necessary you shouldn't use custom data structure. I may see the use for special custom data structures, like octrees, to hold ids, but they are exceptional cases.

    I have some troubles to understand the logic of the code above. I will ask some questions that may not be relative to your problem but it's for me to understand better:

    you are build 100 room entities and 100 gameobjects, but there is no link between the gameobjects and the entities except for the gameobject id. I would have expected at least one implementor to be a monobehaviour, otherwise why using gameobjects at all?

    Every time a room entity is created, you want to build a building entity, this entity has another ID which is the unique ID of another gameobject just created. Knowing that the gameobject IDs are always unique, why do you check if a building entity has been already created? The room entity ID will be always different so the add callback won't be called twice with the same ID.

    Beside this, you may also check the groups. Instead to let the Build Entity know the ID of the Room Entity, you could build a Room Entity and a Building Entity with exactly the same ID, but in two different groups. This is allowed.

    Although you must never assume when EntityViews are known by engines, in your case all the 100 Room entities will be added on the same frame, this means that up to the moment, no Build Entity were created yet, so the entityviewDB won't find any.

    Add and Remove callbacks should be used sparingly. They are especially designed to setup listeners for the DispatchOnSet and DispatchOnChange data binding systems. All the other cases could potentially be solved just polling the entity view DB until data is found using a Svelto.Task coroutine.

    I changed the Svelto example to use less and less the Add and Remove engine callbacks.

    Please add more questions if I didn't help you enough.
     
    andreiagmu likes this.
  13. LazeRs

    LazeRs

    Joined:
    Feb 21, 2015
    Posts:
    13
    Thank you, for answer sebas
    • It was just simple example to describe my problem. In my main proj of course i have monobehaviour implementers etc.
    • My fault, of course i mean :
      Code (CSharp):
      1.  buildingExists = _dTypeToViewId.ContainsKey(entityView.RoomTypeComponent.Type);
      My logic => I create rooms => i check - have i building, who has same type as room => if true just put (building.setParent(room)) room in building , if false create required building and out room in new building .
    • I need get GameObject (to set parent) , so i'm modify game object factory, and now can get game object by id (InstanceID = entity view id). I'm not sure that this is the right way. But to transfer game objects exactly wrong way
    • About - Add and Remove callbacks - ofcource, but always use Svelto.Task coroutine - wrong? it's like a check in the update, instead of using the callback
     
  14. sebas77

    sebas77

    Joined:
    Nov 4, 2011
    Posts:
    1,642
    oh I see now, you want to create N rooms and every room can have a building, but these buldings can be repeated, so you want to reuse an already create object in case the room belongs to a building already created, right?

    what I suggest you to do is to map the Type in to an integer and use another group for the buildings. Like:

    _entityFactory.BuildEntity(roomID, roomImplementors)
    _entityFactory.BuildEntity(buildingType, buildinggroupID, buildingimplementors);

    then you can do if (_entitiesDB.TryQueryEntityView<RoomBuildingEntityView>(new EGID(buildingType, buildinggroupID), out entityView) != == null) reuse entityView

    the good thing about it is that the implementors reused will belong to the gameobject you want to reuse, so you won't ever need to use gameobjects directly. You can use the entity view to disable the gameobject and enable it again and whatever you want to do

    Regarding the problem that the new entity views are not in the database after the add callback, I actually checked the code and I wrote the submission logic in order to be able to find the nodes after they are built inside an Add callback. Maybe I broke the logic, I am going to write a test now and check

    you are right about the polling vs pushing. In this case your solution is probably simpler.
     
    andreiagmu and LazeRs like this.
  15. sebas77

    sebas77

    Joined:
    Nov 4, 2011
    Posts:
    1,642
    Yeah got it, all the add of the same entities of the same type added in one frame must be submitted before having the chance to update the entities db. I may change this, I will think about it.
     
  16. LazeRs

    LazeRs

    Joined:
    Feb 21, 2015
    Posts:
    13
    absolutely

    Will they appear in the _entitiesDB in that frame?

    I think problem is in my logic. I don't have to create (and check to reuse) entity in one frame, but I see no other way out
     
  17. sebas77

    sebas77

    Joined:
    Nov 4, 2011
    Posts:
    1,642
    While I think if changing the code to make it work, let's think about different solutions. To make it work right now, one possible solution can be this:

    - build the room entity in a special group, I assume you need to use the room entities later, so build the entity twice, the first one in the standard group, the second copy in a special group.

    - create an engine that with a svelto task loop around the special group, if it finds any Room Entity, build the relative Building and remove the entity from the special group. It's true you will poll the special group for ever if you don't know when the room entities are done to be created, but it will poll an empty group so it's OK. If you know when the rooms are finished to create, you can interrupt the loop using yield Break.It or ITaskRoutine stop.
     
    LazeRs likes this.
  18. LazeRs

    LazeRs

    Joined:
    Feb 21, 2015
    Posts:
    13
    Thank you a lot! I will try do this.
     
  19. sebas77

    sebas77

    Joined:
    Nov 4, 2011
    Posts:
    1,642
    cool you may not even need to build the room entity twice, so that you can keep it simpler.

    Build once the room entity only in the special group, the loop fetches from the special group, build s the building entity and swaps the room entity group to the normal one. In this way you can start to query the room entities from the normal group only when the building has been built.
     
    LazeRs likes this.
  20. LazeRs

    LazeRs

    Joined:
    Feb 21, 2015
    Posts:
    13
    StandardSchedulers.physicScheduler run more than one time per frame ?
    Somethimes i build "Building" entity before previos "Building" entity added to entitiesDB

    Code (CSharp):
    1.  
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using Assets.Script.DataSources;
    5. using Assets.Script.EntityDescriptors;
    6. using Assets.Script.EntityStructs;
    7. using Assets.Script.EntityViews;
    8. using Assets.Script.Implementers;
    9. using Assets.Script.Implementers.Menu;
    10. using Svelto.Context;
    11. using Svelto.ECS;
    12. using Svelto.Tasks;
    13.  
    14. namespace Assets.Script.Engines
    15. {
    16.     public class BuildingCreationEngine : SingleEntityEngine<BuildingCreationEntityStruct>, IQueryingEntitiesEngine
    17.     {
    18.  
    19.         public IEntitiesDB entitiesDB { get; set; }
    20.  
    21.         readonly ITaskRoutine _taskRoutine;
    22.         private readonly MyGameObjectFactory _gameObjectFactory;
    23.         private readonly IEntityFactory _entityFactory;
    24.         private readonly IEntityFunctions _entityFunctions;
    25.  
    26.         public BuildingBuildingEngine(MyGameObjectFactory factory, IEntityFactory entityFactory, IEntityFunctions entityFunctions)
    27.         {
    28.             _gameObjectFactory = factory;
    29.             _entityFactory = entityFactory;
    30.             _entityFunctions = entityFunctions;
    31.  
    32.             _taskRoutine = TaskRunner.Instance.AllocateNewTaskRoutine().SetEnumerator(_checkForBuilding()).SetScheduler(StandardSchedulers.physicScheduler);
    33.         }
    34.  
    35.         public void Ready()
    36.         {
    37.             //Start
    38.         }
    39.  
    40.  
    41.  
    42.         private IEnumerator _checkForBuilding()
    43.         {
    44.             int roomGroupId = DataService.ROOM_SPECIAL_GROUP;
    45.             int roomDefaultGroupId = DataService.ROOM_DEFAULT_GROUP;
    46.             var _menuData = DataService.MenuDataRequest();
    47.  
    48.             while (true)
    49.             {
    50.  
    51.                 int count;
    52.                 var allrooms = entitiesDB.QueryEntities<roomsEntityView>(roomGroupId, out count);
    53.                 Utility.Console.Log("allrooms count " + count);
    54.                 if (count != 0)
    55.                 {
    56.                     var room = allrooms[0];
    57.                     var type = room.BuildingTypeComponent.BuildingType;
    58.                     _entityFunctions.SwapEntityGroup(room.ID, roomDefaultGroupId);
    59.                     int BuildingsCount;
    60.                     var allBuildings = entitiesDB.QueryEntities<BuildingEntityView>(type, out BuildingsCount);
    61.  
    62.                     if (BuildingsCount == 0)
    63.                     {
    64.  
    65.                         var prefabRectNested = _menuData.BuildingBotPrefabRect;
    66.                         prefabRectNested.ParentScrollRect = _menuData.BuildingParentScrollRect;
    67.                         var go = _gameObjectFactory.Build(prefabRectNested.gameObject, _menuData.BuildingParent);
    68.                         int BuildingId = go.GetInstanceID();
    69.                         List<IImplementor> implementors = new List<IImplementor>();
    70.                         go.GetComponentsInChildren(implementors);
    71.                         implementors.Add(new BuildingTypeImplementor(type));
    72.                         _entityFactory.BuildEntity<BuildingEntityDescriptor>(BuildingId, type, implementors.ToArray());
    73.                         room.ParentViewIdComponent.ID = BuildingId;
    74.                         yield return null;
    75.                     }
    76.                     else
    77.                     {
    78.                         room.ParentViewIdComponent.ID = allBuildings[0].ID.entityID;
    79.                     }
    80.  
    81.                  
    82.                 }
    83.  
    84.                 yield return null;
    85.             }
    86.         }
    87.  
    88.  
    89.  
    90.         protected override void Add(ref BuildingCreationEntityStruct entityView)
    91.         {
    92.             Utility.Console.Log("add BuildingCreationEntityStruct " + entityView.ID);
    93.             _taskRoutine.Start();
    94.         }
    95.  
    96.         protected override void Remove(ref BuildingCreationEntityStruct entityView)
    97.         {
    98.             _taskRoutine.Stop();
    99.         }
    100.  
    101.     }
    102. }
    103.  
     
    Last edited: Jun 19, 2018
  21. sebas77

    sebas77

    Joined:
    Nov 4, 2011
    Posts:
    1,642
    not bad not bad, it's a bit of a sore to the eye, but it's not your fault. The Physic runner should be used only for physic stuff. Physic update can run more than once at frame if the frame rates drops under the physic frame rate, this is standard unity behaviour. One yield return null at the end is enough.
    Don't worry about using the coroutine runner (default one), it won't make any difference to the app.
     
    LazeRs likes this.
  22. sebas77

    sebas77

    Joined:
    Nov 4, 2011
    Posts:
    1,642
    If you want to make the code a bit more readable, you can encapsulate all the creation code into factories that you can inject in the engine.
    all the following can stay inside a factory:

    1. var prefabRectNested = _menuData.BuildingBotPrefabRect;
    2. prefabRectNested.ParentScrollRect = _menuData.BuildingParentScrollRect;
    3. var go = _gameObjectFactory.Build(prefabRectNested.gameObject, _menuData.BuildingParent);
    4. int BuildingId = go.GetInstanceID();
    5. List<IImplementor> implementors = new List<IImplementor>();
    6. go.GetComponentsInChildren(implementors);
    7. implementors.Add(new BuildingTypeImplementor(type));
    8. _entityFactory.BuildEntity<BuildingEntityDescriptor>(BuildingId, type, implementors.ToArray());
    However you can also see the engine itself as a factory, so in this case you can just add more functions to make the code a bit more readable.
     
    Last edited: Jun 19, 2018
    LazeRs likes this.
  23. LazeRs

    LazeRs

    Joined:
    Feb 21, 2015
    Posts:
    13
    Thanks again! You help to understand svelto much faster
    But i have couple question
    • Why add func (from SingleEntityEngine) run with delay?
    • And why in my issue entitiesDB is empty ?
    Code (CSharp):
    1. using Assets.Script.DataSources;
    2. using Assets.Script.EntityViews;
    3. using Assets.Script.Enums;
    4. using Svelto.Context;
    5. using Svelto.ECS;
    6. using Svelto.Utilities;
    7. using UnityEngine;
    8.  
    9. namespace Assets.Script.Engines
    10. {
    11.     public class RoomEngine : SingleEntityEngine<RoomsEntityView>, IQueryingEntitiesEngine
    12.     {
    13.  
    14.         public IEntitiesDB entitiesDB { get; set; }
    15.         private readonly MyGameObjectFactory _gameObjectFactory;
    16.         private readonly IEntityFactory _entityFactory;
    17.  
    18.  
    19.  
    20.         public RoomEngine(MyGameObjectFactory factory, IEntityFactory entityFactory)
    21.         {
    22.             _gameObjectFactory = factory;
    23.             _entityFactory = entityFactory;
    24.         }
    25.  
    26.  
    27.  
    28.         public void Ready()
    29.         {
    30.  
    31.         }
    32.  
    33.  
    34.  
    35.  
    36.  
    37.  
    38.         private void _setRoomParentByViewId(int RoomId, int sectionId)
    39.         {
    40.             var sectionGameObject = _gameObjectFactory.FindById(sectionId);
    41.             if (sectionGameObject == null) { return; }
    42.             var parentObj = sectionGameObject.transform.GetChild(sectionGameObject.transform.childCount - 1);
    43.             var Room = _gameObjectFactory.FindById(RoomId);
    44.             if (Room == null) { return; }
    45.          
    46.             Room.transform.SetParent(parentObj);
    47.             Room.transform.localScale = Vector3.one;
    48.             Room.transform.localPosition = Vector3.zero;
    49.  
    50.             Debug.Log(entitiesDB); // not null
    51.             Debug.Log(entitiesDB.HasAny<RoomsEntityView>()); // false
    52.             Debug.Log(entitiesDB.HasAny<SectionEntityView>()); // false
    53.          
    54.          
    55.             entitiesDB.ExecuteOnEntity<RoomsEntityView>(new EGID(RoomId), _setRoomFrameType);         // not working , entitiesDB empty
    56.  
    57.          
    58.         }
    59.  
    60.         // Set frame type like building
    61.         private void _setRoomFrameType(ref RoomsEntityView Room)
    62.         {        
    63.             int type = Room.SectionTypeComponent.SectionType;
    64.             int buildingCount;
    65.      
    66.             var allbuilding = entitiesDB.QueryEntities<SectionEntityView>(type, out buildingCount);
    67.             var section = allbuilding[0]; // Always just 1 elemenet in allbuilding
    68.          
    69.             Room.FrameComponent.Type = section.FrameComponent.Type;
    70.         }
    71.  
    72.  
    73.  
    74.  
    75.         protected override void Add(ref RoomsEntityView entityView)
    76.         {
    77.             Debug.Log("entityView add");
    78.  
    79.             entityView.ParentViewIdComponent.OnParentIDSet.NotifyOnValueSet(_setRoomParentByViewId);
    80.          
    81.             if (entityView.ParentViewIdComponent.ID != -1) // I dont know why, but first "update" in BuildingCreationEngine faster than that func
    82.             {
    83.                 _setRoomParentByViewId(entityView.ID.entityID, entityView.ParentViewIdComponent.ID);
    84.             }
    85.          
    86.  
    87.         }
    88.  
    89.         protected override void Remove(ref RoomsEntityView entityView)
    90.         {
    91.             Debug.Log("entityView Remove");
    92.  
    93.         }
    94.  
    95.  
    96.     }
    97. }
    98.  
     
  24. sebas77

    sebas77

    Joined:
    Nov 4, 2011
    Posts:
    1,642
    Hello Lazers,

    just an advice first, when you name your engine, do not use just the entity name, but also the behaviour you want to apply to that set of entities through the engine. For example RoomEngine is not ok because it let me understand just that the entities to use are Room, but it doesn't tell me what you are going to do on the Room entities. RoomSetupEngine is a better example.

    I also suggest to use more focused namespaces. The namespace usually should finish with the entity type that you are going to handle with that engine. I know that this goes against the c# convention, but in Svelto.ECS namespaces are a good tool to understand if your code is correctly designed in terms of modularity.

    Assets.Script.Room.Engines
    Assets.Script.Room.Entityviews

    in this way you are going to set some rules about which engines must be able to use which entities. This is a bit advanced as concept and it will make sense to you after you get more familiar with svelto.

    The point about engines and entity views is that you must never assume when the entity will be submitted to the database. Your engine must always work regardless if in the database there are going to be 0 entities, 1 entity or N entities.

    The Ready function is there to let you know when the entitiesDB is different than null. It will always be different than null inside the Add function. so the Ready function is usually where you start using tasks that need to use the entitydb.

    ExecuteOnEntity is a temporary solution needed to use entity structs. this function will be deprecated when I can finally add the c# 7 by ref keyword. It's about being able to use Entities as struct. I don't want to confuse you on this, but it's related to use mutable structs in a cache friendly way. If you don't need this, you can use the normal QueryEntity* to get your entity. It's ok what you have done, but I just want to be sure you get the differences between EntityView (deprecated), EntityViewStructs and EntityStructs.

    I am not sure I understood your problem, but in BuildingCreationEngine you should start the task inside the Ready function and not inside the constructor.
     
    Last edited: Jun 20, 2018
    andreiagmu and LazeRs like this.
  25. LazeRs

    LazeRs

    Joined:
    Feb 21, 2015
    Posts:
    13
    I just don't understand, look at my logic:

    create room => create building ( there i can see all entities in entitiesDB)=> set params room => there i can't see ANY (room / building) entity

    my func _setRoomParentByViewId called, when i have at least one room and building entity, but entitiesDB say me, that i have not.
     
  26. LazeRs

    LazeRs

    Joined:
    Feb 21, 2015
    Posts:
    13
    I understand this point, but i have a lot of sequential logic. Create one huge engine is not good idea. I'm decide use DispatchOnSet , because, i think it's best way (at that point) to connect engines. And if i have at engine 1 some view at entities DB and push some data to engine 2, I expect, that engine 2 (in entities DB) have that data
     
    Last edited: Jun 20, 2018
  27. sebas77

    sebas77

    Joined:
    Nov 4, 2011
    Posts:
    1,642
    Hey Laz,

    Yes you need to split engines, you got it right, as every engine must have (ideally) one responsibility only. However if you now you need a sequence, you need to use the sequencer. The DispatchOnSet and OnChange should always been used as data pushing (binding) not to trigger sequences, this is what the sequencer is for.

    this will improve your code, but not solve your problem. let me see, I will try to figure out the logic:

    I need to know where you create the RoomEntities to understand it. Can you share the whole project somewhere, like on github or something?

    Code (CSharp):
    1.  if (entityView.ParentViewIdComponent.ID != -1) // I dont know why, but first "update" in BuildingCreationEngine faster than that func
    2.             {
    3.                 _setRoomParentByViewId(entityView.ID.entityID, entityView.ParentViewIdComponent.ID);
    4.             }
    this code should not be needed, but the problem there is that you are assuming that the add of the BuildingCreationEngine is called after the Add of the RoomEngine. This is the deal with svelto: to keep engines always modular, in such a way you can plug them in and out to facilitate refactoring, engines must not know each other. Differently than what Unity ECS is doing, is very important that you don't know the order things happening, including when entities are added in engines.

    The only tool you have to guarantee order of execution, is the sequencer.

    so if I understood correctly, it may happen now that the BuildingCreationEngine add is called before the RoomEngine add so that the ID is set before you setup the listener. There are hackish way to solve this (you may add a yield return null at the start of the task to wait one frame), but really you shouldn't assume anything and therefore you cannot add workarounds according assumptions. The way to solve it is the sequencer.

    all that said this:
    entitiesDB.ExecuteOnEntity<RoomsEntityView>(new EGID(RoomId), _setRoomFrameType); // not working , entitiesDB empty

    is very weird I wouldn't expect it. If you share the code, I will debug it and see what's going on.
     
    LazeRs likes this.
  28. LazeRs

    LazeRs

    Joined:
    Feb 21, 2015
    Posts:
    13
    https://github.com/TestLazeR/TestSvelto
     
  29. LazeRs

    LazeRs

    Joined:
    Feb 21, 2015
    Posts:
    13
    Ok, i have something:
    in one func:
    entitiesDB.HasAny<RoomEntityView>(); // = false
    entitiesDB.QueryEntities<RoomEntityView>(out count); //= empty
    entitiesDB.QueryEntities<RoomEntityView>(type,out count); //= NOT empty

    So, if i set group to entity, i can;t find it without that group?
     
    Last edited: Jun 21, 2018
  30. sebas77

    sebas77

    Joined:
    Nov 4, 2011
    Posts:
    1,642
    yes, you must query through EGID (id + group id) if the entity is not in the standard id. It's always through EGID, the funcitons without EGID are just a easier way that assume that the entity is in the standard group.

    Does this solve your problem?
     
  31. LazeRs

    LazeRs

    Joined:
    Feb 21, 2015
    Posts:
    13
    Yes, sorry about that
     
  32. LazeRs

    LazeRs

    Joined:
    Feb 21, 2015
    Posts:
    13
    Code (CSharp):
    1.  
    2.             _entityFactory.BuildEntity<HouseBuildingEntityDescriptor>(101, null);
    3.             yield return new WaitForSecondsEnumerator(2);
    4.             _entityFunctions.RemoveEntity(101);
    5.             yield return new WaitForSecondsEnumerator(2);
    6.             _entityFactory.BuildEntity<HouseBuildingEntityDescriptor>(101, null);
    i have error, FasterDictionaryException: Key already present.

    Why i can't recreate entity with same id?
     
  33. sebas77

    sebas77

    Joined:
    Nov 4, 2011
    Posts:
    1,642
    looks like you found a bug, can you open an issue on github? I will fix it asap
     
  34. LazeRs

    LazeRs

    Joined:
    Feb 21, 2015
    Posts:
    13
  35. sebas77

    sebas77

    Joined:
    Nov 4, 2011
    Posts:
    1,642
    ops, I didn't notice you opened a github issue already, that's all I needed. thanks again!
     
  36. grovemaster

    grovemaster

    Joined:
    Jan 29, 2016
    Posts:
    5
    Situation: I have a board game, say Chess, and I want to load the game from a save file. I know in Main Context I need to set up the entities and engines.
    Problem: I want to read the save game file (it's a text file), then place the Chess pieces in the appropriate positions. I could do this in MainContext after setting up the entities and engines, but that reeks of poor coding practices.
    Another problem: After loading the saved game, the king may be in check. I wish to activate the CheckEngine to ensure the appropriate actions are performed, should any need to be performed.
    Another problem: The problems I describe require access to the IEntitiesDB (which is in the Engines after they are set up) to access the necessary data, but need to be performed after MainContext and before the user can input actions. It is possible for the engines to be set up before MainContext has finished it's activities, such as positioning pieces based on a save game file; that means I cannot use an Engine's Ready() to assert check status, because it may be Ready before the save game file load is complete.
    Questions:
    * What are the entry points into the code after MainContext has run but before any engines are available for use?
    * How can I tell an engine to run just once after MainContext is finished?
    * What is the best practice for loading a simple save game file and then performing engine-based actions after loading but before allowing user actions?
     
    Last edited: Oct 28, 2018
  37. sebas77

    sebas77

    Joined:
    Nov 4, 2011
    Posts:
    1,642
    Hello Grovemaster!

    ECS makes serialization much simpler, however it's true I never faced this problem so far with Svelto. I think I may need to give serialization options via the framework, like an enginesSerialization.Serialize() or something like that, just because the simplest thing is serialize every single entity in the database. EntityStruct are usually simple to serialize, while EntityViewStruct components would need to implement a special interface to tell me how you want to serialize the data. You can do everything on your own even now, but it would be interesting to know your results so I can put everything in the to do list. We may even discussing letting you pull a request if you want to change the framework to do so.
    Engines are not added or removed in run-time, all the engines must be there to start with. The engines runs or not according the entities present, so once you serialize back all the entities you will have basically a snapshot of the last situation.

    As far I can see:

    - you setup the engines regardless the game situation
    - you serialize and create the entities from the save file as if it was a normal game
    - the entities are serialized with the current state, the engines will act on them the first update.

    It shouldn't be harder than this, but I never tried, so I am willing to help you. We can continue on our discord chat as well.
     
  38. apexsoftworks

    apexsoftworks

    Joined:
    Oct 5, 2016
    Posts:
    29
    Hi Sebastiano,

    How are you? How is Svelto ECS coming along? I just found it recently and it looks very promising.

    - Tate
     
  39. sebas77

    sebas77

    Joined:
    Nov 4, 2011
    Posts:
    1,642
    Hey there thanks for the interest, I am not updating this thread anymore since it's a bit pointless with unity ECS around, but since svelto ecs has many reasons to exist, it actually grows pretty steadily. Our community moved to discord, join us there.

    https://discord.gg/3qAdjDb
     
    Last edited: Mar 25, 2019
  40. DragonStrike406

    DragonStrike406

    Joined:
    Dec 18, 2019
    Posts:
    1
    My team will most likely be using Svelto in our project and we will be doing it on 2019.3 so the thing I will be most interested in is will Svelto find itself in visual scripting. Or rather will visual scripting find Svelto. I'll keep you updated on the topic once I have something workable.
     
  41. sebas77

    sebas77

    Joined:
    Nov 4, 2011
    Posts:
    1,642
    join our discord channel if you didn't already, we can discuss it more there
     
  42. Davood_Kharmanzar

    Davood_Kharmanzar

    Joined:
    Sep 20, 2017
    Posts:
    411
    @sebas77

    is Svelto ECS obsolete on 2019.3 when official Unity comes?
    and what is difference with Unity ECS? is this faster? or simpler to implement or what?

    i want to using this to making massive zombie navmesh crowds ... does exists good sample?
    thanks.
     
  43. sebas77

    sebas77

    Joined:
    Nov 4, 2011
    Posts:
    1,642
    Better go to the discord channel, you'll find your answer there.
     
  44. sebas77

    sebas77

    Joined:
    Nov 4, 2011
    Posts:
    1,642
    GilbertoBitt and Inspeinre like this.