Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Voting for the Unity Awards are OPEN! We’re looking to celebrate creators across games, industry, film, and many more categories. Cast your vote now for all categories
    Dismiss Notice
  3. Dismiss Notice

Who is using hybrid right now with the end goal of going pure once unity's engine features catch up?

Discussion in 'Entity Component System' started by hippocoder, Oct 24, 2018.

  1. hippocoder

    hippocoder

    Digital Ape Moderator

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    Currently I'm designing where my code will be ECS-ified. I prefer to design a big structural change before actually doing it so I'm interested in anyone's findings with the hybrid system.

    I have done a fair bit of work in ECS but now i'm touching my main game it's time to gather a few case stories.

    Please share any info or tips.
     
    rigidbuddy and Vacummus like this.
  2. NearAutomata

    NearAutomata

    Joined:
    May 23, 2018
    Posts:
    51
    Unfortunately I don't have and infos or tips but would like to chime in into the discussion with a short story.

    A few months ago I decided to give Unity a try for the very first time in order to get into 3D development in general. Since I am planning a very sophisticated multiplayer project (in fact too sophisticated to be honest) I was going to write multiple prototypes. I didn't enjoy the concept of MonoBehaviours and noticed their flaws in an instant. Not that they are worse than the devil but they aren't the best either. It's like being on a diet and having access to some sweets which you cannot resist to eat until you just eat all of them.

    Once I heard of ECS the news intrigued me, especially in my scenario where I am about to start a project from scratch and would not run into the scenario to refactor everything just to introduce ECS into the project. Hybrid really looks promising since it eases the transition but for me it just feels dirty and bandaid/patchwerk. I want to go full pure from the beginning which is arguably a bad decision in the first place yet I'd like to keep my code concise and not using various paradigms.

    Another reason that really pushes me for using pure in the first place is the job system. Leveraging this combined with the ECS concept of worlds I'd be easily able to mimick multiple instances of the same region in my game world and be really efficient as well.

    I am really struggling to push myself to continue developing the game now since many (necessary) breaking API changes occur all the time and I have to rewrite a lot of my code in order to accomodate the changes. Of course this is to be expected with such a bleeding edge package and paradigm that will be added to Unity in the long run.

    The most daunting thing about the entire ECS package is that it's ridiculously early in development and missing a VAST majority of features that will be introduced at a later date. It is really annoying that I have to come up with ugly bandaid solutions to bring animations etc. into the ECS world only to replace it with the official solutions.

    Right now I am really unsure if I should just commit to go the old route with GO/MBs rather than ECS even though I'd eventually like to use ECS in addition to the new networking features (that are apparently going to be tied into ECS as well?) and I am afraid of the work necessary to refactor going forward from Classic to Hybrid to Pure along the way.
     
    Vacummus and hippocoder like this.
  3. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,653
    Which type of stories you need :) Soon (After Unite LA) and my talk on DevGAMM Minsk 2018 (about ECS in production) I'm planing talk more about our experience :) About Chunks, transfering from Injection to Chunks, practical using of Dynamic Buffers, LOD and Culling systems and other (like in my previous article but more complex, with more in game practices)
     
    Antypodish, psuong, Vacummus and 4 others like this.
  4. hippocoder

    hippocoder

    Digital Ape Moderator

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    All <3 soothe me with knowledge
     
  5. LegendaryTale

    LegendaryTale

    Joined:
    Oct 4, 2018
    Posts:
    2
    I started a 3D project a couple months ago with the intention of being pure ECS as well. Once I reached animation, I found that some form of hybrid was required. I also didn't want to make any significant architectural changes that I would have to re-write later as the pure features became available.

    My solution is one hybrid ComponentSystem and a Dictionary that maps Entities with a GameObject Component to their GameObjects (unpleasant, I know). This ComponentSystem has one responsibility, which is to iterate over all Entities with the GameObject Component, retrieve their GameObject from the Dictionary (handled by the GameObject Component), and through injection with ComponentDataFromEntity have relevant Components update their corresponding aspects on the GameObject. Everything else is still handled in pure ECS, although this does mean that rendering, mesh properties, and parenting are handled by the GameObjects.

    The update logic is in each Component, in a single function specified by an interface. Each update in the ComponentSystem consists of 1 or 2 injected ComponentDataFromEntity and a function call. My plan is to remove the update code from the Component and the ComponentSystem as the corresponding pure ECS feature becomes available; hopefully re-purposing my Components to integrate with that feature's API. The long-term goal is that I can simply remove the ComponentSystem and GameObject Component entirely, along with that Dictionary.

    This is working so far, although I haven't been able to work on the project much lately, so I can't say that I have loaded it with additional requirements yet. It only updates GameObject position and a parent currently. Triggering animations is next on my list since that was the original requirement.

    I expect to add one more hybrid ComponentSystem for reading data from the GameObject and updating its Entity's Components. I would use this for passing bounding box information (accounting for rotation and parenting since the GameObjects will already be handling that for me) to my Components for processing collision in ECS. I'll then phase it out in the same way when I don't need GameObjects.
     
    Vacummus and hippocoder like this.
  6. Vacummus

    Vacummus

    Joined:
    Dec 18, 2013
    Posts:
    191
    I have been working on an top down 2D organism game for a few years now (on the side), where you play the entire life of an organism in a complex ecosystem (more info here). So as you can imagine, ECS is perfect fit for this kind of game. The game heavily depends on Unity's 2D physics, and I am currently working on refactoring a lot of the core logic (AI, movement, etc) to pure ECS, with the 2D physics being the only thing that is not ECS (which still makes that a pretty big performance bottle neck).

    I hate loading screens and one strict requirement I had for my game is that the all assets must be streamed (loading/unloading seamlessly with no performance hiccups). To achieve this, I separated my logic into two layers. The first layer is called the streaming layer which consists of GameObjects that hold Unity components such Rigidbody2D, Collider2D, Joint2D, and SpriteRenderer. These GameObjects are created upfront and never destroyed. They are constantly being recycled through object pooling techniques. The second layer is called the controller layer. Each GameObject in this layer requests a GameObject from the streaming layer to control. The self made components in this layer (such as pathfinding and movement calc components) control the force of the rigidbody, the configuration of joints and colliders, and what sprites are rendered.

    I kind of lucked out with this kind of layered architecture because it allows me completely refactor the controller layer to pure jobified ECS, with the streaming layer being the only thing that uses Unity's traditional components. My hope is that the streaming layer will eventually be refactored out of existence on it's own once Unity starts rolling out pure ECS physics implementations, which will allow me to create something like school fish like organisms.
     
    hippocoder likes this.
  7. psuong

    psuong

    Joined:
    Jun 11, 2014
    Posts:
    118
    For my racing game, I do a mix of both, on the things that I can't make pure yet, I'll use a hybrid solution (e.g colliders, rigidbodies, etc). For majority of the things that I can, I most certainly will make it into a pure approach (spline sampling, checkpoint detection systems, inputs, etc).

    One thing that I've certainly enjoyed about the hybrid approach is that, it's really nice visual way to see what components are associated with a "gameObject". Additionally, with associating hybrid components to a gameObject, it makes it pretty easy to be able to "bake" within the scene at frame 0.

    From there you can certainly collect all of the gameObjects you need (maybe through an MonoBehaviour editor script) and convert them into entities. I certainly do that for static Mesh renderers in the scene from start, and I'll explore all of the new stuff like buffers, chunks, and whatnot soon, I just need to make some time for it.

    For pure entities, what helps me kind of visualize the data I need and the systems I need to build I do something like this:
    upload_2018-10-25_23-26-14.png
    I just write it down on an excel sheet to really understand the data that I need that can be extracted from old MonoBehaviours. (That's not from my racing game but how I was helping a friend of mine refactor his systems to use ECS when it can.)

    Temp website for my racing game here: http://psuong.github.io/games/derby/
     
    hippocoder and Spy-Shifty like this.
  8. Micz84

    Micz84

    Joined:
    Jul 21, 2012
    Posts:
    436
    Is the talk on DevGAMM Minsk you gave is available somewhere online? It would be very interesting to watch and learn from.
     
  9. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,653
    Yes, all talks from DevGAMM (more info here https://devgamm.com/minsk2018/) always recorded and published on YouTube. But, sorry, my talk will be on Russian language :rolleyes: Some talks will be on English or Russian with realtime translation, but not my :(

    P.S. And here I am (schedule on 1st day) :D
    upload_2018-10-26_21-37-35.png
    upload_2018-10-26_21-36-29.png
     
  10. Micz84

    Micz84

    Joined:
    Jul 21, 2012
    Posts:
    436
    That is a shame :( maybe someone will add subtitles later on :)
     
  11. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,653
    I'll write all this and more in article described above :)
     
    Sylmerria, hippocoder and Micz84 like this.
  12. psuong

    psuong

    Joined:
    Jun 11, 2014
    Posts:
    118
    Can't wait to see it dude. :)
     
  13. foxnne

    foxnne

    Joined:
    Apr 18, 2016
    Posts:
    59
    Do you mind explaining a little more in-depth about where and how you sync data between the two layers? This is the same approach I was planning to take but picking through the samples repository, I found a few things for syncing positions from entities to GameObjects but I get errors with the latest Entities package when trying to replicate it (TwoStickShooter -> SyncTransformsSystem) .

    I assume that if I dont want to have to add a ton of wrapper components to gameobjects and rather add components to entities using jobs or archetypes that CopyTransformToGameobject doesn't work, i couldnt get that to work for me, nor can I seem to access both a Transform component and IComponentData from the same system.

    How do you make a component with a reference to the gameobject and access that from a system? (using anything like this following examples I get an error about using unsafe pointers) .
     
    hippocoder likes this.
  14. snacktime

    snacktime

    Joined:
    Apr 15, 2013
    Posts:
    3,356
    I think once ECS is as easy to use as the current paradigm, then it's kind of a foregone conclusion that most will move to it.

    My experience has been that until the rest of the Unity api catches up, you need some type of middle layer that solves for both now and when other api's start to become thread safe.

    My approach starts with separating data from logic everywhere, which I kind of already had as a habit before ECS so this part was already in place.

    I just made a few adjustments so that the data is now ECS compatible. Even if it's not in ECS yet, I make it fairly granular and tag it with IComponent. Where it makes sense I put a thin wrapper around it.

    For example the most command class in my game is GameEntityData which represents most anything that can be interacted with and is networked. It's just a thin wrapper over the actual data which is a bunch of IComponent's. I have a GameEntityDb class that's basically a dictionary and getter/setter. Updates from the OOP side proxy through to ECS via a NativeQueue of updates which are applied every frame. Updates on the ECS side are applied to the objects in GameEntityDb.

    This also enables taking large chunks of existing code and just running them in an IJob as a starting point. Because with the data separated and synchronized with ECS, it mostly just works in a lot of cases.
     
  15. Vacummus

    Vacummus

    Joined:
    Dec 18, 2013
    Posts:
    191
    You should be able to access a MonoBehavior component (like the Transform component) and IComponentData from a system. Check out the example below. if you create a gameObject, and add a GameObjectEntity component, then you can get the entity from the GameObjectEntity component and add IComponetData to it.

    Code (CSharp):
    1. var gameObject = new GameObject();
    2. var entity = gameObject.AddComponent<GameObjectEntity>().Entity;
    3. var entityManager = World.Active.GetOrCreateManager<EntityManager>();
    4.  
    5. entityManager.AddComponentData(entity, new Movement());
    And to query for the Transform and Movement components, you would do the following:
    Code (CSharp):
    1.  
    2. [Inject] Group group;
    3. struct Group
    4. {
    5.     public readonly int Length;
    6.     public ComponentDataArray<Movement> movement;
    7.     public ComponentArray<Transform> tranform;
    8. }
    9.  
    You can also query for the GameObject by adding "public GameObjectArray GameObjectArray;" to your group query.
    So IComponentData can not reference a GameObject. Nor any class for that matter since classes are not safe when using Jobs. But there are other ways around this problem. I have to solve for the same problem when syncing between my two layers, but this is how I do it.

    For the streaming layer, I have GameObjects with the following components: Rigidbody2D, Collider2D, some Joint2D, SpriteRenderer.

    Then for my controller layer, I have GameObjects that have a "StreamedObject" MonoBehavior component. This component is that one that has a reference to the GameObject from the streamed layer (and it's components Rigidbody2D, Collider2D, Joint2D, and SpriteRenderer).

    On the GameObjects in my controller layer I would add GameObjectEntity to them, and then I would add any IComponentData components to that entity, just like in the examples above. One component I have that is added to this entity is the Rigidbody2DForce. I am using Jobs to calculate the Rigidbody2D force based on player input and movement speed. But this Rigidbody2D force needs to be passed to Rigidbody2D component in the streamed layer. I do that with the following system:

    Code (CSharp):
    1.  
    2. [UpdateBefore(typeof(FixedUpdate))]
    3. public class AddForceToRigidbody2DSystem: ComponentSystem
    4. {
    5.     [Inject] Group group;
    6.     struct Group
    7.     {
    8.         public readonly int Length;
    9.         public ComponentDataArray<StreamedObjectHasRigidbody> _filter;
    10.         public ComponentDataArray<Rigidbody2DForce> rigidbodyForce;
    11.         public ComponentArray<StreamedObject> streamedObject;
    12.     }
    13.  
    14.     protected override void OnUpdate()
    15.     {
    16.         var time = Time.deltaTime;
    17.  
    18.         for (var i = 0; i < group.Length; i++)
    19.         {
    20.             var force = group.rigidbodyForceArray[i].Force * time;
    21.             var rigidbody = group.streamedObject[i].Rigidbody;
    22.  
    23.             rigidbody.AddForce(force);
    24.         }
    25.     }
    26. }
    27.  
     
    Last edited: Oct 28, 2018
  16. NearAutomata

    NearAutomata

    Joined:
    May 23, 2018
    Posts:
    51
    @Vacummus Just a minor note: I though the injections and ComponentArray as well as ComponentDataArray are going to be deprecated, aren't they?
     
  17. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    Is there a chart that shows features in ECS and Unity API as well as a roadmap/timescale on full ECS rollout so at least as a developer you can design your game to have the best ECS features at release?

    Or would the best approach be to write the game in none ECS then convert as many systems over as you can or would the reverse be better where you start with ECS and only bring in the Unity API for features not available in ECS?
     
  18. Vacummus

    Vacummus

    Joined:
    Dec 18, 2013
    Posts:
    191
    You are correct, and that's def worth being aware off. Alternatively, we can use the low level api by querying with chunk iterations. But that's way too overkill for simple query calls. Unity is currently working on a simple API that will replace querying by injection. Check out the video link below (fast forward 25 minutes), from last weeks Unite conference, where @Joachim_Ante talks about making queries with lambda functions. Which I really love! But until they have released that, I am going to continue using injections as I don't think migrating from an injections approach to a new api will be a big deal.

     
    hippocoder likes this.
  19. foxnne

    foxnne

    Joined:
    Apr 18, 2016
    Posts:
    59
    Thank you so much for the thorough answer and code examples. I only have a few years experience with c# and ECS has been tough to wrap my head around. This helps so much. Thanks again.

    However, I guess this means that GameObjectEntity and adding components directly to the gameobject is required, rather than creating the entity and its components through Archetypes myself. It does indeed seem to work using GameObjectEntity, but otherwise I guess the Transform isn't shared between the Gameobject and Entity, and I can't find a way to link those two together.
     
    Last edited: Oct 28, 2018
  20. NearAutomata

    NearAutomata

    Joined:
    May 23, 2018
    Posts:
    51
    Since I am experimenting on a project prototype that I want to rewrite from scratch right away using pure ECS I'd rather not use to-be-deprecated paradigms such as injection. Unfortunately though chunk iterations apparently don't allow to query for custom MonoBehaviours or Unity components such a Animators that haven't been exposed to ECS as of now.
     
  21. Micz84

    Micz84

    Joined:
    Jul 21, 2012
    Posts:
    436
    I think the IJobProcessComponentData is a better choice than injection. It won't go away, it is even less verbose.
     
  22. NearAutomata

    NearAutomata

    Joined:
    May 23, 2018
    Posts:
    51
    You can only query against 4 components if you use IJobProcessComponentData which sometimes might not be enough in more complex situations (Position, Scale, Rotation, AIAgent, AIStateX, AIBehaviourY). In those situations you have to use queried chunk iterations. Those however, as stated above, are limited in their features as of now.
     
  23. Micz84

    Micz84

    Joined:
    Jul 21, 2012
    Posts:
    436
    That is true, but you can use RequireComponentTag attribute. It would be nice to be able to specify more components.
     
    laurentlavigne likes this.
  24. Vacummus

    Vacummus

    Joined:
    Dec 18, 2013
    Posts:
    191
    Glad I could help! I do want to mention that in the talk I referenced above, Joachim talked about "IComponent" (about 28:30 minutes in) which is to be used for classes (where as "IComponentData" is for structs). What this means is that you can create a component with a class that holds a reference to a GameObject, and I would expect that we should be able to query "IComponentData" and "IComponent" together within a system. So this will remove the need of GameObjectEntity (for this specific problem mind you), thus removing the need for your entity to be coupled to a GameObject. And I think it's also a solution to how you originally expected to be able to use ECS.
     
  25. SubPixelPerfect

    SubPixelPerfect

    Joined:
    Oct 14, 2015
    Posts:
    224
    Опа, земляк )
     
  26. Guedez

    Guedez

    Joined:
    Jun 1, 2012
    Posts:
    823
    I feel that starting to dabble in ECS is the greatest and worst idea I had in a long time.
    And I am hardly using it the way it should be done, right now I am using it as some sort of tagging system for game objects.
    One day I might go pure ECS, but that shouldn't happen before 2020. Too many complex data structures, like octrees, are simply too hard to make in ECS in able time. I'd rather make game instead.
     
  27. thelebaron

    thelebaron

    Joined:
    Jun 2, 2013
    Posts:
    825
    heh I was literally just getting used to using these too