Ability System - digocorbellini/EGaDS-Open-Project GitHub Wiki

Table of Contents

What is the system?

In the game, players can pick up and drop abilities. When a player holds multiple abilities, their effects on the player occur at the same time. The Ability System is the game system which allows for this to happen. This system provides a standard for ability implementations and allows programmers to interface with a general ability manager through defined methods to add and remove abilities, assign keys to abilities, and determine the number of abilities that can be equipped at one time.

Files

This system is made up of the following files found in Assets/Scripts/AbilitySystem/:

These scripts should not be modified, and all new ability scripts should be placed in Assets/Scripts/Abilities and all ability scriptable objects should be placed in Assets/ScriptableObjects/Abilities.

Declarations

Below is a list of all of the classes present in the system. The methods within each class should be properly commented, so for more information on their use, check the files shown above.

  • Ability - an abstract class which defines the basic components of an ability.
  • TriggeredAbility - an abstract class which all triggered abilities must extend.
  • PassiveAbility - an abstract class which all passive abilities must extend.
  • PlayerComponents - a regular C# struct which serves as a data container for all of the player's components. It is passed to all of the Ability functions by the ability manager so that abilities may have access to all of the player's components.
  • AbilityManager - a MonoBehaviour class attached to the player which implements the ability manager.
  • PlayerController - a MonoBehaviour class attached to the player which implements the basic player movement (walking, jumping, and wall jumping). Also provides some useful methods for telling the player's state

How to use the system

Types of abilities

There are two main types of abilities that are supported: Triggered abilities and Passive abilities.

  • Triggered abilities are abilities which must be activated using the press of a hotkey (checkout the input system).
  • Passive abilities are abilities which do not require any input from the player.

When creating your ability, you must extend either the TriggeredAbility or the PassiveAbility class depending on the type of ability you wish to implement.

Input system

Since players can have multiple active triggered abilities at once, the issue of selecting inputs comes into play. To solve this issue, the ability system has a set of Hotkeys which are just pre-determined keycodes that can be assigned to picked up abilities. A triggered ability assigned to a hotkey will only be activated if that hotkey is pressed. Passive abilities are also assigned to hotkeys, however, they must not use the Hotkey to activate.

As a programmer implementing an ability, you do not have to care about what Hotkey is assigned to your ability since that will be handled by the ability manager. In order to listen for input from the key, we have implemented some methods which you may use: GetKeyDown(), GetKeyUp(), and GetKey(). These methods take no parameters and return true if the specific key event has occurred for the assigned hotkey and false otherwise. Use it as you would with the typical GetKeyDown(), GetKeyUp(), and GetKey() functions of Unity's Input class, except no Keycode must be passed in.

Example:

if (GetKeyDown())
{
    rigidbody2d.velocity = new Vector2(rigidbody2d.velocity.x, speed);
}

Focus

When many abilities can be active at once, there is always the risk of overlap during the activation of abilities. For example: during a dash, a double jump should not be able to occur until after the dash is complete. In order to solve this issue, the AbilityManager has a Focus attribute which allows the focused ability to be the only ability active. That ability must then unacquire the manager's focus once it no longer requires focus.

To acquire the ability manager's focus, an ability should call the AcquireFocus() method in AbilityManager. True is returned if the ability is successfully focused and false otherwise. To unacquire focus, the same ability which is currently holding the focus must call UnacquireFocus(). True is returned if the ability is successfully unfocused and false otherwise.

Example:

public override void AbilityUpdate(PlayerComponents player)
{
    if (player.abilityManager.AcquireFocus(this))
    {
        // ability logic
        // ...
      
        player.abilityManager.UnacquireFocus(this);
    }
}

Guide/Example

For a guide on how to make an example ability, check out Creating Your First Custom Ability. An example is provided in Assets/Scripts/AbilitySystem/Example/.

Typical Workflow

1) Create an ability script

Create a new class named after your ability and extend either the TriggeredAbility or the PassiveAbility class depending on what type of ability you wish to implement. This is the class in which you will be placing your ability implementation. Both ability classes extend Ability which extends ScriptableObject. This is because all abilities will be scriptable objects, so make sure that you also add in the [CreateAssetMenu(menuName = "Abilities/NAME_OF_YOUR_ABILITY")] attribute above your class declaration so that instances of your ability can be made in the unity editor.

This script must be placed in Assets/Scripts/Abilities.

Example:

[CreateAssetMenu(menuName = "Abilities/NAME_OF_YOUR_ABILITY")]
public class ABILITY_NAME_HERE : TriggeredAbility { }

2) Override desired methods

In the ability abstract class, we have provided multiple virtual methods for you to override depending on what functionality you might require.

AbilityStart(), AbilityUpdate(), and AbilityFixedUpdate() are meant to be treated like their MonoBehaviour equivalent Start(), Update(), and FixedUpdate() methods. The goal is for you to program as if you were just creating a regular unity component using a MonoBehaviour script. This means you are also free to declare any new public or private variables you see fit. An essential difference is that the three ability functions mentioned all take in a PlayerComponents object. This object is meant to give you references to all of the components found on the player without having a reference to the player game object and without the need to call GetComponent().

In addition to the typical Monobehaviour methods, we have added an AbilityEnd() method which also takes in a PlayerComponents object and is called by the ability manager whenever the ability is removed from the list of active abilities.

2.5) Override default player movement

Abilities also have the ability to override the player's default movement. This is achieved through the modification of the constant values used for movement. Here are the values available to abilities:

  • Speed Multiplier - factor applied to the player's movement speed
  • Jump Height Addend - Height added to the player's jump height in terms of tiles
  • Fall Speed Multiplier - factor applied to the player's fall speed These values may be set in either the inspector or programmatically.

If an ability wants to use the modified movement constants, they can be accessed through the following AbilityManager methods:

  • GetSpeedMultiplier()
  • GetJumpHeightAddend()
  • GetFallSpeedMultiplier()

3) Get player input

As mentioned in the section of this wiki page about input, you as the programmer do not have to worry about what keys are used to activate a triggered ability. In order to listen for the press of an assigned hotkey, each ability has a method called Get<key event type>() which can be used to listen for events relating to the assigned hotkey.

4) Acquire focus

If your ability requires that no other abilities occur during the activation of your ability, acquire the ability manager's focus. Learn more about how what focus is in the Focus section of this document.

5) Create ability instance

Abilities are scriptable objects. This means that to create an instance of your ability, you must do so in the unity file explorer. You do so by right-clicking in the file explorer, going to create, going to ability, and selecting the name of your ability, as shown below. This scriptable object must be placed in ScriptableObjects/Abilities.

image

You can now modify any properties of your ability in the inspector, as shown below:

image

6) Add ability to ability manager

Now that you have an instance of your ability, you might want to test it. To see it's effect you will have to add your ability to the ability manager found on a player game object. The player game object must have a rigidbody, a collider, and an AbilityManager component. You must acquire a reference to the player object and then call "AddAbility()" on the AbilityManager component to successfully add your ability to the active list of abilities.

⚠️ **GitHub.com Fallback** ⚠️