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. Dismiss Notice

State of ECS & DOTS

Discussion in 'Entity Component System' started by raybarrera, Jan 25, 2021.

  1. apkdev

    apkdev

    Joined:
    Dec 12, 2015
    Posts:
    263
    Wait, what? I had no idea Brian Will works for Unity now! I've been following his extremely underrated youtube channel for years. I highly recommend it for tutorials and presentations about numerous programming topics, including ECS and DOTS.

    https://youtube.com/user/briantwill/videos
     
    Kirsche, NotaNaN, JesOb and 1 other person like this.
  2. bitinn

    bitinn

    Joined:
    Aug 20, 2016
    Posts:
    958
    Doing god's work:

    Sometimes you have to fix it from the *inside*

     
  3. Mortuus17

    Mortuus17

    Joined:
    Jan 6, 2020
    Posts:
    105
    To me the state of DOTS is to bring out packages which conflict with one another. Collections vs Jobs months ago (still not fixed) and now Jobs vs Serialization. Why publish packages at all if there are syntax errors in the DOTS package aggregate? I mean come on... at least publish against the most recent version of the packages. I really don't understand this.
     
    Orimay likes this.
  4. apkdev

    apkdev

    Joined:
    Dec 12, 2015
    Posts:
    263
    I'm certain that with combined community effort we can solve the insurmountable problem of figuring out which experimental package versions to install.
     
  5. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    3,983
    Wasn't this fixed last week? Granted I still haven't tried because Entities is behind, but I think the latest Jobs now depends on the latest Collections.
     
  6. Mortuus17

    Mortuus17

    Joined:
    Jan 6, 2020
    Posts:
    105
    Yes - just don't install the latest versions ;) It's not hard. It's just that a forever-dead release is unnecessary, initially confusing and (I know I'm being a bit rude but it's the unfortunate truth: ) unprofessional.


    Might be :) But as a result the latest Jobs version conflicts with the (non-preview) Serialization package and the issue is thus still not fixed.
     
  7. adammpolak

    adammpolak

    Joined:
    Sep 9, 2018
    Posts:
    450
    preach!!!
     
  8. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,626
    What errors? We've been using 1.5.X with no issues for a long time.
     
    apkdev likes this.
  9. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    3,983
    I was wondering when this webinar would show up in this subforum. For the record I was involved behind-the-scenes, so I may be able to answer some questions regarding it.
     
    NotaNaN, mariandev, Krajca and 4 others like this.
  10. IAL

    IAL

    Joined:
    Nov 11, 2016
    Posts:
    7
    Wait no, you're right. It's the Collections package that causes issues with Entities. Updating collections to latest preview forces you to update the Jobs package to the latest preview and those combined have stuff that's clashing with the Entities package code (due to the change to the type constraint on Jobs parameters).
     
  11. OUTTAHERE

    OUTTAHERE

    Joined:
    Sep 23, 2013
    Posts:
    656
    Bah, Bolt is such a hacky and contrived implementation. Blueprints is so much more... in tune with its workflow.

    I kinda liked the ECS visual scripting. RIP...
     
    deus0 likes this.
  12. hippocoder

    hippocoder

    Digital Ape Moderator

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    Tough crowd :) but we know DOTS works and works very fast. My dream is the ease of use of monobehaviour and, honestly, I want to feel like I'm just making a few more functions than thinking about how DOTS works. I know how ECS and DOTS works, but I don't like having to figure out what syntax Unity in particular wants, or if I'm doing something bad with chunks.

    I wanna figure out my game instead, so Unity has to make it as easy to code in. Done right, it should feel pretty similar to the public Unity of today to develop in. It's not exclusive, you'd be able to dig deep if you're a pro.

    Most of Unity's customers will avoid DOTS if they don't make it brain dead easy on the surface though.
     
  13. Daxten

    Daxten

    Joined:
    Sep 20, 2013
    Posts:
    30
    I just install the newest hybrid renderer always, since it depends on most of these packages and seems to be the most "complete" package... obviously that also didnt get any update for some time :)
     
    deus0 likes this.
  14. deus0

    deus0

    Joined:
    May 12, 2015
    Posts:
    256
    Am I the only one that finds DOTS easier to use than monos xD
     
  15. hippocoder

    hippocoder

    Digital Ape Moderator

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    Depends on the type of game :p There's not a lot of support for some things.
     
    deus0 likes this.
  16. deus0

    deus0

    Joined:
    May 12, 2015
    Posts:
    256
    For sure. I spent the first many months of ECS looking for solutions for physics, animations and audio. It took me a while to develop my own. I was hoping for more out of the box solutions from unity, integrated into this new workflow. Once you get those speedups, it's hard to go back lol. It's really good imo for custom tools and systems for the asset store!
     
    Anthiese, bb8_1 and hippocoder like this.
  17. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    3,983
    Nope. I'm also in that camp.

    I think a lot of it boils down to whether you expect Unity to be a game engine or merely a low-level framework and authoring solution. Both paths have lead to great games, but typically a given team of people will lean one way or the other. For those that lean towards the latter, there's a good chance they will find DOTS easier. DOTS makes it easy to build engine-level features at engine-level performance. Being able to know precisely what touches what and when has allowed me to move away from trial-and-error coding and instead meticulously designing things to be exactly how I want them to be.

    Also, the payoff is longer term. It takes a little more effort up front to get the core gameplay up and running. But you save yourself a ton of time not having to worry about optimization. I'm currently working on a project that could totally be done with Game Objects. I have maybe a couple hundred things on screen at a time compared to the thousands that can be achieved with DOTS. But yet, because I use DOTS, I no longer have to worry about pooling projectiles or audio sources. I don't have to worry about enabling and disabling scene objects that are far away from the player. I no longer have to worry about designers making messy levels. In fact, I had to tell one of the designers that there was no need to break a large level up into sections. I even am able to get away with being sloppy with the code, using "fat" components that are wasteful of cache in a lot of situations. As long as I use jobs and Burst where I can, performance just isn't a problem. And because I have a bunch of control over the system, gameplay bugs are usually simple to track down, since the rules and execution order are explicit.
     
  18. deus0

    deus0

    Joined:
    May 12, 2015
    Posts:
    256
    This is so poetic :) Many thanks.
     
    bb8_1 likes this.
  19. rauiz

    rauiz

    Joined:
    Mar 19, 2013
    Posts:
    40
    100% agree with this. Even though I'm not that big a fan of the ECS part of DOTS, for a some of the reasons mentioned in the Brian Will video there, the rest of DOTS (Burst, NativeCollections, Jobs, BlobAssets -- these are great) has increased my productivity and allowed me as a single developer to do quite a lot more than I would have been able to achieve in the same time with MonoBehavior workflow (working on a Fighting Game with Rollback Networking). Also, "low-level framework + authoring tool" vs "game engine" comment is on point :D

    I really wish Unity keeps supporting the first of these as an alternative since Unity's editor and authoring tools are amazing and all we really need for this are better low-level APIs (separate from ECS) to integrate with our own Jobs/Systems and, optionally, with ECS.
     
    andreiagmu and bb8_1 like this.
  20. hippocoder

    hippocoder

    Digital Ape Moderator

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    This thread has to be productive. Unity knows people's issues with it believe me. The other thread was locked because people just kept moaning and ignoring the fact that this is not being developed in public right now.
     
  21. TheOtherMonarch

    TheOtherMonarch

    Joined:
    Jul 28, 2012
    Posts:
    791
    The only real fundamental issue I see with ECS is the conversion workflow. The other nonfundamental issue is that the supporting packages like animation are just not ready yet.

    If you develop in ECS you basically needs to use it for all your code logic. It really is like porting your code to a different engine written in a different language.

    All over this forum you see people trying to do really weird things with ECS. ECS is a very strict pattern but many people are constantly trying to break that pattern in weird ways. That tell me that many people either don't have enough time with ECS or are not letting ECS fully infect their code logic by fully rewriting their code structure. I really have a hard time understanding Unity's logic in trying to cram it into some kind of dubious meta game engine.
     
    Last edited: Sep 27, 2021
    hippocoder and Tony_Max like this.
  22. PhilSA

    PhilSA

    Joined:
    Jul 11, 2013
    Posts:
    1,926
    I think the main reason why people are having a hard time with ECS is because the learning resources aren't there yet, and the workflows aren't there yet. And I also think the main reason why Unity is developing DOTS privately now is because people were still expecting it to be ready, even though Unity kept saying it was still experimental and kept trying to (unsuccessfully) make the packages harder to get

    While getting massive performance gains without requiring any kind of breaking changes in your code would certainly be very cool, it would also be a nearly impossible engineering challenge, and I don't think it should be considered realistic. Massive improvements very often means massive breaking changes, and I think if Unity knew how to make monobehaviour OOP perform suddenly 10x better, they would've done that instead of developing DOTS.

    Think about it; which one of these two scenarios is more likely to be true?
    1. Unity is making breaking changes because they thought about it and they concluded it was necessary
    2. Unity is making breaking changes because they just enjoy watching their users suffer, even at the cost of losing customers

    I do agree that the conversion workflow is really not good at the moment though. It feels extremely hacky, confusing and error-prone. But sometimes you see people in here saying that not only they don't like the current conversion workflow; they also don't think DOTS should have any conversion workflow at all. And I don't think these people really realize what they're asking. For example, if you want to make your entity be a rigidbody, you want to just have to add 1 component; not 4-5 components. And the only way this can be possible is if you have a conversion workflow. This fragmentation of components is a crucial part of DOTS's performance & multithreadability
     
    Last edited: Sep 27, 2021
  23. JesOb

    JesOb

    Joined:
    Sep 3, 2012
    Posts:
    1,081
    Want to add, Regarding Conversion Workflow, that it good when used right.
    - rigidbody is sample of good use case (but have unresolved question: how to tune entity in runtime and copy back to edit time)
    - velocity is sample of bad use case (we dont want to have conversion burden for such simple components)

    And my personal opinion:
    We need eventually get conversion EditimeECS -> RuntimeECS instead of GameObjects -> ECS
    Currently I see Entities far superior than GameObjects for authoring, just very young today :)
     
  24. TheOtherMonarch

    TheOtherMonarch

    Joined:
    Jul 28, 2012
    Posts:
    791
    Unity already has RequireComponent and has used it for years with Monobehaviours. So adding more then one component is not only a ECS design requirement.

    I would be happier with a What You See Is What You Get interface even if it looked messier in the editor. Even today not everything is exposed by Monobehaviours to the editor. But at least it is reasonably close to WYSIWYG. You can grayout some components if you want a cleaner user interface. But WYSIWYG is very important as a design principle.

    Exactly how that editor interface is implemented is no concern of mine. Except in so far as I don't want to have to write editor components. So it better display fields from my runtime components.
     
    Last edited: Sep 27, 2021
  25. PhilSA

    PhilSA

    Joined:
    Jul 11, 2013
    Posts:
    1,926
    That's not suficient. Depending on the options of your rigidbody (isKinematic, use gravity, etc.....) the types of ECS components that must be added are different. And since the rules of which components must be added under which conditions aren't really obvious, this stuff must be handled automatically through conversion. The engineers that created the physics engine know those rules, but the designers don't. And making one little mistake can result in massive performance losses or things not working anymore

    And then there's the other reason why the conversion workflow exists; an "entity" doesn't really map directly to a "gameObject". For example; in my project I've created a "Stats system" where character stats can have buffs & modifiers, and some stats depend on other stats, etc... In this system, each stat must be an entity. With the conversion workflow, I can easily generate those stat entities at conversion time. Without it, designers would be forced to manually create & setup new entities for every possible character stat. A single character "gameObject" can easily translate to hundreds of entities at runtime. This is a level of complexity that must be hidden at authoring time. And for the sake of making instantiation of prefabs fast at runtime, all of this conversion logic has to be pre-baked in some way
     
    Last edited: Sep 27, 2021
    andreiagmu, Anthiese, Enzi and 3 others like this.
  26. TheOtherMonarch

    TheOtherMonarch

    Joined:
    Jul 28, 2012
    Posts:
    791
    The rules are not that complex. You would need more just RequireComponent. You would also need ExcludeComponent. Maybe other logic modifiers. You would need hierarchical versions.

    You can have this with Monobehaviours as well. A single character "gameObject" can easily translate to hundreds of gameObjects at runtime.

    You don't need to show runtime modified data. Monobehaviours do this using Start().
     
    Last edited: Sep 27, 2021
  27. PhilSA

    PhilSA

    Joined:
    Jul 11, 2013
    Posts:
    1,926
    And that's how you end up with massive spikes in the profiler whenever things get instantiated. With the conversion workflow, everything is pre-processed and ready to get instantiated super efficiently. If you need to do this kind of processing either way; why would you choose to do it at runtime (where it impacts performance) instead of at authoring time? You have to write your own conversion code in both cases, except one approach is free and the other is costly

    I have cases in my own code where the rules are simply way too complex, even for myself who designed the system. It would be way too many things to remember, and way too many possible pitfalls. It would also be 100x more work to create & author prefabs, and that's not even an exaggeration. Even for the cases that aren't that complex, it's easy to forget what the rules were when each type of thing you add has its own specific rules. This would be hell without conversion, no doubt about it
     
    Last edited: Sep 27, 2021
    Anthiese, Enzi, NotaNaN and 3 others like this.
  28. MaNaRz

    MaNaRz

    Joined:
    Aug 24, 2017
    Posts:
    117
    I don't think a Gamedesigner wants or needs to see the implementation details in the Editor. Of course it would be very nice to have a debug view of what would happen at conversion but it needs to be hidden from people who want to actually act in the editor. Not everything is as easy to understand as the physics engine conversion. I have Mono Components that conditionally add ECS components or Add DynamicBuffers based on some generic Component i referenced in a List in my Mono Component. Without conversion this would not be useable at all.
     
    apkdev likes this.
  29. TheOtherMonarch

    TheOtherMonarch

    Joined:
    Jul 28, 2012
    Posts:
    791
    I don't get it I have programmed a lot of pure ECS and it is much simple then what people are doing. I do have some systems like the pathfinder that store data outside of ECS but only 3 systems like this. One for pathfinding, one for networking and one for rollback physics.

    I feel like people are maybe using ECS in places that they should be using a burst job or mixing runtime and editor time. Others may just be writing bad code.

    The physics engine conversion is more complex then anything I do in my own code.
     
    Last edited: Sep 27, 2021
  30. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,292
    Worth mentioning, while conversion kinda sucks - you're not forced to use it.
    Its possible to author components from the MonoBehaviours and still have benefits of both worlds.

    I've developed some tooling that allows to author entities from MonoBehaviour side with minimum runtime performance impact (footprint is similar to creating entities with ECB); Its non-zero, but its way more flexible.

    Add up a custom execution order, and you've got yourself a nice robust burstable framework for any size of simulation.
    Upgrading for high performance is quite simple as well.

    Strip logic from MonoBehaviour -> Slap it to the system -> Add entities from the MonoBehaviour itself.

    Or aggregate archetype based on hash from ScriptableObject, then mass "instantiate" / allocate N entities.

    Extra benefit is that you don't need to re-write extra editors / inspectors on top.


    Entities on their own is quite solid for me and projects I've done so far.
    While just using jobs may look simpler, its way worse to maintain once scope rises.
    For example when you need fast and simplified custom physics engine on top of rendering to speed things up.


    Major pain point is job scheduling cost, system overhead on mobile, and abundance of hacks to actually disable subscene conversion. Or systems / groups that you don't need but they still by default created.
     
    Last edited: Sep 27, 2021
  31. PhilSA

    PhilSA

    Joined:
    Jul 11, 2013
    Posts:
    1,926
    It's totally possible that the systems you've created are things that are inherently not so complex when it comes to the design of your entity archetypes, but the complexity of your conversion can sometimes depend on how optimized you want your systems to be.

    The physics engine could've probably been designed to work with a single rigidbody component at runtime, but then there would be a ton of wasted performance because:
    • some things couldn't be parallelized as much
    • we'd be doing constant condition checks instead of working with archetypes
    • the large components wouldn't be as cache-friendly
    • etc
     
    Last edited: Sep 27, 2021
    NotaNaN and MaNaRz like this.
  32. burningmime

    burningmime

    Joined:
    Jan 25, 2014
    Posts:
    845
    I'd point you to the first part of the video linked above, 'cause he says it in a betetr way than I could ever. ECS is not an appropriate data structure for every game and use case:



    An example use case that does not fit very well at all into ECS is an RPG. You can go around with your character talking to people, and when you talk to them, there's a script for each one -- maybe they play some animations, say some things, trade items, initiate combat, etc. Each character is going to do something different, and interact with other systems and nearby characters in unique ways. That's not very ECS-friendly without developing your own domain-specific scripting language to represent these scripts as data (or using Mono.Cecil to try to convert enumerables to struct types -- which I started on before I said "**** it not worth the effort").

    I don't believe the problem is a lack of education as much as it is an overabundance of marketing suggesting ECS for inappropriate use cases and challenges of hybrid MB/ECS workflows.
    I didn't get far into the conversion system itself, but I want to point out that there's a fundamental flaw (or at least limitation) in the idea that editor code and game code can be fully bifurcated. About a year ago, I was trying to convert this thing to ECS. It lets you build Sims-like buildings in the editor and then have them "cut away" with pieces being added/hidden at runtime based on the player location and camera angle (excuse the dev art):

    upload_2021-9-27_11-51-27.png

    I wanted to be able to spawn those doors as entities. I was told basically that this was impossible and that entities are a mostly runtime thing (you can actually spawn them in the editor as entities and have them render, but when you try to change them or handle undo or adding/removing, you end up with multiples of objects, and it becomes a whole PITA that basically involves writing a whole separate framework for editor rendering vs runtime rendering and oh yeah your systems won't be called and hybrid renderer only gets called when the camera moves so good luck there).

    These are 2 use cases that did not work out in ECS -- RPGs and editor tools. Is the answer "RPGs are a dead genre and Unity doesn't need custom editor extensions". Is the answer that I'm an idiot? Or is the answer maybe that ECS is simply a poor match for these 2 use cases. In both of them, I actually took time to read the docs, read source code, and put several weeks of effort (over a month actually, but the equivalent of a at least a 40-hour week had I been working full-time on it). I'm not averse to systems-level programming; I've done my share of C/C++. And in both cases, I could kinda-sorta make it work with ECS, but not in a way that was natural, maintainable, or avoided bugs; it was a way that was fighting the framework tooth and nail.
     
    Last edited: Sep 27, 2021
  33. PhilSA

    PhilSA

    Joined:
    Jul 11, 2013
    Posts:
    1,926
    I agree with this, although I think there is a simple ECS solution to the specific problem you described. Currently, DOTS does provide tools to do full-on OOP with polymorphism without having to use an ugly hybridECS/GO approach. More on that later

    This scenario could be as simple as:
    • each NPC has a "DoBehaviourOnInteracted" component
    • "DoBehaviourOnInteracted" holds an Entity field representing the "behaviour prefab"
    • When an NPC is being interacted with, it spawns whatever "behaviour prefab" entity it has in its "DoBehaviourOnInteracted" component
    • "behaviour prefab" entities remember the NPC entity that spawned them and they have a component for any possible behaviour: PlayAnimation, StartDialogue, OpenShop, etc...
    • These components get picked up by their respective systems later in the frame, and the behaviours are executed
    This is essentially like an Entity-based event system, where you create events that trigger behaviours

    But there are cases where things are more complicated than that, or cases where doing this in ECS isn't worth it. In those cases, you can simply handle those behaviours with OOP in a managed component (class-based IComponentData) on the entity. So you'd have a "NPCInteractionBehaviour" IComponentData class on your NPC entity, and this would use full-on polymorphism with abstract behaviour classes. I have an example of class-based IComponentData usage here

    To me, it is much more preferable to do things this way rather than using hybrid ECS/MB approaches. I think there's something really tedious about doing hybrid and having to manage the "translation" of data between 2 different architectures in the same project. With class-based IComponentData, you have the full flexibility of OOP, but you still have only one architecture

    I'd file this one under "the workflows aren't there yet". I think the goal here is that your doors/walls/etc... would be in subscenes, and then you'd have Hiding/Unhiding systems added to the Editor world's update loop. It's the kind of thing I've never attempted so I don't know about the problems for making this right now, but if it's not possible right now, we can't just assume it'll never be possible. Only when DOTS reaches v1.0 can we start making those assumptions
     
    Last edited: Sep 27, 2021
    JesOb and NotaNaN like this.
  34. thelebaron

    thelebaron

    Joined:
    Jun 2, 2013
    Posts:
    825
    I think the current paradigm of Just add gameobjects, and magic them into entities! It's super effective! breaks down when I've tried to tackle editor workflows that also exists at runtime(ie a level editor). Am I meant to be managing gameobjects or entities? When I save that data should I be loading it into gameobjects that convert into entities again? Why cant entities be serialized in current scenes anyway? Oh yeah I'd love to try this out more, but there's been a bug regarding blobs or something that has existed since the dawn of time, and breaks some de/serialization. Shouldn't we be having conversations about this prior to dots being released as a 1.0? Sure would be nice if development hadn't magic'd its way behind a harry potter-esque cloak of invisibility.
     
  35. burningmime

    burningmime

    Joined:
    Jan 25, 2014
    Posts:
    845
    Let me expand a bit on what I struggled to get working in ECS. Here's a silly example script (with a few modifications) which I have working in my current framework. This uses my own coroutine system (an expansion on Unity's coroutines which can be arbitrarily paused or queried):

    Code (CSharp):
    1.  
    2. private IEnumerator<IActivity> main(IScriptContext act)
    3. {
    4.     // turn the player towards the NPC and the NPC towards the player
    5.     act.turnTowards(transform, act.player, true);
    6.  
    7.     // The NPC is playing an animation with another NPC in the background; pause that animation until the script completes
    8.     act.pause(backgroundScript);
    9.  
    10.     // Get some data about the dialogue boxes (eg positioning them relative to the person talking,
    11.     // face sprites to display, etc)
    12.     var mPlayer = act.dialogueBox(act.player);
    13.     var mSelf = act.dialogueBox(transform);
    14.  
    15.     // this shows a dialogue box and waits for the player to press a button to continue
    16.     yield return mPlayer.say("Hello!");
    17.  
    18.     // now the NPC will jump up in the air and say some text. we wait until both actions are complete before
    19.     // moving on with this script
    20.     yield return act.animate(transform, AnimationId.JUMP).background(out var anim);
    21.     yield return mSelf.say("I'm jumping and saying something at the same time. Isn't that cool?");
    22.     yield return act.waitFor(anim);
    23.  
    24.     // at this point of the script, we check the player's inventory to see if they have a key item
    25.     yield return mPlayer.say("Uh oh, you must be possessed by an evil spirit!");
    26.     if(act.inventory["beer"] > 0)
    27.     {
    28.         yield return mPlayer.ask("I don't have any holy water, but this could be a good substitute...",
    29.             "Give Pabst Blue Ribbon", "Keep Pabst Blue Ribbon");
    30.         if(mPlayer.choice == 0)
    31.         {
    32.             yield return mPlayer.say("Here try this");
    33.             act.inventory["beer"]--;
    34.  
    35.             // play 2 animations in parallel so it looks like the player is giving an item to the NPC
    36.             yield return act.animate(act.player.transform, AnimationId.GIVE_ITEM).background(giveAnim);
    37.             yield return act.animate(transform, AnimationId.TAKE_ITEM).background(takeAnim);
    38.             yield return act.WaitFor(giveAnim, takeAnim);
    39.  
    40.             // once those animations are complete, do these things in order
    41.             yield return act.animate(transform, AnimationID.DRINK);
    42.             yield return mSelf.say("My soul has been saved thanks to the refreshing power of PBR!");
    43.             act.persistentData["expelled_evil_spirit"] = true;
    44.         }
    45.         else
    46.         {
    47.             yield return mPlayer.say("I'm sorry; I don't have any holy water to give you right now");
    48.         }
    49.     }
    50.     else
    51.     {
    52.         yield return mPlayer.say("I'm sorry; I don't have any holy water to give you. I wonder if I could find a substitute...");
    53.     }
    54. }
    55.  
    Now imagine you have dozens of NPCs that each have scripts like this (think a reasonably complex JRPG; my game is somewhat inspired by Persona if you ever played that). Now I'm sure that would be possible in ECS, but would it be more readable? Would adding new NPCs or tweaking existing scripts be easy? Would I be adding a custom AuthoringComponent for every single script type to hold what other references it might use?

    I would posit that the data structures of ECS are wholly unsuitable for this problem, and add no value.

    I agree there is a problem with hybrid workflows. This is a flaw with ECS (as of Janurary when they stopped updating it). But looking over your post here, I don't see that the workflow you suggest is superior to MB at all:

    Code (CSharp):
    1. [DisallowMultipleComponent]
    2. public class OOPTestAuthoring : MonoBehaviour, IConvertGameObjectToEntity
    3. {
    4.     public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem)
    5.     {
    6.         BehaviourComponent b = new BehaviourComponent();
    7.         b.Behaviours = new List<TestBehaviour>();
    8.         b.Behaviours.Add(new BehaviourA());
    9.         b.Behaviours.Add(new BehaviourC());
    10.         dstManager.AddComponentObject(entity, b);
    11.     }
    12. }
    13. [Serializable]
    14. public class BehaviourComponent : IComponentData
    15. {
    16.     public List<TestBehaviour> Behaviours;
    17.     public void Update(EntityManager entityManager, Entity selfEntity)
    18.     {
    19.         foreach (var b in Behaviours)
    20.         {
    21.             b.Update(entityManager, selfEntity);
    22.         }
    23.     }
    24. }
    25. public class OOPTestSystem : SystemBase
    26. {
    27.     protected override void OnUpdate()
    28.     {
    29.         Entities
    30.             .WithoutBurst()
    31.             .WithStructuralChanges()
    32.             .ForEach((Entity entity, BehaviourComponent behaviour) =>
    33.             {
    34.                 behaviour.Update(EntityManager, entity);
    35.             }).Run();
    36.     }
    37. }
    Just looking at that I see a ton of red flags. Off the top of my head...

    • Let's say I need to find an entity's position. An entity manager query is slower than a direct reference and there's a lot more code than just "transform.position"
    • Let's say I want to see what other entites are nearby. In MB land, that's Physics.OverlapSphere(). How would I do that in this type of system?
    • Do I need a separate authoring component for every one of these scripts if they want to reference other scripts?
    EDIT: Not to say your BehaviourComponent is at all bad; looks like an awesome hack if there are only a few of them needed to bridge some stuff. The problems start to arise when there are hundreds of things like that and major pieces of your game logic are using those. Please don't take my criticisms of it personally. My issues are with the state of ECS right now, not with that particular example.
     
    Last edited: Sep 27, 2021
  36. Guedez

    Guedez

    Joined:
    Jun 1, 2012
    Posts:
    823
    Well, since we are talking about one Entity generating multiple Entities.
    Did anyone figured out yet if it's better to have random access due to multiple entities or one gigantic entity?
     
  37. Enzi

    Enzi

    Joined:
    Jan 28, 2013
    Posts:
    908
    Is this script the method how you want to use it for the whole game?
    I mean, you are not taking advantage of the authoring phase at all and it's what I would call unusable prototype code. You mentioned it's just an example script, anyway, my point is, I have something similar but I've setup data that has really nothing to do with the runtime data. The authoring data is just a fancy editor to setup dialogue data, conditions, triggers, etc... Then this gets translated into runtime data and several, specific systems are using it get the whole package what you're doing in one script.

    Benefit, it's completely generic and doesn't need any code changes, that's the whole point of data oriented design.
     
    Krajca likes this.
  38. burningmime

    burningmime

    Joined:
    Jan 25, 2014
    Posts:
    845
    That's the plan. It's easy for me (and the writer I'm working with) to write. Also easy to read/debug and flexible. The only "prototype" piece in there is the strings being directly in the code, which will be trouble if I ever want to translate it. It also seems to be the industry standard for AAA games to use a scripting language of some sort; eg REDScript for The Witcher, C# for The Sims, Lua for... a lot of games.

    For something with low complexity or a fixed small set of interactions (or artists/designers who completely refuse to code), I can imagine that a visual editor can work. But the go-to seems to be a high-level scripting language of some sort to define the logic. And that code is fairly high-level with the ability to dip down into lower levels if need be -- that's a lot o' logic packed into 50 lines.

    Using a text-based scripting language of some sort (not necessarily C#) has a ton of advantages (eg existing tooling, familiarity for new developers, easy to diff and source control, easy to code review, etc). While there are teams that prefer visual scripting, I don't get the feeling that's the norm and you sacrifice a lot for that power.

    Is that a good thing?

    EDIT: there are tons of systems that underlie this code (eg 8 files and ~5000 LoC for dialogue box positioning/stying, now I realize why every RPG since FF9 always just sticks dialogue at the bottom). A single
    yield return mPlayer.say("Hello!");
    has a lot of infrastructure behind it to make it work.

    [[[For context, I came up with this after playing Pillars of Eternity and noticing that the "cutscenes" were almost entirely text-based with no animations. Playing with RPG maker back in the day, I could make reasonable cutscenes with just a few clicks. So I wanted to make it easy to make complex, interactive, visual scenes without a AAA budget, mocap, etc. The face that these are simple is a feature; I want to hide all those complex systems well behind the script itself. I incorporated some of the ways that The Witcher 3 did cutscenes, too]]]
     
    Last edited: Sep 28, 2021
  39. Enzi

    Enzi

    Joined:
    Jan 28, 2013
    Posts:
    908
    We have C# already. That's what REDscript, Lua, etc... wants to solve.

    From what I gathered, especially AAA has steered away from scripting and gone fully into visual editors for designers, which makes sense because designers should not have too much control to break things, make other logical errors or have any part in the compiling process. Also designers should not code. If they are able to, great, but it shouldn't be a requirement.
    Visual editors, especially node based ones are really great for something you're doing and can tackle any complexity level. If you are ever in the position to grasp the full picture of all the dialogue in your game and how it's intertwining, good luck with loose scripts. If you want to tackle something like Persona, ask yourself, what do you think they are using? Handwritten scripts?

    I'd recommend serializing authoring data to begin with so it's not saved up in a bunch of MB components. That way you have all the benefits you are mentioning. I don't see how you lose a lot of power when you have to write helper methods anyway. It's the same thing really, just called differently.

    Well, one more thing is that the data can be saved outside and is easily translatable/reusable and everyone can use it without much teaching or errors. Data exists outside of compiled dlls so easier updating without a recompiled program. It can be used in asset bundles or just be downloaded. Easier diffing when working with Steam. Lots of advantages if you ask me.
     
  40. Vacummus

    Vacummus

    Joined:
    Dec 18, 2013
    Posts:
    191
    I have to disagree with this. Two years ago I was working on a prototype for an interactive storydriven game that relies on NPCs and dialogue just like in an RPG game, all done in ECS. I built a naive scripting solution based on a fluent API (similar to how the entities package is fluent API). The game consisted of scenes, and each scene consisted of snapshots(story-driven sections that make up a scene/chapter). The player is able to load any snapshot in the game from the menu without any loading screen. Below is an example of a snapshot of my game where a little girl (Gigi) and her brother (Buga) start outside of a Lighthouse and they want to go to the top of a lighthouse to see the sunset. In this snapshot, Gigi is player-controlled and Buga is the NPC.

    The snapshot is an ESC system that queries for SeeSunsetSnapshot and LoadSnapshot(tag) in BaseSnapshotSystem and runs the Load function below when there is a match. The load function creates a bunch of entities relevant to that snapshot (all the dialogue, props, characters, actions, and conditions to watch for).

    For me, this is very readable and very intuitive. I can see exactly what's happening in this snapshot from a big picture view. And I must say building something like this in ECS has been sooo much easier than with the traditional monobehaviour route. I have scene loading systems, dialogue systems, condition systems, action systems, follow target systems, etc., that support the scripting below. And all of it is fully decoupled thanks to the ECS pattern. Sure my condition system is constantly checking for conditions for every frame. But that's the beauty of DOTS, I can go with an easy and naive solution without worrying about performance implications. And if a snapshot ends up not performing very well, I can optimize for that snapshot without affecting other snapshots.

    Once I understood to think in terms of smart data layouts instead of smart code, ECS (and DOD in general) became architecture gold for me.

    Code (CSharp):
    1.  
    2. public class SeeSunsetSnapshotSystem: BaseSnapshotSystem<SeeSunsetSnapshot, LighthouseScene>
    3. {
    4.     protected override void Load(Entity entity, LighthouseScene scene, bool isInitialSceneLoad)
    5.     {
    6.         // Get camera and characters in the scene
    7.         var camera = Build.Camera();
    8.         var buga = new DefualtBuild(scene.buga);
    9.         var gigi = Build.Character(scene.gigi);
    10.  
    11.         // Create all Dialogue data for this sanpshot
    12.         var gigiSeeSunsetDialogue = Build.Dialogue(Keys.Lighthouse_SeeSunset_Gigi_SeeSunset)
    13.             .AddComponent(new DialogueFollowTarget { target = gigi.entity, offest = new float2(3, 3) })
    14.             .SetTimeTolive(2);
    15.         var bugaLetsDoItDialogue = Build.Dialogue(Keys.Lighthouse_SeeSunset_Buga_LetsDoIt)
    16.             .AddComponent(new DialogueFollowTarget { target = buga.entity, offest = new float2(-3, 3) })
    17.             .SetTimeTolive(2);
    18.        
    19.         // Create action to have Buga follow a path to lighthouse top patio
    20.         var bugaMoveToLighthouseTopPatioAction = BuildAction.FollowPathAction(buga, new float2(-17.5f, 25), Area.LighthouseTopPatio);
    21.  
    22.         // Position characters if snapshot is loaded from menu
    23.         if (isInitialSceneLoad)
    24.         {
    25.             buga.SetPosition(AssetId.Scene_Lighthouse_SeeSunset_BugaStart);;
    26.             gigi.SetPosition(AssetId.Scene_Lighthouse_SeeSunset_GigiStart);;
    27.  
    28.             // Set Gigi is as current player controlled character
    29.             EntityManager.RaiseEvent(new OnSetPlayerInput { player = scene.gigi });
    30.             // Set the current area of each character
    31.             EntityManager.RaiseEvent(new OnSetCurrentArea { target = scene.buga, area = Area.LighthouseOuter });
    32.             EntityManager.RaiseEvent(new OnSetCurrentArea { target = scene.gigi, area = Area.LighthouseOuter });
    33.         }
    34.  
    35.         // Make sure camera is following Gigi
    36.         camera.ReplaceFollowTarget(gigi.entity).ClearCameraTargetBounds();
    37.  
    38.         // Once Gigi gets near Buga, play Gigi's dialogue and have bug follow path to lighthouse top patio
    39.         Condition.OnTrue(Condition.WithinDistance(buga, gigi, 5))
    40.             .PlayDialogue(gigiSeeSunsetDialogue)
    41.             .PlayAction(bugaMoveToLighthouseTopPatioAction)
    42.             .DestroySelf();
    43.  
    44.         // Once Gigi's dialogue has played, play Buga's dialogue
    45.         gigiSeeSunsetDialogue.OnDialoguePlayed()
    46.             .PlayDialogue(bugaLetsDoItDialogue);
    47.        
    48.         // When Buga and Gigi reach the lighthouse top patio, load the next snapshot in the scene
    49.         Condition
    50.             .OnTrue(
    51.                 Condition.ActionPlayed(bugaMoveToLighthouseTopPatioAction),
    52.                 Condition.WithinDistance(gigi, buga, 5),
    53.                 gigi.OnAreaEnter(Area.LighthouseTopPatio)
    54.             )
    55.             .LoadSnapshot(SceneData.GetSnapshotEntity<LighthouseScene, BoatRescueSnapshot>())
    56.             .DestroySelf();
    57.     }
    58. }
    59.  
     
    andreiagmu, bb8_1, apkdev and 3 others like this.
  41. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    3,983
    I'd recommend not trying to write scripts inside of ECS systems. They are two separate layers. If anything, a system (or group of systems) should interpret and process the scripts. ECS is fully capable of supporting this, and this is actually fairly common among other ECS-based engines.

    However, you won't see that in DOTS for quite a while still. We still don't have many of the core gameplay features that something like this would interact with. Very few people understand how the animation package works, and even fewer have even bothered to implement audio. There's no native DOTS UI solution either. All of those are pretty essential for that genre. This is more a matter of it being way too early than ECS being the wrong answer.
     
    apkdev and burningmime like this.
  42. burningmime

    burningmime

    Joined:
    Jan 25, 2014
    Posts:
    845
    Most AAA RPGs I know of use scripting languages at some level, but I don't follow GDC much recently. I'm surprised if that's actually the trend, since web design has gone the other direction, with much more of an expectation that frontend designers can at least write some JS instead of using WYSIWYG editors, and the rise of various JS frameworks that mix visuals with code. The whole software industry has been a shift towards more code, not less. Eg when I interned at (big software giant), I was spending hours fighting Visio to make UML design docs, more recently my design documents have been psuedocode on a wiki plus a prototype in a git repo.

    I'm not sure how a visual editor would somehow allow you to "grasp the full picture of all the dialogue in your game and how it's intertwining" any more than scripts. Care to elaborate on that? You're not going to have all the game dialogue in a single, giant, visual editor instead of multiple files, are you? Either you have "loose scripts" or "loose visual editor inputs".

    And yes, Persona 5 had a scripting language; there's some details about it here: https://github.com/TGEnigma/Atlus-Script-Tools

    What's the difference between a compiled DLL for scripts and a compiled set of node data? With asmdefs, you don't recompile your whole program just the script DLL, and with "internal" you're not accidently exposing any more to script code than you would be if you had a Lua API exposed. A script is data.
     
    Last edited: Sep 28, 2021
  43. burningmime

    burningmime

    Joined:
    Jan 25, 2014
    Posts:
    845
    This is helpful; I can see where you're going with this. I still see a ton more boilerplate in there than I'd like, especially stuff like this:

    Code (csharp):
    1.  
    2.         Condition
    3.             .OnTrue(
    4.                 Condition.ActionPlayed(bugaMoveToLighthouseTopPatioAction),
    5.                 Condition.WithinDistance(gigi, buga, 5),
    6.                 gigi.OnAreaEnter(Area.LighthouseTopPatio)
    7.             )
    8.             .LoadSnapshot(SceneData.GetSnapshotEntity<LighthouseScene, BoatRescueSnapshot>())
    9.             .DestroySelf();
    10.  
    That really seems less readable and intuitve than this:

    Code (csharp):
    1.  
    2. if(bugaMoveToLighthouseTopPatioAction.isComplete && distance(gigi, buga) < 5)
    3.     playNextScene(BoatRescueSnapshot);
    4.  
    But it's not such a huge leap that it would be unworkable.
     
    andreiagmu likes this.
  44. Vacummus

    Vacummus

    Joined:
    Dec 18, 2013
    Posts:
    191
    I get where you are coming from. I have managed to reduce the boilerplate with the use of lambda functions. The example below is very close to what you want:
    Code (CSharp):
    1. Condition.If(() => bugaMoveToLighthouseTopPatioAction.HasPlayed() && distance(gigi, buga) < 5)
    2.     .Execute(() => SceneData.LoadSnapshot<LighthouseScene, BoatRescueSnapshot>());
    This is less data-oriented and more of a smart code approach than my previous example so it's def not going to perform as well. Can't use jobs and burst for this. The distance function is accepting two entities, so it needs to use EntityManager to get their positions. Can't help but feel like I am being naughty every time I use this approach. BUT, it's super convenient! A single snapshot for the prototype can contain between 5 to 30 conditions. So the performance loss/gains with this approach are neglectable for most of my use cases.
     
    andreiagmu likes this.
  45. runner78

    runner78

    Joined:
    Mar 14, 2015
    Posts:
    760
  46. unity-freestyle

    unity-freestyle

    Joined:
    Aug 26, 2015
    Posts:
    45
    HeyZoos, MicCode, simfgames and 4 others like this.
  47. Vacummus

    Vacummus

    Joined:
    Dec 18, 2013
    Posts:
    191
    It's a naive approach, but it worked great in my prototype plus it allowed me to do scripting via code instead of an editor (which I way more prefer, though a hybrid of the two would be best). But when you speak of two separate layers, can you speak more about what those layers look like? I assume one of those layers is the scripting/authoring layer and the other is a conversation layer that converts the script to runtime ECS data.
     
  48. Vacummus

    Vacummus

    Joined:
    Dec 18, 2013
    Posts:
    191
    That's all internal and not publicly accessible. It hasn't been merged to master yet and if you look at the bottom of the package file it is pointing to an internal artifactory npm registry (which can probably only be accessed if you are on Unity's internal VPN). But, glad to see a glimmer of progress!
     
    xVergilx likes this.
  49. runner78

    runner78

    Joined:
    Mar 14, 2015
    Posts:
    760
    The samples generally only exist in branches and not in the master.
     
  50. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    3,983
    Honestly, if I look at your example, it looks more like a script hardcoded into a system than a system itself. That's not a bad thing though. You actually have the same two layers. The code you shared doesn't actually handle the follow movement or execute the condition checks. It just sets that data up and it is up to the rest of the systems to execute those behaviors. I bet those other systems neither know nor care what a Gigi is or whether a sunset is a light source or a dishwasher.

    Games often have four layers to them. The lowest layer is the hardware and OS layer. Unity takes care of all of that, and it sits below the ECS. The second layer is the engine layer. This is where physics and rendering and transforms all operate. Unity provides solutions, but we can also write our own. Next is the game logic layer. This is logic like telling a character to jump when a button is pressed. We also write this inside of ECS. Lastly is the scripting layer, which is a really high-level orchestration of smaller behaviors to describe complex situations. This often sits on top of ECS (although your example shows how it can be implemented directly in ECS) and is interpreted either at conversion time or at runtime. Sometimes this is authored visually, sometimes via text.

    What messes people up was that the line between scripting and game logic was blurry in classic MonoBehaviours. However, in DOTS, that line needs to be sharp, and it is the line between the engine and the game logic that is allowed to get blurry instead.
     
    BayernMaik, andreiagmu, JesOb and 4 others like this.