How to create blueprints once, and avoid duplicating your logic across multiple actors.
The Problem
In our game, Act on Instinct, we wanted to have three types of actors:
- Characters
- Vechicles*
- Buildings
All of these actors can receive damage from different sources. But, we don’t want to have to maintain duplicating logic and having three systems for damaging these actors. How can we accomplish this?
* the vehicles have armor plates that take damage based on direction.
Solution
Interfaces and actor components to the rescue!
Interfaces in software development are signatures without any logic. They let me tell you what something does, without you having to know how it works. In UE5, we have Blueprint Interfaces that provide this similar functionality.
⚠️ In general, I recommend putting as many things behind interfaces as you can, even your UI widgets. When you want to create a new implementation, you can swap out the new implementation as long as it implements the interface. Then you avoid having to update N blueprints that rely on that old blueprint.
Actor components in UE5 allow us to create a single instance attached to an actor.
Now, onto the step-by-step.
Concept
Here is the problem, visualized.
Every actor will implement the interface, and then use the actor component to reuse the logic.
Step-by-step
- Create actor component
BP_DamageableComponent
- Add components to
Character
,Building
, etc.
Attaching the component to the Actor
- Create the
BP_DirectionalDamageableComponent
- Attach to the
Vehicle
Integration
Another problem, we only want partially different functionality!
In our Vehicle, we want the directional damage, but want to keep the same logic for healing. How can we accomplish this?
In my solution, I use an abstract class/blueprint as the parent.
⚠️ Be judicious in using abstract blueprints. You can end up with confusing hierarchies that other people will not understand. I try to never go more than 1 level in depth. And usually it should be self-contained that no one else will have to maintain.
Then, we have the below. This provides us the functionality of taking damage by direction.
With this, we have reusable logic for a damaging system.