Finite State Machine Not Quite Right.

Hi,

I’m trying to create a FSM and whilst there aren’t any errors popping up, it isn’t working. The enemy just comes at the player, then starts doing laps around them.

I’m just trying to make the enemy approach where the player is for 2 seconds and then move backwards away from the player for two seconds and then repeat.

using UnityEngine;
using System.Collections;

public class TankScript : MonoBehaviour {
   
    public Transform player;
    public float playerDistance;
    public float rotationDamping;
    public float moveSpeed;
    public static bool isPlayerAlive = true;
    public Rigidbody tankBull;
    public float speed = 20;
    public Transform spawnPoint;
   
   
    public float elapsedTime = 0.0f;
    public float shootRate = 3.0f;
   
    public float lastFireTime;
    public float fireDelay;
    private float time;
    GameObject MockronBossFour;
    float lockPos = 0;
    private Vector3 tankPos;

    public enum State {
        LookAtPlayer,
        Chase,
        Back,
        };

    private State _state;
    IEnumerator Start () {
       
       
                //MockronBossFour = GameObject.Find ("MockronBossFour");
                //MockronBossFour.transform.rotation = Quaternion.Euler (transform.rotation.eulerAngles.x, lockPos, transform.rotation.eulerAngles.z);
                //tankPos = transform.position;
                StartCoroutine ("waitTwoSeconds");
                waitTwoSeconds();
                StartCoroutine ("waitTwoSecondsAgain");
                waitTwoSecondsAgain ();

                _state = State.LookAtPlayer;
        while (true) {
                    if (playerDistance < 80f) {
                    switch (_state) {
                    case State.LookAtPlayer:
                    LookAtThePlayer ();
                    break;

                    switch (_state) {
                    case State.Chase:
                    ChasePlayer ();
                    break;

                    switch (_state) {
                    case State.Back:
                    EnemyBack ();
                    break;

                    yield return 0;
                }
                }
                }
                }
                }
        }   
   
    void Update () {
                //transform.position = new Vector3 (transform.position.x, 50, transform.position.z);
                //transform.eulerAngles = new Vector3 (0, transform.eulerAngles.y, transform.eulerAngles.z);
                playerDistance = Vector3.Distance (player.position, transform.position);
        }
       
 
   
   
   
    private void LookAtThePlayer()
    {
                Quaternion rotation = Quaternion.LookRotation (player.position - transform.position);
                transform.rotation = Quaternion.Slerp (transform.rotation, rotation, Time.deltaTime * rotationDamping);
                if (Time.time > lastFireTime + fireDelay) {
                        //fire
                        lastFireTime = Time.time;
                }
       
       
       
                if (elapsedTime < shootRate) {
                        elapsedTime++;
                } else {
                        Rigidbody instantiatedTankBull = Instantiate (tankBull, spawnPoint.position, transform.rotation) as Rigidbody;
                        instantiatedTankBull.velocity = transform.TransformDirection (new Vector3 (0, 0, speed));
                        elapsedTime = 0;
                }

                Debug.Log ("Yep");
        }
        IEnumerator waitTwoSeconds() {
           
           
        yield return new WaitForSeconds(2);
        _state = State.Chase;
   
    }


    private void ChasePlayer ()
    {
                Quaternion rotation = Quaternion.LookRotation (player.position - transform.position);
                transform.rotation = Quaternion.Slerp (transform.rotation, rotation, Time.deltaTime * rotationDamping);
                transform.Translate (Vector3.forward * moveSpeed * Time.deltaTime);
                if (Time.time > lastFireTime + fireDelay) {
                        //fire
                        lastFireTime = Time.time;
                }
           
           
           
                if (elapsedTime < shootRate) {
                        elapsedTime++;
                } else {
                        Rigidbody instantiatedTankBull = Instantiate (tankBull, spawnPoint.position, transform.rotation) as Rigidbody;
                        instantiatedTankBull.velocity = transform.TransformDirection (new Vector3 (0, 0, speed));
                        elapsedTime = 0;
               
        }
        Debug.Log ("Hepp");
    }
        IEnumerator waitTwoSecondsAgain() {
       
       
        yield return new WaitForSeconds(2);
        _state = State.Back;
       
    }

           
   
   
    private void EnemyBack (){
        Quaternion rotation = Quaternion.LookRotation (player.position - transform.position);
        transform.rotation = Quaternion.Slerp (transform.rotation, rotation, Time.deltaTime * rotationDamping);
        transform.Translate (Vector3.back * moveSpeed * Time.deltaTime);
                if(Time.time > lastFireTime + fireDelay)
                {
                    //fire
                    lastFireTime = Time.time;
                }
               
               
               
                if(elapsedTime < shootRate)
                {
                    elapsedTime++;
                }
               
               
               
                else{
                    Rigidbody instantiatedTankBull = Instantiate (tankBull, spawnPoint.position, transform.rotation) as Rigidbody;
                    instantiatedTankBull.velocity = transform.TransformDirection (new Vector3 (0, 0, speed));
                    elapsedTime = 0;
                   
                Debug.Log ("Mon");   
                _state = State.Chase;
}
       
}
}

I believe there are some odd stuff going on in that code, will try to guide you through it. :slight_smile:

First of all in your Start method you call both WaitTwoSeconds() two times, aswell as WaitTwoSecondsAgain() two times, I suppose you want them to run as coroutines though, looking at what they do but the way the methods are set up they will not repeat. Also since you start both routines at the same time and both of them waits for 2 seconds they will both trigger their actions after 2 seconds, meaning that you will switch between State.Back and State.Chase at the same time.

Also you’ve nested three switch blocks into each other, there’s no purpose for that in this case, just create a single switch block with all cases in that one instead.

Try something like this and see if it works :slight_smile:

private const float TWO_SECONDS = 2.0f;            //Simply two seconds cached
    private float _lastStateChange;                    //Last time we switched state
    private bool _active;                            //Toggle instance active
    private State _state;
   
    public void Start() {
        _lastStateChange = Time.time;
        _active = true;                                //Toggle this instance active
        StartCoroutine( FiniteStateMachine() );        //and start the FSM
    }
   
    private IEnumerator FiniteStateMachine() {
        while( _active ) {
            if( (Time.time - _lastStateChange) > TWO_SECONDS ) {
                ToggleState();                        //Switch current state if enough time has passed
            }
           
            if( playerDistance < 80f ) {
                switch( _state ) {
                case State.LookAtPlayer:
                    LookAtThePlayer();
                    break;
               
                case State.Chase:
                    ChasePlayer();
                    break;
               
                case State.Back:
                    EnemyBack();
                    break;
                }
            }
           
            playerDistance = Vector3.Distance( player.position, transform.position );        //Coroutines work pretty much like the Update method so no use in having an extra method update all the time
            yield return null;
        }
    }
   
    private void ToggleState() {
        if( _state == State.Chase ) {
            _state = State.Back;
        }
        else {
            _state = State.Chase;
        }
       
        _lastStateChange = Time.time;                //Keep track of when we last changed the state
    }