Collider / coroutine timer issues

Collider / coroutine timer issues

Now this may be a physics issue as it seems more of a collider issue - specifically using multiple box colliders inside 1 empty gameobject. Oh and aplogies about the lengthy thread.
My game (for my degree dissertation) consists of two 3D levels the players walks around and each level consists of 4 zones. What i want to do is start and stop individual timers for each zone so i can work out how long they spend in each area.
I have sussed this part using coroutines to do my timers (see code below) and the whole things works as intended except that the issue is, as these zones are made up of multiple box colliders in an empty gameobject (say one zone is an L-shaped, meaning its made up of 2 cuboids), each zone is not exactly simple shapes. Basically a maze of 10x10 floors (planes), set out in a maze like fashion with various zones (see image below). For each of these zones, i have an empty gameobject with multiple box colliders to make up the volume of each zone. These are set to IsTrigger so that the player can wander through but doing so trigger the OnTriggerxxx() events to start and stop the coroutines.
The trouble is where these box colliders join internally inside the same zone. The timer stops when the player crosses from one collider to another, even though they're in the same game object (zone) and therefore compound.
The timer either stops or it seems to multiply the timer speed when you cross between collider borders, multipling each time a boundary is crossed, so walking backwards and forwards between colliders makes the timer keep going faster and faster and obviously not correct time data.
So questions are:
Is there something i can add to the OnTriggerStay() events to keep the timers going or to stop the build up of speed in the timers? Which is obviously a build up of the triggered coroutines stacking on each other, escalating the speed of the clock whenever the player keeps crosses ANY colliders.
or......
Is there a way to correct the issue with the colliders? Like create them as one single collider? This by far seems the most obvious issue. If i could make a single collider the shape of the zone, so like a 3D L-shape or other bespoke shape. Think tetris in 3D as a maze, similar shapes basically.

This is the script i've written for the timer routine. I bet its something really obvious haha.
Thanks,
Paul..
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class AreaTime_A : MonoBehaviour {

    private float startTime;
    public Text timerText;
    public Text timerText_HW;
    public Text timerText_ST;
    public Text timerText_IZ;
    public Text timerText_OUT;
    private bool TimerOn = false;
    private bool TimerOn_HW = false;
    private bool TimerOn_ST = false;
    private bool TimerOn_IZ = false;
    private bool TimerOn_OUT = false;

    public int minutes;
    public int seconds;
    public int seconds_HW;
    public int minutes_HW;
    public int seconds_ST;
    public int minutes_ST;
    public int seconds_IZ;
    public int minutes_IZ;
    public int seconds_OUT;
    public int minutes_OUT;

    // Use this for initialization
    void Start () {

        startTime = Time.time;
        TimerStart();
    }

    void Awake () {
        DontDestroyOnLoad (this);
    }

    void Update () {
    }

    public void TimerStart() {
        TimerOn = true;
        StartCoroutine (TOTAL_Timer());
    }

    public void TimerEnd() {
        StopCoroutine (TOTAL_Timer());
        TimerOn = false;
        TimerOn_HW = false;
        TimerOn_ST = false;
        TimerOn_IZ = false;
        TimerOn_OUT = false;
    }

    private IEnumerator TOTAL_Timer() {
        while (TimerOn) {
            yield return new WaitUntil (()=> TimerOn);
            seconds++;

            if (seconds == 60) {
                minutes++;
                seconds = 0;
            }
            timerText.text = minutes.ToString() + ":" + seconds.ToString();
        }
    }

    private IEnumerator HW_Timer() {
        while (TimerOn_HW) {
            yield return new WaitUntil (()=> TimerOn_HW);
            seconds_HW++;

            if (seconds_HW == 60) {
                minutes_HW++;
                seconds_HW = 0;
            }
            timerText_HW.text = minutes_HW.ToString() + ":" + seconds_HW.ToString();
        }
    }

    private IEnumerator ST_Timer() {
        while (TimerOn_ST) {
            yield return new WaitUntil (()=> TimerOn_ST);
            seconds_ST++;

            if (seconds_ST == 60) {
                minutes_ST++;
                seconds_ST = 0;
            }
            timerText_ST.text = minutes_ST.ToString() + ":" + seconds_ST.ToString();
        }
    }

    private IEnumerator IZ_Timer() {
        while (TimerOn_IZ) {
            yield return new WaitUntil (()=> TimerOn_IZ);
            seconds_IZ++;

            if (seconds_IZ == 60) {
                minutes_IZ++;
                seconds_IZ = 0;
            }
            timerText_IZ.text = minutes_IZ.ToString() + ":" + seconds_IZ.ToString();
        }
    }

    private IEnumerator OUT_Timer() {
        while (TimerOn_OUT) {
            yield return new WaitUntil (()=> TimerOn_OUT);
            seconds_OUT++;

            if (seconds_OUT == 60) {
                minutes_OUT++;
                seconds_OUT = 0;
            }
            timerText_OUT.text = minutes_OUT.ToString() + ":" + seconds_OUT.ToString();
        }
    }

    void OnTriggerEnter(Collider other)
    {
        if (other.gameObject.name == "HW_Collider") 
        {
            TimerOn_HW = true;
            StartCoroutine (HW_Timer());
            timerText_HW.color = Color.yellow;
        }
        else if (other.gameObject.name == "ST_Collider")
        {
            TimerOn_ST = true;
            StartCoroutine (ST_Timer());
            timerText_ST.color = Color.magenta;
        }
        else if (other.gameObject.name == "IZ_Collider")
        {
            TimerOn_IZ = true;
            StartCoroutine (IZ_Timer());
            timerText_IZ.color = Color.green;
        }
        else if (other.gameObject.name == "OUT_Collider")
        {
            TimerOn_OUT = true;
            StartCoroutine (OUT_Timer());
            timerText_OUT.color = Color.blue;
        }
    }

    void OnTriggerExit(Collider other)
    {
        if (other.gameObject.name == "HW_Collider") 
        {
            TimerOn_HW = false;
            StopCoroutine (HW_Timer());
            timerText_HW.color = Color.white;
        }
        else if (other.gameObject.name == "ST_Collider")
        {
            TimerOn_ST = false;
            StopCoroutine (ST_Timer());
            timerText_ST.color = Color.white;
        }
        else if (other.gameObject.name == "IZ_Collider")
        {
            TimerOn_IZ = false;
            StopCoroutine (IZ_Timer());
            timerText_IZ.color = Color.white;
        }
        else if (other.gameObject.name == "OUT_Collider")
        {
            TimerOn_OUT = false;
            StopCoroutine (OUT_Timer());
            timerText_OUT.color = Color.white;
        }
    }

    void OnTriggerStay(Collider other)
    {
        if (other.gameObject.name == "HW_Collider") 
        {
            timerText_HW.color = Color.yellow;
        }
        else if (other.gameObject.name == "ST_Collider")
        {
            timerText_ST.color = Color.magenta;
        }
        else if (other.gameObject.name == "IZ_Collider")
        {
            timerText_IZ.color = Color.green;
        }
        else if (other.gameObject.name == "OUT_Collider")
        {
            timerText_OUT.color = Color.blue;
        }
    }
}

