I’m trying to write some generic systems for reuse. For example, I might create an Entity with “Foo” and “Request” components to cause another system to initialize me a new Entity with a Foo component. This allows me to keep the logic for initialization of new archetype instances confined to a single system, rather than at the various call sites of CreateEntity. I have been trying to create an inheritable system that allows me to quickly write a system supporting this pattern.
The interface I had in mind:
public class FooBarFactory : RequestSystem<Foo, Bar> { // literally all of the boilerplate
protected override JobHandle Preprocess(NativeArray<Foo> foos, NativeArray<Bar> bars) {
// return some job initializing each foo and bar
// parent system will execute this, create the entity, destroy the request entity
}
}
And, well, for a correctly-written RequestSystem<T0, T1>, the above works. However, as we all know, you need to have a separate (but nearly identical) RequestSystem definition for every number of type arguments + every combination of component/shared data you want to support. If C# supported metaprogramming, this would be fine, but it’s an ugly workaround to write all that out. RequestSystem<T0, T1, T2, T3Shared, T4Shared> is gross.
I considered, instead, having a single system that processes all the Request entities via chunk iteration. You then could, based on the archetype of each chunk, allow some other injected system to schedule the job to modify the components attached to the original request. Then, this RequestSystem could simply take all the now-updated-components from the Request entity and set them on a new Entity built from an Archetype defined by the injected system. This also works, in the simplest case, but becomes problematic when the injected systems’ jobs try to get write access to the same component at the same time (even, sadly, when they are not in the same chunk, which is always). Suppose you want a (Request, Foo, Bar) and a (Request, Bar) made at the same time. Both injected systems will attempt to mutate Bar components in the provided chunk so the RequestSystem can, at the end, move the data from those Bar components to the final entity being created. Despite the fact that they are operating on different chunks, this mutation is disallowed, even with [NativeDisableParallelForRestriction].
On top of that, it is confusing to me how I would even create the resultant Entities from the RequestSystem. Were I to schedule a job that blindly executes on all Request components to create the entities for each, I would need to pass some sort of collection of types to the job that can be iterated upon for each different archetype. Otherwise the job would not know how many SetComponent calls to do after creating the Entity.
tl;dr - I would like to reduce amount of code in certain systems that reuse logic across a variable number of component types. How?
