Post by Admin on Jul 16, 2016 4:26:16 GMT
William Taylor @willt Sep 05 2015 01:41
@mzaks Thanks for the response. Im sure eventually this will all click. Question if I have a transform and want to set the rotation as eulerAngles as part of my movement system. How should I structure my componets/systems to accomplish this ?
CodePoKE @gjroelofs Sep 05 2015 02:35
@sschmid What is the current way to use NoneOf, AnyOf after sschmid/Entitas-CSharp@3081a3c ?
William Taylor @willt Sep 05 2015 03:50
Am I correct in assuming if you need things running in FixedUpdate and Update, you would create two systems and place all things needing FixedUpdate in one and regular Update in the other ?
Maxim Zaks @mzaks Sep 05 2015 03:54
@willt yes, you could do that because they both are executed on the main thread. I would prefer to have only one run loop but I guess it is ok to have two. Again as long they are on the same thread. If you want to do multithreading the simplest way is to have one entity pool for each thread. So no sharing of entities between threads. You than have to think about how to sync data from one pool into another.
@willt about your previous question. the simples way is to have a component like this
github.com/sschmid/Match-One/blob/master/Assets/Sources/Features/RenderResource/ViewComponent.cs
you than can get to transform from the game object or even to a transform of a child if your game objects, if it is hierarchical. Again traversing game object could cost some performance but you cross that bridge when you get there and see that your move system is slow.
William Taylor @willt Sep 05 2015 05:17
@mzaks Okay thanks. I'm working through it now. I don't see any way around having two run loops if I need something to run in FixedUpdate (for physics) and other stuff in Update
Simon Schmid @sschmid Sep 05 2015 05:37
@willt If you have 2 run loops and both of them work on the same pool, you'll run into problems if you want to build a deterministic game... If determinism is not important to you, then you're fine
CodePoKE @gjroelofs Sep 05 2015 06:24
Does anyone else have issues when deleting components? I find that they keep coming back into the Generated folder.
``` @lsjroberts @mzaks @sschmid
We are considering adapting Entitas for a larger project, however the above is a real concern for us.
How does Entitas deal with a large codebase, or primarily under what circumstances will I have to go through my entire code and rip out all Entitas code to make sure the code generator can work?
CodePoKE @gjroelofs Sep 05 2015 06:34
Is it also possible to register a Component to all pools by default? This would come in handy with re-used components such as ones used to denote hierarchy or tags
astro75 @astro75 Sep 05 2015 06:45
@gjroelofs Entitas code generator works only on code that compiles. If you change existing components the previously generated code would not compile. I think I will try to write roslyn powered generator that would solve this issue.
Maxim Zaks @mzaks Sep 05 2015 06:46
@sschmid is right. The order of execution is not deterministic if you mix fixed and no fixed update. However it could be fine, dependent on the type of game and features you need in the game @willt
William Taylor @willt Sep 05 2015 06:48
Hoping someone makes a step by step tutorial soon covering different usage scenarios. If I figure this out soon ill post something up.
Maxim Zaks @mzaks Sep 05 2015 06:48
@astro75 that would be great. I wanted to do some MonoDevelop based plugins. But Roslyn would be great specifically for Windows users.
astro75 @astro75 Sep 05 2015 06:51
@mzaks It is impossible to code in Monodevelop after tasting Resharper :smile:
Maxim Zaks @mzaks Sep 05 2015 06:53
@gjroelofs I am not sure I understand the question. Do you mean entitas components or unity mono behaviours? You can mark an entitas component as singleton. This will make it and the entity that holds it accessible directly from a pool
@astro75 I am OSX user 😢
CodePoKE @gjroelofs Sep 05 2015 06:56
@mzaks Currently, when defining an Entitas Component you add in the PoolAttribute so the generates properly generates the code for those Pools; or leave it out so it generates for the default pool IIRC. How do you define a Component that will be added to all pools automatically?
So you can have Entitas Components in a binary per example.
Examples of these would be Entitas Components which provide common functionality (Tag /Group/Player/etc)
@mzaks @sschmid Also, how we use the Matcher None Of / Either of functionality since its removal in sschmid/Entitas-CSharp@3081a3c ?
Maxim Zaks @mzaks Sep 05 2015 07:01
@gjroelofs if you have multiple pools you can add multiple attributes than this component can be added to entities created from different pools. Binary however is another problem. Because code generator uses partial class feature to extend class. For this the class have to be present as a source file.
CodePoKE @gjroelofs Sep 05 2015 07:01
Also, I'm currently working on serialization / Editor usage, and I'm having some problems finding how to add a list of IComponent back on a Entity. How do I retrieve the proper idx of a Component given only an instance of a Component? Generated *ComponentIds doesn't seem to provide utility methods for this.
William Taylor @willt Sep 05 2015 07:03
Do you need to have a SetPool in every System ? If not when should you ?
CodePoKE @gjroelofs Sep 05 2015 07:03
The generated code for Components doesn't touch the Component class as far as I can see; so there should not be any problem with this?
But the prime concern is how I can enable a Component by default for >all< pools?
William Taylor @willt Sep 05 2015 07:05
Also if I have a PlayerController and I need say a RigidBody2D and an Animator should I add those directly to my Prefab or as components when creating the entity ?
Maxim Zaks @mzaks Sep 05 2015 07:07
@gjroelofs for reactive systems you can implement
github.com/sschmid/Entitas-CSharp/blob/develop/Entitas/Entitas/Interfaces/IExcludeComponents.cs
github.com/sschmid/Entitas-CSharp/blob/develop/Entitas/Entitas/Interfaces/IEnsureComponents.cs
Interfaces which will filter out entities according to the matcher.
This mechanism is much less error proven than the AnyOf and NoneOf matchers this is why we removed it.
Maxim Zaks @mzaks Sep 05 2015 07:12
@gjroelofs the reverse lookup component type to component index might be a good addition. I agree.
@willt do you mean ISetPool interface? it is up to you, it is just a convenience to inject the pool into the system.
CodePoKE @gjroelofs Sep 05 2015 07:15
How do we define these for Groups, or to filter on in entities?
Could you explain a bit on the "less error proven"?
Maxim Zaks @mzaks Sep 05 2015 07:16
@willt having a game object totally configured already as a prefab or adding the mono behaviours in a system on the fly is also a matter of taste and specific requirements I would say. I don't see no direct pros and cons to be honest.
William Taylor @willt Sep 05 2015 07:16
@mzaks yes. Is that better than calling Pool.pool ?
@mzaks I understand. I'm evaluating the idea of having all the components added dynamically or not. Maybe for something like random enemies it would work well but for a static-ish player it wouldn't.
Maxim Zaks @mzaks Sep 05 2015 07:18
@willt Pool.pool means that you have a singleton (global shared instance) this can have it's downsides for example if you want to test or if you want to move systems from one project to another.
Maxim Zaks @mzaks Sep 05 2015 07:33
@gjroelofs we stumbled many time upon the fact that when we use AnyOf or NoneOf we end up with logical bugs. Specifically when we combine them. For example a standalone NoneOf will trigger on everything that is not inside of it. It is really dangerous as a standalone matcher. So you have to combine it with other matchers. When you start combining matcher it is kind of logical but really hard to understand.
As a matter of fact even Prolog define logical rules as A :- B1, B2, B3
this is equivalent to A = AllOff(B1, B2, B3)
If you want to express Any off in prolog you have to write
A :- B1
A :- B2
Im context of reactive systems we added
github.com/sschmid/Entitas-CSharp/blob/develop/Entitas/Entitas/Interfaces/IMultiReactiveSystem.cs
To express any of relations ship in a convenient manner.
If you want to work directly on the Groups you would have to create multiple AllOff matcher and combine the checks with binary operators like && || and !
The Abstract Matcher is btw. still in present
github.com/sschmid/Entitas-CSharp/blob/develop/Entitas/Entitas/Matcher/AbstractMatcher.cs
You can "bring back" NoneOf and AnyOf Matchers in your own project if you want to. We however decided that they are to dangerous and therefor we removed it from the repository.
CodePoKE @gjroelofs Sep 05 2015 08:03
I'm not certain how Prolog has anything to do with it. But I'll bite for the sake of discussion :-) , Prolog has the OR operator ( and the NOT operator, and do just fine in combinations:
all :- A, B, C.
any :- A ; B ; C.
none :- +A, +B, +C.
(Although, yes in general OR is preferred in different heads, but this is done for code clarity)
For the matcher; why not go with the Artemis approach?
A matcher is then an instance containing three matchers for AND, ANY and NONE. github.com/junkdog/artemis-odb/blob/master/artemis/src/main/java/com/artemis/Aspect.java
But I take it the logical fallacy is made by the programmer then if I understand you correctly?
Maxim Zaks @mzaks Sep 05 2015 08:13
Yes this is what we saw repeatedly. Bugs due to misunderstanding of what the matcher describes and specifically how it affects the observing of a group.
E.g. a group wihich is defined by AllOf(A, B)
I want to react when an entity is added to the group.
I have an entity e1. where I add component A, this triggers the event. Than I add component B this doesn't trigger the event because e1 is already in the group. I can now replace A it still doesn't trigger anything because now it is still in the group because of the B.
However If I would create two groups one for A and another for B. Then I monitor both of them I will get every change which happens to entity e1.
this is what we achieve with IMultiReactiveSystem
Again you could argue that this is logical and you shouldn't have expected events to trigger. However we saw the even we made this mistake from time to time. What about someone who is new to the concept.
CodePoKE @gjroelofs Sep 05 2015 08:28
Ah, ok; so the concept is more in terms of the implementation of the Group.
My main concern is to use the Matcher on live, single queries.
I do not understand your example though; as the entity e1 should not have been added to the group until both A & B components were added.
Following what you just said, with the current implementation this definition is still allowed.
In the current code, I can do: Pools.a.GetGroup(Matcher.AllOf(A, B)).
I do not expect this code to trigger an event for an entity which has only the component A (as you describe it).
If this is true, then I would consider this a bug?
Simon Schmid @sschmid Sep 05 2015 08:29
I think, @mzaks mixed sth up in his example
CodePoKE @gjroelofs Sep 05 2015 08:31
@sschmid Could you maybe explain the reasoning behind the choice a bit more?
I'm a co-developer of artemis-odb, and we've extended the codebase in house to support event systems for our Java project.
For Unity we'd like to use Entitas, and I've used the concept of Aspect/Matcher in quite a few places for ducktyping.
So it feels strange to me that this could cause such issues, and gives me the inclination that I'm maybe missing a paradigm in Entitas which makes it harder.
Simon Schmid @sschmid Sep 05 2015 08:37
@gjroelofs true, we have a solution that replaces AnyOf, NoneOf in combination with ReactiveSystem by using MultiReactiveSystem (AnyOf) and IExcludeComponents for NoneOf. But for inline queries, we only have AllOf supported. After long discussions we removed AnyOf and NoneOf matchers, because ppl interpreted matchers containing all 3 sorts of them in different ways. Personally, I didn't have had problems with it. Another reason was, that we want to go for bitwise comparison in the future. It got really complicated having mixes of AllOf, AnyOf and NoneOf. Maybe we can take a look at artemis and get inspired
Simon Schmid @sschmid Sep 05 2015 08:42
I remember AnyOf and NoneOf being really useful for inline queries, but ppl also used them in weird ways as triggers for reactive systems that led to bugs.
The Aspects look interesting, maybe worth checking out a little bit closer
CodePoKE @gjroelofs Sep 05 2015 08:48
Might be that I'll push a PR tomorrow for a translation of the Aspect.
Simon Schmid @sschmid Sep 05 2015 08:49
that'd be awesome
CodePoKE @gjroelofs Sep 05 2015 08:49
My prime concern at the moment is the stability; how has the code generator held up in larger projects?
And how have you dealt with refactoring of components? (Refactoring on the generated code?)
Simon Schmid @sschmid Sep 05 2015 08:50
@willt you can implement ISetPool whenever you need the pool in the system. This way you can use the system on different pool (of the same type). If you use Pools.myPool instead, it's tightly coupled to myPool
William Taylor @willt Sep 05 2015 08:51
@sschmid Whats the best way to remove a component from your project? I always get a bunch of errors and struggle to get code regenerated properly.
@sschmid Oh okay thanks for the Pools explanation
Simon Schmid @sschmid Sep 05 2015 08:53
@gjroelofs I don't see any problems with the code generator in bigger projects. Once you get the hang of refactoring components, that's easy to do, too.
@willt
Delete the component
Delete the generated component found in the generated folder
Generate again to update the componentIds
William Taylor @willt Sep 05 2015 08:55
@sschmid I did that and it kept coming back. I had to remove it out of ComponentIds to really get rid of it. It was the first id in there if that makes a difference
Simon Schmid @sschmid Sep 05 2015 08:56
When clicking "generate" make sure
the solution compiles
Unity has finished compiling (the loading circle in the lower right corner is gone)
Otherwise you might generate based on the "old" dll
William Taylor @willt Sep 05 2015 08:56
@sschmid Okay ill look out for that next time
Really need a way to re-generate outside of unity if possible.
Simon Schmid @sschmid Sep 05 2015 08:57
when unity compiling indicator is still visible, you will generate based on the old dll an therefore deleted components seem to reappear
Maxim Zaks @mzaks Sep 05 2015 08:58
Ok so honestly I can't find any very good example from my current project except for maybe this one:
NotProducingBuilding = AllOf(Building, AnyOf(ProductionPaused, Upgrading), NoneOf(AccessingWater))
which is generally correct but pretty much complicated, this is also due to the fact that we started avoiding nested matchers. But nether the less I would be delighted to be proven wrong about the matchers because I see the benefit in complex expressions specifically when you want to express complex duck typing rules.
Simon Schmid @sschmid Sep 05 2015 08:58
you could have a post compile script in monodeveop generating components, similar to github.com/sschmid/Entitas-CSharp/blob/develop/Entitas.CodeGenerator/Program.cs
William Taylor @willt Sep 05 2015 08:59
Cool I'll look into that
Simon Schmid @sschmid Sep 05 2015 09:00
or you could write a command line tool, whatever suits you best. The generate button in unity is just one way to generate...
William Taylor @willt Sep 05 2015 09:01
I'm using VS but I think a hook into the IDE would suit me best. Ill post something on the wiki after I add it so others can see
Simon Schmid @sschmid Sep 05 2015 09:02
@mzaks yes, we should we should "reopen" the discussion on matchers next week... again
@willt awesome
Maxim Zaks @mzaks Sep 05 2015 09:04
@sschmid :+1:
CodePoKE @gjroelofs Sep 05 2015 09:11
@mzaks I agree that nesting matchers will just lead to confusion all around, see github.com/junkdog/artemis-odb/wiki/Aspect for an alternative
Simon Schmid @sschmid Sep 05 2015 09:18
Aspect.all(A, B, C).one(X, Y, Z).exclude(U, V) would be the same as
Matcher.AllOf(A, B, C, AnyOf(X, Y ,Z), NoneOf(U, V))
@mzaks we have to talk next week
Maxim Zaks @mzaks Sep 05 2015 09:19
looking forward
CodePoKE @gjroelofs Sep 05 2015 09:22
@sschmid Yes, that example would be; but by not accepting matchers as the arguments you can no longer nest the matchers.
Which removes most of the confusion, and actually will probably be more performant. (as you don't delegate and aggregate all the checks into 3 bitsets)
Hmm, I did find a problem with the code generator. If the code generator produces compile errors in your project you will need to strip all usage of Entitas out of your project to get it to compile again
E.g.: I had a Component with a Pool Attribute
The code generator borked on a class while I was working with it, it didn't finish the compile which left me in a broken state
The component then had a dep on the PoolAttribute which removed the possibility of compiling again.
Maxim Zaks @mzaks Sep 05 2015 17:43
@gjroelofs the current code generate relies on runtime reflection. This is a weakness because the code has to compile and be able to load into the memory before you can trigger it again. And as generated code and the code that uses generated code is part of the same solution, the odds that you might end up in a non compiling code when you do disruptive changes (not just adding new components, but renaming or deleting or even adding fields to components) are high.
This is why yesterday we had a discussion with @astro75 about using Roslyn or MonoDevelop build in parser as base for the generator. IDE parsers are more forgiving they have to be able to create AST even if your code does not compile. After all, developing is a process of making code uncouple first
For the current situation. We had the discussion about the problems about code generator in end June and start July, when we went public with Entitas and people where new to the framework. Most of the people also described the concern that it could slow them down, but after some time and really working with it and not just playing around (playing around implies destructive changes by default) we see less and less complains. This is also the same behaviour that we see at Wooga. I work with Entitas-CSharp already for a year in a team of four people. And we have another two teams which are a bit smaller. Some more teams at Wooga use Entitas-CSharp not as there main architecture but to solve some specific problems. And people say that code generator can be inconvenient but most of the time it fades away because you add more than you remove and as if you follow Single Responsibility Principle, the component and it's generated methods is used in a manageable amount of places.
@gjroelofs last comment should also answer the question about how production ready Entitas-CSharp is.
I just looked at the stats in my current project, we have 138 components in total (used in 3 pools) and 409 Systems.
Christian Schuster @chrischu Sep 05 2015 18:54
By the way regarding Roslyn on Mac: Afaik it is possible to run Roslyn on Mono (and therefore Mac/Linux), although it does not seem to be super-trivial (yet). See here: github.com/mono/roslyn
I haven't tested it though (seeing as I neither have a Linux nor a OSX machine), but maybe you could try it and see if that is a possible solution to the "does not compile problem".
CodePoKE @gjroelofs Sep 05 2015 19:53
@mzaks I fully understood the current framework ran on runtime reflection.
My main concern is the creation of cyclical generated dependencies (Component <-> Generated Code <-> Component), along with a policy of deleting generated code before confirming that the newly generated code works. In my small project this has already once given me the necessity of having to throw away all Entitas code to make it compile again
astro75 @astro75 Sep 05 2015 21:42
@chrischu I think if we use roslyn as dlls it should work in in both CLR and Mono runtime. github.com/mono/roslyn is possibly made to compile roslyn using mono (which we don't need).
Maxim Zaks @mzaks Sep 05 2015 23:48
As the topic comes back I repost my comment from end of July again
Now to the generator part.
Yes it can become inconvenient specifically because the generator works with Runtime reflection. So if the code does not compile you can't generate again. In large projects I would suggest to create a separate C# project/solution which contains just component classes and than use generator code so that it will produce generated extensions. This however means that you don't get Unity Menu, you have to do it manually, or write a demon which will trigger on every file change.
Now when you want to go with the standard way there you have to be aware of following caveats:
Renaming Component properties is the easiest one. Just use rename refactoring of your IDE and generate. Things should not break because properties only affect method parameter names in the generated methods. Your code should be safe.
Renaming Component name. Again use rename refactoring of your IDE, but after generate you will get compile errors due to the fact that Component name is reflected in generated Method Names. This is in my experience easily fixable. However if you want to avoid the hassle you can go to the generated class after you rename refactored your component name and than perform rename refactoring on each method name. It is up to 7 method names. It is inconvenient, but not as inconvenient as solving compile errors. Press generate and you are good to go.
Adding new properties to component. This by itself is fine, However after generate you will get compile errors. Because 2 method will get more parameters (AddXXX/ReplaceXXX). If your IDE lets you refactor method signature, than you are good. However if we are reasonable you have to change your code anyways so that you can add and replace components according to new set of properties. In this case having compile errors is actually a good thing.
Removing properties from component. This will directly lead to compilation errors because you probably used it somewhere already or at least the generated classes are using them. In this case you can just comment out implementation inside of (AddXXX/ReplaceXXX) methods of generated classes and fix your code which uses a property which you just removed. Press generate and you are good to go.
Deleting component. Delete the generated class completely, fix your code.
CodePoKE @gjroelofs Sep 05 2015 23:51
Maybe it would be interesting to post this caveat in the README as well?
And thank you for the write up
Maxim Zaks @mzaks Sep 05 2015 23:51
@gjroelofs I am not sure why there should be a cycle Componets <-> Generated Code
Normally you have something like Component <- Generated Code <- Game logic
If you change something in component you break generated code (4.) and (5.) in my list.
Ach I think I get the point that if you use Class attribute inside of the component and this one is also generated ...
CodePoKE @gjroelofs Sep 05 2015 23:52
@mzaks Exactly; the PoolAttributes attached to Components
Maxim Zaks @mzaks Sep 05 2015 23:53
Hm ... this is again a typical caveat when you still not sure how many pools you need (so in early stages of development). But a fair point.
CodePoKE @gjroelofs Sep 05 2015 23:54
Isn't this a general use case? If the dev uses any PoolAttribute (to designate which Pool the Component belongs to), you create a cycle Component <-> Generated Code <-> Component.
As the PoolAttribute generation will not trigger if the code does not compile AFAIK.
(But it could)
@mzaks Thanks for the response. Im sure eventually this will all click. Question if I have a transform and want to set the rotation as eulerAngles as part of my movement system. How should I structure my componets/systems to accomplish this ?
CodePoKE @gjroelofs Sep 05 2015 02:35
@sschmid What is the current way to use NoneOf, AnyOf after sschmid/Entitas-CSharp@3081a3c ?
William Taylor @willt Sep 05 2015 03:50
Am I correct in assuming if you need things running in FixedUpdate and Update, you would create two systems and place all things needing FixedUpdate in one and regular Update in the other ?
Maxim Zaks @mzaks Sep 05 2015 03:54
@willt yes, you could do that because they both are executed on the main thread. I would prefer to have only one run loop but I guess it is ok to have two. Again as long they are on the same thread. If you want to do multithreading the simplest way is to have one entity pool for each thread. So no sharing of entities between threads. You than have to think about how to sync data from one pool into another.
@willt about your previous question. the simples way is to have a component like this
github.com/sschmid/Match-One/blob/master/Assets/Sources/Features/RenderResource/ViewComponent.cs
you than can get to transform from the game object or even to a transform of a child if your game objects, if it is hierarchical. Again traversing game object could cost some performance but you cross that bridge when you get there and see that your move system is slow.
William Taylor @willt Sep 05 2015 05:17
@mzaks Okay thanks. I'm working through it now. I don't see any way around having two run loops if I need something to run in FixedUpdate (for physics) and other stuff in Update
Simon Schmid @sschmid Sep 05 2015 05:37
@willt If you have 2 run loops and both of them work on the same pool, you'll run into problems if you want to build a deterministic game... If determinism is not important to you, then you're fine
CodePoKE @gjroelofs Sep 05 2015 06:24
Does anyone else have issues when deleting components? I find that they keep coming back into the Generated folder.
``` @lsjroberts @mzaks @sschmid
We are considering adapting Entitas for a larger project, however the above is a real concern for us.
How does Entitas deal with a large codebase, or primarily under what circumstances will I have to go through my entire code and rip out all Entitas code to make sure the code generator can work?
CodePoKE @gjroelofs Sep 05 2015 06:34
Is it also possible to register a Component to all pools by default? This would come in handy with re-used components such as ones used to denote hierarchy or tags
astro75 @astro75 Sep 05 2015 06:45
@gjroelofs Entitas code generator works only on code that compiles. If you change existing components the previously generated code would not compile. I think I will try to write roslyn powered generator that would solve this issue.
Maxim Zaks @mzaks Sep 05 2015 06:46
@sschmid is right. The order of execution is not deterministic if you mix fixed and no fixed update. However it could be fine, dependent on the type of game and features you need in the game @willt
William Taylor @willt Sep 05 2015 06:48
Hoping someone makes a step by step tutorial soon covering different usage scenarios. If I figure this out soon ill post something up.
Maxim Zaks @mzaks Sep 05 2015 06:48
@astro75 that would be great. I wanted to do some MonoDevelop based plugins. But Roslyn would be great specifically for Windows users.
astro75 @astro75 Sep 05 2015 06:51
@mzaks It is impossible to code in Monodevelop after tasting Resharper :smile:
Maxim Zaks @mzaks Sep 05 2015 06:53
@gjroelofs I am not sure I understand the question. Do you mean entitas components or unity mono behaviours? You can mark an entitas component as singleton. This will make it and the entity that holds it accessible directly from a pool
@astro75 I am OSX user 😢
CodePoKE @gjroelofs Sep 05 2015 06:56
@mzaks Currently, when defining an Entitas Component you add in the PoolAttribute so the generates properly generates the code for those Pools; or leave it out so it generates for the default pool IIRC. How do you define a Component that will be added to all pools automatically?
So you can have Entitas Components in a binary per example.
Examples of these would be Entitas Components which provide common functionality (Tag /Group/Player/etc)
@mzaks @sschmid Also, how we use the Matcher None Of / Either of functionality since its removal in sschmid/Entitas-CSharp@3081a3c ?
Maxim Zaks @mzaks Sep 05 2015 07:01
@gjroelofs if you have multiple pools you can add multiple attributes than this component can be added to entities created from different pools. Binary however is another problem. Because code generator uses partial class feature to extend class. For this the class have to be present as a source file.
CodePoKE @gjroelofs Sep 05 2015 07:01
Also, I'm currently working on serialization / Editor usage, and I'm having some problems finding how to add a list of IComponent back on a Entity. How do I retrieve the proper idx of a Component given only an instance of a Component? Generated *ComponentIds doesn't seem to provide utility methods for this.
William Taylor @willt Sep 05 2015 07:03
Do you need to have a SetPool in every System ? If not when should you ?
CodePoKE @gjroelofs Sep 05 2015 07:03
The generated code for Components doesn't touch the Component class as far as I can see; so there should not be any problem with this?
But the prime concern is how I can enable a Component by default for >all< pools?
William Taylor @willt Sep 05 2015 07:05
Also if I have a PlayerController and I need say a RigidBody2D and an Animator should I add those directly to my Prefab or as components when creating the entity ?
Maxim Zaks @mzaks Sep 05 2015 07:07
@gjroelofs for reactive systems you can implement
github.com/sschmid/Entitas-CSharp/blob/develop/Entitas/Entitas/Interfaces/IExcludeComponents.cs
github.com/sschmid/Entitas-CSharp/blob/develop/Entitas/Entitas/Interfaces/IEnsureComponents.cs
Interfaces which will filter out entities according to the matcher.
This mechanism is much less error proven than the AnyOf and NoneOf matchers this is why we removed it.
Maxim Zaks @mzaks Sep 05 2015 07:12
@gjroelofs the reverse lookup component type to component index might be a good addition. I agree.
@willt do you mean ISetPool interface? it is up to you, it is just a convenience to inject the pool into the system.
CodePoKE @gjroelofs Sep 05 2015 07:15
How do we define these for Groups, or to filter on in entities?
Could you explain a bit on the "less error proven"?
Maxim Zaks @mzaks Sep 05 2015 07:16
@willt having a game object totally configured already as a prefab or adding the mono behaviours in a system on the fly is also a matter of taste and specific requirements I would say. I don't see no direct pros and cons to be honest.
William Taylor @willt Sep 05 2015 07:16
@mzaks yes. Is that better than calling Pool.pool ?
@mzaks I understand. I'm evaluating the idea of having all the components added dynamically or not. Maybe for something like random enemies it would work well but for a static-ish player it wouldn't.
Maxim Zaks @mzaks Sep 05 2015 07:18
@willt Pool.pool means that you have a singleton (global shared instance) this can have it's downsides for example if you want to test or if you want to move systems from one project to another.
Maxim Zaks @mzaks Sep 05 2015 07:33
@gjroelofs we stumbled many time upon the fact that when we use AnyOf or NoneOf we end up with logical bugs. Specifically when we combine them. For example a standalone NoneOf will trigger on everything that is not inside of it. It is really dangerous as a standalone matcher. So you have to combine it with other matchers. When you start combining matcher it is kind of logical but really hard to understand.
As a matter of fact even Prolog define logical rules as A :- B1, B2, B3
this is equivalent to A = AllOff(B1, B2, B3)
If you want to express Any off in prolog you have to write
A :- B1
A :- B2
Im context of reactive systems we added
github.com/sschmid/Entitas-CSharp/blob/develop/Entitas/Entitas/Interfaces/IMultiReactiveSystem.cs
To express any of relations ship in a convenient manner.
If you want to work directly on the Groups you would have to create multiple AllOff matcher and combine the checks with binary operators like && || and !
The Abstract Matcher is btw. still in present
github.com/sschmid/Entitas-CSharp/blob/develop/Entitas/Entitas/Matcher/AbstractMatcher.cs
You can "bring back" NoneOf and AnyOf Matchers in your own project if you want to. We however decided that they are to dangerous and therefor we removed it from the repository.
CodePoKE @gjroelofs Sep 05 2015 08:03
I'm not certain how Prolog has anything to do with it. But I'll bite for the sake of discussion :-) , Prolog has the OR operator ( and the NOT operator, and do just fine in combinations:
all :- A, B, C.
any :- A ; B ; C.
none :- +A, +B, +C.
(Although, yes in general OR is preferred in different heads, but this is done for code clarity)
For the matcher; why not go with the Artemis approach?
A matcher is then an instance containing three matchers for AND, ANY and NONE. github.com/junkdog/artemis-odb/blob/master/artemis/src/main/java/com/artemis/Aspect.java
But I take it the logical fallacy is made by the programmer then if I understand you correctly?
Maxim Zaks @mzaks Sep 05 2015 08:13
Yes this is what we saw repeatedly. Bugs due to misunderstanding of what the matcher describes and specifically how it affects the observing of a group.
E.g. a group wihich is defined by AllOf(A, B)
I want to react when an entity is added to the group.
I have an entity e1. where I add component A, this triggers the event. Than I add component B this doesn't trigger the event because e1 is already in the group. I can now replace A it still doesn't trigger anything because now it is still in the group because of the B.
However If I would create two groups one for A and another for B. Then I monitor both of them I will get every change which happens to entity e1.
this is what we achieve with IMultiReactiveSystem
Again you could argue that this is logical and you shouldn't have expected events to trigger. However we saw the even we made this mistake from time to time. What about someone who is new to the concept.
CodePoKE @gjroelofs Sep 05 2015 08:28
Ah, ok; so the concept is more in terms of the implementation of the Group.
My main concern is to use the Matcher on live, single queries.
I do not understand your example though; as the entity e1 should not have been added to the group until both A & B components were added.
Following what you just said, with the current implementation this definition is still allowed.
In the current code, I can do: Pools.a.GetGroup(Matcher.AllOf(A, B)).
I do not expect this code to trigger an event for an entity which has only the component A (as you describe it).
If this is true, then I would consider this a bug?
Simon Schmid @sschmid Sep 05 2015 08:29
I think, @mzaks mixed sth up in his example
CodePoKE @gjroelofs Sep 05 2015 08:31
@sschmid Could you maybe explain the reasoning behind the choice a bit more?
I'm a co-developer of artemis-odb, and we've extended the codebase in house to support event systems for our Java project.
For Unity we'd like to use Entitas, and I've used the concept of Aspect/Matcher in quite a few places for ducktyping.
So it feels strange to me that this could cause such issues, and gives me the inclination that I'm maybe missing a paradigm in Entitas which makes it harder.
Simon Schmid @sschmid Sep 05 2015 08:37
@gjroelofs true, we have a solution that replaces AnyOf, NoneOf in combination with ReactiveSystem by using MultiReactiveSystem (AnyOf) and IExcludeComponents for NoneOf. But for inline queries, we only have AllOf supported. After long discussions we removed AnyOf and NoneOf matchers, because ppl interpreted matchers containing all 3 sorts of them in different ways. Personally, I didn't have had problems with it. Another reason was, that we want to go for bitwise comparison in the future. It got really complicated having mixes of AllOf, AnyOf and NoneOf. Maybe we can take a look at artemis and get inspired
Simon Schmid @sschmid Sep 05 2015 08:42
I remember AnyOf and NoneOf being really useful for inline queries, but ppl also used them in weird ways as triggers for reactive systems that led to bugs.
The Aspects look interesting, maybe worth checking out a little bit closer
CodePoKE @gjroelofs Sep 05 2015 08:48
Might be that I'll push a PR tomorrow for a translation of the Aspect.
Simon Schmid @sschmid Sep 05 2015 08:49
that'd be awesome
CodePoKE @gjroelofs Sep 05 2015 08:49
My prime concern at the moment is the stability; how has the code generator held up in larger projects?
And how have you dealt with refactoring of components? (Refactoring on the generated code?)
Simon Schmid @sschmid Sep 05 2015 08:50
@willt you can implement ISetPool whenever you need the pool in the system. This way you can use the system on different pool (of the same type). If you use Pools.myPool instead, it's tightly coupled to myPool
William Taylor @willt Sep 05 2015 08:51
@sschmid Whats the best way to remove a component from your project? I always get a bunch of errors and struggle to get code regenerated properly.
@sschmid Oh okay thanks for the Pools explanation
Simon Schmid @sschmid Sep 05 2015 08:53
@gjroelofs I don't see any problems with the code generator in bigger projects. Once you get the hang of refactoring components, that's easy to do, too.
@willt
Delete the component
Delete the generated component found in the generated folder
Generate again to update the componentIds
William Taylor @willt Sep 05 2015 08:55
@sschmid I did that and it kept coming back. I had to remove it out of ComponentIds to really get rid of it. It was the first id in there if that makes a difference
Simon Schmid @sschmid Sep 05 2015 08:56
When clicking "generate" make sure
the solution compiles
Unity has finished compiling (the loading circle in the lower right corner is gone)
Otherwise you might generate based on the "old" dll
William Taylor @willt Sep 05 2015 08:56
@sschmid Okay ill look out for that next time
Really need a way to re-generate outside of unity if possible.
Simon Schmid @sschmid Sep 05 2015 08:57
when unity compiling indicator is still visible, you will generate based on the old dll an therefore deleted components seem to reappear
Maxim Zaks @mzaks Sep 05 2015 08:58
Ok so honestly I can't find any very good example from my current project except for maybe this one:
NotProducingBuilding = AllOf(Building, AnyOf(ProductionPaused, Upgrading), NoneOf(AccessingWater))
which is generally correct but pretty much complicated, this is also due to the fact that we started avoiding nested matchers. But nether the less I would be delighted to be proven wrong about the matchers because I see the benefit in complex expressions specifically when you want to express complex duck typing rules.
Simon Schmid @sschmid Sep 05 2015 08:58
you could have a post compile script in monodeveop generating components, similar to github.com/sschmid/Entitas-CSharp/blob/develop/Entitas.CodeGenerator/Program.cs
William Taylor @willt Sep 05 2015 08:59
Cool I'll look into that
Simon Schmid @sschmid Sep 05 2015 09:00
or you could write a command line tool, whatever suits you best. The generate button in unity is just one way to generate...
William Taylor @willt Sep 05 2015 09:01
I'm using VS but I think a hook into the IDE would suit me best. Ill post something on the wiki after I add it so others can see
Simon Schmid @sschmid Sep 05 2015 09:02
@mzaks yes, we should we should "reopen" the discussion on matchers next week... again
@willt awesome
Maxim Zaks @mzaks Sep 05 2015 09:04
@sschmid :+1:
CodePoKE @gjroelofs Sep 05 2015 09:11
@mzaks I agree that nesting matchers will just lead to confusion all around, see github.com/junkdog/artemis-odb/wiki/Aspect for an alternative
Simon Schmid @sschmid Sep 05 2015 09:18
Aspect.all(A, B, C).one(X, Y, Z).exclude(U, V) would be the same as
Matcher.AllOf(A, B, C, AnyOf(X, Y ,Z), NoneOf(U, V))
@mzaks we have to talk next week
Maxim Zaks @mzaks Sep 05 2015 09:19
looking forward
CodePoKE @gjroelofs Sep 05 2015 09:22
@sschmid Yes, that example would be; but by not accepting matchers as the arguments you can no longer nest the matchers.
Which removes most of the confusion, and actually will probably be more performant. (as you don't delegate and aggregate all the checks into 3 bitsets)
Hmm, I did find a problem with the code generator. If the code generator produces compile errors in your project you will need to strip all usage of Entitas out of your project to get it to compile again
E.g.: I had a Component with a Pool Attribute
The code generator borked on a class while I was working with it, it didn't finish the compile which left me in a broken state
The component then had a dep on the PoolAttribute which removed the possibility of compiling again.
Maxim Zaks @mzaks Sep 05 2015 17:43
@gjroelofs the current code generate relies on runtime reflection. This is a weakness because the code has to compile and be able to load into the memory before you can trigger it again. And as generated code and the code that uses generated code is part of the same solution, the odds that you might end up in a non compiling code when you do disruptive changes (not just adding new components, but renaming or deleting or even adding fields to components) are high.
This is why yesterday we had a discussion with @astro75 about using Roslyn or MonoDevelop build in parser as base for the generator. IDE parsers are more forgiving they have to be able to create AST even if your code does not compile. After all, developing is a process of making code uncouple first
For the current situation. We had the discussion about the problems about code generator in end June and start July, when we went public with Entitas and people where new to the framework. Most of the people also described the concern that it could slow them down, but after some time and really working with it and not just playing around (playing around implies destructive changes by default) we see less and less complains. This is also the same behaviour that we see at Wooga. I work with Entitas-CSharp already for a year in a team of four people. And we have another two teams which are a bit smaller. Some more teams at Wooga use Entitas-CSharp not as there main architecture but to solve some specific problems. And people say that code generator can be inconvenient but most of the time it fades away because you add more than you remove and as if you follow Single Responsibility Principle, the component and it's generated methods is used in a manageable amount of places.
@gjroelofs last comment should also answer the question about how production ready Entitas-CSharp is.
I just looked at the stats in my current project, we have 138 components in total (used in 3 pools) and 409 Systems.
Christian Schuster @chrischu Sep 05 2015 18:54
By the way regarding Roslyn on Mac: Afaik it is possible to run Roslyn on Mono (and therefore Mac/Linux), although it does not seem to be super-trivial (yet). See here: github.com/mono/roslyn
I haven't tested it though (seeing as I neither have a Linux nor a OSX machine), but maybe you could try it and see if that is a possible solution to the "does not compile problem".
CodePoKE @gjroelofs Sep 05 2015 19:53
@mzaks I fully understood the current framework ran on runtime reflection.
My main concern is the creation of cyclical generated dependencies (Component <-> Generated Code <-> Component), along with a policy of deleting generated code before confirming that the newly generated code works. In my small project this has already once given me the necessity of having to throw away all Entitas code to make it compile again
astro75 @astro75 Sep 05 2015 21:42
@chrischu I think if we use roslyn as dlls it should work in in both CLR and Mono runtime. github.com/mono/roslyn is possibly made to compile roslyn using mono (which we don't need).
Maxim Zaks @mzaks Sep 05 2015 23:48
As the topic comes back I repost my comment from end of July again
Now to the generator part.
Yes it can become inconvenient specifically because the generator works with Runtime reflection. So if the code does not compile you can't generate again. In large projects I would suggest to create a separate C# project/solution which contains just component classes and than use generator code so that it will produce generated extensions. This however means that you don't get Unity Menu, you have to do it manually, or write a demon which will trigger on every file change.
Now when you want to go with the standard way there you have to be aware of following caveats:
Renaming Component properties is the easiest one. Just use rename refactoring of your IDE and generate. Things should not break because properties only affect method parameter names in the generated methods. Your code should be safe.
Renaming Component name. Again use rename refactoring of your IDE, but after generate you will get compile errors due to the fact that Component name is reflected in generated Method Names. This is in my experience easily fixable. However if you want to avoid the hassle you can go to the generated class after you rename refactored your component name and than perform rename refactoring on each method name. It is up to 7 method names. It is inconvenient, but not as inconvenient as solving compile errors. Press generate and you are good to go.
Adding new properties to component. This by itself is fine, However after generate you will get compile errors. Because 2 method will get more parameters (AddXXX/ReplaceXXX). If your IDE lets you refactor method signature, than you are good. However if we are reasonable you have to change your code anyways so that you can add and replace components according to new set of properties. In this case having compile errors is actually a good thing.
Removing properties from component. This will directly lead to compilation errors because you probably used it somewhere already or at least the generated classes are using them. In this case you can just comment out implementation inside of (AddXXX/ReplaceXXX) methods of generated classes and fix your code which uses a property which you just removed. Press generate and you are good to go.
Deleting component. Delete the generated class completely, fix your code.
CodePoKE @gjroelofs Sep 05 2015 23:51
Maybe it would be interesting to post this caveat in the README as well?
And thank you for the write up
Maxim Zaks @mzaks Sep 05 2015 23:51
@gjroelofs I am not sure why there should be a cycle Componets <-> Generated Code
Normally you have something like Component <- Generated Code <- Game logic
If you change something in component you break generated code (4.) and (5.) in my list.
Ach I think I get the point that if you use Class attribute inside of the component and this one is also generated ...
CodePoKE @gjroelofs Sep 05 2015 23:52
@mzaks Exactly; the PoolAttributes attached to Components
Maxim Zaks @mzaks Sep 05 2015 23:53
Hm ... this is again a typical caveat when you still not sure how many pools you need (so in early stages of development). But a fair point.
CodePoKE @gjroelofs Sep 05 2015 23:54
Isn't this a general use case? If the dev uses any PoolAttribute (to designate which Pool the Component belongs to), you create a cycle Component <-> Generated Code <-> Component.
As the PoolAttribute generation will not trigger if the code does not compile AFAIK.
(But it could)