Hi all,
I’ve been working on a few systems lately including an Attribute/Stats system, and one pattern keeps coming up that is bothering me regarding re-usable systems logic processing “lists” of something on entities.
Afaik there is no way to write a system that can query for a variable number of components that all have something in common?
If I would have a query that looks for component A OR B OR C etc. then I’m still not sure how I could iterate over a variable number of components on the entities found? And even then I wouldn’t have an interface to cast them to to process them in a common way.
I feel like I might be missing something…
For this Stats System:
- Entities can have a variable number of “stats” (ie Health, Food, Armor, Speed etc).
- Each stat has things like: CurrentValue, MinValue, MaxValue, NormalizedValue, ChangeOverTime.
- Then each stat has a “modifier stack” to add/subtract or multiply values for Min/Max and ChangeOverTime.
- The modifier stack is stored in a Dynamic Buffer.
- You add/remove entries in the modifier stack with “event” entities that a system responds to and edits the buffer.
- Over the lifetime of an entity, the number of items in the modifier stack changes but the number of stats stays the same.
- Every time the modifier stack changes we re-calculate the stat values as needed.
I want to have a generic system that can respond to changes in that stack and recalculate all the values as needed.
At a quick glance it feels that Stats should be component data (since the count doesn’t change), and the modifier stack should be a dynamic buffer (since the count does change).
I’ve come up with a few approaches, and don’t really like any of them… but perhaps leaning most towards #3
1) Each stat is a ComponentData (Health, Food etc).
- Pro: Very easy to read and write current stat values
- Pro: Easy to write systems that respond to conditions like Health.Value == 0.
- Pro: Full gain of sequential memory layout.
- Con: Since we don’t have inheritance or polymorphism, I would need to write a system for each stat that has the same logic. ie HealthStatSystem, FoodStatSystem and so on… And all those systems would have the same logic to bake down the modifier stack from the dynamic buffer.
2) All entity stats stored together in a “Stats bag” Dynamic Buffer.
- Pro: One system can do all the calculations to bake down the modifier stacks into the stat values.
- Con: Not as easy to read/write stat values.
- Con: Harder to write systems that respond to conditions like Health.Value == 0.
- Con: Memory layout not as ideal (not all Health stats together in sequence)
3) A generic system where each entry in modifier stack can write directly into a separate component on target entity.
- A rough version of this would be something like: “if (mod.TargetStat == StatType.Health) { healthComponent.Value = bakedValue; }”
- Pro: We gain all the benefits of memory layout when reading stats
- Pro: Systems can respond to conditions like Health.Value == 0
- Con: The stat component references would need to be brought in as GetComponentDataFromEntity()
- Con: Since the number of stats varies from entity to entity, we would need to do .Exists checks before accessing components from array
- Con: System needs write access to all stat components
- Con: “Hard coded” relationship of stat type and what component to set.
Any ideas would be appreciated
Cheers,
Siggi.