[英]Waiting for event inside Unity Coroutine?
所以我有一個 Unity 協程方法,其中有一些對象。 這些對象表示從某處服務器收集的值,並在准備就緒時發送Updated
事件。
我想知道最好的方法是在 Unity 的協程中等待更新所有值。
public IEnumerator DoStuff()
{
foreach(var val in _updateableValues)
{
if (val.Value != null) continue;
else **wait for val.Updated to be fired then continue**
}
//... here I do stuff with all the values
// I use the WWW class here, so that's why it's a coroutine
}
做這樣的事情最好的方法是什么?
謝謝!
沒有內置的直接方法來等待事件本身,但您可以使用同步嵌套協程來等待事件設置的標志:
//Flag
bool eventHappened;
//Event subscriber that sets the flag
void OnEvent(){
eventHappened=true;
}
//Coroutine that waits until the flag is set
IEnumerator WaitForEvent() {
yield return new WaitUntil(eventHappened);
eventHappened=false;
}
//Main coroutine, that stops and waits somewhere within it's execution
IEnumerator MainCoroutine(){
//Other stuff...
yield return StartCoroutine(WaitForEvent());
//Oher stuff...
}
考慮到這一點,創建一個等待UnityEvent
的通用協程很容易:
private IEnumerator WaitUntilEvent(UnityEvent unityEvent) {
var trigger = false;
Action action = () => trigger = true;
unityEvent.AddListener(action.Invoke);
yield return new WaitUntil(()=>trigger);
unityEvent.RemoveListener(action.Invoke);
}
我認為更好的方法是每幀檢查服務器,而不是不假思索地等待一段時間。
public IEnumerator DoStuff()
{
/* wait until we have the value we want */
while( value != desiredValue)
yield return null
//after this loop, start the real processing
}
這是我的小修復。
為什么有一個包含一行代碼的協程? 我直接在需要的地方使用 WaitUntil。 不過我發現,WaitUntil 不需要布爾值,但需要一個布爾謂詞函數 - 所以我在下面的示例中提供了這個。
該示例是來自游戲的真實運行代碼。 它在第一個場景中運行,在我開始之前開始播放音樂 - 稍微長一點 - 加載其他東西。
//
public class AudioSceneInitializer : MonoBehaviour
{
[SerializeField] private GlobalEventsSO globalEvents;
//globalEvents is a scriptable object that - in my case - holds all events in the game
[SerializeField] private AudioManager audioManager;
//The audio manager which in my case will raise the audio ready event
[SerializeField] public GameObject audioGO;// contains AudioSources used by AudioManager
private bool audioReady = false;
// Start is called before the first frame update
IEnumerator Start()
{
if (audioManager != null)
//better check, if we don't produce the event, we wait forever
{
//subscribe to event
globalEvents.audioManagerReadyEvent += OnAudioReady;
//do something to raise the event - else we wait forever
audioManager.onInitialization(this);//"this" needed to hand over the audioGO to the AudioManager
// waits until the flag is set;
yield return new WaitUntil(IsAudioReady);
}
//now that we have music playing, we load the actual game
SceneManager.LoadSceneAsync(1, LoadSceneMode.Additive);
}
//Event subscriber that sets the flag
void OnAudioReady()
{
//unsubscribing, if - as in my case - it happens only once in the game
globalEvents.audioManagerReadyEvent -= OnAudioReady;
audioReady = true;
}
//predicate function that WaitUntil expects
private bool IsAudioReady()
{
return audioReady;
}
public void OnDisable()
{
audioManager.onCleanup();
}
}
自旋鎖是一種解決方案,但不是 CPU 非常溫和的解決方案。 在自旋鎖中,您只需等待變量具有特定值,否則會休眠幾毫秒。
public IEnumerator DoStuff()
{
/* wait until we have the value we want */
while( value != desiredValue)
yield return new WaitForSeconds(0.001f);
//after this loop, start the real processing
}
也許您可能想考慮重構您的代碼,以便不需要自旋鎖,但可以實現更多基於中斷/事件的方法。 這意味着,如果您更新了一個值並且在它發生之后必須發生某些事情,請在更改該值后直接啟動它。 在 C# 中,甚至有一個用於該設計模式的接口INotifyPropertyChanged
(請參閱MSDN ),但您也可以輕松地自己設計它,例如,通過在特定值更改時觸發事件。 如果你想要一個比自旋鎖更好的解決方案,我們需要更多關於你到底想在這里做出什么反應的信息,但這應該會給你一些想法。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.