How to allow only one object in a trigger at a time?

I have the following scenario (Diagram 1); the grey circles are AI agents and the green squares are trigger boxes. Diagram 2 is a zoomed in version of Diagram 1. Each trigger has the same tag, so an agent will look for the closest trigger to them and move towards it. When an agent’s transform reaches the center of the trigger they start to earn points, and after a random amount of time they move on.

When an agent has started to earn points I’d like to change the trigger’s tag to “occupied” so that other agents don’t try to move to this trigger, and instead look for the closest “vacant” one. The problem is the timing. I cannot change the tag OnTriggerEnter because the agent inside is still moving towards the center of the trigger, and therefore changing the tag would make it think the trigger is occupied and it would start looking for another trigger. But at the same time, not changing it allows other agents to continue to enter the trigger.

I have tried to change the tag during OnTriggerStay, as soon as the agent starts earning points. And when the agent leaves (OnTriggerExit), the tag is set back to vacant so that other agents can use it. The trouble here is that if there are multiple agents entering the trigger at around the same time, the trigger has no way of knowing the difference between them. For example, Agent 1 enters the trigger, but just before they reach the trigger’s center, Agent 2 enters the trigger. At this point there’s two agents in the trigger, but the trigger doesn’t know that. So Agent 1 could trigger OnTriggerEnter, while Agent 2 can then trigger OnTriggerStay, skipping OnTriggerEnter.

And that is the basis of the issue. I would like to be able to have OnTriggerEnter, Stay and Exit triggered by only one agent at a time. How could I do this?

Here is my script:

using UnityEngine;
using System.Collections;

public class AICover : MonoBehaviour {
	
	public string enemyTag;
	public string coverFreeTag;
	public string coverNotFreeTag;
	public string uniqueID;
	private EnemyUniqueID enemyUniqueID;
	public bool almostNotFree = false;
	public Transform coverTransform;
	private NavMeshAgent enemyNavMeshAgentScript;
	private float enemyNavMeshAgentOrigRadius;
	
	// Use this for initialization
	void Start () {
		coverTransform = transform;
		coverFreeTag = coverTransform.gameObject.tag;
	}
	
	// Update is called once per frame
	void Update () {
	
	}
	
	void OnTriggerEnter (Collider other) {
		if (other.CompareTag (enemyTag)) {
			enemyNavMeshAgentScript = other.GetComponent<NavMeshAgent> ();
			enemyNavMeshAgentOrigRadius = enemyNavMeshAgentScript.radius;
			enemyNavMeshAgentScript.radius = 0.0001f;
			if (coverTransform.gameObject.tag == coverFreeTag && !almostNotFree) {	
				uniqueID = System.DateTime.Now.ToString ("ddMMyyyyHHmmss") + Random.Range (1, 1000).ToString ();
				enemyUniqueID = other.GetComponent<EnemyUniqueID> ();
				enemyUniqueID.enemyID = uniqueID;
				almostNotFree = true;
			}
        }
    }
    
    void OnTriggerStay (Collider other) {
		if (other.CompareTag (enemyTag) && coverTransform.gameObject.tag == coverFreeTag && almostNotFree && uniqueID == enemyUniqueID.enemyID) {
			coverTransform.gameObject.tag = coverNotFreeTag;
	    }
    }
    
    void OnTriggerExit (Collider other) {
		if (other.CompareTag(enemyTag)) {
			enemyUniqueID = other.GetComponent<EnemyUniqueID>();
			enemyNavMeshAgentScript.radius = enemyNavMeshAgentOrigRadius;
			if (coverTransform.gameObject.tag == coverNotFreeTag && almostNotFree && uniqueID == enemyUniqueID.enemyID) {
				coverTransform.gameObject.tag = coverFreeTag;
				almostNotFree = false;
			}
        }
    }
}

Instead of changing tags on your cover objects use variables in your cover class. You can create a method to determine if an agent can move towards that as their target by returning if it is occupied.

Additionally for your uniqueID you can use System.Guid.NewGuid().ToString()(read more here)

I’d do something like this:

  1. An agent looks for the closest non-ocuppied trigger
  2. The agent starts moving towards it
  3. On each update it checks if the trigger has become occupied by other. If it has, goes back to step 1
  4. If it enters a trigger, store a reference in that object to the agent that entered, this would be the “occupied” flag.
  5. When it finished earning points, clear the object reference so other AI agents can move there even if the current one hasn’t leave the trigger

So, when an agent checks if a square is occupied, it actually check if the reference is not null AND if it’s no a reference to itself. In the case where two agents enter the same square, only one will make it as the reference, so the other one will move away form it.