Creating a new unit Ability

RTS Starter Kit contains an API for ability creation. You can make your own abilities for units with custom logic.

An ability is split into two classes:

  • A data class, deriving from AbilityData (a plain ScriptableObject, marked with CreateAssetMenu so you can create asset instances of it in the Project window). It only holds config fields (icon, sound, etc.) - no gameplay logic.

  • A logic class, deriving from Ability<TData>, where TData is your data class. This is a plain C# class (not a ScriptableObject) - it is found and instantiated automatically at runtime by the asset, matched to its data class by the generic argument, so you don’t need to register it anywhere yourself.

Here is the built-in Carry Out ability as an example:

using InsaneSystems.RTSKit.Core;
using UnityEngine;

namespace InsaneSystems.RTSKit.Data.Abilities
{
  // Built-in ability data as example

  [CreateAssetMenu(fileName = "CarryOut", menuName = Globals.AssetName + "/Abilities/Carry Out")]
  public class CarryOutData : AbilityData { }
}
namespace InsaneSystems.RTSKit.Gameplay.Abilities
{
  // Built-in ability logic as example

  public sealed class CarryOutAbility : Ability<CarryOutData>
  {
    protected override void StartUseAction()
    {
      if (UnitOwner.TryGetModule<CarryModule>(out var carryModule))
        carryModule.ExitAllUnits();
    }
  }
}

You have 3 methods to override on your logic class:

InitAction - called once when the unit spawns and the ability initializes.

StartUseAction - called when the player clicks the ability icon and it is possible to use it (ability icon active, etc.).

UpdateAction - called every frame (from the AbilitiesModule of the specific unit).

You can also override CanUse() to add a custom usage condition - it returns true by default.

Don’t forget to add a custom CreateAssetMenu to your data class, so you can create asset instances of your ability from the Project window.

If you need to pass some extra data to your ability (for example, a custom transform or a value tweaked per-instance), just add it as a field directly to your data class - since it’s a regular ScriptableObject, you can set it up per-instance in the Inspector, and read it from your logic class through the inherited data field:

namespace InsaneSystems.RTSKit.Data.Abilities
{
  [CreateAssetMenu(fileName = "SomeAbility", menuName = Globals.AssetName + "/Abilities/Some Ability")]
  public class SomeAbilityData : AbilityData
  {
    public Transform CustomTransform;
    public GameObject CustomGameObject;
  }
}
namespace InsaneSystems.RTSKit.Gameplay.Abilities
{
  public sealed class SomeAbility : Ability<SomeAbilityData>
  {
    protected override void StartUseAction()
    {
      data.CustomTransform.position += Vector3.forward;
      data.CustomGameObject.SetActive(false);
    }
  }
}

If instead you need something that comes from the specific unit instance owning the ability (not shared config), add a regular MonoBehaviour component to the unit’s prefab and fetch it through the inherited UnitOwner property:

using UnityEngine;

// Just an example component for an ability which needs additional info from the unit's GameObject.
// Add this component to your unit prefab and set up your fields as you need.
public class AbilityAdditionalSettings : MonoBehaviour
{
  public Transform CustomTransform;
  public GameObject CustomGameObject;
}

Next, in the ability, you can get this component using this code:

namespace InsaneSystems.RTSKit.Gameplay.Abilities
{
  public sealed class SomeAbility : Ability<SomeAbilityData>
  {
    AbilityAdditionalSettings additionalSettings;

    protected override void InitAction()
    {
      additionalSettings = UnitOwner.GetComponent<AbilityAdditionalSettings>();
    }

    protected override void StartUseAction()
    {
      additionalSettings.CustomTransform.position += Vector3.forward;
      additionalSettings.CustomGameObject.SetActive(false);
    }
  }
}

It will fetch the component once from the unit owning this ability, and then you can use it in any of your ability’s methods.

So, you can make any abilities you want this way.