@Opeth001 I think your approach looks similar to mine. The basic philosophy is to use composition of various tags and data on an “ability entity”. These two high-level discussions were invaluable to me and I studied them in detail:
I didn’t use any fancy or odd concepts - everything I used falls within the ordinary ECS programming paradigm that a beginner DOTS programmer would know. No codegen. I didn’t even use tertle’s events.
In those reddit descriptions and in my implementation, each ability has a DynamicBuffer where ImpactData is just Entity target and float3 hitPos. Whenever any system decides it wants to create an impact, it appends an impact to that buffer via ecb. Then in the next frame all the systems can react to that event (display pfx, play sounds, apply damages, etc) by checking if the impact buffer is not empty. As I mentioned in @PhilSA 's thread, I used Approach C for accumulating multiple damage modifiers, speed modifiers, etc in one frame, so each enemy has a DynamicBuffer - so there are also systems checking if the modifier buffers are empty and applying those modifiers. There is a system that clears the buffer at the end of each frame.
In addition to the “impact” event, each ability also has a boolean trigger state so that any systems can react during the frame whenever a trigger event occurs. And of course, at any given time systems can operate on the ability (like performing linear movement for a bullet, applying physics gravity, etc). So in summary - there are two events that systems are interested in: trigger and impact.
On every ability I have a TriggerValidityData, which is important because it accounts for all the variables that might modify when the ability is allowed to trip the trigger event. There is a system that continually updates this based on does the targeter have a target, is the turret/weapon pointing in the right direction, etc. In the case of a player triggered ability, the criteria would also include if the player clicked an ability button or pressed an ability hotkey.
One difference is that all of my abilities are only for NPCs… My “player” doesn’t really have abilities yet, but I think they would be similar with the only major difference being that they would require additional input from the user-input system when determining if the ability can be used or not. And they would specify an icon for the HUD.
A second difference is that I don’t use netcode, so I don’t know if my system is compatible with networking or not.
The last difference is that I used prefabs instead of scriptable objects, which I may convert to scriptables at some point (should be fairly easy if I can find the time). I guess the reason I used prefabs at the time is because some of my abilities have models with multiple child objects that need to be positioned appropriately. For example, a sniper shot ability has a muzzle flash and a beam flash (see screenshot below)
The one problem I had with the concepts defined in two reddit posts is that they call for creating an ability whenever it is time to “fire” and then destroying it when it is done. Well I tried that, and it got ugly for a few reasons:
- Some abilities need some persistence. For example, one ability I have is a laser beam that fires whenever its targeter chooses a valid target. Well it’s firing at say 4 per second, causing damage each time, but it also has a beam that needs to be constantly enabled and adjusting to the target as long as a target exists - and it needs audio engange/disengage/loop sounds to be played. Also, what if you want passive abilities like the ability to heal any nearby friendlies over time, or boost their stats?
- It was counter-intuitive to set the cooldown-interval (or fire rate) on the “spell-caster or weapon” instead of embedding it with the rest of the stats on the ability. The designer would have to look in two places.
- It doesn’t support things like a projectile that can go out and impact an enemy, and then move to another one (like a mini-drone or something).
So the simple solution was to embed the cooldown interval into the ability itself. That solved many issues, however I then had the issue of handling projectile abilities that need to destroy themselves. The simple solution was to make a new component to spawn a new ability when the trigger event happens. So a cannon turret has a persistent “CannonSpawner” ability that spawns "CannonBullet"s (which destroy themselves on impact).

It works great! With just with that one mechanism, I can make all kinds of crazy things like abilities that spawn multiple bullet abilities on launch, abilities that explode on impact and fire out other abilities, etc.
One last thing - splash damage was a bit difficult to implement for abilities that destroy themselves on impact. I ultimately fixed that with proper system ordering. If you’re curios about how that was solved I can go into more detail.
It’s outside of the scope of the topic, but I also discovered a pretty easy way to handle over-time effects (like damage-over-time, slowdown-over-time, etc. After one failed attempt, I’m pretty happy with my final solution and it’s also very extensible and easy to add new over-time effects.
As for performance, ECBs are used a decent amount, but I don’t see any reason why my approach would have any big performance hits. I haven’t benchmarked, but I see no obvious reason why it wouldn’t scale to many hundreds of thousands of NPCs with a few abilities. I suspect the limiting factor would be having too many particle effects, but they are pooled game objects and are started in the main thread. There are a few places where CDFE is used like when fetching a particular targeter, getting a target’s position, or maybe to flash a muzzle, but they are relatively rare. Nearly all of the systems act directly on the ability without getting parameters via CDFE.
Overall I’d say that the most difficult thing I’ve built in my game so far was this ability system. It’s weird because for so little code, it sure was tough coming up with a workable design that is flexible enough to handle lots of future abilities! Ultimately, I think its success can be attributed to using the composition principal. Unlike the traditional OOP approaches to skill/spell/ability systems, my system doesn’t have a big huge behemoth structure with a GUI that has a zillion options. Recreating that would have taken me years. Instead, there are a bunch of little systems all reacting to abilities built via composition and as a result, it’s actually very little code. I see no reason why I couldn’t use this framework to build a mind-control ability.
To keep it interesting, here are some screenshots of a cannon turret that fires bullets that cause splash damage and burn-over-time damage. Keep in mind that this composition could be done via scriptable objects instead of prefabs.
Anyhow… That’s just a summary of how I pulled it off. I’m sure there is lots of room for improvement.