#Ability System

1 messages · Page 1 of 1 (latest)

tribal roost
#

What is the best way to create a complex ability system with ECS? handling complex passives, skill interaction and complex abilities?

minor knot
#

What's the use case?
Tons of entities cast abilities or only player casts them?

velvet walrus
#

Since an "ability" could be almost anything, it would be good to have one or two concrete examples to work with

tribal roost
#

a concrete example is diablo 4 where i can cast firebolts, meteors, jump and attack on ground

#

or a passive where it adds 48 fire damage on hit

#

This things like on-hit modifiers (or regen 5 health on take damage) i don`t know how to do in dots too

#

@velvet walrus do you think polymorphic structs can help?

tribal roost
velvet walrus
#

At its core, an ability might be something that has a cooldown, a cost, and an action to execute when fired. There are many possible implementations of this, but one would be:

  • a unit has a dynamic buffer of ability entities
  • each ability entity has an Ability component, which contains things like cooldown time, mana cost, entity prefab to spawn when firing the ability, etc
  • a AbilityCooldownSystem updates ability cooldowns on ability entities
  • when a player system detects that we've pressed the input to fire ability 3, we get the ability entity at the 3rd index in our ability entities buffer, and we instantiate the ability's action prefab (if the cooldown/cost allows it)
  • systems now process the logic of that ability action's entity. A fireball action would do spherecasts and move to detect hits, etc....
tribal roost
#

But how to compose that ability logic? it can jump, dash, create another entity (like a meteor) or create a projectile.

Is it fine to have like a tagged union with all possible abilities type? won`t this system become huge?

#

a system that can create projectiles, aoes, dash, jump apply damage and apply a stun

#

It looks like it will require all components and run single threaded

velvet walrus
#

With the example implementation I've described above, launching an ability just means instantiating an action entity prefab that the ability stores. Each ability can instantiate a different action entity with different components, and each type of action will have its own types of components/systems

#

the exception to this would be passives. For passives, the ability entity could have an extra component (along with a system) that runs the passive logic. Passive abilities would have their action prefab set to Entity.Null so they don't have anything to "execute" on button press

tribal roost
#

Thank you. It looks good

#

What about melee skills? is it fine to create "damage effect" entities?

minor knot
#

hmmm

#

that just gave me an idea

#

of having entity - processor with buffer

#

basically, ability has a reference to some effect entity.
When ability wants that effect to work, it gets it's buffer and adds relevant data to it

velvet walrus
#

the ability's action entity for a "Do a big sword slash" ability could just have a component with a reference to the unit that casted it. A system iterates on that component, activates a control for doing that specific melee attack on the caster unit, and destroys the action entity

You could avoid having to spawn an ability action for this, if for example you implement different types of abilities in the Ability component:

  • some are passive
  • some spawn an action entity (or maybe even a buffer of action entities)
  • some activate pre-established flags on a target entity
  • etc...

You'd just do a switch statement over a AbilityType enum when firing the ability

tropic scroll
#

pretty sure this topic is a nerd snipe 🤣

velvet walrus
#

it's the best 😁

#

And then there are "Stats and Buffs", which is very related to Abilities, but can be a whole other topic in itself. It can either be really simple or really complex depending on your needs

#

For Stats/Buffs, the implementation will vary a lot depending on how often stats are read VS how often they are changed in your game, as well as other factors (do you need stats "reacting" to other stat changes?)

For example, if you have an RTS/MOBA kind of game where units have buffable MoveSpeed and VisionRange, and you might have 5000 units on the map, that's 5000 units that must read the final buffed value of MoveSpeed and VisionRange every frame. In a case like this, you might want a Stats/Buff system where the final buffed value of the stat lives directly as a simple float in a component on the unit entities, because that's what would be the most efficient for read access (compared to getting the value from a map, a buffer, or from another entity). The downside of this would be that stat changes will most likely be more expensive than they could be otherwise.

If you have a game that has really heavy stat change needs relatively to stat read needs, a different approach would be best. You'd have to optimize for stat changes to be fast, even if that means sacrificing stat read performance a little bit. In this solution, your stat values might live in maps or buffers, etc....

And finally, you have to figure out how much jobs overhead you'd be comfortable with. You can come up with a solution where there is one job per stat type, but an ARPG could easily have 100 stat types, and that would mean 100 jobs running all the time just for stats. If those jobs do change-filtering in order to detect stat changes, that's 100 change-filtering jobs adding a constant update cost to your game. And if you want the game to be online and use client-side prediction, you might run the game sim 10X per frame, for a total of 1000 change-filtering jobs running all the time. At that point, just the job scheduling cost alone will leave a visible mark on the profiler, even when nothing is actually happening. For that reason, I'd tend to avoid the 1-job-per-stat-type approach