Polymorphism(?) Problem

Hello everyone!

I am currently building an abilities system and I have 2 types of abilities. One is a basic “permanent” ability, and the other is an ActiveTimeAbility. Both of these have a TriggerAbility() method. I have implemented 2 abilities, one of which derives from AbilityBase and the other from ActiveTimeAbilityBase. I added these 2 abilities as components to the player, and I get them using GetComponents in my PlayerController’s Awake() method. When I access this array and call TriggerAbility() for the one deriving ActiveTimeAbility, it calls the AbilityBase’s TriggerAbility() instead of the ActiveTimeAbilityBase’s TriggerAbility().

I need the ActiveTimeAbilityBase TriggerAbility() to be called instead of the AbilityBase’s.

I know that if I just go into the player controller and change it to the type ActiveTimeAbilityBase, it will work; however, that’s not ideal as I want any type of ability to be able to dragged into the player.

I think this is a polymorphism problem, and I’m not sure how to go about solving this? Any tips would be appreciated!

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public abstract class ActiveTimeAbilityBase : AbilityBase {
    public float activeTime;
    public Timer activeTimer;

    private new void Awake() {
        activeTimer = new Timer(activeTime);
        activeTimer.TimerCompleteEvent += OnActiveTimerEnd;
        base.Awake();
    }

    private new void Update() {
        activeTimer.Update(Time.deltaTime);
        Debug.Log(activeTimer.TimeRemaining);
        base.Update();
    }

    public new void TriggerAbility() {
        if (cooldownTimer.IsActive || activeTimer.IsActive) {
            return;
        }

        AbilityStart();
        activeTimer.Start();
    }

    private void OnActiveTimerEnd() {
        AbilityEnd();
        cooldownTimer.Start();
    }

    protected abstract void AbilityEnd();
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public abstract class AbilityBase : MonoBehaviour {
    public string name;
    public float cooldownTime;
    public Timer cooldownTimer;

    protected void Awake() {
        cooldownTimer = new Timer(cooldownTime);
    }

    protected void Update() {
        cooldownTimer.Update(Time.deltaTime);
    }

    public void TriggerAbility() {
        if (cooldownTimer.IsActive) {
            return;
        }

        AbilityStart();
        cooldownTimer.Start();
    }

    protected abstract void AbilityStart();
}
public AbilityBase[] abilities;
...
private void Awake() {
        abilities = GetComponents<AbilityBase>();
}
...
if (Input.GetKeyDown(KeyCode.E)) {
            abilities[1].TriggerAbility();
}

Welp, I feel stupid. I realized what I did.

I had to make the TriggerAbility() in AbilityBase virtual and then override it in ActiveTimeAbilityBase. Nothing to see here. :slight_smile:

3 Likes