Я пытаюсь узнать, как работает асинхронное программирование в Unity, используя библиотеку UniTask.
Допустим, у нас есть этот код:
private async UniTask LoadGameAsync()
{
//this should be the right way to await the scene loading
await LoadSceneAsync("Level_01");
Debug.Log("Level 01 finished loading.");
//does this work?
//I'm uncertain if this is correct because I await a non async method. But then again it returns a task ... No compiler error.
await LoadScene("Level_02");
Debug.Log("Level 02 finished loading.");
//why all this?
//because I want to load multiple scenes simultaneously and only when all are finished loading, hide a loading screen.
//Does this work the way I intend to or are there pitfalls to avoid? If so, what's the correct way of implementing something like this?
UniTask level03Task = LoadScene("Level_03");
UniTask level03UITask = LoadScene("Level_03_UI");
await UniTask.WhenAll(level03Task, level03UITask);
Debug.Log("Level 03 and its UI finished loading, hide loading screen now.");
}
private async UniTask LoadSceneAsync(string sceneName)
{
await SceneManager.LoadSceneAsync(sceneName).ToUniTask();
}
private UniTask LoadScene(string sceneName)
{
return SceneManager.LoadSceneAsync(sceneName).ToUniTask();
}
Комментарии в коде в основном суммируют мой вопрос. Можно ли вернуть UniTask из метода, а затем дождаться его? Это почему-то кажется неправильным, потому что метод по сути такой же, как и асинхронный, но... не асинхронный.
Если это не работает так, как задумано, каков будет правильный способ реализовать то, что я себе представляю?
Обратите внимание, что UniTask по сравнению с Task — это структура, а не класс. Не уверен, имеет ли это значение при создании задач для дальнейшего использования.
Спасибо!
Спасибо! Тем не менее мне интересно, то же самое происходит и с UniTask, потому что UniTask — это структура, а не класс.
Я не уверен, правильно ли это, потому что жду неасинхронного метода. Но опять же возвращает задачу... Никакой ошибки компилятора.
Вы ожидаете не методов, а ожидаемых объектов. Пока что вы ожидаете не метод, а объект, который он возвращает. Это могут быть Task
, UniTask
или любые другие объекты, совместимые с ключевым словом await
.
Под капотом async/await
реконструируется в конечный автомат без ключевых слов async/await
, а только с вызовом методов ожидаемых объектов с некоторыми вспомогательными объектами для его создания или чем-то еще. Вы можете попробовать проверить это и другие синтаксические сахара в SharpLab.
//why all this? //because I want to load multiple scenes simultaneously and only when all are finished loading, hide a loading screen. //Does this work the way I intend to or are there pitfalls to avoid? If so, what's the correct way of implementing something like this? UniTask level03Task = LoadScene("Level_03"); UniTask level03UITask = LoadScene("Level_03_UI"); await UniTask.WhenAll(level03Task, level03UITask); Debug.Log("Level 03 and its UI finished loading, hide loading screen now.");
Давайте разберемся с этим.
UniTask level03Task = LoadScene("Level_03");
UniTask level03UITask = LoadScene("Level_03_UI");
Эти две строки будут выполнены немедленно. Они запускают процесс загрузки сцен и создают дескрипторы (UniTask
), которые можно использовать для работы с этим процессом.
await UniTask.WhenAll(level03Task, level03UITask);
В этой строке две задачи используются для создания третьей, которая будет дескриптором их выполнения. И после того, как эта задача создана, она мгновенно await
редактируется. Таким образом, выполнение метода продолжится только после завершения ожидаемой задачи. И оно будет завершено после выполнения двух задач по загрузке сцены.
Пока что, если вы хотите отобразить любой пользовательский интерфейс загрузки, вам нужно показать его перед этой строкой и скрыть после этой строки.
Обратите внимание, что UniTask по сравнению с Task — это структура, а не класс. Не уверен, имеет ли это значение при создании задач для дальнейшего использования.
В общем, UniTask
и Task
имеют гораздо более глубокие различия, помимо типов struct
и class
. В большинстве случаев вы не заметите никаких различий между ними при работе с Unity API. Возможно, я могу отметить, что UniTask
по умолчанию выполняются внутри основного цикла Unity, а Task
по умолчанию выполняются в отдельном потоке, что может привести к проблемам с доступом к некоторым API Unity.
Спасибо за такой подробный и развернутый ответ! Итак, подведем итог: мой код работает так, как задумано, верно?
@MeisterderMagie, верно.
Связанный материал: Зачем использовать async и return await, если можно вернуть Task<T> напрямую?