Implementating a coroutine within an interface

Hi guys, I’ve been following the live tutorial on interfaces to make an AI state machine .I’ve pasted the code below (but only included one implementation of the interface to keep it short!)

using UnityEngine;
using System.Collections;

public class StatePatternEnemy : MonoBehaviour 
{
    public float searchingTurnSpeed = 120f;
    public float searchingDuration = 4f;
    public float sightRange = 20f;
    public Transform[] wayPoints;
    public Transform eyes;
    public Vector3 offset = new Vector3 (0,.5f,0);
    public MeshRenderer meshRendererFlag;


    [HideInInspector] public Transform chaseTarget;
    [HideInInspector] public IEnemyState currentState;
    [HideInInspector] public ChaseState chaseState;
    [HideInInspector] public AlertState alertState;
    [HideInInspector] public PatrolState patrolState;
    [HideInInspector] public NavMeshAgent navMeshAgent;

    private void Awake()
    {
        chaseState = new ChaseState (this);
        alertState = new AlertState (this);
        patrolState = new PatrolState (this);

        navMeshAgent = GetComponent<NavMeshAgent> ();
    }

    // Use this for initialization
    void Start () 
    {
        currentState = patrolState;
    }
    
    // Update is called once per frame
    void Update () 
    {
        currentState.UpdateState ();
    }

    private void OnTriggerEnter(Collider other)
    {
        currentState.OnTriggerEnter (other);
    }
}

The interface:

using UnityEngine;
using System.Collections;

public interface IEnemyState
{

    void UpdateState();

    void OnTriggerEnter (Collider other);

    void ToPatrolState();

    void ToAlertState();

    void ToChaseState();
}

…and an implementation of the interface:

using UnityEngine;
using System.Collections;

public class PatrolState : IEnemyState 

{
    private readonly StatePatternEnemy enemy;
    private int nextWayPoint;

    public PatrolState (StatePatternEnemy statePatternEnemy)
    {
        enemy = statePatternEnemy;
    }

    public void UpdateState()
    {
        Look ();
        Patrol ();
    }
    
    public void OnTriggerEnter (Collider other)
    {
        if (other.gameObject.CompareTag ("Player"))
            ToAlertState ();
    }
    
    public void ToPatrolState()
    {
        Debug.Log ("Can't transition to same state");
    }
    
    public void ToAlertState()
    {
        enemy.currentState = enemy.alertState;
    }
    
    public void ToChaseState()
    {
        enemy.currentState = enemy.chaseState;
    }

    private void Look()
    {
        RaycastHit hit;
        if (Physics.Raycast (enemy.eyes.transform.position, enemy.eyes.transform.forward, out hit, enemy.sightRange) && hit.collider.CompareTag ("Player")) {
            enemy.chaseTarget = hit.transform;
            ToChaseState();
        }
    }

    void Patrol ()
    {
        enemy.meshRendererFlag.material.color = Color.green;
        enemy.navMeshAgent.destination = enemy.wayPoints [nextWayPoint].position;
        enemy.navMeshAgent.Resume ();

        if (enemy.navMeshAgent.remainingDistance <= enemy.navMeshAgent.stoppingDistance && !enemy.navMeshAgent.pathPending) {
            nextWayPoint =(nextWayPoint + 1) % enemy.wayPoints.Length;
        
        }


    }
}

Not having used interfaces before, it seems like a really nice and clean pattern but I’ve encountered a problem when in an implementation of the interface I’ve tried to use a coroutine. I appreciate why this doesn’t work - implementations of the interface don’t inherit from a monobehaviour. So my question is, what is the cleanest method starting a coroutine within an implementation of an interface?

As I understand it there are two ways:

  1. In the interface, also inherit from a monobehaviour (as I say I’m pretty inexperienced with interfaces). However, I know doing this means I can’t call a constructor, and I’d also need to add all the interfaces to the gameobject as components? Is that correct?

  2. Within the interface, use the reference to StatePatternEnemy (called enemy) and use that to trigger the coroutine from StatePatternBehaviour. So as an example I’d add this code to StatePatternEnemy:

    public void TriggerCoroutine(IEnumerator whichCoroutine ){
    	StartCoroutine(whichCoroutine);
    }
    

and within PatrolState, I’d add this:

	public void StartState()
	{
            // enemy is a reference to StatePatternEnemy
		enemy.TriggerCoroutine( ScanBehaviour () );

	}

and within PatrolState also add the actual coroutine:

	IEnumerator ScanBehaviour()
	{ 
		// Coroutine code, do whatever....
			yield return null;
		 
	}

…and if I wanted to stop a coroutine, I’d repeat as above adding a CancelCoroutine function in StatePatternEnemy that uses StopCoroutine (instead of start).

Any advice on this would be much appreciated!

Thanks!

There are several solutions for this. The easiest way is to use a simple singleton MonoBehaviour that runs the coroutines. I once created the CoroutineHelper class which does exactly this. It provides a way to run coroutines on a singleton instance.

In your case you only need a MonoBehaviour singleton so you can use it’s StartCoroutine method. The actual coroutine (the generator method that returns IEnumerator) can be defined in your custom class.

So all you would need is a singleton implementation which you can use like that:

// CoroutineHost.cs
public class CoroutineHost : MonoBehaviourSingleton<CoroutineHost> { }

With that class you can simply do:

CoroutineHost.Instance.StartCoroutine( ... );

in any class.

edit

If you want to be able to stop a coroutine you have to store the actual coroutine reference which you need to stop it

Coroutine someCoroutine;

someCoroutine = CoroutineHost.Instance.StartCoroutine( MyCoroutine() );

To stop that coroutine you just have to do this:

CoroutineHost.Instance.StopCoroutine( someCoroutine );

You can not use StopCoroutine( MyCoroutine() ) as “MyCoroutine” will return a new IEnumerator each time you call it.

public class CoroutineHost : MonoBehaviourSingleton { }

MonoBehaviourSingleton says it does not exist??

Don’t know if it’s the correct way but it seems to works fine if you put System.Collections. in front of the IEnumerator:

using UnityEngine;

public interface IWeapon
{
    System.Collections.IEnumerator Fire(Transform target);
}