C# Update UI Text Depending on Enemy State

I’ve been searching for a solution to my problem for a few days now and I don’t really feel like I’m getting anywhere.
Basically, I’m making a scene which has enemy AI that reacts to the player character and changes states depending on certain conditions (like a state machine). It’s going okay (for example, the enemy searches is the player is close then chases when they see them, etc.) but one thing I’d like to do is display in a message box using UI text what state the enemy is in (and which enemy it is as well; there’s three in my scene). I’m not sure how to do this. It should be easy and I know I need the Update function but there’s a problem. Each enemy state (Patrol, Chase, etc.) has it’s own script where the appropriate action takes place depending on the state. There’s a central class the different states inherit from. Without inheriting Monobehaviour, I can’t use the Update() function to update my text to change depending on the current state.
I’ve been searching for a solution for days and I’m at my wit’s end. I don’t normally like to come here to ask for help; not that I don’t appreciate it but I don’t like people thinking I rush here for every little problem because honestly I don’t. Usually I find a solution online, but I feel like I’m going round in circles with this and not getting closer to the solution, and no-one else has this problem. My state machine doesn’t use enums, which now I realise is a bit silly, but I got the state script from a tutorial on the Unity tutorials (found here: https://unity3d.com/learn/tutorials/topics/scripting/using-interfaces-make-state-machine-ai).
I have to finish my project by this coming Friday because of a deadline and it’s actually the key thing I need to show users how my enemy state system works. I’ll post the main enemy scripts and one of the state scripts (I have about five of those in total). I’m not a programming pro but I’m better than I was about a year ago before I started learning C# properly. Thanks for any help or suggestions.

using UnityEngine;
using UnityEngine.UI;
using System.Collections;

public class StatePatternEnemy : MonoBehaviour {

    //Sets speed for enemy turning while searching for the player
    public float searchingTurnSpeed = 120f;
    //The amount of time the enemy spends looking for the player
    public float searchingDuration = 4f;
    //The range in which the enemy can detect or see the player
    public float sightRange = 20f;
    //This sets up waypoints for the enemy to follow
    public Transform[] wayPoints;
    public Transform eyes;
    public Vector3 offset = new Vector3 (0f,0.5f,0f);
    //Indicates what state the enemy is in
    public MeshRenderer meshRendererFlag;
    //Stores reference to bullet prefab
    public GameObject bulletPrefab;
    //Stores reference to text from message box UI component
    public Text messageBoxText;
    //Set location for bullet spawn location
    public Transform bulletSpawn;
    //UPDATE: 31/03/17: Enemy model cannot currently be animated due to errors from imported model
    //Get reference for Animator to control animations
    //Animator anim;
    //Reference to the audio source.
    public AudioSource enemyAudio;
    //The sound to play when the enemy dies.
    public AudioClip deathClip;
    //The sound to play when the enemy is wounded.
    public AudioClip damagedClip;


    //These variables are hidden from the Inspector as they don't need to be edited
    //Locates the enemy's current chase target (the player)
    [HideInInspector] public Transform chaseTarget;
    //Variable for enemy's current state
    [HideInInspector] public IEnemyState currentState;
    //Variable for the chase state
    [HideInInspector] public ChaseState chaseState;
    //Variable for the alert state
    [HideInInspector] public AlertState alertState;
    //Variable for the patrol state
    [HideInInspector] public PatrolState patrolState;
    //Variable for the dead state
    [HideInInspector] public DeadState deadState;
    //Variable for the attack state
    [HideInInspector] public AttackState attackState;
    //Variable for enemy health
    [HideInInspector] public EnemyHealth enemyHealth;
    //Reference to NavMeshAgent component that allows AI to move around scene automatically
    [HideInInspector] public NavMeshAgent navMeshAgent;

    private void Awake()
    {
        chaseState = new ChaseState (this);
        alertState = new AlertState (this);
        patrolState = new PatrolState (this);
        deadState = new DeadState (this);
        attackState = new AttackState (this);
        //Gets the NavMeshAgent component before the game starts to run
        navMeshAgent = GetComponent<NavMeshAgent> ();
        enemyAudio = GetComponent<AudioSource> ();
    }

    void Start()
    {
        //When the game starts to run, the default state for the enemy will be patrol
        currentState = patrolState;
        //Get message box text component when scene runs
        //messageBoxText = GetComponent<Text> ();
        //Get Animator component
        //anim = GetComponent<Animator> ();
    }

    public string GetCurrentState()
    {
        return currentState.GetType ().ToString ();
    }

    void Update()
    {
        //Update the current state of the enemy
        currentState.UpdateState ();
        //Updates message box text depending on enemy state
        //UPDATE 31/03/17. This if statement is what I tried to do to get the text to update but it didn't work. I was trying to get the current state.
        if (currentState == patrolState) {
            messageBoxText.text = "Enemy is patrolling";
        }
        messageBoxText.text = "Patrolling";

    }

    void OnCollisionEnter(Collision col)
    {
        //If a bullet collides with an enemy and their health is more than 0...
        if (col.gameObject.tag == "Bullet" /*&& enemyHealth.currentHealth > 0*/)
        {
            //Play the audio clip of the enemy being hurt
            enemyAudio.clip = damagedClip;
            enemyAudio.Play ();
        }
    }

    private void OnTriggerEnter (Collider other)
    {
        currentState.OnTriggerEnter (other);
    }
}
using UnityEngine;
using System.Collections;

public interface IEnemyState
{

    void UpdateState();

    void OnTriggerEnter (Collider other);

    void ToPatrolState();

    void ToAlertState();

    void ToChaseState();

    void ToDeadState();

    void ToAttackState();
}
using UnityEngine;
using UnityEngine.UI;
using System.Collections;

public class ChaseState : IEnemyState {

    //Gets a reference to the StatePatternEnemy script
    private readonly StatePatternEnemy enemy;

    //This checks whether or not the player is in range of the enemy and determines what action the enemy will take
    bool playerInRange;

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

    //Updates the Look and Chase functions during the chase state's operation
    public void UpdateState()
    {
        Look ();
        Chase ();
    }

    public void OnTriggerEnter (Collider other)
    {
        //If the entering collider is the player...
        if (other.CompareTag ("Player"))
        {
            //...the player is in range.
            playerInRange = true;
        }
       
    }
   
    public void OnTriggerExit (Collider other)
    {
        //If the exiting collider is the player...
        if (other.CompareTag ("Player"))
        {
            //...the player is no longer in range.
            playerInRange = false;
        }
    }

    //This is left empty because the enemy doesn't go from chase state to patrol state
    public void ToPatrolState()
    {

    }

    //Logic for alert state. Simply sets enemy into alert state.
    public void ToAlertState()
    {
        enemy.currentState = enemy.alertState;
    }

    //This state is also empty because it's already in chase state
    public void ToChaseState()
    {

    }

    public void ToDeadState()
    {
        //Sets enemy into dead state from alert state
        enemy.currentState = enemy.deadState;
    }

    public void ToAttackState()
    {
        enemy.currentState = enemy.attackState;
    }

    void Update()
    {
        //enemy.messageBoxText.text = "Enemy is chasing the player";
    }

    //This function sets up the logic so that if the player is detected by the enemy, the enemy will chase the player
    private void Look()
    {
        Debug.Log ("Enemy chasing");
        //The raycast is used to detect the player
        RaycastHit hit;
        //Enemy sets target on player
        Vector3 enemyToTarget = (enemy.chaseTarget.position + enemy.offset) - enemy.eyes.transform.position;
        //If the enemy GameObject detects the player when they are within range and can see the player...
        if (Physics.Raycast (enemy.eyes.transform.position, enemyToTarget, out hit, enemy.sightRange) && hit.collider.CompareTag ("Player")) {
            //...the enemy will start to chase the player depending on what the raycast hit
            enemy.chaseTarget = hit.transform;

        }
        //If the player is in range of the enemy...
        else if (playerInRange)
        {
            //Go into the attack state
            ToAttackState();
        }

        //If the scene doesn't meet the above requirements, instead the enemy will return to alert state
        else
        {
            ToAlertState ();
        }

       
    }

    //Logic for the chase function
    private void Chase()
    {
        Debug.Log ("Enemy chasing");
        enemy.meshRendererFlag.material.color = Color.red;
        //Sets the enemy's destination target on the player
        enemy.navMeshAgent.destination = enemy.chaseTarget.position;
        //Resumes movement along current path after pause.
        enemy.navMeshAgent.Resume ();
        enemy.messageBoxText.text = "Enemy is chasing";
    }
}

What you have seems like it should work. StatePatternEnemy’s Update method looks fine. Are you getting an error? Is the text not showing up correctly?

There’s no errors when I run the code. The text just doesn’t show up. What I have is a UI text object that has placeholder text. In each state I want the text to reflect what state is currently active. This script is attached to the enemies in my scene and the UI text object is assigned (it’s the only UI text object in the scene). The only time it seems to work is in the Update function of my base state class and I tried to update the text through the Update method and use if statements to check which state was active and then set the text accordingly, but this didn’t work. The text just won’t change, it’s so frustrating but I know the answer is right there.

I don’t know if it’s your entire code, but if this is all that’s really there:

void Update()
    {
        //Update the current state of the enemy
        currentState.UpdateState ();
        //Updates message box text depending on enemy state
        //UPDATE 31/03/17. This if statement is what I tried to do to get the text to update but it didn't work. I was trying to get the current state.
        if (currentState == patrolState) {
            messageBoxText.text = "Enemy is patrolling";
        }
        messageBoxText.text = "Patrolling";

    }

Then it’s just always going to say “Patrolling” because you set it to that at the end, regardless of what happens before. That’s probably supposed to be an “else”. You’d also need if statements for every state, not just patrolling. Something like:

void Update()
    {
        //Update the current state of the enemy
        currentState.UpdateState ();
       
        if (currentState == patrolState)
            messageBoxText.text = "Enemy is patrolling";        
        else if (currentState == chaseState) 
            messageBoxText.text = "Enemy is chasing";
        else if (currentState == alertState) 
             messageBoxText.text = "Enemy is alert";
        //etc for all the states
        else
             messageBoxText.text = "Uknown state";
    }

You could also use a “switch” statement instead of a bunch of if/elses and you could use an event to only change the text when the state changes instead of doing it every frame, but I’d try to get it working as it currently is before changing it. Are you sure the enemy’s state is actually changing?

makeshiftwings, this half worked. I added in the else if statements for each state with it’s own message and commented out the “Patrolling” text. It now says “Enemy is patrolling”, which means the first if statement worked, but the text won’t change when the enemy changes state though.

In regards to the state actually changing, it seems to be working because I have Debug.Log in each state class to make sure it is, and it does. Also, the enemy reacts according to whatever instructions are in each state class.

It seems to be working now. I have noticed that the states change rather rapidly so the text doesn’t stay the same for very long, but I can have a UI text for each enemy now I’ve got this working, so thank you very much for your help, makeshiftwings. If I run into more problems in regards to this issue I’ll probably report back but for now it’s fixed so thanks again! Glad I came here now.