I am trying to implement an ability system in Unity for a game similar to XCOM (turn based, team consists multiple of units, grid based). Every unit have X action points per turn.

I would like to do it in the way that everything that unit can do per turn is an "ability"

  • Moving is an "ability"
  • Attack is an "ability"
  • Spell is an "ability", etc...

I searched and found out that that it would be suitable to use Scriptable objects. The idea is there would be a base abilty class from which other abilities inherit.

public class AbilityBase : ScriptableObject 
    public string desc;
    public int actionCost;
    public virtual void Execute(){};

And other abilities would look like this

public class HealAbility: AbilityBase
    public virtual override void Execute()
        //Check if Unit has enough action points
        //If so then apply heal

Which would propably work fine. Every unit would have some sort of AbilityManager which would hold references to the ScriptableObjects abilities.

The problem with this solution is:

  • Some abilities (Taking cover, healing) can be applied on caster itself. It can get a reference from inside Execute method.
  • Some others needs to have targed in form of a unit (Attack on another unit)
  • Some needs a target in form of a grid Node (throwing a granade, AOE spell)

The Execute method takes zero argument and inherited classes cannot modify that.


  1. How could I solve this problem with missing target references? (How to pass target into a ability?) The target of abilities could also be of a different type.
  2. Abilities that takes target needs to pick it first. So it should indicate that to the player (for example other units light up). Same with an ability that takes a grid node (Allowed nodes would light up). Should this be implemented from inside the ability itself or should it handle the parent script that uses abilities?

My possible solution to questions:

  1. Since the game is grid based, the all the targets will propably be on an grid node. So theoretically passing an grid node or an array of nodes to the Execute method could work. void Execute(GridNode[] nodesToCastOn) Then the Execute method would look up which unit is standing on the nodes and apply its effect on them.
  2. I think it should not handle the ability script, because it the ability should not wait for player input.

If anyone managed to read this far I thank you. Do you thing my solutions would be Correct? If no, could you elaborate why not and/or breafly explain your solution?

I will be thankfull for any insight from you guys.


1 Answer 1


Make subclasses:

public abstract class AbilityBase : ScriptableObject {
    public string desc;
    public int actionCost;

public abstract class UntargetedAbility : AbilityBase {
    public abstract void Execute();

public abstract class UnitTargetedAbility : AbilityBase {
    public abstract bool IsValidTarget(Unit candidate);
    public abstract void Execute(Unit target);

public abstract class LocationTargetedAbility : AbilityBase {
    public abstract bool IsValidTargetZone(Location candidate, List<Location> area);
    public abstract void Execute(GridLocation target);

Extend the appropriate one for each effect.

Then your code for selecting an ability might look something like this:

bool TrySelect(AbilityBase ability)

    if (ability.actionCost > actionBudget) return false;

    switch(ability) {

        case UntargetedAbility untargeted:
            actionBudget -= ability.actionCost;
            return true;

        case UnitTargetedAbility unitTargeted:
            // Don't deduct cost yet - player may cancel before selecting target.
            return true;

        case LocationTargetedAbility locationTargeted:
            // Again, don't deduct cost when it can still be cancelled.
            return true;
            throw new System.NotImplementedException($"Unknown ability type {ability.GetType().Name}");

Here I'm assuming you have something like a GameStateManager state machine that can hold the reference to the selected ability while in the target picking state, invoking the IsValidTarget / IsValidTargetZone predicates to display appropriate selection feedback, and then carry on to executing the ability (and deducting the cost) once a valid target has been selected.


You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .