Trouble with Coroutines and abstract method inheritance

Hi, I’m having some trouble with inheritance: This is the code I am struggling with (I left out fluff that I thought might not be relevant, ignore random variables that aren’t declared)

public abstract class BaseClass : Monobehavior
{
    protected bool inCooldown = false;
    public virtual void Update()
    {
        if (!inCooldown)
        {
            Debug.Log(gameObject.name+": Updating");
            StartCoroutine("Attack");
        }
    }
    protected IEnumerator Attack()
    {
        inCooldown = true;
        Debug.Log(gameObject.name+": Starting Attack Coroutine");
        AttackLogic();
        yield return new WaitForSeconds(attackCooldown);
        inCooldown = false;
    }
    public abstract void AttackLogic();
}
public class ChildClass : BaseClass
{
    public override void AttackLogic()
    {
        Debug.Log(gameObject.name+" is attacking now");
    }
}

In my scene, I have an object with ChildClass on it. In the console, it will log both the “Gameobject: Updating” and “Gameobject: Starting Attack Coroutine”, but it doesn’t log “Gameobject is attacking now”, so It isn’t calling the child’s AttackLogic Class. I am really confused by this, does anyone have any insight?

This should work, you have some typos but as you said that you’re successfully getting the logs this can’t be the cause. Copy/Paste your code here and I just got the expected results

Maybe this part is doing something not so irrelevant

Or try to start from this base case that you paste here and go adding your additional code parts one by one, but the concept is right

While making an overrideable Update() method in a base flavor of monobehavior is all happy and well and 100% legal in OO land and makes the computer science guys smile, beware that most Unity developers will not remember to call base.Update(); when they derive from your base.

Should they? Yes, yes, of course they should.

But will they? In my experience it is often overlooked.

Are there other examples in the Unity codebase and commonly found example codes that do this? Almost NEVER. It’s just not an EXPECTED thing for Unity scripting land developers, even if it is the “right” thing in an inherited language.

If you’re lucky, an oversight like this will cause an extremely obvious bug that is fixed right away. Or it might cause extremely subtle errors that take a long time to be realized and tracked down.

When in Rome… :slight_smile:

2 Likes

Maybe if I try posting the full code I have here, for the two classes?

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

public abstract class BaseTowerScript : MonoBehaviour
{
    private MenuPanel menuPanel;
    [SerializeField] private Transform shootTransform;
    [SerializeField] private LayerMask onlyMinions;

    [SerializeField] protected float attackCooldown = 1f;
    [SerializeField] protected float attackRadius = 10f;
    [SerializeField] protected float projectileSpeed = 10f;
    [SerializeField] protected float attackPower = 1f;
   
    protected bool inCooldown;

    [SerializeField] protected List<Transform> nearbyEnemies;
    [SerializeField] protected Transform firstEnemy;

    protected virtual void Start()
    {
        menuPanel = MenuPanel.instance;
        nearbyEnemies = new List<Transform>();
    }
   
    private void OnDrawGizmos()
    {
        Gizmos.color = Color.green;
        Gizmos.DrawWireSphere(shootTransform.position, attackRadius);
    }

    public void OnMouseDown()
    {
        menuPanel.ResetMenu();
        SetMenuItems();
    }

    public Transform GetTarget()
    {
        return firstEnemy;
    }
   
    public virtual void Update()
    {
        if (!inCooldown)
        {
            GetNearbyEnemies();
            Debug.Log(gameObject.name+": Updating, nearby enemies is: "+nearbyEnemies.Count);
            if(nearbyEnemies.Count > 0)
                StartCoroutine("Attack");
        }
    }

    protected IEnumerator Attack()
    {
        inCooldown = true;
        Debug.Log(gameObject.name+": Starting Attack Coroutine");
        AttackLogic();
        yield return new WaitForSeconds(attackCooldown);
        inCooldown = false;
    }
   
    public abstract void AttackLogic();

    protected void GetNearbyEnemies()
    {
        nearbyEnemies = new List<Transform>();
        Collider[] enemies = Physics.OverlapSphere(shootTransform.position, attackRadius, onlyMinions);
        float furthestDist = 0;
        int targetIndex = 0;
        for (int i = 0; i < enemies.Length; i++)
        {
            MinionMovement mm = enemies[i].GetComponent<MinionMovement>();
            nearbyEnemies.Add(enemies[i].transform);
            if (mm.GetDistanceCovered() > furthestDist)
            {
                furthestDist = mm.GetDistanceCovered();
                targetIndex = i;
            }

            firstEnemy = enemies[targetIndex].transform;
        }
    }

    public abstract void SetMenuItems();
    public abstract void MenuClick(int index);

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

public class StarfishTower : BaseTowerScript
{
    [SerializeField] private Animator anim;
    [SerializeField] private Transform graphic;

    //AttackLogic runs whenever the tower is ready to attack
    public override void AttackLogic()
    {
        Debug.Log(gameObject.name+" is attacking now");
    }

    public override void SetMenuItems()
    {
        throw new System.NotImplementedException();
    }

    public override void MenuClick(int index)
    {
        throw new System.NotImplementedException();
    }
}

If you see anything here that screams ERROR please let me know haha, otherwise thanks, I will try rebuilding it from the ground up again when I have time.

Just copy/paste your both codes removing some custom classes from your project

7558672--934879--capture.gif

You really should replace this with a direct call:

StartCoroutine( Attack());

unless you’re planning on doing something clevererer…

Rebuilding the objects from the ground up fixed this, thank you!

1 Like