In this lesson, you will learn how to decouple ability activation from the specific gameplay logic using the Command and Strategy patterns to create a truly modular system.
The Problem: Monolithic Skills
Beginners often build abilities by creating a giant script attached to a player. If you want to add a "Fireball," you write code for cooldowns, input checks, and damage calculation inside one class. If you want a "Heal," you write it all over again. This leads to rigid code that is impossible to maintain.
The Solution: Command and Strategy
We can split the ability into two distinct parts:
- The Command (
IAbilityCommand): Handles the "activation" lifecycle. It verifies if the player has enough mana, checks the cooldown timer, and triggers the animation. It doesn't care what the ability does; it only cares when it is allowed to run. - The Strategy (
IAbilityStrategy): Defines the unique "impact" behavior. This is a ScriptableObject that implements the actual effect—like spawning a projectile or modifying a stat.
By injecting an IAbilityStrategy into an IAbilityCommand, you can mix and match behaviors at runtime.
public interface IAbilityStrategy {
void Execute(GameObject caster);
}
public class AbilityCommand {
private IAbilityStrategy _strategy;
public void Execute(GameObject caster) {
if (CanAffordAbility()) {
_strategy.Execute(caster);
}
}
}
Why this scales
Because your strategies are ScriptableObjects, you can create new abilities without writing a single line of new logic for the player controller. You simply create a new Data asset, assign it a strategy (e.g., ExplosionStrategy), and hook it into the AbilityCommand. This prevents your character scripts from becoming massive, unmanageable files, as the behavior is now strictly defined by the data you inject.
Key Takeaway: By separating the activation command from the execution strategy, you create a plug-and-play architecture where abilities are defined by swappable data rather than rigid, hard-coded scripts.