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.