I am trying to populate various objects in Unity based on some triggers and some data. The data that I am using looks something like this Key === O2 Values are ==== { collected, collected, collected, absent}Key === O3 Values are ==== { collected, collected, present } stored in a Dictionary object Dictionary<string, List<string>> textMap. Following script is attached to these objects:
using System;
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
public class SensePlayerProximity : MonoBehaviour {
bool disableEntry = false;
bool disableExit = false;
public bool isCollected = false;
//List<Collider2D> triggerList = new List<Collider2D>();
// Use this for initialization
static Dictionary<string, List<string>> textMap = new Dictionary<string, List<string>>
{
{ "O2", new List<string>() { "present", "absent", "absent", "absent" }},
{ "O3",new List<string>() { "absent", "absent", "absent" }}
};
private void OnTriggerEnter2D(Collider2D collision)
{
if (disableEntry || isCollected)
return;
StartCoroutine(disableTriggersForThisCollectible(10));
List<String> values;
foreach (KeyValuePair<string, List<string>> kvp in textMap)
{
values = kvp.Value;
int foundAtIndex = values.IndexOf("absent");
if (foundAtIndex > -1)
{
gameObject.GetComponent<TextMeshProUGUI>().text = kvp.Key;
values[foundAtIndex] = "present";
textMap.Remove(kvp.Key);
textMap.Add(kvp.Key, values);
logTextMap();
return;
}
}
// if nothing is found, then default the text to empty, since nothing left to be collected now
gameObject.GetComponent<TextMeshProUGUI>().text = "";
}
//called when something exits the trigger
private void OnTriggerExit2D(Collider2D collision)
{
if (disableExit || isCollected)
return;
//Debug.Log("Player is leaving me.. :(");
string key = gameObject.GetComponent<TextMeshProUGUI>().text;
StartCoroutine(disableTriggersForThisCollectible(0.1f));
List<string> values = textMap[key];
int index = -1;
if (values != null)
index = values.IndexOf("present");
if(index >= 0)
{
values[index] = "absent";
textMap.Remove(key);
textMap.Add(key, values);
gameObject.GetComponent<TextMeshProUGUI>().text = "";
logTextMap();
}
}
IEnumerator disableEntryTrigger(float t)
{
disableEntry = true;
// disable the trigger collider for t seconds
yield return new WaitForSeconds(t);
disableEntry = false;
}
IEnumerator disableExitTrigger(float t)
{
disableExit = true;
// disable the trigger collider for t seconds
yield return new WaitForSeconds(t);
disableExit = false;
}
void logTextMap()
{
string debugString = "";
foreach (KeyValuePair<string, List<string>> kvp in textMap)
{
debugString += "Key === " + kvp.Key + " Values are ==== { " + String.Join(", ", kvp.Value.ToArray()) + " }";
}
Debug.Log(debugString);
}
}
The script detects trigger collisions with BoxCollider2D attached to my player, and it has "Sensor" tag name attached to it. I disable the triggers for 10s whenever OnTriggerEnter2D event occurs and for 0.1s whenever OnTriggerExit2D event occurs.
I have some fixed textobjects scattered in my level and trying to populate the text in them on the basis of this script above. This script is attached to every such text object.
With the help of these events, I detect if the player is in the vicinity of a text object. If the player is found, then a random key will be populated from the textMap provided that key has at least one value which says "absent". Each key has a list of values, which could be "absent", "present", or "collected". "absent" means that the key is absent from the camera view and thus can be assigned to new collectible text objects. "present" means that the key is present in the current camera view and is not available for the other text objects. "collected" means that the key has already been collected and is not available either. In the example value of textMap which I shown above, for example, there can be 4 copies of the key "O2" in the map, and 3 copies of the key "O3". Out of these, 3 "O2" and 2 "O3" have already been collected. Only 1 copy of "O2" can be assigned to newly triggered text objects and no copy of "O3" is available for them. The script works mostly as expected, except a few time which I am not able to debug. The debug log show that one copy of "O3" is already present in the view, but I went to my scene and could not find "O3" anywhere. I am afraid that this might be happening because all the triggered text objects (to which the above script is attached) are trying to modify the textMap at the same time. I have wasted a lot of time trying to figure this out, but I am just banging my head in the wall. I'd really appreciate if someone could point me in the right direction. My scene with these objects is shown below:

Edit: I found the problem to be DontDestroyOnLoad. The said gameobjest to which SensePlayerProximity script is attached are all children of a DontDestroyOnLoad gameobject called ScenePersist. The problem is happening only when I reload the scene, upon player death. When the scene reloads, new ScenePersist is loaded in the scene, and just before it called the triggerentry method on children before being destroyed. Because of this, OnTriggerEnter2D is called twice, instead of once. How do I fix this problem?
One way to fix this would be to keep all these objects far away from the player spawn point, so that the triggers doesn't occur, but that is not a good way to fix it. Another is to run a coroutine enableTriggers in start method, i.e. disable the triggers by default, but that is not a good solution either.
void Start () {
disableEntry=true;
disableExit = true;
StartCoroutine(enableTriggers());
}
IEnumerator enableTriggers()
{
yield return new WaitForSeconds(0);
disableEntry = false;
disableExit = false;
}
This is how my scene hierarchy looks like:
Here the ScenePersist is set to DontDestroyOnLoad, and it has a lots of sub-child objects (highlighted as collectible) to which the SensePlayerProximity is attached.

disableTriggersForThisCollectiblemethod.