Если вы на старте игры качаете более чем 100 файлов, то есть один забавный способ ускорить загрузку до 30%.
Контента в игре у вас много, а размер билда должен быть <150МБ.
Потому часть вы запаковали в 100 Asset Bundle'ов.
А на старте игры качаете их из CDN через Unity Web Request.
В коде это выглядит примерно так:
IEnumerator DownloadAssetBundle(string url, Action<byte[]> onSuccess)
{
using var request = UnityWebRequestAssetBundle.GetAssetBundle(url);
var op = request.SendWebRequest();
while (!op.isDone)
yield return null;
onSuccess?.Invoke(request.downloadHandler.data);
}
Уверен что у каждого в проекте есть похожий кусок.
Есть идеи как его ускорить? 😬
Ответ:
Application.targetFrameRate = 120
Дисклеймер:
Как и всегда, есть нюансы. Перед тем как применять подобное у себя, тщательно изучите ограничения платформ для вашего проекта.
🔸Как так получается?
— Если вы знаете что такое Synchronization Context, то возможно, вы уже догадываетесь в чем дело.
Это специальный класс в dotnet, который представляет из себя очередь delegate'ов на MoveNext (машин состояний), которые будут вызваны в момент когда наше приложение готово продолжить выполнение асинхронного кода.
В unity синхронизация вызывается в UnityEngine.PlayerLoop.Update.ScriptRunDelayedTasks из нативной части, путем вызова статического метода UnitySynchronizationContext.ExecuteTasks.
🔸Или проще говоря:
— Все продолжения ваших асинхронных операций длиннее одного кадра будут вызваны на следующий кадр в
Update.ScriptRunDelayedTasks фазе.🔸Риторический вопрос:
Действительно ли время загрузки файлов по сети привязано к длине кадра?
— Вообще нет. Установление соединения и скачивание идет в другом потоке в нативной части.
Но вот результаты и обновление состояний синхронизированы с основным потоком и происходят в UnityEngine.PlayerLoop.EarlyUpdate.UnityWebRequestUpdate.
🔸Это значит:
Что если файл качается 45ms, а ваша игра работает при 30FPS, то ~15ms данные будут ждать синхронизации.
Того самого вызова MoveNext для вашего метода
DownloadAssetBundle.Т.е. данные в памяти в нативной части будут через 45ms, но у себя вы их получите только через 66ms.
И буст (местами до 30%) можно получить как раз за счет снижения времени "простоя" данных в ожидании вызова нужной фазы PlayerLoop'а.
Это тот забавный случай, когда магическая строчка в одном месте почему-то дает ощутимый результат 🤣
Так что, как идею, берите на вооружение увеличение FPS на время загрузки и возвращение его в нормальное значение после 😅
❗️Но блин, ребят, это не silver bullet. Проверяйте последствия на своих проектах.
На слабых девайсах это может только сделать хуже.
Если интересно копнуть больше в эту тему, рекомендую посмотреть видео с конференции CLRium.
CLRium #6: Контексты синхронизации (SynchronizationContext)
У меня огромная куча тем по архитектуре которым хочется с вами поделиться, но пока я сильно загружен ведением курса.
Статьи пока выходят редко, но блог не заброшен, вернусь совсем скоро как закончу курс 🫡
Ставь 👍 если тебе по кайфу такая движуха.
#будни@UniArchitect