Closed jonathanpmast closed 7 years ago
I don't think that really solves the problem though, to solve the problem of dependencies a programmer should be able to define those dependencies and have the system sort the execution order (if at all possible) itself. This would better done at generation time, by a generator, or compile time using annotations.
Something like:
[dependsOn(SystemB)]
SystemA {
...
}
However this still doesn't really hit the spot, two better alternatives would be: 1) immutable global data (components) thats is only changeable at the end of a frame through callbacks, or dedicated events. 2) defining systems as flows, where one system takes as input the output of previous systems.
The first option is actually easier to accomplish, even using the current Entitas code, incidentally this also solves the problem of executing systems in parallel.
Yeah, I am having the same problem. I put systems in features grouped by functionality, but this makes priority very tricky sometimes. I have lots of systems and it is very hard to keep them in the right order.
Would be nice if we could just ensure that all systems picked up every change. Probably will cause some unwanted side effects, but with a large number of systems ensuring system ordering is correct is very painful.
The priority on a system might be an easy alternative.
Challenge Accepted. ;)
Sounds like fun.
The only problem I currently see with generating system chains is with the system constructors. If you add an attribute to a system with a priority and in a data provider gather all systems with that attribute you would then pass the data along to a generator which would add the types to chain. But what if they have a constructor which isn't empty? How would you fill them?
Systems with parameter-less constructors could easily be generated though.
@T2RKUS could you give an example for how it would be used?
add an attribute like: [System(10)]
onto a system etc. It would gather all systems with System
attributes and generate a single chain of systems from the highest priority to the lowest. System with priority 0 would be called before a system with priority 10.
Don't instantiate them from a generator. Just leave everything as is and in a final step - before the context calls the Initializers - execute a Sort() or whatever is needed to get them in order. I see more of a problem with nested systems in @RichPruitts usecase. Since each Feature is responsible for executing its child systems, the sorting can only be relative to siblings.
Wouldn't that mean rewriting the Systems class?
If you want to integrate it properly, yes. On the other hand, you can probably derive your own system and do it in there.
In that case we can create a new systems class called PrioritySystems which derive from Systems with a Sort method (as you said) and then sort the systems by priority value on a priority interface. Something like that perhaps?
Didn't realize the lists were protected.
I've run into this problem recently, too. I am now experimenting with class attributes. I've divided all systems into phases (which is something like the priority you were talking about - but with names instead of numbers). The usage is something like:
[SystemPhase(Phase.SomePhase)]
public class ASystem : ReactiveSystem<GameEntity>
This helps me to prevent order collisions by defining logical groups with exact order. But this is sometimes not enough. So I have another layer of control with something like this:
[ExecutesAfter(typeof(AnotherSystem), typeof(YetAnotherSystem))]
public class ASystem : ReactiveSystem<GameEntity>
It makes a graph of dependencies and than adds the systems to each logical group with corresponding order (if there are no cycles).
I don't know if it solves all my problems but it looks like it could save me some time and make the systems not that fragile.
why build up a dependency graph when you can simply prioritize them by a number and sort them by it.
if you want to a "phase" you could have several systems with '100' priority and begin another phase at '200' for example. A system of priority '1' would run before '2' etc.
Well, I find it easier to just declare that a system must run after some other systems rather than managing priorities with numbers.
What is the benefit of managing priorities? It seems to me that it is not really different from what I was doing earlier - having a list of systems in my GameController and thinking about what system goes first.
I like the dependency diagram because some systems are completely independent so I do not have to specify any order. But then there are some system that must be enabled after like 3 other systems so I just write it as [ExecutesAfter(..)] and it finds the order itself. I just have to specify the dependencies and don't have to worry about the bigger picture.
PS: I have the "phases" mainly because my game has also a multiplayer and I need to maintain some invariants regarding systems order. It could work without them - just with dependecies.
Well if you find it easier, fair enough. I personally find the priority approach easier because I don't have to deal with a graph of dependencies; simpler code. Might not be easier to read but it forces me to keep the execution order in mind yet still easier than rearranging systems around. I can just change a number and it can be every '100' or so if I want to run things in between.
@T2RKUS Defining dependencies explicitly has the advantage of being easier to use, less error prone, more informative, and save the programmer the actual work of managing dependencies.
Specifying indexes gives you no more than just a bit of more convenient way to do what can already be done.
Hi, sorry joining late to this conversation :)
Not sure if I'm missing sth, but I can't see an advantage having priorities over a simple list of systems like:
systems.Add(systemA); // prio 1
systems.Add(systemB); // prio 2
There's no functionality added, right? I only see 2 downsides:
MoveSystem
that contains information about the execution order and use it in an other game, doesn't make sense, right? Adding an index to the systems hides the execution order from the outside and might feel like magic, which should always be avoided imo. Systems should be managed and ordered by a supervisor, the Systems
class, e.g. in the GameController.I wouldn't recommend it, but if you want to use priorities anyway, you could add a custom interface for the priority and implement it in systems and use it to sort and populate the systems class.
It just goes counter to the concept of features as has been stated. I see both sides to this argument, my primary concern is the order of execution for systems is implicitly determined by the order they are added to the supervisor, rather than configuration.
This means that if I have a lot of systems--which any non-trivial simulation will have dozens or more--then in having to basically add all of the systems to a collection in a serial process one after another.
I suppose as has been said, one way to do this is to just add them all to a dictionary, sort them, and add them based on sort order at initialization time.
my primary concern is the order of execution for systems is implicitly determined by the order they are added to the supervisor
True, but I'd say it's explicitly determined :)
If a solution like the sorted dict makes sense for you, you can definitely do that.
This means that if I have a lot of systems--which any non-trivial simulation will have dozens or more--
Very true! My previous project had ~700 systems (including parent systems). I heavily use parent / child systems which helps a lot to manage these many systems. I imagine using explicit priorities alone would be very difficult to do, especially since developing a game is a dynamic process where you add and remove systems frequently. Adding a system should result in re-applying and re-thinking priorities of existing systems.
I heavily use parent / child systems which helps a lot to manage these many systems.
🤔 what's a parent system?
Just a container with subsystems. I use them to group and manage systems
I see. So really in this case the Feature
is the Parent System and it contains a bunch of Child systems.
This is where my concern comes in around execution priority as if I break Systems down logically like this I have to crawl through all the Parents/Features to determine in what order the execute.
Obviously ways to fix this outside of Entitas, I've just seen the priority method leveraged in other ECS so was curious if/why not in Entitas.
Closing now 🚀
As I understand Entitas system execution today, the order of operation is based on what order the systems are added to the
Systems
object/collection.Has there been any thought or appetite toward giving the developer overt control over execution order? I.e. when I add a system to the collection, I give an int priority. Every time a system is added to the
Systems
object they are sorted internally and executed in order.Just a thought, as currently there requires a lot of thought as to what order your code is written for adding these systems.