c# - 按下主页按钮或返回主菜单时取消协程
问题描述
我正在做的一些借口;我目前正在通过在协程中设置 interactable = false 来锁定我的技能按钮。通过 textmeshpro 显示剩余秒数的文本,并在倒计时结束时将其设置为无效。但是当按下主页按钮/返回主菜单时我遇到了问题。我想刷新我的按钮冷却时间并在按下时停止协程。但它保持在锁定位置。
这是我的冷却协程
static List<CancellationToken> cancelTokens = new List<CancellationToken>();
...
public IEnumerator StartCountdown(float countdownValue, CancellationToken cancellationToken)
{
try
{
this.currentCooldownDuration = countdownValue;
// Deactivate myButton
this.myButton.interactable = false;
//activate text to show remaining cooldown seconds
this.m_Text.SetActive(true);
while (this.currentCooldownDuration > 0 && !cancellationToken.IsCancellationRequested)
{
this.m_Text.GetComponent<TMPro.TextMeshProUGUI>().text = this.currentCooldownDuration.ToString(); //Showing the Score on the Canvas
yield return new WaitForSeconds(1.0f);
this.currentCooldownDuration--;
}
}
finally
{
// deactivate text and Reactivate myButton
// deactivate text
this.m_Text.SetActive(false);
// Reactivate myButton
this.myButton.interactable = true;
}
}
static public void cancelAllCoroutines()
{
Debug.Log("cancelling all coroutines with total of : " + cancelTokens.Count);
foreach (CancellationToken ca in cancelTokens)
{
ca.IsCancellationRequested = true;
}
}
void OnButtonClick()
{
CancellationToken cancelToken = new CancellationToken();
cancelTokens.Add(cancelToken);
Coroutine co;
co = StartCoroutine(StartCountdown(cooldownDuration, cancelToken));
myCoroutines.Add(co);
}
这是我按下主页按钮/返回主菜单时捕捉到的地方。当抓住它并弹出 pauseMenu
public void PauseGame()
{
GameObject menu = Instantiate(PauseMenu);
menu.transform.SetParent(Canvas.transform, false);
gameManager.PauseGame();
EventManager.StartListening("ReturnMainMenu", (e) =>
{
Cooldown.cancelAllCoroutines();
Destroy(menu);
BackToMainMenu();
EventManager.StopListening("ReturnMainMenu");
});
...
当游戏暂停时我也会停止时间
public void PauseGame() {
Time.timeScale = 0.0001f;
}
解决方案
CancellationToken
在这种情况下,您使用不正确。CancellationToken
是一个包装CancellationTokenSource
这样的结构:
public bool IsCancellationRequested
{
get
{
return source != null && source.IsCancellationRequested;
}
}
因为它是一个结构,所以它通过 value传递,这意味着您存储在列表中的实例与您Coroutine
拥有的实例不同。
处理取消的典型方法是创建一个CancellationTokenSource并传递它Token
。每当您想取消它时,只需.Cancel()
调用CancellationTokenSource
. 采用这种方式的原因是只能CancellationToken
通过“源”引用而不是令牌的消费者来取消。
在您的情况下,您正在创建一个根本没有来源的令牌,因此我建议进行以下更改:
首先,将您的cancelTokens
列表更改为:
List<CancellationTokenSource>
接下来,将您的OnButtonClick()
方法更改为如下所示:
public void OnButtonClick()
{
// You should probably call `cancelAllCoroutines()` here
cancelAllCoroutines();
var cancellationTokenSource = new CancellationTokenSource();
cancelTokens.Add(cancellationTokenSource);
Coroutine co = StartCoroutine(StartCountdown(cooldownDuration, cancellationTokenSource.Token));
myCoroutines.Add(co);
}
最后,将您的cancelAllCoroutines()
方法更改为:
public static void CancelAllCoroutines()
{
Debug.Log("cancelling all coroutines with total of : " + cancelTokens.Count);
foreach (CancellationTokenSource ca in cancelTokens)
{
ca.Cancel();
}
// Clear the list as @Jack Mariani mentioned
cancelTokens.Clear();
}
我建议阅读有关Cancellation Tokens的文档,或者像 @JLum 建议的那样,使用 Unity 提供的StopCoroutine方法。
编辑:我忘了提到,建议CancallationTokenSources
在不再使用时将其处理掉,以确保不会发生内存泄漏。我建议您在OnDestroy()挂钩中执行此操作,MonoBehaviour
如下所示:
private void OnDestroy()
{
foreach(var source in cancelTokens)
{
source.Dispose();
}
}
编辑2:正如@Jack Mariani 在他的回答中提到的那样,CancellationTokenSources
在这种情况下,多重是矫枉过正的。Coroutine
它真正允许您做的就是对取消哪个进行更细粒度的控制。在这种情况下,您将一次性取消它们,所以是的,优化将是只创建其中一个。这里可以进行多种优化,但它们超出了这个问题的范围。我没有将它们包括在内,因为我觉得它会使这个答案过于膨胀。
但是,我会争论他关于CancellationToken
“主要用于任务”的观点。直接从 MSDN文档的前几行中提取:
从 .NET Framework 4 开始,.NET Framework 使用统一模型来协作取消异步或长时间运行的同步操作。该模型基于称为取消令牌的轻量级对象
CancellationTokens
是轻量级的物体。在大多数情况下,它们只是简单Structs
地引用 a CancellationTokenSource
。他的回答中提到的“开销”可以忽略不计,在我看来,在考虑可读性和意图时完全值得。
您可以使用索引传递大量负载或使用文字booleans
订阅事件,这些方法将起作用。string
但代价是什么?令人困惑且难以阅读的代码?我会说不值得。
选择最终是你的。
推荐阅读
- angular - 更改垫子滑块的颜色
- .net - 功能
在剃刀页面上返回空字符串 - firebase - 我想知道如何在使用 firebase 运行应用程序时保持登录状态
- javascript - FullCalendar 在事件标题中显示 HTML
- php - 使用 dev_appserver.py 托管项目会为部署到 Google Cloud 提供不同的结果
- vagrant - 带有 Hyper-V 的 Vagrantbox 未启动
- css - 以 id 为目标的元素内的某处按 id 定位元素
- node.js - 如何在 cron 作业中设置 UTC 时区
- python - 从列表 Python 中删除后缀
- algorithmic-trading - 将 mql4 EA 转换为 mql5