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:
-
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?
-
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!