Counting how many enemies are there in a certain enum state C#

So I’m currently working on an Arkham Style AI for a 2.5D brawler game. I’m deciding to use different enum states for the enemies to be in including Idling, Engaging and Retreating. I want to make it so that if there are two enemies that are in an engaging state, the other enemies will be in an idling state and not be able to attack the player and if enemies that are idling get too close to the player, they will retreat and switch to a returning state. I just don’t know how to be able to count the numbers of enemies in a scene that are in a certain enum state like how many are in an engaging state.

This is the enum for my enemy

 public enum RobotState {ENGAGING, IDLING, RETREATING}

I would probably maintain list of enemies. 1 for each state, this way you can just get a count of each list to see how many are in that list. If there are already the max number, then an enemy can’t switch to that state. You could also just maintain ints and add or subtract. The reason I would do list of enemies is so you can easily access which enemies are in the given state. So if you wanted to loop through your idling enemies to see if they could change states, you can just access the idling list for example.

You could try using a for loop that increments a counter for each one in the engaging state.

There’s a bajillion ways to approach this…

How I’d go about it is have a state object that represents the state of all AI entities that are currently active.

public interface IAIGroup
{

    void RegisterActive(IAIEntity ai);
    void UnregisterActive(IAIEntity ai);
    void IEnumerable<IAIEntity> GetEntities();

}

public interface IAIEntity
{
    AIState CurrentState { get; }
  
    void Think();
    void ChangeState(AIState state);
}

public enum AIState
{
    Engaging,
    Idling,
    Retreating
}

Now AIEntity should register and unregister with the AIGroup when it becomes active/inactive.

public class AIEntity : MonoBehaviour, IAIEntity
{
  
    private IAIGroup _group;
    private AIState _state;
  
    void Awake()
    {
        //get a reference to your IAIGroup in some way... that's up to you
        //I'm using a simple service retrieve design here
        _group = Services.Get<IAIGroup>();
    }
  
    void OnEnable()
    {
        _group.RegisterActive(this);
    }
  
    void OnDisable()
    {
        _group.UnregisterActive(this);
    }
  
    public AIState CurrentState { get { return _state; } }
  
    public void Think()
    {
        switch(_state)
        {
            case AIState.Engaging:
                {
                    //do stuff
                }
                break;
            case AIState.Idling:
                {
                    //do stuff
                }
                break;
            case AIState.Retreating:
                {
                    //do stuff
                }
                break;
        }
    }
  
    public void ChangeState(AIState state)
    {
        _state = state;
      
        //react to the change of state in some way
        switch(_state)
        {
            case AIState.Engaging:
                {
                    //do stuff
                }
                break;
            case AIState.Idling:
                {
                    //do stuff
                }
                break;
            case AIState.Retreating:
                {
                    //do stuff
                }
                break;
        }
    }
  
}

Now we’re going to want to be able to count how many entities are in a given state:

public static class AIGroupUtils
{
    public static int CountInState(this IAIGroup grp, AIState state)
    {
        int cnt = 0;
        foreach(var e in grp.GetEntities())
        {
            if(e.CurrentState == state) cnt++;
        }
        return cnt;
    }
}

(note - there are more efficient ways of doing this, we’re getting the basic structure down first… then we can optimize)

So in our Think method we may do something like:

    public void Think()
    {
        switch(_state)
        {
            case AIState.Engaging:
                {
                    if(_group.CountInState(AIState.Engaging) > 2)
                    {
                        this.ChangeState(AIState.Retreating);
                        break;
                    }
                  
                    //do stuff
                }
                break;
            case AIState.Idling:
                {
                    //do stuff
                }
                break;
            case AIState.Retreating:
                {
                    //do stuff
                }
                break;
        }
    }

As for making the actual counting faster. We could have the IAIGroup actually track that information for you as a state, so you don’t have to do the expensive query every time. Effectively caching that information for us:

public interface IAIGroup
{

    void RegisterActive(IAIEntity ai);
    void UnregisterActive(IAIEntity ai);
    void IEnumerable<IAIEntity> GetEntities();
  
    int CountInState(AIState state);
    void SignalStateChange(IAIEntity entity);

}

public class AIGroup : MonoBehaviour, IAIGroup
{

    private HashSet<IAIEntity> _entities = new HashSet<IAIEntity>();
  
    private int _engagingCount;
    private int _idlingCount;
    private int _retreatingCount;
    private bool _dirty;
  
    public void RegisterActive(IAIEntity entity)
    {
        _entities.Add(entity);
        _dirty = true;
    }
  
    public void UnregisterActive(IAIEntity entity)
    {
        _entities.Remove(entity);
        _dirty = true;
    }
  
    public void IEnumerable<IAIEntity> GetEntities()
    {
        return _entities;
    }
  
    public int CountInState(AIState state)
    {
        if(_dirty)
        {
            this.Clean();
        }
      
        switch(_state)
        {
            case AIState.Engaging:
                return _engagingCount;
            case AIState.Idling:
                return _idlingCount;
            case AIState.Retreating:
                return _retreatingCount;
            default:
                return 0;
        }
    }
  
    public void SignalStateChange(IAIEntity entity)
    {
        _dirty = true;
    }
  
    private void Clean()
    {
        _engagingCount = 0;
        _idlingCount = 0;
        _retreatingCount = 0;
      
        foreach(var e in _entities)
        {
            switch(e.CurrentState)
            {
                case AIState.Engaging:
                    _engagingCount++;
                    break;
                case AIState.Idling:
                    _idlingCount++;
                    break;
                case AIState.Retreating:
                    _retreatingCount++;
                    break;
            }
        }

        _dirty = false;
    }
  
}

And of course signal to the group on state change:

    public void ChangeState(AIState state)
    {
        _state = state;
        _group.SignalStateChange(this);
      
        //react to the change of state in some way
        switch(_state)
        {
            case AIState.Engaging:
                {
                    //do stuff
                }
                break;
            case AIState.Idling:
                {
                    //do stuff
                }
                break;
            case AIState.Retreating:
                {
                    //do stuff
                }
                break;
        }
    }

Now of course, this isn’t a fully functional AI system here. This is all just example stuff. But it’s a general idea for what you’re looking forward to layed out in a built up structure.

With that said… no I did not run this code, I wrote it here in the WYSIWYG for the post, there is probably spelling mistakes galore. Use it as a starting point, not as a defacto answer.

I’m so sorry, I’m not an experienced programmer so I’m not familiar with C#'s interface. I’m still trying to understand with this method your using, I also now trying to learn and understand Interafaces in C#. Is it okay if you could further explain the details in your code?