Best practices for Actor Components with abstract functionality

Hi,

I’m trying to figure out what the best practices are for Actor Components with abstract functionality.

Scenario:

I have an Actor Component called Shoot Ability, this gives an actor the ability to start and stop shooting through events called from the Actor’s blueprint.

The shoot functionality requires a few parameters that determine the origin and direction of the bullets that are spawned, but these differ from Actor to Actor.

Here are the things that I have tried:

  • Have variables in the Shoot Ability that are updated every tick through the Actor. This seems a bit wasteful and prone to errors, since nothing indicates to the person implementing the ability into an Actor that this has to be done.

  • Add the variables to the event. This works and is clean, but does not work when we have start and stop shooting events - since the variable should be updated as we are shooting. The Actor should not have to know or care about the fire rate etc.

  • Created an interface called Shooter that has functions which return the required parameters, implemented by the Actor. Using an interface for the abstract behavior is the cleanest but introduces tight coupling between the Shoot Ability Actor Component and Shooter interface. There doesn’t seem to be a way to force an Actor to use an interface if an Actor Component is present, nor does it seem to be possible to introduce abstract functions to Actor Components to get rid of the separate interface. I also have not found a way to override the functions in the Actor without dealing with inheritance which would bring its own suite of problems.

Happy to hear how you tackle this and what the best practices are!

In something like a “shoot ability component” you are likely to hold the basic methods like “has enough ammunition?” “reload” “create projectile” but not much more.

Depending on how big your system is you might end up having to split it again into multiple systems, think of:

  • Animation system ((Animation Blueprint) “has it finished shooting?”)
  • Weapon system ((Actor / UObject) “Is this weapon broken?”).

That is how I often end up writing code on different systems.

For the actor component I suggest using datatables (datatable row handle) as a setup var (editable on the editor of parent actor), or as parameter for relevant functions. From the datatable you can then load things like ammo specific data, or even a reference to a UObject holding logic for how the projectile should behave. This way you can inject your data / logic at all times.