unity3d 等待Unity协同程序内的事件?

2uluyalo  于 2022-12-19  发布在  其他
关注(0)|答案(4)|浏览(151)

我有一个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
}

做这样的事情最好的方法是什么?
谢谢!

11dmarpk

11dmarpk1#

没有内置的直接方法来等待事件本身,但是可以使用同步嵌套协程来等待事件设置的标志:

//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);
}
ars1skjm

ars1skjm2#

我认为一个更好的方法是检查服务器的每一帧,而不是等待大量的时间没有任何思考。

public IEnumerator DoStuff()
{
     /* wait until we have the value we want */
     while( value != desiredValue)
     yield return null
     //after this loop, start the real processing
}

这是我的小把戏。

mbzjlibv

mbzjlibv3#

我直接在需要的地方使用WaitUntil,但是我发现WaitUntil不需要布尔值,而是需要布尔 predicate 函数--所以我在下面的示例中提供了这个函数。
这个例子是真实的的运行代码从一个游戏。它运行在第一个场景,在那里我开始音乐之前,我这样做-略长-加载其他东西。

//         
     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();
        }
    }
wgeznvg7

wgeznvg74#

自旋锁是一种解决方案,但不是一个非常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),但您也可以轻松地自行设计,例如,当某个值发生变化时触发一个事件。如果您想要比自旋锁更好的解决方案,我们需要更多关于您在这里想要做出什么React的信息,但这应该会给予您一些想法。

相关问题