Solutions/Answers:

Answer 1:

This happens because you’re not stopping the coroutines that you started in the first place, because you have no reference to them.

In order to fix the problem, you need to declare IEnumerator variables that will store the reference to the coroutines, so that you can stop the exact same one referenced by the variable.

Like this:

IEnumerator totalTimer;
IEnumerator hwTimer;
IEnumerator stTimer;
IEnumerator izTimer;
IEnumerator outTimer;

void Awake() {
    totalTimer = TOTAL_Timer();
    hwTimer = HW_Timer();
    stTimer = ST_Timer();
    izTimer = IZ_Timer();
    outTimer = OUT_Timer();     
}

Then, when you need to start and stop one of those coroutines, just use, for example when starting and stopping TOTAL_Timer(), StartCoroutine(totalTimer) and StopCoroutine(totalTimer).

Answer 2:

Do your coroutines really stop when you do stopcoroutine? You might have to save them like Coroutine coroutine1 = StartCoroutine(1); and then stop it with StopCoroutine(coroutine1);

Answer 3:

I suggest tracking the count of colliders from each zone that the player is in with OnTriggerEnter and OnTriggerExit, and only starting or stopping a coroutine in Update.

The code you posted in a comment will start a new timer if the timer is running, not if it isn’t. A small change would fix it. Try this for your Update:

void Update() {
    if (Count_HW > 0) {
        if (!TimerOn_HW) {
            TimerOn_HW = true;
            StartCoroutine(HW_Timer());
        }
    } else {
        if (TimerOn_HW) {
            TimerOn_HW = false;
            // coroutine ends automatically
        }

        // bonus: with this kind of enter/exit code, it's always good to check for "impossible" conditions

        if (Count_HW < 0) {
            Debug.LogError("Somehow, we exited " + -Count_HW + " more HW colliders than we entered!");
            Count_HW = 0;
        }
    }

    // same idea for the other timers
}

References