internal readonly struct CarComponent : IComponent {
public readonly float Speed = 100f;
[Slider(1,20f)] public readonly float Mass = 1;
public CarComponent() {}
}
and of a system: [Filter(typeof(CarComponent), typeof(AgentImpulseComponent))]
[Filter(ContextAccessorFilter.NoneOf, typeof(DisableAgentComponent), typeof(CarEngineStoppedComponent) )]
internal class CarMoverSystem : IFixedUpdateSystem {
public void FixedUpdate(Context context) {
foreach (var e in context.Entities) {
var car = e.GetComponent<CarComponent>();
Vector2 startVelocity = e.TryGetVelocity()?.Velocity ?? Vector2.Zero;
var result = DoSomeCalculations(car, startVelocity);
e.RemoveFriction();
e.SetVelocity(result);
}
}
}
Looks a bit like DOTS (but I haven't really tried that one either). A system is a class, and which interface it implements determines when it runs. Haven't seen yet where systems are get added to the game / how systems of the same kind get ordered. I'm also curious how a system that does multiple different querries (e.g. one for cars and one for obstacles) looks like.Bang seems to use source code generators – unlike the generic GetComponent<T> above, TryGetVelocity, RemoveFriction and SetVelocity seem to be generated based on the existence of the Velocity and Friction components. I'm not sure why that is, maybe to cache the component id?
This isn't an archetype-based framework, instead each entity stores a dictionary of component id => component and each system tracks what entities match (also, each entity stores hooks for e.g. when a component gets added, which is used update which systems know about this entity). I don't know how good that's for performance, but for plenty of games performance isn't as important as ease of development.
Also looks like changes are applied immediately. Does this mean that if a system add/remove a component to an entity that affects whether the entity matches the systems filter, it depends on the entity id whether the system processes the entity the same tick?