【问题标题】:Timer not stoppinng after StopCoroutine called调用 StopCoroutine 后计时器未停止
【发布时间】:2017-12-26 18:27:21
【问题描述】:

我不确定为什么会发生这种情况,但我有一个倒计时计时器,它会在用户点击调用 StopTimerButton() 的按钮时触发并停止。但是即使我调用了 StopCoroutine,计时器也会继续倒计时,直到它达到零,然后调用 RestartGame()。我的代码中缺少什么?

using System.Collections;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;

public class CountdownTimer : MonoBehaviour
{
    public static CountdownTimer countdownTimerInstance = null; // create singleton
    public Object startingScene;
    public GameObject timeOutWarningDialog;
    public float preCountdownLength;
    public float countdownLength;

    private GameObject timerDialogBoxInstance;
    private GameObject timerDialogCountdownText;
    private Text timerDialogCountdownTextTarget;
    private GameObject canvas;
    private IEnumerator warningCounter;
    private IEnumerator preCounter;
    private Button stopCountButton;
    private float countdownInterval = 1.0f;
    private bool preCountActive;
    private bool warningCountActive;

    void Awake()
    {
        ResetCountStates();

        if (countdownTimerInstance == null)
            countdownTimerInstance = this;
        else if (countdownTimerInstance != null)
            Destroy(gameObject);
        DontDestroyOnLoad(gameObject);
    }

    void Update()
    {
        bool userActive = GameManager.userActive;
        bool onIntroScreen = GameManager.onIntroScreen;

        if (!userActive && !onIntroScreen && !preCountActive)
            StartPreCountTimer(preCountdownLength); 
        else if (userActive && !onIntroScreen && preCountActive)
            StopPreCountTimer();
    }

    void StartPreCountTimer(float length)
    {
        preCountActive = true;
        preCounter = RunTimer(length);
        StartCoroutine(preCounter);
        Debug.Log("PreCount Started");
    }

    void StopPreCountTimer()
    {
        preCountActive = false;
        StopCoroutine(preCounter);
        Debug.Log("PreCount Stopped");
    }

    void WarningDialog(float length)
    {
        preCountActive = false;
        warningCountActive = true;
        canvas = GameObject.FindGameObjectWithTag("Canvas");
        timerDialogBoxInstance = Instantiate(timeOutWarningDialog); // instantiate timeout warning dialog
        timerDialogBoxInstance.transform.SetParent(canvas.transform, false);
        timerDialogBoxInstance.SetActive(true);
        timerDialogCountdownText = GameObject.FindGameObjectWithTag("CountdownText"); // get reference to countdown text GO
        timerDialogCountdownTextTarget = timerDialogCountdownText.GetComponent<Text>(); // get countdown textfield component
        stopCountButton = timerDialogBoxInstance.GetComponentInChildren<Button>(); // get reference to keep playing button
        stopCountButton.onClick.AddListener(StopTimerButton); // add button listener

        if (warningCountActive && !preCountActive && timerDialogBoxInstance != null)
        {
            warningCounter = RunTimer(length); // create new reference to counter, resets countdown to countdownLength
            StartCoroutine(warningCounter);
        }
    }

    IEnumerator RunTimer(float seconds)
    {
        // PRECOUNT TIMER
        if (!warningCountActive)
        {
            float s = seconds;
            while (s > 0)
            {
                yield return new WaitForSeconds(countdownInterval);
                s -= countdownInterval;
                Debug.Log("PreCount: " + s);
            }

            if (s == 0)
            {
                preCountActive = false;
                warningCountActive = true;
                WarningDialog(countdownLength);
            }
        }

        // WARNING DIALOG TIMER
        if (warningCountActive)
        {
            float s = seconds;
            while (s > 0)
            {
                yield return new WaitForSeconds(countdownInterval);
                if (timerDialogBoxInstance != null)
                    timerDialogCountdownTextTarget.text = s.ToString();
                s -= countdownInterval;
                Debug.Log("WarningCountdown: " + s);
            }

            if (s == 0)
                if (timerDialogBoxInstance != null)
                {
                    StopCoroutine(warningCounter);
                    Destroy(timerDialogBoxInstance);
                    RestartGame();
                }
        }
    }

    void StopTimerButton()
    {
        warningCountActive = false;
        StopCoroutine(warningCounter);

        if (timerDialogBoxInstance != null)
        {    
            Destroy(timerDialogBoxInstance);
            timerDialogBoxInstance = null;
        }
        Debug.Log("Restart Cancelled");
    }

    void ResetCountStates()
    {
        preCountActive = false;
        warningCountActive = false;
    }

    void RestartGame()
    {
        ResetCountStates();
        SceneManager.LoadScene(startingScene.name);
    }
}

【问题讨论】:

  • 什么时候调用WarningDialog函数?
  • 在 PreCountTimer 达到零后(在 Runtimer 中)我调用 WarningDialog()。
  • 如何使用StopTimerButton 函数启动要停止的计时器?
  • 我在 RunTimer 中使用以下行启动计时器:WarningDialog(countdownLength);

标签: c# unity3d timer coroutine


【解决方案1】:

脚本太长,无法找出问题,但我注意到可能是问题所在。

在您的 WarningDialog 函数中,查看以下代码块:

if (warningCountActive && !preCountActive && timerDialogBoxInstance != null)
{
    warningCounter = RunTimer(length); // create new reference to counter, resets countdown to countdownLength
    StartCoroutine(warningCounter);
}

StopTimerButton() 函数可能无法正常工作,因为每次 RunTimer 函数仍在运行并且您调用 WarningDialog 函数时,名为 warningCounter 的协程函数引用会丢失,因为它在您调用时会被覆盖做warningCounter = RunTimer(length);

您必须将warningCounter = RunTimer(length); 移动到Start() 函数,以便warningCounter 仅被初始化一次

【讨论】:

  • 你试过了吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-03-23
  • 2015-11-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-21
相关资源
最近更新 更多